summaryrefslogtreecommitdiffstats
path: root/tinyDAV
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV')
-rw-r--r--tinyDAV/droid-makefile137
-rw-r--r--tinyDAV/include/tinydav.h43
-rw-r--r--tinyDAV/include/tinydav/audio/coreaudio/tdav_consumer_coreaudio.h64
-rw-r--r--tinyDAV/include/tinydav/audio/coreaudio/tdav_producer_coreaudio.h42
-rw-r--r--tinyDAV/include/tinydav/audio/directsound/tdav_consumer_dsound.h67
-rw-r--r--tinyDAV/include/tinydav/audio/directsound/tdav_producer_dsound.h66
-rw-r--r--tinyDAV/include/tinydav/audio/tdav_consumer_audio.h83
-rw-r--r--tinyDAV/include/tinydav/audio/tdav_jitterbuffer.h329
-rw-r--r--tinyDAV/include/tinydav/audio/tdav_producer_audio.h68
-rw-r--r--tinyDAV/include/tinydav/audio/tdav_session_audio.h98
-rw-r--r--tinyDAV/include/tinydav/audio/tdav_speex_denoise.h64
-rw-r--r--tinyDAV/include/tinydav/audio/waveapi/tdav_consumer_waveapi.h68
-rw-r--r--tinyDAV/include/tinydav/audio/waveapi/tdav_producer_waveapi.h68
-rw-r--r--tinyDAV/include/tinydav/codecs/amr/tdav_codec_amr.h95
-rw-r--r--tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv16.h67
-rw-r--r--tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv32.h0
-rw-r--r--tinyDAV/include/tinydav/codecs/dtmf/tdav_codec_dtmf.h49
-rw-r--r--tinyDAV/include/tinydav/codecs/g711/g711.h44
-rw-r--r--tinyDAV/include/tinydav/codecs/g711/tdav_codec_g711.h59
-rw-r--r--tinyDAV/include/tinydav/codecs/g729/tdav_codec_g729.h80
-rw-r--r--tinyDAV/include/tinydav/codecs/gsm/tdav_codec_gsm.h59
-rw-r--r--tinyDAV/include/tinydav/codecs/h261/tdav_codec_h261.h84
-rw-r--r--tinyDAV/include/tinydav/codecs/h263/tdav_codec_h263.h116
-rw-r--r--tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264.h105
-rw-r--r--tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264_rtp.h123
-rw-r--r--tinyDAV/include/tinydav/codecs/ilbc/tdav_codec_ilbc.h64
-rw-r--r--tinyDAV/include/tinydav/codecs/mp4ves/tdav_codec_mp4ves.h85
-rw-r--r--tinyDAV/include/tinydav/codecs/msrp/tdav_codec_msrp.h51
-rw-r--r--tinyDAV/include/tinydav/codecs/speex/tdav_codec_speex.h79
-rw-r--r--tinyDAV/include/tinydav/codecs/theora/tdav_codec_theora.h90
-rw-r--r--tinyDAV/include/tinydav/msrp/tdav_consumer_msrp.h51
-rw-r--r--tinyDAV/include/tinydav/msrp/tdav_producer_msrp.h41
-rw-r--r--tinyDAV/include/tinydav/msrp/tdav_session_msrp.h104
-rw-r--r--tinyDAV/include/tinydav/tdav.h80
-rw-r--r--tinyDAV/include/tinydav/tdav_win32.h46
-rw-r--r--tinyDAV/include/tinydav/video/tdav_converter_video.h100
-rw-r--r--tinyDAV/include/tinydav/video/tdav_runnable_video.h54
-rw-r--r--tinyDAV/include/tinydav/video/tdav_session_video.h105
-rw-r--r--tinyDAV/include/tinydav_config.h80
-rw-r--r--tinyDAV/src/audio/coreaudio/tdav_consumer_coreaudio.c269
-rw-r--r--tinyDAV/src/audio/coreaudio/tdav_producer_coreaudio.c229
-rw-r--r--tinyDAV/src/audio/directsound/tdav_consumer_dsound.c377
-rw-r--r--tinyDAV/src/audio/directsound/tdav_producer_dsound.c320
-rw-r--r--tinyDAV/src/audio/tdav_consumer_audio.c311
-rw-r--r--tinyDAV/src/audio/tdav_jitterbuffer.c1034
-rw-r--r--tinyDAV/src/audio/tdav_producer_audio.c95
-rw-r--r--tinyDAV/src/audio/tdav_session_audio.c892
-rw-r--r--tinyDAV/src/audio/tdav_speex_denoise.c221
-rw-r--r--tinyDAV/src/audio/waveapi/tdav_consumer_waveapi.c406
-rw-r--r--tinyDAV/src/audio/waveapi/tdav_producer_waveapi.c393
-rw-r--r--tinyDAV/src/codecs/amr/tdav_codec_amr.c810
-rw-r--r--tinyDAV/src/codecs/bv/tdav_codec_bv16.c249
-rw-r--r--tinyDAV/src/codecs/bv/tdav_codec_bv32.c0
-rw-r--r--tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c123
-rw-r--r--tinyDAV/src/codecs/g711/g711.c295
-rw-r--r--tinyDAV/src/codecs/g711/tdav_codec_g711.c304
-rw-r--r--tinyDAV/src/codecs/g729/tdav_codec_g729.c454
-rw-r--r--tinyDAV/src/codecs/gsm/tdav_codec_gsm.c208
-rw-r--r--tinyDAV/src/codecs/h261/tdav_codec_h261.c539
-rw-r--r--tinyDAV/src/codecs/h263/tdav_codec_h263.c1230
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264.c871
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c356
-rw-r--r--tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c256
-rw-r--r--tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c634
-rw-r--r--tinyDAV/src/codecs/msrp/tdav_codec_msrp.c110
-rw-r--r--tinyDAV/src/codecs/speex/tdav_codec_speex.c282
-rw-r--r--tinyDAV/src/codecs/theora/tdav_codec_theora.c707
-rw-r--r--tinyDAV/src/msrp/tdav_consumer_msrp.c0
-rw-r--r--tinyDAV/src/msrp/tdav_producer_msrp.c0
-rw-r--r--tinyDAV/src/msrp/tdav_session_msrp.c946
-rw-r--r--tinyDAV/src/tdav.c419
-rw-r--r--tinyDAV/src/tdav_win32.c58
-rw-r--r--tinyDAV/src/video/tdav_converter_video.c285
-rw-r--r--tinyDAV/src/video/tdav_runnable_video.c95
-rw-r--r--tinyDAV/src/video/tdav_session_video.c675
-rw-r--r--tinyDAV/test/test.c61
-rw-r--r--tinyDAV/test/test.vcproj201
-rw-r--r--tinyDAV/test/test_sessions.h217
-rw-r--r--tinyDAV/tinyDAV.sln184
-rw-r--r--tinyDAV/tinyDAV.vcproj710
80 files changed, 18934 insertions, 0 deletions
diff --git a/tinyDAV/droid-makefile b/tinyDAV/droid-makefile
new file mode 100644
index 0000000..6ace46b
--- /dev/null
+++ b/tinyDAV/droid-makefile
@@ -0,0 +1,137 @@
+APP := lib$(PROJECT)_$(MARCH).$(EXT)
+
+THIRDPARTIES_INC := ../thirdparties/android/include
+THIRDPARTIES_LIB := ../thirdparties/android/lib
+THIRDPARTIES_MARCH_LIB := ../thirdparties/android/lib/$(MARCH)
+
+FFMPEG_CFLAGS := -DHAVE_FFMPEG=1
+FFMPEG_LDFLAGS := -lavutil -lswscale -lavcore -lavcodec -lx264 -ltheora -logg
+
+OPENCORE_ARM_CFLAGS := -DHAVE_OPENCORE_AMR=1
+OPENCORE_ARM_LDFLAGS := -lopencore-amrnb
+
+ifeq ($(NEON), yes)
+ILBC_CFLAGS := -DHAVE_ILBC=1
+SPEEX_CFLAGS := -DHAVE_SPEEX_DSP=0 -DHAVE_LIB_SPEEX=1
+else
+ILBC_CFLAGS := -DHAVE_ILBC=0
+SPEEX_CFLAGS := -DHAVE_SPEEX_DSP=0 -DHAVE_LIB_SPEEX=0
+endif
+ILBC_LDFLAGS := -liLBC
+SPEEX_LDFLAGS := -lspeexdsp -lspeex
+
+LIBGSM_CFLAGS := -DHAVE_LIBGSM=1
+LIBGSM_LDFLAGS := -lgsm
+
+BV16_CFLAGS := -DHAVE_BV16=0 -I$(THIRDPARTIES_INC)/BroadVoice16/bv16 -I$(THIRDPARTIES_INC)/BroadVoice16/bvcommon
+BV16_LDFLAGS := -lbv16
+
+ifeq ($(G729), yes)
+ G729_CFLAGS := -DHAVE_G729=1 -DG729_ENABLE_VAD=0 -D__unix__
+ G729_LDFLAGS := -lg729b_$(MARCH)
+
+ # Reset all other ITU CODECS
+ OPENCORE_ARM_CFLAGS := -DHAVE_OPENCORE_AMR=0
+ OPENCORE_ARM_LDFLAGS :=
+ BV16_CFLAGS := -DHAVE_BV16=0
+ BV16_LDFLAGS :=
+else
+ G729_CFLAGS := -DHAVE_G729=0
+ G729_LDFLAGS :=
+endif
+
+CFLAGS := $(CFLAGS_LIB) -I$(THIRDPARTIES_INC) $(ILBC_CFLAGS) $(LIBGSM_CFLAGS) \
+ $(FFMPEG_CFLAGS) $(SPEEX_CFLAGS) $(OPENCORE_ARM_CFLAGS) $(BV16_CFLAGS) $(G729_CFLAGS) \
+ -I../tinySAK/src -I../tinyNET/src -I../tinySDP/include -I../tinyRTP/include -I../tinyMEDIA/include -I../tinyMSRP/include -I./include \
+ -DJB_HISTORY_SIZE=500
+
+LDFLAGS := $(LDFLAGS_LIB) -L$(THIRDPARTIES_LIB) -L$(THIRDPARTIES_MARCH_LIB) \
+ $(FFMPEG_LDFLAGS) $(SPEEX_LDFLAGS) $(OPENCORE_ARM_LDFLAGS) $(ILBC_LDFLAGS) $(LIBGSM_LDFLAGS) $(BV16_LDFLAGS) $(G729_LDFLAGS)\
+ -ltinySAK_$(MARCH) -ltinyNET_$(MARCH) -ltinySDP_$(MARCH) -ltinyRTP_$(MARCH) -ltinyMEDIA_$(MARCH) -ltinyMSRP_$(MARCH) -lm -lgcc
+
+all: $(APP)
+
+OBJS = \
+ src/tdav.o
+
+ ### audio
+OBJS += src/audio/tdav_consumer_audio.o \
+ src/audio/tdav_jitterbuffer.o \
+ src/audio/tdav_producer_audio.o \
+ src/audio/tdav_session_audio.o \
+ src/audio/tdav_speex_denoise.o
+
+ ### video
+OBJS += src/video/tdav_converter_video.o \
+ src/video/tdav_runnable_video.o \
+ src/video/tdav_session_video.o
+
+ ### msrp
+OBJS += src/msrp/tdav_consumer_msrp.o \
+ src/msrp/tdav_producer_msrp.o \
+ src/msrp/tdav_session_msrp.o
+
+
+ ### codecs (AMR)
+OBJS += src/codecs/amr/tdav_codec_amr.o
+
+ ### codecs (G.711)
+OBJS += src/codecs/g711/g711.o \
+ src/codecs/g711/tdav_codec_g711.o
+
+ ### codecs (G.729)
+OBJS += src/codecs/g729/tdav_codec_g729.o
+
+ ### codecs (GSM)
+OBJS += src/codecs/gsm/tdav_codec_gsm.o
+
+ ### codecs (BV16)
+OBJS += src/codecs/bv/tdav_codec_bv16.o \
+ src/codecs/bv/tdav_codec_bv32.o
+
+ ### codecs (iLBC)
+OBJS += src/codecs/ilbc/tdav_codec_ilbc.o
+
+ ### codecs (Speex)
+OBJS += src/codecs/speex/tdav_codec_speex.o
+
+ ### codecs (DTMF)
+OBJS += src/codecs/dtmf/tdav_codec_dtmf.o
+
+ ### codecs (H.261)
+OBJS += src/codecs/h261/tdav_codec_h261.o
+
+ ### codecs (H.263)
+OBJS += src/codecs/h263/tdav_codec_h263.o
+
+ ### codecs (H.264)
+OBJS += src/codecs/h264/tdav_codec_h264.o \
+ src/codecs/h264/tdav_codec_h264_rtp.o
+
+ ### codecs (Theora)
+OBJS += src/codecs/theora/tdav_codec_theora.o
+
+ ### codecs (MP4V-ES)
+OBJS += src/codecs/mp4ves/tdav_codec_mp4ves.o
+
+ ### codecs (MSRP)
+OBJS += src/codecs/msrp/tdav_codec_msrp.o
+
+
+$(APP): $(OBJS)
+ifeq ($(EXT), a)
+ $(AR) rcs $@ $^
+else
+ $(CC) $(LDFLAGS) -o $@ $^
+endif
+
+%.o: %.c
+ $(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@
+
+install: $(APP)
+ $(ANDROID_SDK_ROOT)/tools/adb remount
+ $(ANDROID_SDK_ROOT)/tools/adb push $(APP) $(LIB_DIR)/$(APP)
+ $(ANDROID_SDK_ROOT)/tools/adb shell chmod 777 $(LIB_DIR)/$(APP)
+
+clean:
+ @rm -f $(OBJS) $(APP) \ No newline at end of file
diff --git a/tinyDAV/include/tinydav.h b/tinyDAV/include/tinydav.h
new file mode 100644
index 0000000..964c30c
--- /dev/null
+++ b/tinyDAV/include/tinydav.h
@@ -0,0 +1,43 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tinydav.h
+ * @brief tinyDAV API.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYMEDIA_TINYDAV_H
+#define TINYMEDIA_TINYDAV_H
+
+#include "tinydav/tdav.h"
+
+// sessions
+#include "tinydav/audio/tdav_session_audio.h"
+// codecs
+#include "tinydav/codecs/g711/tdav_codec_g711.h"
+
+/* == tinyMEDIA == */
+#include "tinymedia.h"
+
+#endif /* TINYMEDIA_TINYDAV_H */
diff --git a/tinyDAV/include/tinydav/audio/coreaudio/tdav_consumer_coreaudio.h b/tinyDAV/include/tinydav/audio/coreaudio/tdav_consumer_coreaudio.h
new file mode 100644
index 0000000..b79d1fd
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/coreaudio/tdav_consumer_coreaudio.h
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_coreaudio.h
+ * @brief Audio Consumer for MacOSX and iOS platforms.
+ *
+ * @author Laurent Etiemble <laurent.etiemble(at)gmail.com>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 letiemble
+ */
+#ifndef TINYDAV_CONSUMER_COREAUDIO_H
+#define TINYDAV_CONSUMER_COREAUDIO_H
+
+#include "tinydav_config.h"
+
+#if HAVE_COREAUDIO
+
+#include <AudioToolbox/AudioToolbox.h>
+#include "tinydav/audio/tdav_consumer_audio.h"
+
+TDAV_BEGIN_DECLS
+
+#define CoreAudioPlayBuffers 3
+
+typedef struct tdav_consumer_coreaudio_s
+{
+ TDAV_DECLARE_CONSUMER_AUDIO;
+
+ tsk_bool_t started;
+
+ AudioStreamBasicDescription description;
+ AudioQueueRef queue;
+ AudioQueueBufferRef buffers[CoreAudioPlayBuffers];
+
+ tsk_size_t buffer_size;
+}
+tdav_consumer_coreaudio_t;
+
+TINYDAV_GEXTERN const tmedia_consumer_plugin_def_t *tdav_consumer_coreaudio_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_COREAUDIO */
+
+#endif /* TINYDAV_CONSUMER_COREAUDIO_H */
diff --git a/tinyDAV/include/tinydav/audio/coreaudio/tdav_producer_coreaudio.h b/tinyDAV/include/tinydav/audio/coreaudio/tdav_producer_coreaudio.h
new file mode 100644
index 0000000..9a30242
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/coreaudio/tdav_producer_coreaudio.h
@@ -0,0 +1,42 @@
+/**@file tdav_consumer_coreaudio.h
+ * @brief Audio Producer for MacOSX and iOS platforms.
+ *
+ * @author Laurent Etiemble <laurent.etiemble(at)gmail.com>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 letiemble
+ */
+#ifndef TINYDAV_PRODUCER_COREAUDIO_H
+#define TINYDAV_PRODUCER_COREAUDIO_H
+
+#include "tinydav_config.h"
+
+#if HAVE_COREAUDIO
+
+#include <AudioToolbox/AudioToolbox.h>
+#include "tinydav/audio/tdav_producer_audio.h"
+
+TDAV_BEGIN_DECLS
+
+#define CoreAudioRecordBuffers 3
+
+typedef struct tdav_producer_coreaudio_s
+{
+ TDAV_DECLARE_PRODUCER_AUDIO;
+
+ tsk_bool_t started;
+
+ AudioStreamBasicDescription description;
+ AudioQueueRef queue;
+ AudioQueueBufferRef buffers[CoreAudioRecordBuffers];
+
+ tsk_size_t buffer_size;
+}
+tdav_producer_coreaudio_t;
+
+TINYDAV_GEXTERN const tmedia_producer_plugin_def_t *tdav_producer_coreaudio_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_COREAUDIO */
+
+#endif /* TINYDAV_PRODUCER_COREAUDIO_H */
diff --git a/tinyDAV/include/tinydav/audio/directsound/tdav_consumer_dsound.h b/tinyDAV/include/tinydav/audio/directsound/tdav_consumer_dsound.h
new file mode 100644
index 0000000..5dfec92
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/directsound/tdav_consumer_dsound.h
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_dsound.h
+ * @brief Microsoft DirectSound consumer.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CONSUMER_DSOUND_H
+#define TINYDAV_CONSUMER_DSOUND_H
+
+#include "tinydav_config.h"
+
+#if HAVE_DSOUND_H
+
+#include "tinydav/audio/tdav_consumer_audio.h"
+
+#include <dsound.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT 4
+
+typedef struct tdav_consumer_dsound_s
+{
+ TDAV_DECLARE_CONSUMER_AUDIO;
+
+ tsk_bool_t started;
+ tsk_size_t bytes_per_notif;
+ void* tid[1];
+
+ LPDIRECTSOUND device;
+ LPDIRECTSOUNDBUFFER primaryBuffer;
+ LPDIRECTSOUNDBUFFER secondaryBuffer;
+ HANDLE notifEvents[TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT];
+}
+tdav_consumer_dsound_t;
+
+TINYDAV_GEXTERN const tmedia_consumer_plugin_def_t *tdav_consumer_dsound_plugin_def_t;
+
+
+TDAV_END_DECLS
+
+#endif /* HAVE_DSOUND_H */
+
+#endif /* TINYDAV_CONSUMER_DSOUND_H */
diff --git a/tinyDAV/include/tinydav/audio/directsound/tdav_producer_dsound.h b/tinyDAV/include/tinydav/audio/directsound/tdav_producer_dsound.h
new file mode 100644
index 0000000..c2b78b9
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/directsound/tdav_producer_dsound.h
@@ -0,0 +1,66 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_dsound.h
+ * @brief Microsoft DirectSound producer.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_PRODUCER_DSOUND_H
+#define TINYDAV_PRODUCER_DSOUND_H
+
+#include "tinydav_config.h"
+
+#if HAVE_DSOUND_H
+
+#include "tinydav/audio/tdav_producer_audio.h"
+
+#include <dsound.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_DSOUNS_PRODUCER_NOTIF_POS_COUNT 4
+
+typedef struct tdav_producer_dsound_s
+{
+ TDAV_DECLARE_PRODUCER_AUDIO;
+
+ tsk_bool_t started;
+ tsk_size_t bytes_per_notif;
+ void* tid[1];
+
+ LPDIRECTSOUNDCAPTURE device;
+ LPDIRECTSOUNDCAPTUREBUFFER captureBuffer;
+ HANDLE notifEvents[TDAV_DSOUNS_PRODUCER_NOTIF_POS_COUNT];
+}
+tdav_producer_dsound_t;
+
+TINYDAV_GEXTERN const tmedia_producer_plugin_def_t *tdav_producer_dsound_plugin_def_t;
+
+
+TDAV_END_DECLS
+
+#endif /* HAVE_DSOUND_H */
+
+#endif /* TINYDAV_PRODUCER_DSOUND_H */
diff --git a/tinyDAV/include/tinydav/audio/tdav_consumer_audio.h b/tinyDAV/include/tinydav/audio/tdav_consumer_audio.h
new file mode 100644
index 0000000..91d3518
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/tdav_consumer_audio.h
@@ -0,0 +1,83 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_audio.h
+ * @brief Base class for all Audio consumers.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CONSUMER_AUDIO_H
+#define TINYDAV_CONSUMER_AUDIO_H
+
+#include "tinydav_config.h"
+
+#include "tinymedia/tmedia_consumer.h"
+
+#include "tinydav/audio/tdav_jitterbuffer.h"
+
+#include "tsk_safeobj.h"
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_CONSUMER_AUDIO(self) ((tdav_consumer_audio_t*)(self))
+
+typedef struct tdav_consumer_audio_s
+{
+ TMEDIA_DECLARE_CONSUMER;
+
+ uint8_t channels;
+ uint32_t rate;
+ uint8_t bits_per_sample;
+ uint8_t ptime;
+
+ struct{
+ jitterbuffer *jbuffer;
+ uint8_t jcodec;
+ uint64_t ref_timestamp;
+ } jb;
+
+ struct tmedia_denoise_s* denoise;
+
+ TSK_DECLARE_SAFEOBJ;
+}
+tdav_consumer_audio_t;
+
+TINYDAV_API int tdav_consumer_audio_init(tdav_consumer_audio_t* self);
+TINYDAV_API int tdav_consumer_audio_cmp(const tsk_object_t* consumer1, const tsk_object_t* consumer2);
+#define tdav_consumer_audio_prepare(self, codec) tmedia_consumer_prepare(TDAV_CONSUMER_AUDIO(self), codec)
+#define tdav_consumer_audio_start(self) tmedia_consumer_start(TDAV_CONSUMER_AUDIO(self))
+#define tdav_consumer_audio_consume(self, buffer, size) tmedia_consumer_consume(TDAV_CONSUMER_AUDIO(self), buffer, size)
+#define tdav_consumer_audio_pause(self) tmedia_consumer_pause(TDAV_CONSUMER_AUDIO(self))
+#define tdav_consumer_audio_stop(self) tmedia_consumer_stop(TDAV_CONSUMER_AUDIO(self))
+TINYDAV_API int tdav_consumer_audio_put(tdav_consumer_audio_t* self, void** data, tsk_size_t size, const tsk_object_t* proto_hdr);
+TINYDAV_API void* tdav_consumer_audio_get(tdav_consumer_audio_t* self, tsk_size_t* out_size);
+void tdav_consumer_audio_set_denoise(tdav_consumer_audio_t* self, struct tmedia_denoise_s* denoise);
+TINYDAV_API int tdav_consumer_audio_reset(tdav_consumer_audio_t* self);
+TINYDAV_API int tdav_consumer_audio_deinit(tdav_consumer_audio_t* self);
+
+#define TDAV_DECLARE_CONSUMER_AUDIO tdav_consumer_audio_t __consumer_audio__
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CONSUMER_AUDIO_H */
diff --git a/tinyDAV/include/tinydav/audio/tdav_jitterbuffer.h b/tinyDAV/include/tinydav/audio/tdav_jitterbuffer.h
new file mode 100644
index 0000000..0d8aacc
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/tdav_jitterbuffer.h
@@ -0,0 +1,329 @@
+/* File from: http://cms.speakup.nl/tech/opensource/jitterbuffer/verslag-20051209.pdf/ */
+
+/*******************************************************
+ * jitterbuffer:
+ * an application-independent jitterbuffer, which tries
+ * to achieve the maximum user perception during a call.
+ * For more information look at:
+ * http://www.speakup.nl/opensource/jitterbuffer/
+ *
+ * Copyright on this file is held by:
+ * - Jesse Kaijen <jesse@speakup.nl>
+ * - SpeakUp <info@speakup.nl>
+ *
+ * Contributors:
+ * Jesse Kaijen <jesse@speakup.nl>
+ *
+ * Version: 1.1
+ *
+ * Changelog:
+* 1.0 => 1.1 (2006-03-24) (thanks to Micheal Jerris, freeswitch.org)
+ * - added MSVC 2005 project files
+ * - added JB_NOJB as return value
+ *
+ *
+ * This program is free software, distributed under the terms of:
+ * - the GNU Lesser (Library) General Public License
+ * - the Mozilla Public License
+ *
+ * if you are interested in an different licence type, please contact us.
+ *
+ * How to use the jitterbuffer, please look at the comments
+ * in the headerfile.
+ *
+ * Further details on specific implementations,
+ * please look at the comments in the code file.
+ */
+
+#ifndef TINYDAV_JITTERBUFFER_H_
+#define TINYDAV_JITTERBUFFER_H_
+
+#include "tinydav_config.h"
+
+TDAV_BEGIN_DECLS
+
+/***********
+ * The header file consists of four parts.
+ * - configuration constants, structs and parameter definitions
+ * - functions
+ * - How to use the jitterbuffer and
+ * which responsibilities do YOU have
+ * - debug messages explained
+ */
+
+
+// configuration constants
+/* Number of historical timestamps to use in calculating jitter and jitterbuffer size */
+#ifndef JB_HISTORY_SIZE
+# define JB_HISTORY_SIZE 500
+#endif
+
+/* minimum jitterbuffer size, disabled if 0 */
+#define JB_MIN_SIZE 0
+/* maximum jitterbuffer size, disabled if 0 */
+#define JB_MAX_SIZE 0
+ /* maximum successive interpolating frames, disabled if 0 */
+#define JB_MAX_SUCCESSIVE_INTERP 0
+/* amount of extra delay allowed before shrinking */
+#define JB_ALLOW_EXTRA_DELAY 30
+/* ms between growing */
+#define JB_WAIT_GROW 60
+/* ms between shrinking */
+#define JB_WAIT_SHRINK 250
+/* ms that the JB max may be off */
+#define JB_MAX_DIFF 6000 //in a RTP stream the max_diff may be 3000 packets (most packets are 20ms)
+
+//structs
+typedef struct jb_info {
+ long frames_received; /* Number of frames received by the jitterbuffer */
+ long frames_late; /* Number of frames that were late */
+ long frames_lost; /* Number of frames that were lost */
+ long frames_ooo; /* Number of frames that were Out Of Order */
+ long frames_dropped; /* Number of frames that were dropped due shrinkage of the jitterbuffer */
+ long frames_dropped_twice; /* Number of frames that were dropped because this timestamp was already in the jitterbuffer */
+
+ long delay; /* Current delay due the jitterbuffer */
+ long jitter; /* jitter measured within current history interval*/
+ long losspct; /* recent lost frame percentage (network and jitterbuffer loss) */
+
+ long delay_target; /* The delay where we want to grow to */
+ long losspct_jb; /* recent lost percentage due the jitterbuffer */
+ long last_voice_ms; /* the duration of the last voice frame */
+ short silence; /* If we are in silence 1-yes 0-no */
+ long iqr; /* Inter Quartile Range of current history, if the squareroot is taken it is a good estimate of jitter */
+} jb_info;
+
+typedef struct jb_frame {
+ void *data; /* the frame data */
+ long ts; /* the senders timestamp */
+ long ms; /* length of this frame in ms */
+ int type; /* the type of frame */
+ int codec; /* codec of this frame, undefined if nonvoice */
+ struct jb_frame *next, *prev; /* pointers to the next and previous frames in the queue */
+} jb_frame;
+
+typedef struct jb_hist_element {
+ long delay; /* difference between time of arrival and senders timestamp */
+ long ts; /* senders timestamp */
+ long ms; /* length of this frame in ms */
+ int codec; /* wich codec this frame has */
+} jb_hist_element;
+
+typedef struct jb_settings {
+ /* settings */
+ long min_jb; /* defines a hard clamp to use in setting the jitterbuffer delay */
+ long max_jb; /* defines a hard clamp to use in setting the jitterbuffer delay */
+ long max_successive_interp; /* the maximum count of successive interpolations before assuming silence */
+ long extra_delay; /* amount of extra delay allowed before shrinking */
+ long wait_grow; /* ms between growing */
+ long wait_shrink; /* ms between shrinking */
+ long max_diff; /* maximum number of milliseconds the jitterbuffer may be off */
+} jb_settings;
+
+typedef struct jitterbuffer {
+ struct jb_hist_element hist[JB_HISTORY_SIZE]; /* the history of the last received frames */
+ long hist_sorted_delay[JB_HISTORY_SIZE]; /* a sorted buffer of the delays (lowest first) */
+ long hist_sorted_timestamp[JB_HISTORY_SIZE]; /* a sorted buffer of the timestamps (lowest first) */
+
+ int hist_pointer; /* points to index in history for next entry */
+ long last_adjustment; /* the time of the last adjustment (growing or shrinking) */
+ long next_voice_time; /* the next ts is to be read from the jb (senders timestamp) */
+ long cnt_successive_interp; /* the count of consecutive interpolation frames */
+ long silence_begin_ts; /* the time of the last CNG frame, when in silence */
+ long min; /* the clock difference within current history interval */
+ long current; /* the present jitterbuffer adjustment */
+ long target; /* the target jitterbuffer adjustment */
+ long last_delay; /* the delay of the last packet, used for calc. jitter */
+
+ jb_frame *voiceframes; /* queued voiceframes */
+ jb_frame *controlframes; /* queued controlframes */
+ jb_settings settings; /* the settings of the jitterbuffer */
+ jb_info info; /* the statistics of the jitterbuffer */
+} jitterbuffer;
+
+//parameter definitions
+/* return codes */
+#define JB_OK 0
+#define JB_EMPTY 1
+#define JB_NOFRAME 2
+#define JB_INTERP 3
+#define JB_NOJB 4
+
+
+/* frame types */
+#define JB_TYPE_CONTROL 1
+#define JB_TYPE_VOICE 2
+#define JB_TYPE_SILENCE 3
+
+/* the jitterbuffer behaives different for each codec. */
+/* Look in the code if a codec has his function defined */
+/* default is g711x behaiviour */
+#define JB_CODEC_SPEEX 10 //NOT defined
+#define JB_CODEC_ILBC 9 //NOT defined
+#define JB_CODEC_GSM_EFR 8
+#define JB_CODEC_GSM_FR 7 //NOT defined
+#define JB_CODEC_G723_1 6
+#define JB_CODEC_G729A 5
+#define JB_CODEC_G729 4
+#define JB_CODEC_G711x_PLC 3
+#define JB_CODEC_G711x 2
+#define JB_CODEC_OTHER 1 //NOT defined
+
+
+/*
+ * Creates a new jitterbuffer and sets the default settings.
+ * Always use this function for creating a new jitterbuffer.
+ */
+jitterbuffer *jb_new();
+
+/*
+ * The control frames and possible personal settings are kept.
+ * History and voice/silence frames are destroyed.
+ */
+void jb_reset(jitterbuffer *jb);
+
+/*
+ * Resets the jitterbuffer totally, all the control/voice/silence frames are destroyed
+ * default settings are put as well.
+ */
+void jb_reset_all(jitterbuffer *jb);
+
+/*
+ * Destroy the jitterbuffer and any frame within.
+ * Always use this function for destroying a jitterbuffer,
+ * otherwise there is a chance of memory leaking.
+ */
+void jb_destroy(jitterbuffer *jb);
+
+/*
+ * Define your own settings for the jitterbuffer. Only settings !=0
+ * are put in the jitterbuffer.
+ */
+void jb_set_settings(jitterbuffer *jb, jb_settings *settings);
+
+/*
+ * Get the statistics for the jitterbuffer.
+ * Copying the statistics directly for the jitterbuffer won't work because
+ * The statistics are only calculated when calling this function.
+ */
+void jb_get_info(jitterbuffer *jb, jb_info *stats);
+
+/*
+ * Get the current settings of the jitterbuffer.
+ */
+void jb_get_settings(jitterbuffer *jb, jb_settings *settings);
+
+/*
+ * Gives an estimation of the MOS of a call given the
+ * packetloss p, delay d, and wich codec is used.
+ * The assumption is made that the echo cancelation is around 37dB.
+ */
+float jb_guess_mos(float p, long d, int codec);
+
+/*
+ * returns JB_OK if there are still frames left in the jitterbuffer
+ * otherwise JB_EMPTY is returned.
+ */
+int jb_has_frames(jitterbuffer *jb);
+
+/*
+ * put a packet(frame) into the jitterbuffer.
+ * *data - points to the packet
+ * type - type of packet, JB_CONTROL|JB_VOICE|JB_SILENCE
+ * ms - duration of frame (only voice)
+ * ts - timestamp sender
+ * now - current timestamp (timestamp of arrival)
+ * codec - which codec the frame holds (only voice), if not defined, g711x will be used
+ *
+ * if type==control @REQUIRE: *data, type, ts, now
+ * if type==voice @REQUIRE: *data, type, ms, ts, now @OPTIONAL: codec
+ * if type==silence @REQUIRE: *data, type, ts, now
+ * on return *data is undefined
+ */
+void jb_put(jitterbuffer *jb, void *data, int type, long ms, long ts, long now, int codec);
+
+/*
+ * Get a packet from the jitterbuffer if it's available.
+ * control packets have a higher priority above voice and silence packets
+ * they are always delivered as fast as possible. The delay of the jitterbuffer
+ * doesn't work for these packets.
+ * @REQUIRE 1<interpl <= jb->settings->extra_delay (=default JB_ALLOW_EXTRA_DELAY)
+ *
+ * return will be:
+ * JB_OK, *data points to the packet
+ * JB_INTERP, please interpolate for interpl milliseconds
+ * JB_NOFRAME, no frame scheduled
+ * JB_EMPTY, the jitterbuffer is empty
+ */
+int jb_get(jitterbuffer *jb, void **data, long now, long interpl);
+
+/* debug functions */
+typedef void (*jb_output_function_t)(const char *fmt, ...);
+void jb_setoutput(jb_output_function_t warn, jb_output_function_t err, jb_output_function_t dbg);
+
+
+/*******************************
+ * The use of the jitterbuffer *
+ *******************************
+ * Always create a new jitterbuffer with jb_new().
+ * Always destroy a jitterbuffer with jb_destroy().
+ *
+ * There is no lock(mutex) mechanism, that your responsibility.
+ * The reason for this is that different environments require
+ * different ways of implementing a lock.
+ *
+ * The following functions require a lock on the jitterbuffer:
+ * jb_reset(), jb_reset_all(), jb_destroy(), jb_set_settings(),
+ * jb_get_info(), jb_get_settings(), jb_has_frames(), jb_put(),
+ * jb_get()
+ *
+ * The following functions do NOT require a lock on the jitterbuffer:
+ * jb_new(), jb_guess_mos()
+ *
+ * Since control packets have a higher priority above any other packet
+ * a call may already be ended while there is audio left to play. We
+ * advice that you poll the jitterbuffer if there are frames left.
+ *
+ * If the audiopath is oneway (eg. voicemailbox) and the latency doesn't
+ * matter, we advice to set a minimum jitterbuffer size. Then there is
+ * less loss and the quality is better.
+ */
+
+
+/****************************
+ * debug messages explained *
+ ****************************
+ * N - jb_new()
+ * R - jb_reset()
+ * r - jb_reset_all()
+ * D - jb_destroy()
+ * S - jb_set_settings()
+ * H - jb_has_frames()
+ * I - jb_get_info()
+ * S - jb_get_settings()
+ * pC - jb_put() put Control packet
+ * pT - jb_put() Timestamp was already in the queue
+ * pV - jb_put() put Voice packet
+ * pS - jb_put() put Silence packet
+ *
+ * A - jb_get()
+ * // below are all the possible debug info when trying to get a packet
+ * gC - get_control() - there is a control message
+ * gs - get_voice() - there is a silence frame
+ * gS - get_voice() - we are in silence
+ * gL - get_voice() - are in silence, frame is late
+ * gP - get_voice() - are in silence, play frame (end of silence)
+ * ag - get_voicecase() - grow little bit (diff < interpl/2)
+ * aG - get_voicecase() - grow interpl
+ * as - get_voicecase() - shrink by voiceframe we throw out
+ * aS - get_voicecase() - shrink by interpl
+ * aN - get_voicecase() - no time yet
+ * aL - get_voicecase() - frame is late
+ * aP - get_voicecase() - play frame
+ * aI - get_voicecase() - interpolate
+ */
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_JITTERBUFFER_H_ */
+
diff --git a/tinyDAV/include/tinydav/audio/tdav_producer_audio.h b/tinyDAV/include/tinydav/audio/tdav_producer_audio.h
new file mode 100644
index 0000000..f7faa54
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/tdav_producer_audio.h
@@ -0,0 +1,68 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_audio.h
+ * @brief Base class for all Audio producers.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_PRODUCER_AUDIO_H
+#define TINYDAV_PRODUCER_AUDIO_H
+
+#include "tinydav_config.h"
+
+#include "tinymedia/tmedia_producer.h"
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_BITS_PER_SAMPLE_DEFAULT 16
+
+#define TDAV_PRODUCER_AUDIO(self) ((tdav_producer_audio_t*)(self))
+
+typedef struct tdav_producer_audio_s
+{
+ TMEDIA_DECLARE_PRODUCER;
+
+ uint8_t channels;
+ uint32_t rate;
+ uint8_t bits_per_sample;
+ uint8_t ptime;
+}
+tdav_producer_audio_t;
+
+TINYDAV_API int tdav_producer_audio_init(tdav_producer_audio_t* self);
+TINYDAV_API int tdav_producer_audio_cmp(const tsk_object_t* producer1, const tsk_object_t* producer2);
+#define tdav_producer_audio_prepare(self, codec) tmedia_producer_prepare(TMEDIA_PRODUCER(self), codec)
+#define tmedia_producer_audio_set_callback(self, callback, callback_data) tmedia_producer_set_callback(TMEDIA_PRODUCER(self), callback, callback_data)
+#define tdav_producer_audio_start(self) tdav_producer_start(TMEDIA_PRODUCER(self))
+#define tdav_producer_audio_pause(self) tdav_producer_pause(TMEDIA_PRODUCER(self))
+#define tdav_producer_audio_stop(self) tdav_producer_stop(TMEDIA_PRODUCER(self))
+TINYDAV_API int tdav_producer_audio_deinit(tdav_producer_audio_t* self);
+
+#define TDAV_DECLARE_PRODUCER_AUDIO tdav_producer_audio_t __producer_audio__
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_PRODUCER_AUDIO_H */
+
diff --git a/tinyDAV/include/tinydav/audio/tdav_session_audio.h b/tinyDAV/include/tinydav/audio/tdav_session_audio.h
new file mode 100644
index 0000000..7af6643
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/tdav_session_audio.h
@@ -0,0 +1,98 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_session_audio.h
+ * @brief Audio Session plugin.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_SESSION_AUDIO_H
+#define TINYDAV_SESSION_AUDIO_H
+
+#include "tinydav_config.h"
+
+#include "tinymedia/tmedia_session.h"
+
+#include "tsk_safeobj.h"
+
+TDAV_BEGIN_DECLS
+
+// Forward declaration
+struct trtp_manager_s;
+struct tdav_consumer_audio_s;
+
+typedef tsk_list_t tdav_session_audio_dtmfe_L_t;
+
+typedef struct tdav_session_audio_s
+{
+ TMEDIA_DECLARE_SESSION_AUDIO;
+
+ tsk_bool_t useIPv6;
+
+ struct {
+ unsigned created;
+ unsigned started:1;
+ } timer;
+
+ struct {
+ tmedia_codec_t* codec;
+ void* buffer;
+ tsk_size_t buffer_size;
+ } encoder;
+
+ struct {
+ void* buffer;
+ tsk_size_t buffer_size;
+ } decoder;
+
+ char* local_ip;
+ //uint16_t local_port;
+
+ /* NAT Traversal context */
+ tnet_nat_context_handle_t* natt_ctx;
+
+ char* remote_ip;
+ uint16_t remote_port;
+
+ tsk_bool_t rtcp_enabled;
+
+ struct trtp_manager_s* rtp_manager;
+
+ struct tmedia_consumer_s* consumer;
+ struct tmedia_producer_s* producer;
+ struct tmedia_denoise_s* denoise;
+
+ tdav_session_audio_dtmfe_L_t* dtmf_events;
+
+ TSK_DECLARE_SAFEOBJ;
+}
+tdav_session_audio_t;
+
+#define TDAV_SESSION_AUDIO(self) ((tdav_session_audio_t*)(self))
+
+TINYDAV_GEXTERN const tmedia_session_plugin_def_t *tdav_session_audio_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_SESSION_AUDIO_H */
diff --git a/tinyDAV/include/tinydav/audio/tdav_speex_denoise.h b/tinyDAV/include/tinydav/audio/tdav_speex_denoise.h
new file mode 100644
index 0000000..03a086a
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/tdav_speex_denoise.h
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_speex_denoise.h
+ * @brief Speex Denoiser (Noise suppression, AGC, AEC) Plugin
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_SPEEX_DENOISE_H
+#define TINYDAV_SPEEX_DENOISE_H
+
+#include "tinydav_config.h"
+
+#if HAVE_SPEEX_DSP
+
+#include "tinymedia/tmedia_denoise.h"
+
+#include <speex/speex_preprocess.h>
+#include <speex/speex_echo.h>
+
+TDAV_BEGIN_DECLS
+
+/** Speex denoiser*/
+typedef struct tdav_speex_denoise_s
+{
+ TMEDIA_DECLARE_DENOISE;
+
+ SpeexPreprocessState *preprocess_state;
+ SpeexEchoState *echo_state;
+
+ spx_int16_t* echo_output_frame;
+ uint32_t frame_size;
+ tsk_bool_t vad_on;
+}
+tdav_speex_denoise_t;
+
+const tmedia_denoise_plugin_def_t *tdav_speex_denoise_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* #if HAVE_SPEEX_DSP */
+
+#endif /* TINYDAV_SPEEX_DENOISE_H */
diff --git a/tinyDAV/include/tinydav/audio/waveapi/tdav_consumer_waveapi.h b/tinyDAV/include/tinydav/audio/waveapi/tdav_consumer_waveapi.h
new file mode 100644
index 0000000..aa93afc
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/waveapi/tdav_consumer_waveapi.h
@@ -0,0 +1,68 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_waveapi.h
+ * @brief Audio Consumer for Win32 and WinCE platforms.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CONSUMER_WAVEAPI_H
+#define TINYDAV_CONSUMER_WAVEAPI_H
+
+#include "tinydav_config.h"
+
+#if HAVE_WAVE_API
+
+#include "tinydav/audio/tdav_consumer_audio.h"
+
+#include <windows.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_WAVEAPI_CONSUMER_NOTIF_POS_COUNT 4
+
+typedef struct tdav_consumer_waveapi_s
+{
+ TDAV_DECLARE_CONSUMER_AUDIO;
+
+ tsk_bool_t started;
+
+ WAVEFORMATEX wfx;
+ HWAVEOUT hWaveOut;
+ LPWAVEHDR hWaveHeaders[TDAV_WAVEAPI_CONSUMER_NOTIF_POS_COUNT];
+ tsk_size_t bytes_per_notif;
+
+ void* tid[1];
+ HANDLE events[2];
+ CRITICAL_SECTION cs;
+}
+tdav_consumer_waveapi_t;
+
+TINYDAV_GEXTERN const tmedia_consumer_plugin_def_t *tdav_consumer_waveapi_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_WAVE_API */
+
+#endif /* TINYDAV_CONSUMER_WAVEAPI_H */
diff --git a/tinyDAV/include/tinydav/audio/waveapi/tdav_producer_waveapi.h b/tinyDAV/include/tinydav/audio/waveapi/tdav_producer_waveapi.h
new file mode 100644
index 0000000..ffee69c
--- /dev/null
+++ b/tinyDAV/include/tinydav/audio/waveapi/tdav_producer_waveapi.h
@@ -0,0 +1,68 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_waveapi.h
+ * @brief Audio Consumer for Win32 and WinCE platforms.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_PRODUCER_WAVEAPI_H
+#define TINYDAV_PRODUCER_WAVEAPI_H
+
+#include "tinydav_config.h"
+
+#if HAVE_WAVE_API
+
+#include "tinydav/audio/tdav_producer_audio.h"
+
+#include <windows.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_WAVEAPI_PRODUCER_NOTIF_POS_COUNT 4
+
+typedef struct tdav_producer_waveapi_s
+{
+ TDAV_DECLARE_PRODUCER_AUDIO;
+
+ tsk_bool_t started;
+
+ WAVEFORMATEX wfx;
+ HWAVEIN hWaveIn;
+ LPWAVEHDR hWaveHeaders[TDAV_WAVEAPI_PRODUCER_NOTIF_POS_COUNT];
+ tsk_size_t bytes_per_notif;
+
+ void* tid[1];
+ HANDLE events[2];
+ CRITICAL_SECTION cs;
+}
+tdav_producer_waveapi_t;
+
+TINYDAV_GEXTERN const tmedia_producer_plugin_def_t *tdav_producer_waveapi_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_WAVE_API */
+
+#endif /* TINYDAV_PRODUCER_WAVEAPI_H */
diff --git a/tinyDAV/include/tinydav/codecs/amr/tdav_codec_amr.h b/tinyDAV/include/tinydav/codecs/amr/tdav_codec_amr.h
new file mode 100644
index 0000000..b72d1a0
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/amr/tdav_codec_amr.h
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_amr.h
+ * @brief AMR-NB and AMR-WB codecs.
+ * RTP payloader/depayloader are based on RFC 4867
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_AMR_H
+#define TINYDAV_CODEC_AMR_H
+
+#include "tinydav_config.h"
+
+#if HAVE_OPENCORE_AMR
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <opencore-amrnb/interf_dec.h>
+#include <opencore-amrnb/interf_enc.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_CODEC_AMR(self) ((tdav_codec_amr_t*)(self))
+
+typedef enum tdav_codec_amr_type_e
+{
+ tdav_codec_amr_type_nb,
+ tdav_codec_amr_type_wb,
+}
+tdav_codec_amr_type_t;
+
+typedef enum tdav_codec_amr_mode_e
+{
+ tdav_codec_amr_mode_oa,
+ tdav_codec_amr_mode_be,
+}
+tdav_codec_amr_mode_t;
+
+/** Base class for all AMR codecs */
+typedef struct tdav_codec_amr_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ tdav_codec_amr_type_t type;
+ tdav_codec_amr_mode_t mode;
+
+ enum Mode encoder_mode;
+ void* encoder;
+ void* decoder;
+
+ unsigned modes:16; /**< 0..7 for NB and 0..8 for WB plus SID, SPEECH_LOST, NO_DATA etc etc */
+ unsigned mcp:2; /**< mode-change-periode (1 or 2) */
+ unsigned mcc:2; /**< mode-change-capability (1 or 2) */
+ unsigned mcn:1; /**< mode-change-neighnor (0 or 1) */
+ unsigned crc:1; /**< 0 or 1 */
+ unsigned robust_sorting:1; /**< robust-sorting (0 or 1) */
+}
+tdav_codec_amr_t;
+
+#define TDAV_DECLARE_CODEC_AMR tdav_codec_amr_t __codec_amr__
+
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_amrnb_oa_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_amrnb_be_plugin_def_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_amrwb_oa_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_amrwb_be_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_OPENCORE_AMR */
+
+#endif /* TINYDAV_CODEC_AMR_H */
diff --git a/tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv16.h b/tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv16.h
new file mode 100644
index 0000000..0b5ffff
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv16.h
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_bv16.h
+ * @brief BroadVoice16 codec
+ * The payloader/depayloader follow RFC 4298
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_BV16_H
+#define TINYDAV_CODEC_BV16_H
+
+#include "tinydav_config.h"
+
+#if HAVE_BV16
+
+#include "tinymedia/tmedia_codec.h"
+
+
+TDAV_BEGIN_DECLS
+
+/** BV16 codec */
+typedef struct tdav_codec_bv16_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ struct {
+ void *state;
+ void *bs;
+ void *x;
+ } encoder;
+
+ struct {
+ void *state;
+ void *bs;
+ void *x;
+ } decoder;
+}
+tdav_codec_bv16_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_bv16_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_BV16 */
+
+#endif /* TINYDAV_CODEC_BV16_H */
diff --git a/tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv32.h b/tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv32.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/bv/tdav_codec_bv32.h
diff --git a/tinyDAV/include/tinydav/codecs/dtmf/tdav_codec_dtmf.h b/tinyDAV/include/tinydav/codecs/dtmf/tdav_codec_dtmf.h
new file mode 100644
index 0000000..13f6392
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/dtmf/tdav_codec_dtmf.h
@@ -0,0 +1,49 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_dtmf.h
+ * @brief DTMF (RFC 4733) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_DTMF_H
+#define TINYDAV_CODEC_DTMF_H
+
+#include "tinydav_config.h"
+
+#include "tinymedia/tmedia_codec.h"
+
+TDAV_BEGIN_DECLS
+
+typedef struct tdav_codec_dtmf_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+}
+tdav_codec_dtmf_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_dtmf_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CODEC_DTMF_H */
diff --git a/tinyDAV/include/tinydav/codecs/g711/g711.h b/tinyDAV/include/tinydav/codecs/g711/g711.h
new file mode 100644
index 0000000..9981f00
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/g711/g711.h
@@ -0,0 +1,44 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_g711.h
+ * @brief G.711u and G.711a (a.k.a PCMU and PCMA) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_G711_IMPLEMENTATION_H
+#define TINYDAV_CODEC_G711_IMPLEMENTATION_H
+
+#include "tinydav_config.h"
+
+TDAV_BEGIN_DECLS
+
+unsigned char linear2alaw(short pcm_val);
+short alaw2linear(unsigned char a_val);
+unsigned char linear2ulaw(short pcm_val);
+short ulaw2linear(unsigned char u_val);
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CODEC_G711_IMPLEMENTATION_H */
diff --git a/tinyDAV/include/tinydav/codecs/g711/tdav_codec_g711.h b/tinyDAV/include/tinydav/codecs/g711/tdav_codec_g711.h
new file mode 100644
index 0000000..73fd018
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/g711/tdav_codec_g711.h
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_g711.h
+ * @brief G.711u and G.711a (a.k.a PCMU and PCMA) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_G711_H
+#define TINYDAV_CODEC_G711_H
+
+#include "tinydav_config.h"
+
+#include "tinymedia/tmedia_codec.h"
+
+TDAV_BEGIN_DECLS
+
+/** G.711u codec */
+typedef struct tdav_codec_g711u_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+}
+tdav_codec_g711u_t;
+
+/** G.711a codec */
+typedef struct tdav_codec_g711a_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+}
+tdav_codec_g711a_t;
+
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_g711a_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_g711u_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CODEC_G711_H */
diff --git a/tinyDAV/include/tinydav/codecs/g729/tdav_codec_g729.h b/tinyDAV/include/tinydav/codecs/g729/tdav_codec_g729.h
new file mode 100644
index 0000000..92c5a9a
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/g729/tdav_codec_g729.h
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_g729.h
+ * @brief G729a codec.
+ * Source from: http://www.itu.int/rec/T-REC-G.729-199611-S!AnnA/en
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_G729_H
+#define TINYDAV_CODEC_G729_H
+
+#include "tinydav_config.h"
+
+#if HAVE_G729
+
+#include "tinymedia/tmedia_codec.h"
+
+#include "g729b/typedef.h"
+#include "g729b/ld8a.h"
+
+
+TDAV_BEGIN_DECLS
+
+/** G.729abb codec */
+typedef struct tdav_codec_g729ab_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ struct{
+ Word16 prm[PRM_SIZE+1]; /* Analysis parameters + frame type */
+ Word16 serial[SERIAL_SIZE]; /* Output bitstream buffer */
+
+ Word16 frame; /* frame counter */
+
+ /* For G.729B */
+ Word16 vad_enable;
+ } encoder;
+
+ struct{
+ Word16 serial[SERIAL_SIZE]; /* Serial stream */
+ Word16 synth_buf[L_FRAME+M], *synth; /* Synthesis */
+ Word16 parm[PRM_SIZE+2]; /* Synthesis parameters */
+ Word16 Az_dec[MP1*2]; /* Decoded Az for post-filter */
+ Word16 T2[2]; /* Pitch lag for 2 subframes */
+
+ /* For G.729B */
+ Word16 Vad;
+ } decoder;
+}
+tdav_codec_g729ab_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_g729ab_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CODEC_G729_H */
+
+#endif
diff --git a/tinyDAV/include/tinydav/codecs/gsm/tdav_codec_gsm.h b/tinyDAV/include/tinydav/codecs/gsm/tdav_codec_gsm.h
new file mode 100644
index 0000000..0ed0d17
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/gsm/tdav_codec_gsm.h
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_gsm.h
+ * @brief GSM Full Rate Codec (Based on libgsm)
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_GSM_H
+#define TINYDAV_CODEC_GSM_H
+
+#include "tinydav_config.h"
+
+#if HAVE_LIBGSM
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <gsm.h>
+
+TDAV_BEGIN_DECLS
+
+/** GSM codec */
+typedef struct tdav_codec_gsm_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ gsm encoder;
+ gsm decoder;
+}
+tdav_codec_gsm_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_gsm_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_LIBGSM */
+
+#endif /* TINYDAV_CODEC_GSM_H */
diff --git a/tinyDAV/include/tinydav/codecs/h261/tdav_codec_h261.h b/tinyDAV/include/tinydav/codecs/h261/tdav_codec_h261.h
new file mode 100644
index 0000000..939d19b
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/h261/tdav_codec_h261.h
@@ -0,0 +1,84 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h261.h
+ * @brief H.261 codec plugin.
+ * RTP payloader follows RFC 4587
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_H261_H
+#define TINYDAV_CODEC_H261_H
+
+#include "tinydav_config.h"
+
+#if HAVE_FFMPEG
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <libavcodec/avcodec.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_CODEC_H261(self) ((tdav_codec_h261_t*)(self))
+
+/** H.2261 codec */
+typedef struct tdav_codec_h261_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_h261_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h261_plugin_def_t;
+TDAV_END_DECLS
+
+
+#endif /* HAVE_FFMPEG */
+
+#endif /* TINYDAV_CODEC_H261_H */
diff --git a/tinyDAV/include/tinydav/codecs/h263/tdav_codec_h263.h b/tinyDAV/include/tinydav/codecs/h263/tdav_codec_h263.h
new file mode 100644
index 0000000..3103e4a
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/h263/tdav_codec_h263.h
@@ -0,0 +1,116 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h263.h
+ * @brief H.263-1996 and H.263-1998 codec plugins.
+ * RTP payloader follows RFC 4629 for H263+ and RFC 2190 for H263.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_H263_H
+#define TINYDAV_CODEC_H263_H
+
+#include "tinydav_config.h"
+
+#if HAVE_FFMPEG
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <libavcodec/avcodec.h>
+
+TDAV_BEGIN_DECLS
+
+#define TDAV_CODEC_H263(self) ((tdav_codec_h263_t*)(self))
+
+typedef enum tdav_codec_h263_type_e
+{
+ tdav_codec_h263_1996,
+ tdav_codec_h263_1998,
+ tdav_codec_h263_2000,
+}
+tdav_codec_h263_type_t;
+
+/** H.263-1996 codec */
+typedef struct tdav_codec_h263_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ tdav_codec_h263_type_t type;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_h263_t;
+
+#define TDAV_DECLARE_CODEC_H263 tdav_codec_h263_t __codec_h263__
+
+int tdav_codec_h263_init(tdav_codec_h263_t* self, tdav_codec_h263_type_t type, enum CodecID encoder, enum CodecID decoder);
+int tdav_codec_h263_deinit(tdav_codec_h263_t* self);
+
+/** H.263-1998 codec */
+typedef struct tdav_codec_h263p_s
+{
+ TDAV_DECLARE_CODEC_H263;
+}
+tdav_codec_h263p_t;
+
+/** H.263-2000 codec */
+typedef struct tdav_codec_h263pp_s
+{
+ TDAV_DECLARE_CODEC_H263;
+}
+tdav_codec_h263pp_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h263_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h263p_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h263pp_plugin_def_t;
+
+TDAV_END_DECLS
+
+
+#endif /* HAVE_FFMPEG */
+
+#endif /* TINYDAV_CODEC_H263_H */
diff --git a/tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264.h b/tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264.h
new file mode 100644
index 0000000..17c7680
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264.h
@@ -0,0 +1,105 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h264.h
+ * @brief H.264 codec plugin
+ * RTP payloader/depayloader follows RFC 3984.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_H264_H
+#define TINYDAV_CODEC_H264_H
+
+#include "tinydav_config.h"
+
+#if HAVE_FFMPEG && (!defined(HAVE_H264) || HAVE_H264)
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <libavcodec/avcodec.h>
+
+TDAV_BEGIN_DECLS
+
+// Because of FD, declare it here
+typedef enum packetization_mode_e{
+ Single_NAL_Unit_Mode = 0, /* Single NAL mode (Only nals from 1-23 are allowed) */
+ Non_Interleaved_Mode = 1, /* Non-interleaved Mode: 1-23, 24 (STAP-A), 28 (FU-A) are allowed */
+ Interleaved_Mode = 2 /* 25 (STAP-B), 26 (MTAP16), 27 (MTAP24), 28 (FU-A), and 29 (FU-B) are allowed.*/
+}
+packetization_mode_t;
+
+typedef enum tdav_codec_h264_profile_e
+{
+ tdav_codec_h264_bp99,
+
+ tdav_codec_h264_bp10,
+ tdav_codec_h264_bp20,
+ tdav_codec_h264_bp30,
+}
+tdav_codec_h264_profile_t;
+
+typedef struct tdav_codec_h264_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ tdav_codec_h264_profile_t profile;
+
+ packetization_mode_t pack_mode;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+ int frame_count;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ void* accumulator;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_h264_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h264_bp10_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h264_bp20_plugin_def_t;
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_h264_bp30_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_FFMPEG */
+
+#endif /* TINYDAV_CODEC_H264_H */ \ No newline at end of file
diff --git a/tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264_rtp.h b/tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264_rtp.h
new file mode 100644
index 0000000..83001b1
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/h264/tdav_codec_h264_rtp.h
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h264_rtp.h
+ * @brief H.264 payloader/depayloder as per RFC 3984
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_H264_RTP_H
+#define TINYDAV_CODEC_H264_RTP_H
+
+#include "tinydav_config.h"
+
+#if HAVE_FFMPEG && (!defined(HAVE_H264) || HAVE_H264)
+
+#include "tsk_common.h"
+
+TDAV_BEGIN_DECLS
+
+#if TDAV_UNDER_WINDOWS
+# define H264_RTP_PAYLOAD_SIZE 1300
+#else
+# define H264_RTP_PAYLOAD_SIZE 900
+#endif
+
+#define H264_START_CODE_PREFIX_SIZE 4
+
+struct tdav_codec_h264_s;
+
+extern uint8_t H264_START_CODE_PREFIX[4];
+
+typedef enum profile_idc_e {
+ profile_idc_none = 0,
+
+ profile_idc_baseline = 66,
+ profile_idc_extended = 88,
+ profile_idc_main = 77,
+ profile_idc_high = 100
+}
+profile_idc_t;
+
+typedef struct profile_iop_s {
+ unsigned constraint_set0_flag:1;
+ unsigned constraint_set1_flag:1;
+ unsigned constraint_set2_flag:1;
+ unsigned reserved_zero_5bits:5;
+}
+profile_iop_t;
+
+typedef enum level_idc_e {
+ level_idc_none = 0,
+
+ level_idc_1_0 = 10,
+ level_idc_1_b = 14,
+ level_idc_1_1 = 11,
+ level_idc_1_2 = 12,
+ level_idc_1_3 = 13,
+ level_idc_2_0 = 20,
+ level_idc_2_1 = 21,
+ level_idc_2_2 = 22,
+ level_idc_3_0 = 30
+}
+level_idc_t;
+
+
+/* 5.2. Common Structure of the RTP Payload Format
+ Type Packet Type name Section
+ ---------------------------------------------------------
+ 0 undefined -
+ 1-23 NAL unit Single NAL unit packet per H.264 5.6
+ 24 STAP-A Single-time aggregation packet 5.7.1
+ 25 STAP-B Single-time aggregation packet 5.7.1
+ 26 MTAP16 Multi-time aggregation packet 5.7.2
+ 27 MTAP24 Multi-time aggregation packet 5.7.2
+ 28 FU-A Fragmentation unit 5.8
+ 29 FU-B Fragmentation unit 5.8
+ 30-31 undefined -
+*/
+typedef enum nal_unit_type_e{
+ undefined_0 = 0,
+ nal_unit,
+ stap_a = 24,
+ stap_b = 25,
+ mtap16 = 26,
+ mtap24 = 27,
+ fu_a = 28,
+ fu_b = 29,
+ undefined_30 = 30,
+ undefined_31 = 31
+}
+nal_unit_type_t;
+
+int tdav_codec_h264_parse_profile(const char* profile_level_id, profile_idc_t *p_idc, profile_iop_t *p_iop, level_idc_t *l_idc);
+int tdav_codec_h264_get_pay(const void* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp);
+
+void tdav_codec_h264_rtp_callback(struct tdav_codec_h264_s *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+
+TDAV_END_DECLS
+
+#endif /* HAVE_FFMPEG */
+
+#endif /* TINYDAV_CODEC_H264_RTP_H */
diff --git a/tinyDAV/include/tinydav/codecs/ilbc/tdav_codec_ilbc.h b/tinyDAV/include/tinydav/codecs/ilbc/tdav_codec_ilbc.h
new file mode 100644
index 0000000..974b7b8
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/ilbc/tdav_codec_ilbc.h
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_ilbc.h
+ * @brief iLBC codec
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_ILBC_H
+#define TINYDAV_CODEC_ILBC_H
+
+#include "tinydav_config.h"
+
+#if HAVE_ILBC
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <ilbc/iLBC_define.h>
+#include <ilbc/iLBC_encode.h>
+#include <ilbc/iLBC_decode.h>
+
+TDAV_BEGIN_DECLS
+
+/** iLBC codec */
+typedef struct tdav_codec_ilbc_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ iLBC_Enc_Inst_t encoder;
+ iLBC_Dec_Inst_t decoder;
+
+ float encblock[BLOCKL_MAX];
+ float decblock[BLOCKL_MAX];
+}
+tdav_codec_ilbc_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_ilbc_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_ILBC */
+
+#endif /* TINYDAV_CODEC_ILBC_H */
diff --git a/tinyDAV/include/tinydav/codecs/mp4ves/tdav_codec_mp4ves.h b/tinyDAV/include/tinydav/codecs/mp4ves/tdav_codec_mp4ves.h
new file mode 100644
index 0000000..ce75cba
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/mp4ves/tdav_codec_mp4ves.h
@@ -0,0 +1,85 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_mp4ves.h
+ * @brief MP4V-ES codec plugin
+ * RTP payloader/depayloader follows RFC 3016.
+ * ISO-IEC-14496-2: http://www.csus.edu/indiv/p/pangj/aresearch/video_compression/presentation/ISO-IEC-14496-2_2001_MPEG4_Visual.pdf
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Th Dec 2 16:54:58 2010 mdiop
+ */
+#ifndef TINYDAV_CODEC_MP4VES_H
+#define TINYDAV_CODEC_MP4VES_H
+
+#include "tinydav_config.h"
+
+#if HAVE_FFMPEG
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <libavcodec/avcodec.h>
+
+TDAV_BEGIN_DECLS
+
+typedef struct tdav_codec_mp4ves_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ int profile;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_mp4ves_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_mp4ves_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_FFMPEG */
+
+#endif /* TINYDAV_CODEC_MP4VES_H */
+
diff --git a/tinyDAV/include/tinydav/codecs/msrp/tdav_codec_msrp.h b/tinyDAV/include/tinydav/codecs/msrp/tdav_codec_msrp.h
new file mode 100644
index 0000000..12b7057
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/msrp/tdav_codec_msrp.h
@@ -0,0 +1,51 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_msrp.h
+ * @brief The Message Session Relay Protocol (MSRP) fake codec.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_MSRP_H
+#define TINYDAV_CODEC_MSRP_H
+
+#include "tinydav_config.h"
+
+#include "tinymedia/tmedia_codec.h"
+
+TDAV_BEGIN_DECLS
+
+/** MSRP codec */
+typedef struct tdav_codec_msrp_s
+{
+ TMEDIA_DECLARE_CODEC_MSRP;
+}
+tdav_codec_msrp_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_msrp_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CODEC_MSRP_H */
diff --git a/tinyDAV/include/tinydav/codecs/speex/tdav_codec_speex.h b/tinyDAV/include/tinydav/codecs/speex/tdav_codec_speex.h
new file mode 100644
index 0000000..9c70fb1
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/speex/tdav_codec_speex.h
@@ -0,0 +1,79 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_ilbc.h
+ * @brief Speex codecs
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_SPEEX_H
+#define TINYDAV_CODEC_SPEEX_H
+
+#include "tinydav_config.h"
+
+#if HAVE_LIB_SPEEX
+
+#include "tinymedia/tmedia_codec.h"
+
+#include <speex/speex.h>
+
+TDAV_BEGIN_DECLS
+
+typedef enum tdav_codec_speex_type_e
+{
+ tdav_codec_speex_type_nb,
+ tdav_codec_speex_type_wb,
+ tdav_codec_speex_type_uwb,
+}
+tdav_codec_speex_type_t;
+
+/** Speex codec */
+typedef struct tdav_codec_speex_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ tdav_codec_speex_type_t type;
+
+ struct{
+ void* state;
+ SpeexBits bits;
+ tsk_size_t size;
+ } encoder;
+
+ struct {
+ void* state;
+ SpeexBits bits;
+ spx_int16_t* buffer;
+ tsk_size_t size;
+ } decoder;
+}
+tdav_codec_speex_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_speex_nb_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CODEC_SPEEX_H */
+
+#endif /* TINYDAV_CODEC_SPEEX_H */
diff --git a/tinyDAV/include/tinydav/codecs/theora/tdav_codec_theora.h b/tinyDAV/include/tinydav/codecs/theora/tdav_codec_theora.h
new file mode 100644
index 0000000..38c7dbb
--- /dev/null
+++ b/tinyDAV/include/tinydav/codecs/theora/tdav_codec_theora.h
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_theora.h
+ * @brief Theora codec plugin
+ * RTP payloader/depayloader follows draft-barbato-avt-rtp-theora-01.
+ * For more information about Theora, http://www.theora.org/doc/Theora.pdf.
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CODEC_THEORA_H
+#define TINYDAV_CODEC_THEORA_H
+
+#include "tinydav_config.h"
+
+#if HAVE_FFMPEG
+
+#include "tinymedia/tmedia_codec.h"
+
+#include "tsk_buffer.h"
+
+#include <libavcodec/avcodec.h>
+
+TDAV_BEGIN_DECLS
+
+typedef struct tdav_codec_theora_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+
+ uint64_t conf_last;
+ int conf_count;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ tsk_bool_t opened;
+ uint8_t conf_ident[3];
+ tsk_buffer_t* conf_pkt;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_theora_t;
+
+TINYDAV_GEXTERN const tmedia_codec_plugin_def_t *tdav_codec_theora_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* HAVE_FFMPEG */
+
+#endif /* TINYDAV_CODEC_THEORA_H */ \ No newline at end of file
diff --git a/tinyDAV/include/tinydav/msrp/tdav_consumer_msrp.h b/tinyDAV/include/tinydav/msrp/tdav_consumer_msrp.h
new file mode 100644
index 0000000..9bb9017
--- /dev/null
+++ b/tinyDAV/include/tinydav/msrp/tdav_consumer_msrp.h
@@ -0,0 +1,51 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_msrp.h
+ * @brief The Message Session Relay Protocol (MSRP) consumer.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CONSUMER_MSRP_H
+#define TINYDAV_CONSUMER_MSRP_H
+
+#include "tinydav_config.h"
+
+TDAV_BEGIN_DECLS
+
+
+#define TDAV_CONSUMER_MSRP(self) ((tdav_consumer_msrp_t*)(self))
+
+
+typedef struct tdav_consumer_msrp_s
+{
+ TMEDIA_DECLARE_CONSUMER;
+}
+tdav_consumer_msrp_t;
+
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_CONSUMER_MSRP_H */
diff --git a/tinyDAV/include/tinydav/msrp/tdav_producer_msrp.h b/tinyDAV/include/tinydav/msrp/tdav_producer_msrp.h
new file mode 100644
index 0000000..4a14402
--- /dev/null
+++ b/tinyDAV/include/tinydav/msrp/tdav_producer_msrp.h
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_msrp.h
+ * @brief The Message Session Relay Protocol (MSRP) producer.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_PRODUCER_MSRP_H
+#define TINYDAV_PRODUCER_MSRP_H
+
+#include "tinydav_config.h"
+
+TDAV_BEGIN_DECLS
+
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_PRODUCER_MSRP_H */
diff --git a/tinyDAV/include/tinydav/msrp/tdav_session_msrp.h b/tinyDAV/include/tinydav/msrp/tdav_session_msrp.h
new file mode 100644
index 0000000..f4be82a
--- /dev/null
+++ b/tinyDAV/include/tinydav/msrp/tdav_session_msrp.h
@@ -0,0 +1,104 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_session_msrp.h
+ * @brief The Message Session Relay Protocol (MSRP) session.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+
+#ifndef TINYDAV_SESSION_MSRP_H
+#define TINYDAV_SESSION_MSRP_H
+
+#include "tinydav_config.h"
+
+#include "tinymsrp/session/tmsrp_sender.h"
+#include "tinymsrp/session/tmsrp_receiver.h"
+#include "tmsrp.h"
+
+#include "tnet_transport.h"
+
+#include "tinymedia/tmedia_session.h"
+
+TDAV_BEGIN_DECLS
+
+typedef enum tdav_msrp_setup_e
+{
+ msrp_setup_active,
+ msrp_setup_passive,
+ msrp_setup_actpass,
+ msrp_setup_holdconn
+}
+tdav_msrp_setup_t;
+
+typedef struct tdav_session_msrp_s
+{
+ TMEDIA_DECLARE_SESSION_MSRP;
+
+ tsk_bool_t useIPv6;
+
+ tnet_transport_t *transport;
+ tmsrp_config_t* config;
+ tdav_msrp_setup_t setup;
+ tnet_fd_t connectedFD; // FullDuplex Socket
+ tmsrp_sender_t* sender;
+ tmsrp_receiver_t* receiver;
+
+ char* local_ip;
+ //uint16_t local_port;
+
+ /* NAT Traversal context */
+ tnet_nat_context_handle_t* natt_ctx;
+
+ char* remote_ip;
+ uint16_t remote_port;
+
+ char* neg_accept_type;
+ char* neg_accept_w_type;
+ char* accept_types;
+ char* accept_w_types;
+ uint64_t chunck_duration;
+
+ struct {
+ char* path; //full-path
+ char* selector;
+ char* disposition;
+ char* date;
+ char* icon;
+ char* transfer_id;
+ unsigned sent:1;
+ } file;
+
+ unsigned fresh_conn:1;
+ unsigned offerer:1;
+ unsigned send_bodiless:1;
+}
+tdav_session_msrp_t;
+
+TINYDAV_GEXTERN const tmedia_session_plugin_def_t *tdav_session_msrp_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_SESSION_MSRP_H */
diff --git a/tinyDAV/include/tinydav/tdav.h b/tinyDAV/include/tinydav/tdav.h
new file mode 100644
index 0000000..65c718c
--- /dev/null
+++ b/tinyDAV/include/tinydav/tdav.h
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav.h
+ * @brief tinyDAV.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYMEDIA_TDAV_H
+#define TINYMEDIA_TDAV_H
+
+#include "tinydav_config.h"
+
+#include "tsk_common.h" /* tsk_bool_t */
+
+TDAV_BEGIN_DECLS
+
+typedef enum tdav_codec_id_e
+{
+ tdav_codec_id_none = 0x00000000,
+
+ tdav_codec_id_amr_nb_oa = 0x00000001<<0,
+ tdav_codec_id_amr_nb_be = 0x00000001<<1,
+ tdav_codec_id_amr_wb_oa = 0x00000001<<2,
+ tdav_codec_id_amr_wb_be = 0x00000001<<3,
+ tdav_codec_id_gsm = 0x00000001<<4,
+ tdav_codec_id_pcma = 0x00000001<<5,
+ tdav_codec_id_pcmu = 0x00000001<<6,
+ tdav_codec_id_ilbc = 0x00000001<<7,
+ tdav_codec_id_speex_nb = 0x00000001<<8,
+ tdav_codec_id_speex_wb = 0x00000001<<9,
+ tdav_codec_id_speex_uwb = 0x00000001<<10,
+ tdav_codec_id_bv16 = 0x00000001<<11,
+ tdav_codec_id_bv32 = 0x00000001<<12,
+ tdav_codec_id_evrc = 0x00000001<<13,
+ tdav_codec_id_g729ab = 0x00000001<<14,
+
+ /* room for new Audio codecs */
+
+ tdav_codec_id_h261 = 0x00010000<<0,
+ tdav_codec_id_h263 = 0x00010000<<1,
+ tdav_codec_id_h263p = 0x00010000<<2,
+ tdav_codec_id_h263pp = 0x00010000<<3,
+ tdav_codec_id_h264_bp10 = 0x00010000<<4,
+ tdav_codec_id_h264_bp20 = 0x00010000<<5,
+ tdav_codec_id_h264_bp30 = 0x00010000<<6,
+ tdav_codec_id_theora = 0x00010000<<7,
+ tdav_codec_id_mp4ves_es = 0x00010000<<8,
+
+}
+tdav_codec_id_t;
+
+TINYDAV_API int tdav_init();
+TINYDAV_API void tdav_set_codecs(tdav_codec_id_t codecs);
+TINYDAV_API tsk_bool_t tdav_codec_is_supported(tdav_codec_id_t codec);
+TINYDAV_API int tdav_deinit();
+
+TDAV_END_DECLS
+
+#endif /* TINYMEDIA_TDAV_H */
diff --git a/tinyDAV/include/tinydav/tdav_win32.h b/tinyDAV/include/tinydav/tdav_win32.h
new file mode 100644
index 0000000..4e85a9e
--- /dev/null
+++ b/tinyDAV/include/tinydav/tdav_win32.h
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav_win32.h
+ * @brief tinyDAV WIN32 helper functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYMEDIA_TDAV_WIN32_H
+#define TINYMEDIA_TDAV_WIN32_H
+
+#include "tinydav_config.h"
+
+#if TDAV_UNDER_WINDOWS
+
+#include <windows.h>
+
+TDAV_BEGIN_DECLS
+
+void tdav_win32_print_error(const char* func, HRESULT hr);
+
+TDAV_END_DECLS
+
+#endif /* TDAV_UNDER_WINDOWS */
+
+#endif /* TINYMEDIA_TDAV_WIN32_H */
diff --git a/tinyDAV/include/tinydav/video/tdav_converter_video.h b/tinyDAV/include/tinydav/video/tdav_converter_video.h
new file mode 100644
index 0000000..4d52fba
--- /dev/null
+++ b/tinyDAV/include/tinydav/video/tdav_converter_video.h
@@ -0,0 +1,100 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav_converter_video.h
+ * @brief Video converter.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_CONVERTER_VIDEO_H
+#define TINYDAV_CONVERTER_VIDEO_H
+
+#include "tinydav_config.h"
+
+
+#include "tinymedia/tmedia_common.h"
+
+#if HAVE_FFMPEG || HAVE_SWSSCALE
+#include <libswscale/swscale.h>
+#include <libavcodec/avcodec.h>
+#endif
+
+#include "tsk_object.h"
+
+TDAV_BEGIN_DECLS
+
+typedef struct tdav_converter_video_s
+{
+ TSK_DECLARE_OBJECT;
+
+#if HAVE_FFMPEG || HAVE_SWSSCALE
+ struct SwsContext *context;
+
+ enum PixelFormat pixfmt;
+
+ AVFrame* srcFrame;
+ AVFrame* dstFrame;
+
+ struct {
+ struct SwsContext *context;
+ AVFrame* frame;
+ uint8_t* buffer;
+ } rot;
+
+#endif
+
+ tsk_size_t srcWidth;
+ tsk_size_t srcHeight;
+
+ tsk_size_t dstWidth;
+ tsk_size_t dstHeight;
+
+ tsk_bool_t toYUV420;
+
+ int rotation;
+}
+tdav_converter_video_t;
+
+tdav_converter_video_t* tdav_converter_video_create(tsk_size_t srcWidth, tsk_size_t srcHeight, tsk_size_t dstWidth, tsk_size_t dstHeight, tmedia_chroma_t chroma, tsk_bool_t toYUV420);
+tsk_size_t tdav_converter_video_convert(tdav_converter_video_t* self, const void* buffer, void** output, tsk_size_t* output_max_size);
+
+#define tdav_converter_video_init(self, _rotation/*...To be completed with other parameters*/) \
+ if((self)){ \
+ (self)->rotation = (_rotation); \
+ }
+
+#define tdav_converter_video_flip(frame, height) \
+ frame->data[0] += frame->linesize[0] * (height -1); \
+ frame->data[1] += frame->linesize[1] * ((height -1)/2); \
+ frame->data[2] += frame->linesize[2] * ((height -1)/2); \
+ \
+ frame->linesize[0] *= -1; \
+ frame->linesize[1] *= -1; \
+ frame->linesize[2] *= -1;
+
+TINYDAV_GEXTERN const tsk_object_def_t *tdav_converter_video_def_t;
+
+TDAV_END_DECLS
+
+
+#endif /* TINYDAV_CONVERTER_VIDEO_H */
diff --git a/tinyDAV/include/tinydav/video/tdav_runnable_video.h b/tinyDAV/include/tinydav/video/tdav_runnable_video.h
new file mode 100644
index 0000000..3b461de
--- /dev/null
+++ b/tinyDAV/include/tinydav/video/tdav_runnable_video.h
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav_runnable_video.h
+ * @brief Video runnable used by codecs.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_RUNNABLE_VIDEO_H
+#define TINYDAV_RUNNABLE_VIDEO_H
+
+#include "tinydav_config.h"
+
+#include "tsk_runnable.h"
+
+TDAV_BEGIN_DECLS
+
+typedef struct tdav_runnable_video_s
+{
+ TSK_DECLARE_RUNNABLE;
+
+ const void* userdata;
+}
+tdav_runnable_video_t;
+
+tdav_runnable_video_t* tdav_runnable_video_create(tsk_runnable_func_run run_f, const void* userdata);
+int tdav_runnable_video_start(tdav_runnable_video_t* self);
+int tdav_runnable_video_stop(tdav_runnable_video_t* self);
+
+TINYDAV_GEXTERN const tsk_object_def_t *tdav_runnable_video_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_RUNNABLE_VIDEO_H */
diff --git a/tinyDAV/include/tinydav/video/tdav_session_video.h b/tinyDAV/include/tinydav/video/tdav_session_video.h
new file mode 100644
index 0000000..01a80f2
--- /dev/null
+++ b/tinyDAV/include/tinydav/video/tdav_session_video.h
@@ -0,0 +1,105 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_session_video.h
+ * @brief Video Session plugin.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#ifndef TINYDAV_SESSION_VIDEO_H
+#define TINYDAV_SESSION_VIDEO_H
+
+#include "tinydav_config.h"
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinymedia/tmedia_session.h"
+
+#include "tsk_safeobj.h"
+
+TDAV_BEGIN_DECLS
+
+// Forward declaration
+struct trtp_manager_s;
+struct tdav_consumer_video_s;
+struct tdav_converter_video_s;
+
+typedef struct tdav_session_video_s
+{
+ TMEDIA_DECLARE_SESSION_VIDEO;
+
+ tsk_bool_t useIPv6;
+
+ char* local_ip;
+
+ char* remote_ip;
+ uint16_t remote_port;
+
+ /* NAT Traversal context */
+ tnet_nat_context_handle_t* natt_ctx;
+
+ tsk_bool_t rtcp_enabled;
+
+ struct trtp_manager_s* rtp_manager;
+
+ struct{
+ void* buffer;
+ tsk_size_t buffer_size;
+
+ void* conv_buffer;
+ tsk_size_t conv_buffer_size;
+ } encoder;
+
+ struct{
+ void* buffer;
+ tsk_size_t buffer_size;
+
+ void* conv_buffer;
+ tsk_size_t conv_buffer_size;
+ } decoder;
+
+ struct tmedia_consumer_s* consumer;
+ struct tmedia_producer_s* producer;
+ struct {
+ tsk_size_t consumerLastWidth;
+ tsk_size_t consumerLastHeight;
+ struct tdav_converter_video_s* fromYUV420;
+
+ tsk_size_t producerWidth;
+ tsk_size_t producerHeight;
+ tsk_size_t xProducerSize;
+ struct tdav_converter_video_s* toYUV420;
+ } conv;
+
+ TSK_DECLARE_SAFEOBJ;
+}
+tdav_session_video_t;
+
+#define TDAV_SESSION_VIDEO(self) ((tdav_session_video_t*)(self))
+
+TINYDAV_GEXTERN const tmedia_session_plugin_def_t *tdav_session_video_plugin_def_t;
+
+TDAV_END_DECLS
+
+#endif /* TINYDAV_SESSION_VIDEO_H */
diff --git a/tinyDAV/include/tinydav_config.h b/tinyDAV/include/tinydav_config.h
new file mode 100644
index 0000000..7663ffe
--- /dev/null
+++ b/tinyDAV/include/tinydav_config.h
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+#ifndef TINYDAV_CONFIG_H
+#define TINYDAV_CONFIG_H
+
+#ifdef __SYMBIAN32__
+#undef _WIN32 /* Because of WINSCW */
+#endif
+
+/* Windows (XP/Vista/7/CE and Windows Mobile) macro definition.
+*/
+#if defined(WIN32)|| defined(_WIN32) || defined(_WIN32_WCE)
+# define TDAV_UNDER_WINDOWS 1
+#endif
+
+#if (TDAV_UNDER_WINDOWS || defined(__SYMBIAN32__)) && defined(TINYDAV_EXPORTS)
+# define TINYDAV_API __declspec(dllexport)
+# define TINYDAV_GEXTERN __declspec(dllexport)
+#elif (TDAV_UNDER_WINDOWS || defined(__SYMBIAN32__)) /*&& defined(TINYDAV_IMPORTS)*/
+# define TINYDAV_API __declspec(dllimport)
+# define TINYDAV_GEXTERN __declspec(dllimport)
+#else
+# define TINYDAV_API
+# define TINYDAV_GEXTERN extern
+#endif
+
+/* Guards against C++ name mangling
+*/
+#ifdef __cplusplus
+# define TDAV_BEGIN_DECLS extern "C" {
+# define TDAV_END_DECLS }
+#else
+# define TDAV_BEGIN_DECLS
+# define TDAV_END_DECLS
+#endif
+
+#ifdef _MSC_VER
+#if HAVE_FFMPEG // FFMPeg warnings (treated as errors)
+# pragma warning (disable:4244)
+#endif
+# define inline __inline
+# define _CRT_SECURE_NO_WARNINGS
+#endif
+
+/* Detecting C99 compilers
+ */
+#if (__STDC_VERSION__ == 199901L) && !defined(__C99__)
+# define __C99__
+#endif
+
+#include <stdint.h>
+#ifdef __SYMBIAN32__
+#include <stdlib.h>
+#endif
+
+#if HAVE_CONFIG_H
+ #include "../config.h"
+#endif
+
+#endif // TINYDAV_CONFIG_H
diff --git a/tinyDAV/src/audio/coreaudio/tdav_consumer_coreaudio.c b/tinyDAV/src/audio/coreaudio/tdav_consumer_coreaudio.c
new file mode 100644
index 0000000..39fe1a9
--- /dev/null
+++ b/tinyDAV/src/audio/coreaudio/tdav_consumer_coreaudio.c
@@ -0,0 +1,269 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_coreaudio.c
+ * @brief Audio Consumer for MacOSX and iOS platforms.
+ *
+ * @authors
+ * - Laurent Etiemble <laurent.etiemble(at)gmail.com>
+ * - Mamadou Diop <diopmamadou(at)doubango(dot)org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 letiemble
+ */
+#include "tinydav/audio/coreaudio/tdav_consumer_coreaudio.h"
+
+
+// http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioQueueReference/Reference/reference.html
+#if HAVE_COREAUDIO
+
+#include "tsk_thread.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+static void __handle_output_buffer(void *userdata, AudioQueueRef queue, AudioQueueBufferRef buffer) {
+ OSStatus ret;
+ void *data;
+ tsk_size_t out_size = 0;
+ tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)userdata;
+
+ if (!consumer->started) {
+ return;
+ }
+
+ if((data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer), &out_size))){
+ // If we can get audio to play, then copy in the buffer
+ memcpy(buffer->mAudioData, data, TSK_MIN(consumer->buffer_size, out_size));
+ TSK_FREE(data);
+ } else{
+ // Put silence if there is no audio to play
+ memset(buffer->mAudioData, 0, consumer->buffer_size);
+ }
+
+ // Re-enqueue the buffer
+ ret = AudioQueueEnqueueBuffer(consumer->queue, buffer, 0, NULL);
+}
+
+/* ============ Media Consumer Interface ================= */
+#define tdav_consumer_coreaudio_set tsk_null
+
+int tdav_consumer_coreaudio_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec)
+{
+ OSStatus ret;
+ tsk_size_t i;
+ tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)self;
+
+ if(!consumer || !codec && codec->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TDAV_CONSUMER_AUDIO(consumer)->channels = codec->plugin->audio.channels;
+ TDAV_CONSUMER_AUDIO(consumer)->rate = codec->plugin->rate;
+ /* codec should have ptime */
+
+ // Set audio category
+ UInt32 category = kAudioSessionCategory_PlayAndRecord;
+ AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
+
+ // Create the audio stream description
+ AudioStreamBasicDescription *description = &(consumer->description);
+ description->mSampleRate = TDAV_CONSUMER_AUDIO(consumer)->rate;
+ description->mFormatID = kAudioFormatLinearPCM;
+ description->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ description->mChannelsPerFrame = TDAV_CONSUMER_AUDIO(consumer)->channels;
+ description->mFramesPerPacket = 1;
+ description->mBitsPerChannel = TDAV_CONSUMER_AUDIO(consumer)->bits_per_sample;
+ description->mBytesPerPacket = description->mBitsPerChannel / 8 * description->mChannelsPerFrame;
+ description->mBytesPerFrame = description->mBytesPerPacket;
+ description->mReserved = 0;
+
+ int packetperbuffer = 1000 / TDAV_CONSUMER_AUDIO(consumer)->ptime;
+ consumer->buffer_size = description->mSampleRate * description->mBytesPerFrame / packetperbuffer;
+
+ // Create the playback audio queue
+ ret = AudioQueueNewOutput(&(consumer->description),
+ __handle_output_buffer,
+ consumer,
+ NULL,
+ NULL,
+ 0,
+ &(consumer->queue));
+
+ for(i = 0; i < CoreAudioPlayBuffers; i++) {
+ // Create the buffer for the queue
+ ret = AudioQueueAllocateBuffer(consumer->queue, consumer->buffer_size, &(consumer->buffers[i]));
+ if (ret) {
+ break;
+ }
+
+ // Clear the data
+ memset(consumer->buffers[i]->mAudioData, 0, consumer->buffer_size);
+ consumer->buffers[i]->mAudioDataByteSize = consumer->buffer_size;
+
+ // Enqueue the buffer
+ ret = AudioQueueEnqueueBuffer(consumer->queue, consumer->buffers[i], 0, NULL);
+ if (ret) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int tdav_consumer_coreaudio_start(tmedia_consumer_t* self)
+{
+ OSStatus ret;
+ tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)self;
+
+ if(!consumer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(consumer->started){
+ TSK_DEBUG_WARN("Consumer already started");
+ return 0;
+ }
+
+ consumer->started = tsk_true;
+ ret = AudioQueueStart(consumer->queue, NULL);
+
+ return ret;
+}
+
+int tdav_consumer_coreaudio_consume(tmedia_consumer_t* self, void** buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
+{
+ tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)self;
+
+ if(!consumer || !buffer || !*buffer || !size){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ // buffer is already decoded
+ return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(consumer), buffer, size, proto_hdr);
+}
+
+int tdav_consumer_coreaudio_pause(tmedia_consumer_t* self)
+{
+ OSStatus ret;
+ tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)self;
+
+ if(!consumer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ ret = AudioQueuePause(consumer->queue);
+
+ return ret;
+}
+
+int tdav_consumer_coreaudio_stop(tmedia_consumer_t* self)
+{
+ OSStatus ret;
+ tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)self;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!consumer->started){
+ TSK_DEBUG_WARN("Consumer not started");
+ return 0;
+ }
+
+ consumer->started = tsk_false;
+ ret = AudioQueueStop(consumer->queue, false);
+
+ return ret;
+}
+
+//
+// coreaudio consumer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_consumer_coreaudio_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_consumer_coreaudio_t *consumer = self;
+ if(consumer){
+ /* init base */
+ tdav_consumer_audio_init(TDAV_CONSUMER_AUDIO(consumer));
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_consumer_coreaudio_dtor(tsk_object_t * self)
+{
+ tdav_consumer_coreaudio_t *consumer = self;
+ if(consumer){
+ // Stop the consumer if not done
+ if(consumer->started){
+ tdav_consumer_coreaudio_stop(self);
+ }
+
+ // Free all buffers and dispose the queue
+ if (consumer->queue) {
+ tsk_size_t i;
+
+ for(i=0; i<CoreAudioPlayBuffers; i++){
+ AudioQueueFreeBuffer(consumer->queue, consumer->buffers[i]);
+ }
+
+ AudioQueueDispose(consumer->queue, true);
+ }
+
+ /* deinit base */
+ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(consumer));
+ }
+
+ return self;
+}
+
+/* object definition */
+static const tsk_object_def_t tdav_consumer_coreaudio_def_s =
+{
+ sizeof(tdav_consumer_coreaudio_t),
+ tdav_consumer_coreaudio_ctor,
+ tdav_consumer_coreaudio_dtor,
+ tdav_consumer_audio_cmp,
+};
+
+/* plugin definition*/
+static const tmedia_consumer_plugin_def_t tdav_consumer_coreaudio_plugin_def_s =
+{
+ &tdav_consumer_coreaudio_def_s,
+
+ tmedia_audio,
+ "Apple CoreAudio consumer",
+
+ tdav_consumer_coreaudio_set,
+ tdav_consumer_coreaudio_prepare,
+ tdav_consumer_coreaudio_start,
+ tdav_consumer_coreaudio_consume,
+ tdav_consumer_coreaudio_pause,
+ tdav_consumer_coreaudio_stop
+};
+
+const tmedia_consumer_plugin_def_t *tdav_consumer_coreaudio_plugin_def_t = &tdav_consumer_coreaudio_plugin_def_s;
+
+#endif /* HAVE_COREAUDIO */
diff --git a/tinyDAV/src/audio/coreaudio/tdav_producer_coreaudio.c b/tinyDAV/src/audio/coreaudio/tdav_producer_coreaudio.c
new file mode 100644
index 0000000..e0cc3e8
--- /dev/null
+++ b/tinyDAV/src/audio/coreaudio/tdav_producer_coreaudio.c
@@ -0,0 +1,229 @@
+/**@file tdav_producer_coreaudio.c
+ * @brief Audio Producer for MacOSX and iOS platforms.
+ *
+ * @authors
+ * - Laurent Etiemble <laurent.etiemble(at)gmail.com>
+ * - Mamadou Diop <diopmamadou(at)doubango(dot)org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 letiemble
+ */
+#include "tinydav/audio/coreaudio/tdav_producer_coreaudio.h"
+
+
+// http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioQueueReference/Reference/reference.html
+
+#if HAVE_COREAUDIO
+
+#include "tsk_thread.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+void __handle_input_buffer (void *userdata, AudioQueueRef queue, AudioQueueBufferRef buffer, const AudioTimeStamp *start_time, UInt32 number_packet_descriptions, const AudioStreamPacketDescription *packet_descriptions ) {
+ OSStatus ret;
+ tdav_producer_coreaudio_t* producer = (tdav_producer_coreaudio_t*)userdata;
+
+ if (!producer->started) {
+ return;
+ }
+
+ // Alert the session that there is new data to send
+ if(TMEDIA_PRODUCER(producer)->enc_cb.callback) {
+ TMEDIA_PRODUCER(producer)->enc_cb.callback(TMEDIA_PRODUCER(producer)->enc_cb.callback_data, buffer->mAudioData, buffer->mAudioDataByteSize);
+ }
+
+ // Re-enqueue the buffer
+ ret = AudioQueueEnqueueBuffer(producer->queue, buffer, 0, NULL);
+}
+
+/* ============ Media Producer Interface ================= */
+#define tdav_producer_coreaudio_set tsk_null
+
+int tdav_producer_coreaudio_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec)
+{
+ OSStatus ret;
+ tsk_size_t i;
+ tdav_producer_coreaudio_t* producer = (tdav_producer_coreaudio_t*)self;
+
+ if(!producer || !codec && codec->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TDAV_PRODUCER_AUDIO(producer)->channels = codec->plugin->audio.channels;
+ TDAV_PRODUCER_AUDIO(producer)->rate = codec->plugin->rate;
+ /* codec should have ptime */
+
+
+ // Set audio category
+ UInt32 category = kAudioSessionCategory_PlayAndRecord;
+ AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
+
+ // Create the audio stream description
+ AudioStreamBasicDescription *description = &(producer->description);
+ description->mSampleRate = TDAV_PRODUCER_AUDIO(producer)->rate;
+ description->mFormatID = kAudioFormatLinearPCM;
+ description->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ description->mChannelsPerFrame = TDAV_PRODUCER_AUDIO(producer)->channels;
+ description->mFramesPerPacket = 1;
+ description->mBitsPerChannel = TDAV_PRODUCER_AUDIO(producer)->bits_per_sample;
+ description->mBytesPerPacket = description->mBitsPerChannel / 8 * description->mChannelsPerFrame;
+ description->mBytesPerFrame = description->mBytesPerPacket;
+ description->mReserved = 0;
+
+ int packetperbuffer = 1000 / TDAV_PRODUCER_AUDIO(producer)->ptime;
+ producer->buffer_size = description->mSampleRate * description->mBytesPerFrame / packetperbuffer;
+
+ // Create the record audio queue
+ ret = AudioQueueNewInput(&(producer->description),
+ __handle_input_buffer,
+ producer,
+ NULL,
+ kCFRunLoopCommonModes,
+ 0,
+ &(producer->queue));
+
+ for(i = 0; i < CoreAudioRecordBuffers; i++) {
+ // Create the buffer for the queue
+ ret = AudioQueueAllocateBuffer(producer->queue, producer->buffer_size, &(producer->buffers[i]));
+ if (ret) {
+ break;
+ }
+
+ // Clear the data
+ memset(producer->buffers[i]->mAudioData, 0, producer->buffer_size);
+ producer->buffers[i]->mAudioDataByteSize = producer->buffer_size;
+
+ // Enqueue the buffer
+ ret = AudioQueueEnqueueBuffer(producer->queue, producer->buffers[i], 0, NULL);
+ if (ret) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_producer_coreaudio_start(tmedia_producer_t* self)
+{
+ OSStatus ret;
+ tdav_producer_coreaudio_t* producer = (tdav_producer_coreaudio_t*)self;
+
+ if(!producer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(producer->started){
+ TSK_DEBUG_WARN("Producer already started");
+ return 0;
+ }
+
+ producer->started = tsk_true;
+ ret = AudioQueueStart(producer->queue, NULL);
+
+ return ret;
+}
+
+int tdav_producer_coreaudio_pause(tmedia_producer_t* self)
+{
+ OSStatus ret;
+ tdav_producer_coreaudio_t* producer = (tdav_producer_coreaudio_t*)self;
+
+ if(!producer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ ret = AudioQueuePause(producer->queue);
+
+ return ret;
+}
+
+int tdav_producer_coreaudio_stop(tmedia_producer_t* self)
+{
+ OSStatus ret;
+ tdav_producer_coreaudio_t* producer = (tdav_producer_coreaudio_t*)self;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!producer->started){
+ TSK_DEBUG_WARN("Producer not started");
+ return 0;
+ }
+
+ producer->started = tsk_false;
+ ret = AudioQueueStop(producer->queue, false);
+
+ return ret;
+}
+
+
+//
+// CoreAudio producer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_producer_coreaudio_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_producer_coreaudio_t *producer = self;
+ if(producer){
+ /* init base */
+ tdav_producer_audio_init(TDAV_PRODUCER_AUDIO(producer));
+ /* init self */
+ // TODO
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_producer_coreaudio_dtor(tsk_object_t * self)
+{
+ tdav_producer_coreaudio_t *producer = self;
+ if(producer){
+ // Stop the producer if not done
+ if(producer->started){
+ tdav_producer_coreaudio_stop(self);
+ }
+
+ // Free all buffers and dispose the queue
+ if (producer->queue) {
+ tsk_size_t i;
+
+ for(i=0; i<CoreAudioRecordBuffers; i++){
+ AudioQueueFreeBuffer(producer->queue, producer->buffers[i]);
+ }
+ AudioQueueDispose(producer->queue, true);
+ }
+
+ /* deinit base */
+ tdav_producer_audio_deinit(TDAV_PRODUCER_AUDIO(producer));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_producer_coreaudio_def_s =
+{
+ sizeof(tdav_producer_coreaudio_t),
+ tdav_producer_coreaudio_ctor,
+ tdav_producer_coreaudio_dtor,
+ tdav_producer_audio_cmp,
+};
+/* plugin definition*/
+static const tmedia_producer_plugin_def_t tdav_producer_coreaudio_plugin_def_s =
+{
+ &tdav_producer_coreaudio_def_s,
+
+ tmedia_audio,
+ "Apple CoreAudio producer",
+
+ tdav_producer_coreaudio_set,
+ tdav_producer_coreaudio_prepare,
+ tdav_producer_coreaudio_start,
+ tdav_producer_coreaudio_pause,
+ tdav_producer_coreaudio_stop
+};
+const tmedia_producer_plugin_def_t *tdav_producer_coreaudio_plugin_def_t = &tdav_producer_coreaudio_plugin_def_s;
+
+#endif /* HAVE_COREAUDIO */
diff --git a/tinyDAV/src/audio/directsound/tdav_consumer_dsound.c b/tinyDAV/src/audio/directsound/tdav_consumer_dsound.c
new file mode 100644
index 0000000..dd8d6ed
--- /dev/null
+++ b/tinyDAV/src/audio/directsound/tdav_consumer_dsound.c
@@ -0,0 +1,377 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_dsound.c
+ * @brief Microsoft DirectSound consumer.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/directsound/tdav_consumer_dsound.h"
+
+#if HAVE_DSOUND_H
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "dsound.lib")
+# pragma comment(lib, "dxguid.lib")
+#endif
+
+#include "tinydav/tdav_win32.h"
+
+#include "tsk_thread.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <initguid.h>
+
+#define tdav_consumer_dsound_set tsk_null
+
+static void *__playback_thread(void *param)
+{
+ tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)param;
+
+ HRESULT hr;
+ LPVOID lpvAudio1, lpvAudio2;
+ DWORD dwBytesAudio1, dwBytesAudio2;
+
+ void* data;
+ int index;
+
+ TSK_DEBUG_INFO("__playback_thread -- START");
+
+ SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS);
+
+ for(;;){
+ DWORD dwEvent = WaitForMultipleObjects(sizeof(dsound->notifEvents)/sizeof(HANDLE), dsound->notifEvents, FALSE, INFINITE);
+
+ if(!dsound->started){
+ break;
+ }
+ else {
+ tsk_size_t out_size = 0;
+ data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(dsound), &out_size);
+ index = (dwEvent == (TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT-1)) ? 0 : (dwEvent + 1);
+
+ // lock
+ if((hr = IDirectSoundBuffer_Lock(dsound->secondaryBuffer, (index * dsound->bytes_per_notif), dsound->bytes_per_notif, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundBuffer_Lock", hr);
+ goto next;
+ }
+
+ if(data){
+ // copy data to dsound buffers
+ memcpy(lpvAudio1, data, TSK_MIN(dwBytesAudio1, out_size));
+ if(lpvAudio2){
+ memcpy(lpvAudio2, ((LPBYTE*)data) + dwBytesAudio1, dwBytesAudio2);
+ }
+ }
+ else{
+ // Put silence
+ memset(lpvAudio1, 0, dwBytesAudio1);
+ if(lpvAudio2){
+ memset(lpvAudio2, 0, dwBytesAudio2);
+ }
+ }
+
+ // unlock
+ if((hr = IDirectSoundBuffer_Unlock(dsound->secondaryBuffer, lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundBuffer_UnLock", hr);
+ goto next;
+ }
+next:
+ TSK_FREE(data);
+ }
+ }
+
+ TSK_DEBUG_INFO("__playback_thread -- STOP");
+
+
+ return tsk_null;
+}
+
+
+
+
+/* ============ Media Consumer Interface ================= */
+int tdav_consumer_dsound_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec)
+{
+ HRESULT hr;
+ HWND hWnd;
+
+ WAVEFORMATEX wfx = {0};
+ DSBUFFERDESC dsbd = {0};
+
+ tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)self;
+
+ if(!dsound){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(dsound->device || dsound->primaryBuffer || dsound->secondaryBuffer){
+ TSK_DEBUG_ERROR("Consumer already prepared");
+ return -2;
+ }
+
+ TDAV_CONSUMER_AUDIO(dsound)->channels = codec->plugin->audio.channels;
+ TDAV_CONSUMER_AUDIO(dsound)->rate = codec->plugin->rate;
+
+ /* Create sound device */
+ if((hr = DirectSoundCreate(NULL, &dsound->device, NULL) != DS_OK)){
+ tdav_win32_print_error("DirectSoundCreate", hr);
+ return -3;
+ }
+
+ /* Set CooperativeLevel */
+ if((hWnd = GetConsoleWindow()) || (hWnd = GetDesktopWindow()) || (hWnd = GetForegroundWindow())){
+ if((hr = IDirectSound_SetCooperativeLevel(dsound->device, hWnd, DSSCL_PRIORITY)) != DS_OK){
+ tdav_win32_print_error("IDirectSound_SetCooperativeLevel", hr);
+ }
+ }
+
+ /* Creates the primary buffer and apply format */
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nChannels = TDAV_CONSUMER_AUDIO(dsound)->channels;
+ wfx.nSamplesPerSec = TDAV_CONSUMER_AUDIO(dsound)->rate;
+ wfx.wBitsPerSample = TDAV_CONSUMER_AUDIO(dsound)->bits_per_sample;
+ wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample/8);
+ wfx.nAvgBytesPerSec = (wfx.nSamplesPerSec * wfx.nBlockAlign);
+
+ /* Average bytes (count) for each notification */
+ dsound->bytes_per_notif = ((wfx.nAvgBytesPerSec * TDAV_CONSUMER_AUDIO(dsound)->ptime)/1000);
+
+ dsbd.dwSize = sizeof(DSBUFFERDESC);
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+ dsbd.dwBufferBytes = 0;
+ dsbd.lpwfxFormat = NULL;
+
+ if((hr = IDirectSound_CreateSoundBuffer(dsound->device, &dsbd, &dsound->primaryBuffer, NULL)) != DS_OK){
+ tdav_win32_print_error("IDirectSound_CreateSoundBuffer", hr);
+ return -4;
+ }
+ if((hr = IDirectSoundBuffer_SetFormat(dsound->primaryBuffer, &wfx)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundBuffer_SetFormat", hr);
+ return -5;
+ }
+
+ /* Creates the secondary buffer and apply format */
+ dsbd.dwFlags = (DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS);
+ dsbd.dwBufferBytes = (TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT * dsound->bytes_per_notif);
+ dsbd.lpwfxFormat = &wfx;
+
+ if((hr = IDirectSound_CreateSoundBuffer(dsound->device, &dsbd, &dsound->secondaryBuffer, NULL)) != DS_OK){
+ tdav_win32_print_error("IDirectSound_CreateSoundBuffer", hr);
+ return -6;
+ }
+
+
+ return 0;
+}
+
+int tdav_consumer_dsound_start(tmedia_consumer_t* self)
+{
+ tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)self;
+
+ tsk_size_t i;
+ HRESULT hr;
+ LPDIRECTSOUNDNOTIFY lpDSBNotify;
+ DSBPOSITIONNOTIFY pPosNotify[TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT] = {0};
+
+ if(!dsound){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!dsound->device || !dsound->primaryBuffer || !dsound->secondaryBuffer){
+ TSK_DEBUG_ERROR("Consumer not prepared");
+ return -2;
+ }
+
+ if(dsound->started){
+ TSK_DEBUG_WARN("Consumer already started");
+ return 0;
+ }
+
+ if((hr = IDirectSoundBuffer_QueryInterface(dsound->secondaryBuffer, &IID_IDirectSoundNotify, (LPVOID*)&lpDSBNotify)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundBuffer_QueryInterface", hr);
+ return -3;
+ }
+
+ /* Events associated to notification points */
+ for(i = 0; i<sizeof(dsound->notifEvents)/sizeof(HANDLE); i++){
+ dsound->notifEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pPosNotify[i].dwOffset = ((dsound->bytes_per_notif * i) + dsound->bytes_per_notif) - 1;
+ pPosNotify[i].hEventNotify = dsound->notifEvents[i];
+ }
+ if((hr = IDirectSoundNotify_SetNotificationPositions(lpDSBNotify, TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT, pPosNotify)) != DS_OK){
+ IDirectSoundNotify_Release(lpDSBNotify);
+ tdav_win32_print_error("IDirectSoundBuffer_QueryInterface", hr);
+ return -4;
+ }
+
+ if((hr = IDirectSoundNotify_Release(lpDSBNotify))){
+ tdav_win32_print_error("IDirectSoundNotify_Release", hr);
+ }
+
+ /* start the reader thread */
+ tsk_thread_create(&dsound->tid[0], __playback_thread, dsound);
+
+ /* Start the buffer */
+ if((hr = IDirectSoundBuffer_Play(dsound->secondaryBuffer,0, 0, DSBPLAY_LOOPING)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundNotify_Release", hr);
+ return -5;
+ }
+
+ dsound->started = tsk_true;
+
+ return 0;
+}
+
+int tdav_consumer_dsound_consume(tmedia_consumer_t* self, void** buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
+{
+ tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)self;
+
+ if(!dsound || !buffer || !*buffer || !size){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ /* buffer is already decoded */
+ return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(dsound), buffer, size, proto_hdr);
+}
+
+int tdav_consumer_dsound_pause(tmedia_consumer_t* self)
+{
+ return 0;
+}
+
+int tdav_consumer_dsound_stop(tmedia_consumer_t* self)
+{
+ tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)self;
+
+ HRESULT hr;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!dsound->started){
+ TSK_DEBUG_WARN("Consumer not started");
+ return 0;
+ }
+
+ /* should be done here */
+ dsound->started = tsk_false;
+
+ /* stop thread */
+ if(dsound->tid[0]){
+ tsk_thread_join(&(dsound->tid[0]));
+ }
+
+ if((hr = IDirectSoundBuffer_Stop(dsound->secondaryBuffer)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundBuffer_Stop", hr);
+ }
+ if((hr = IDirectSoundBuffer_SetCurrentPosition(dsound->secondaryBuffer, 0)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundBuffer_SetCurrentPosition", hr);
+ }
+
+ return 0;
+}
+
+
+//
+// WaveAPI consumer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_consumer_dsound_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_consumer_dsound_t *consumer = self;
+ if(consumer){
+ /* init base */
+ tdav_consumer_audio_init(TDAV_CONSUMER_AUDIO(consumer));
+ /* init self */
+
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_consumer_dsound_dtor(tsk_object_t * self)
+{
+ tdav_consumer_dsound_t *dsound = self;
+ if(dsound){
+ tsk_size_t i;
+
+ /* stop */
+ if(dsound->started){
+ tdav_consumer_dsound_stop(self);
+ }
+
+ /* deinit base */
+ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(dsound));
+ /* deinit self */
+ // Delete secondary buffer
+ if(dsound->primaryBuffer){
+ IDirectSoundBuffer_Release(dsound->primaryBuffer);
+ }
+ if(dsound->secondaryBuffer){
+ IDirectSoundBuffer_Release(dsound->secondaryBuffer);
+ }
+ if(dsound->device){
+ IDirectSound_Release(dsound->device);
+ }
+ for(i = 0; i<sizeof(dsound->notifEvents)/sizeof(HANDLE); i++){
+ if(dsound->notifEvents[i]){
+ CloseHandle(dsound->notifEvents[i]);
+ }
+ }
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_consumer_dsound_def_s =
+{
+ sizeof(tdav_consumer_dsound_t),
+ tdav_consumer_dsound_ctor,
+ tdav_consumer_dsound_dtor,
+ tdav_consumer_audio_cmp,
+};
+/* plugin definition*/
+static const tmedia_consumer_plugin_def_t tdav_consumer_dsound_plugin_def_s =
+{
+ &tdav_consumer_dsound_def_s,
+
+ tmedia_audio,
+ "Microsoft DirectSound consumer",
+
+ tdav_consumer_dsound_set,
+ tdav_consumer_dsound_prepare,
+ tdav_consumer_dsound_start,
+ tdav_consumer_dsound_consume,
+ tdav_consumer_dsound_pause,
+ tdav_consumer_dsound_stop
+};
+const tmedia_consumer_plugin_def_t *tdav_consumer_dsound_plugin_def_t = &tdav_consumer_dsound_plugin_def_s;
+
+
+#endif /* HAVE_DSOUND_H */ \ No newline at end of file
diff --git a/tinyDAV/src/audio/directsound/tdav_producer_dsound.c b/tinyDAV/src/audio/directsound/tdav_producer_dsound.c
new file mode 100644
index 0000000..8e80ec6
--- /dev/null
+++ b/tinyDAV/src/audio/directsound/tdav_producer_dsound.c
@@ -0,0 +1,320 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_dsound.c
+ * @brief Microsoft DirectSound producer.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/directsound/tdav_producer_dsound.h"
+
+#if HAVE_DSOUND_H
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "dsound.lib")
+# pragma comment(lib, "dxguid.lib")
+#endif
+
+#include "tinydav/tdav_win32.h"
+
+#include "tsk_thread.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <initguid.h>
+
+#define tdav_producer_dsound_set tsk_null
+
+static void *__playback_thread(void *param)
+{
+ tdav_producer_dsound_t* dsound = (tdav_producer_dsound_t*)param;
+
+ HRESULT hr;
+ LPVOID lpvAudio1, lpvAudio2;
+ DWORD dwBytesAudio1, dwBytesAudio2;
+
+ TSK_DEBUG_INFO("__record_thread -- START");
+
+ SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS);
+
+ for(;;){
+ DWORD dwEvent = WaitForMultipleObjects(sizeof(dsound->notifEvents)/sizeof(HANDLE), dsound->notifEvents, FALSE, INFINITE);
+
+ if(!dsound->started){
+ break;
+ }
+ else {
+ // lock
+ if((hr = IDirectSoundCaptureBuffer_Lock(dsound->captureBuffer, (dwEvent * dsound->bytes_per_notif), dsound->bytes_per_notif, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundCaptureBuffer_Lock", hr);
+ goto next;
+ }
+
+ if(TMEDIA_PRODUCER(dsound)->enc_cb.callback){
+ if(lpvAudio2){
+ TMEDIA_PRODUCER(dsound)->enc_cb.callback(TMEDIA_PRODUCER(dsound)->enc_cb.callback_data, lpvAudio1, dwBytesAudio1);
+ TMEDIA_PRODUCER(dsound)->enc_cb.callback(TMEDIA_PRODUCER(dsound)->enc_cb.callback_data, lpvAudio2, dwBytesAudio2);
+ }
+ else{
+ TMEDIA_PRODUCER(dsound)->enc_cb.callback(TMEDIA_PRODUCER(dsound)->enc_cb.callback_data, lpvAudio1, dwBytesAudio1);
+ }
+ }
+
+ // unlock
+ if((hr = IDirectSoundCaptureBuffer_Unlock(dsound->captureBuffer, lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundCaptureBuffer_Unlock", hr);
+ goto next;
+ }
+next:;
+ }
+ }
+
+ TSK_DEBUG_INFO("__record_thread -- STOP");
+
+
+ return tsk_null;
+}
+
+
+
+
+/* ============ Media Producer Interface ================= */
+int tdav_producer_dsound_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec)
+{
+ HRESULT hr;
+
+ WAVEFORMATEX wfx = {0};
+ DSCBUFFERDESC dsbd = {0};
+
+ tdav_producer_dsound_t* dsound = (tdav_producer_dsound_t*)self;
+
+ if(!dsound || !codec){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(dsound->device || dsound->captureBuffer){
+ TSK_DEBUG_ERROR("Producer already prepared");
+ return -2;
+ }
+
+ TDAV_PRODUCER_AUDIO(dsound)->channels = codec->plugin->audio.channels;
+ TDAV_PRODUCER_AUDIO(dsound)->rate = codec->plugin->rate;
+
+ /* Create capture device */
+ if((hr = DirectSoundCaptureCreate(NULL, &dsound->device, NULL) != DS_OK)){
+ tdav_win32_print_error("DirectSoundCaptureCreate", hr);
+ return -3;
+ }
+
+ /* Creates the capture buffer */
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nChannels = TDAV_PRODUCER_AUDIO(dsound)->channels;
+ wfx.nSamplesPerSec = TDAV_PRODUCER_AUDIO(dsound)->rate;
+ wfx.wBitsPerSample = TDAV_PRODUCER_AUDIO(dsound)->bits_per_sample;
+ wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample/8);
+ wfx.nAvgBytesPerSec = (wfx.nSamplesPerSec * wfx.nBlockAlign);
+
+ /* Average bytes (count) for each notification */
+ dsound->bytes_per_notif = ((wfx.nAvgBytesPerSec * TDAV_PRODUCER_AUDIO(dsound)->ptime)/1000);
+
+ dsbd.dwSize = sizeof(DSCBUFFERDESC);
+ dsbd.dwBufferBytes = (TDAV_DSOUNS_PRODUCER_NOTIF_POS_COUNT * dsound->bytes_per_notif);
+ dsbd.lpwfxFormat = &wfx;
+
+ if((hr = IDirectSoundCapture_CreateCaptureBuffer(dsound->device, &dsbd, &dsound->captureBuffer, NULL)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundCapture_CreateCaptureBuffer", hr);
+ return -4;
+ }
+
+ return 0;
+}
+
+int tdav_producer_dsound_start(tmedia_producer_t* self)
+{
+ tdav_producer_dsound_t* dsound = (tdav_producer_dsound_t*)self;
+
+ tsk_size_t i;
+ HRESULT hr;
+ LPDIRECTSOUNDNOTIFY lpDSBNotify;
+ DSBPOSITIONNOTIFY pPosNotify[TDAV_DSOUNS_PRODUCER_NOTIF_POS_COUNT] = {0};
+
+ if(!dsound){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!dsound->device || !dsound->captureBuffer){
+ TSK_DEBUG_ERROR("Producer not prepared");
+ return -2;
+ }
+
+ if(dsound->started){
+ TSK_DEBUG_WARN("Producer already started");
+ return 0;
+ }
+
+ if((hr = IDirectSoundCaptureBuffer_QueryInterface(dsound->captureBuffer, &IID_IDirectSoundNotify, (LPVOID*)&lpDSBNotify)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundCaptureBuffer_QueryInterface", hr);
+ return -3;
+ }
+
+ /* Events associated to notification points */
+ for(i = 0; i<sizeof(dsound->notifEvents)/sizeof(HANDLE); i++){
+ dsound->notifEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pPosNotify[i].dwOffset = ((dsound->bytes_per_notif * i) + dsound->bytes_per_notif) - 1;
+ pPosNotify[i].hEventNotify = dsound->notifEvents[i];
+ }
+ if((hr = IDirectSoundNotify_SetNotificationPositions(lpDSBNotify, TDAV_DSOUNS_PRODUCER_NOTIF_POS_COUNT, pPosNotify)) != DS_OK){
+ IDirectSoundNotify_Release(lpDSBNotify);
+ tdav_win32_print_error("IDirectSoundBuffer_QueryInterface", hr);
+ return -4;
+ }
+
+ if((hr = IDirectSoundNotify_Release(lpDSBNotify))){
+ tdav_win32_print_error("IDirectSoundNotify_Release", hr);
+ }
+
+ /* start the reader thread */
+ tsk_thread_create(&dsound->tid[0], __playback_thread, dsound);
+
+ /* Start the buffer */
+ if((hr = IDirectSoundCaptureBuffer_Start(dsound->captureBuffer, DSBPLAY_LOOPING)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundCaptureBuffer_Start", hr);
+ return -5;
+ }
+
+ dsound->started = tsk_true;
+
+ return 0;
+}
+
+int tdav_producer_dsound_pause(tmedia_producer_t* self)
+{
+ return 0;
+}
+
+int tdav_producer_dsound_stop(tmedia_producer_t* self)
+{
+ tdav_producer_dsound_t* dsound = (tdav_producer_dsound_t*)self;
+
+ HRESULT hr;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!dsound->started){
+ TSK_DEBUG_WARN("Producer not started");
+ return 0;
+ }
+
+ /* should be done here */
+ dsound->started = tsk_false;
+
+ /* stop thread */
+ if(dsound->tid[0]){
+ tsk_thread_join(&(dsound->tid[0]));
+ }
+
+ if((hr = IDirectSoundCaptureBuffer_Stop(dsound->captureBuffer)) != DS_OK){
+ tdav_win32_print_error("IDirectSoundCaptureBuffer_Stop", hr);
+ }
+
+ return 0;
+}
+
+
+//
+// WaveAPI producer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_producer_dsound_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_producer_dsound_t *producer = self;
+ if(producer){
+ /* init base */
+ tdav_producer_audio_init(TDAV_PRODUCER_AUDIO(producer));
+ /* init self */
+
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_producer_dsound_dtor(tsk_object_t * self)
+{
+ tdav_producer_dsound_t *dsound = self;
+ if(dsound){
+ tsk_size_t i;
+
+ /* stop */
+ if(dsound->started){
+ tdav_producer_dsound_stop(self);
+ }
+
+ /* deinit base */
+ tdav_producer_audio_deinit(TDAV_PRODUCER_AUDIO(dsound));
+ /* deinit self */
+ if(dsound->captureBuffer){
+ IDirectSoundCaptureBuffer_Release(dsound->captureBuffer);
+ }
+ if(dsound->device){
+ IDirectSoundCapture_Release(dsound->device);
+ }
+ for(i = 0; i<sizeof(dsound->notifEvents)/sizeof(HANDLE); i++){
+ if(dsound->notifEvents[i]){
+ CloseHandle(dsound->notifEvents[i]);
+ }
+ }
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_producer_dsound_def_s =
+{
+ sizeof(tdav_producer_dsound_t),
+ tdav_producer_dsound_ctor,
+ tdav_producer_dsound_dtor,
+ tdav_producer_audio_cmp,
+};
+/* plugin definition*/
+static const tmedia_producer_plugin_def_t tdav_producer_dsound_plugin_def_s =
+{
+ &tdav_producer_dsound_def_s,
+
+ tmedia_audio,
+ "Microsoft DirectSound producer",
+
+ tdav_producer_dsound_set,
+ tdav_producer_dsound_prepare,
+ tdav_producer_dsound_start,
+ tdav_producer_dsound_pause,
+ tdav_producer_dsound_stop
+};
+const tmedia_producer_plugin_def_t *tdav_producer_dsound_plugin_def_t = &tdav_producer_dsound_plugin_def_s;
+
+
+#endif /* HAVE_DSOUND_H */ \ No newline at end of file
diff --git a/tinyDAV/src/audio/tdav_consumer_audio.c b/tinyDAV/src/audio/tdav_consumer_audio.c
new file mode 100644
index 0000000..289e0c4
--- /dev/null
+++ b/tinyDAV/src/audio/tdav_consumer_audio.c
@@ -0,0 +1,311 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_audio.c
+ * @brief Base class for all Audio consumers.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/tdav_consumer_audio.h"
+
+#include "tinymedia/tmedia_denoise.h"
+#include "tinyrtp/rtp/trtp_rtp_header.h"
+
+#include "tsk_memory.h"
+#include "tsk_time.h"
+#include "tsk_debug.h"
+
+#if TSK_UNDER_WINDOWS
+# include <Winsock2.h> // timeval
+#elif defined(__SYMBIAN32__)
+# include <_timeval.h>
+#else
+# include <sys/time.h>
+#endif
+
+#define TDAV_BITS_PER_SAMPLE_DEFAULT 16
+#define TDAV_CHANNELS_DEFAULT 2
+#define TDAV_RATE_DEFAULT 8000
+#define TDAV_PTIME_DEFAULT 20
+
+#define TDAV_10MS 10
+#define TDAV_10MS_FRAME_SIZE(self) (((self)->rate * TDAV_10MS)/1000)
+#define TDAV_PTIME_FRAME_SIZE(self) (((self)->rate * (self)->ptime)/1000)
+
+int static size_of_short = sizeof(short);
+
+/** Initialize audio consumer */
+int tdav_consumer_audio_init(tdav_consumer_audio_t* self)
+{
+ int ret;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ /* base */
+ if((ret = tmedia_consumer_init(TMEDIA_CONSUMER(self)))){
+ return ret;
+ }
+
+ /* self (should be update by prepare() by using the codec's info)*/
+ self->bits_per_sample = TDAV_BITS_PER_SAMPLE_DEFAULT;
+ self->channels = TDAV_CHANNELS_DEFAULT;
+ self->rate = TDAV_RATE_DEFAULT;
+ self->ptime = TDAV_PTIME_DEFAULT;
+
+ /* self:jitterbuffer */
+ if(!self->jb.jbuffer){
+ self->jb.jbuffer = jb_new();
+ self->jb.jcodec = JB_CODEC_OTHER; // FIXME: e.g. JB_CODEC_G711x
+ }
+
+ tsk_safeobj_init(self);
+
+ return 0;
+}
+
+/**
+* Generic function to compare two consumers.
+* @param consumer1 The first consumer to compare.
+* @param consumer2 The second consumer to compare.
+* @retval Returns an integral value indicating the relationship between the two consumers:
+* <0 : @a consumer1 less than @a consumer2.<br>
+* 0 : @a consumer1 identical to @a consumer2.<br>
+* >0 : @a consumer1 greater than @a consumer2.<br>
+*/
+int tdav_consumer_audio_cmp(const tsk_object_t* consumer1, const tsk_object_t* consumer2)
+{
+ return (TDAV_CONSUMER_AUDIO(consumer1) - TDAV_CONSUMER_AUDIO(consumer2));
+}
+
+/* put data (bytes not shorts) into the jitter buffer (consumers always have ptime of 20ms) */
+int tdav_consumer_audio_put(tdav_consumer_audio_t* self, void** data, tsk_size_t size, const tsk_object_t* proto_hdr)
+{
+ const trtp_rtp_header_t* rtp_hdr = (const trtp_rtp_header_t*)proto_hdr;
+ int i, _10ms_size_shorts, _10ms_size_bytes;
+ long now, ts;
+ short* _10ms_buf; // 10ms frame
+
+ if(!self || !data || !*data || !self->jb.jbuffer || !rtp_hdr){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* synchronize the reference timestamp */
+ if(!self->jb.ref_timestamp){
+ uint64_t epoch = tsk_time_epoch();
+ struct timeval tv;
+ long ts = (rtp_hdr->timestamp/(self->rate/1000));
+ //=> Do not use (see clock_gettime() on linux): tsk_gettimeofday(&tv, tsk_null);
+ tv.tv_sec = (long)(epoch)/1000;
+ tv.tv_usec = (long)(epoch - (tv.tv_sec*1000))*1000;
+
+ tv.tv_sec -= (ts / self->rate);
+ tv.tv_usec -= (ts % self->rate) * 125;
+ if((tv.tv_usec -= (tv.tv_usec % (TDAV_10MS * 10000))) <0){
+ tv.tv_usec += 1000000;
+ tv.tv_sec -= 1;
+ }
+ self->jb.ref_timestamp = tsk_time_get_ms(&tv);
+
+ switch(rtp_hdr->payload_type){
+ // FIXME: TMEDIA_CODEC_FORMAT_* are "char*" just define int values to avoid char comparison
+ case 8: /*TMEDIA_CODEC_FORMAT_G711a*/
+ case 0: /* TMEDIA_CODEC_FORMAT_G711u */
+ self->jb.jcodec = JB_CODEC_G711x;
+ break;
+ case 18: /* TMEDIA_CODEC_FORMAT_G729 */
+ self->jb.jcodec = JB_CODEC_G729A;
+ break;
+ case 3: /* TMEDIA_CODEC_FORMAT_GSM */
+ self->jb.jcodec = JB_CODEC_GSM_EFR;
+ break;
+
+ default:
+ self->jb.jcodec = JB_CODEC_OTHER;
+ break;
+ }
+ }
+
+ tsk_safeobj_lock(self);
+ // split as several 10ms frames
+ now = (long) (tsk_time_now()-self->jb.ref_timestamp);
+ ts = (long)(rtp_hdr->timestamp/(self->rate/1000));
+ _10ms_size_shorts = TDAV_10MS_FRAME_SIZE(self);
+ _10ms_size_bytes = _10ms_size_shorts * size_of_short;
+ for(i=0; i<(int)(size/_10ms_size_bytes);i++){
+ if((_10ms_buf = tsk_calloc(_10ms_size_shorts, size_of_short))){
+ memcpy(_10ms_buf, &((uint8_t*)*data)[i*_10ms_size_bytes], _10ms_size_bytes);
+ jb_put(self->jb.jbuffer, _10ms_buf, JB_TYPE_VOICE, TDAV_10MS, ts, now, self->jb.jcodec);
+ _10ms_buf = tsk_null;
+ }
+ ts += TDAV_10MS;
+ }
+ tsk_safeobj_unlock(self);
+
+ return 0;
+}
+
+/* get data drom the jitter buffer (consumers should always have ptime of 20ms) */
+void* tdav_consumer_audio_get(tdav_consumer_audio_t* self, tsk_size_t* out_size)
+{
+ void* data = tsk_null;
+ int jret;
+
+ int i, _10ms_count, _10ms_size_bytes, _10ms_size_shorts;
+ long now;
+ short* _10ms_buf = tsk_null;
+
+ *out_size = 0;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ _10ms_size_shorts = TDAV_10MS_FRAME_SIZE(self);
+ _10ms_size_bytes = (_10ms_size_shorts * size_of_short);
+ _10ms_count = (TDAV_PTIME_FRAME_SIZE(self)/_10ms_size_shorts);
+ now = (long) (tsk_time_now()-self->jb.ref_timestamp);
+
+ tsk_safeobj_lock(self);
+ for(i=0; i<_10ms_count; i++){
+
+ jret = jb_get(self->jb.jbuffer, (void**)&_10ms_buf, now, TDAV_10MS);
+
+ //if(!_10ms_buf){
+ // TSK_DEBUG_ERROR("NO DATA");
+ //}
+
+ switch(jret){
+ case JB_INTERP:
+ TSK_DEBUG_INFO("JB_INTERP");
+ jb_reset_all(self->jb.jbuffer);
+ if((data = tsk_realloc(data, _10ms_size_bytes * _10ms_count))){
+ *out_size = _10ms_size_bytes * _10ms_count;
+ memset(data, 0, *out_size); // silence
+ }
+ i = _10ms_count; // for exit
+ break;
+ case JB_OK:
+ case JB_EMPTY:
+ case JB_NOFRAME:
+ case JB_NOJB:
+ {
+ if(data){
+ if((data = tsk_realloc(data, (*out_size + _10ms_size_bytes)))){
+ if(_10ms_buf && (jret == JB_OK)){
+ /* copy data */
+ memcpy(&((uint8_t*)data)[*out_size], _10ms_buf, _10ms_size_bytes);
+ }
+ else{
+ /* copy silence */
+ memset(&((uint8_t*)data)[*out_size], 0, _10ms_size_bytes);
+ }
+ *out_size += _10ms_size_bytes;
+ }
+ else{ /* realloc failed */
+ *out_size = 0;
+ }
+ }
+ else{
+ data = _10ms_buf, _10ms_buf = tsk_null;
+ *out_size = _10ms_size_bytes;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ TSK_FREE(_10ms_buf);
+ }
+ tsk_safeobj_unlock(self);
+
+ // Denoise()
+ if(data && *out_size == _10ms_size_bytes*2){
+ if(self->denoise && self->denoise->opened){
+ tmedia_denoise_echo_playback(self->denoise, data);
+ }
+ }
+ //else{
+ // TSK_DEBUG_WARN("Invalid buffer");
+ //}
+
+ return data;
+}
+
+/* set denioiser */
+void tdav_consumer_audio_set_denoise(tdav_consumer_audio_t* self, struct tmedia_denoise_s* denoise)
+{
+ TSK_OBJECT_SAFE_FREE(self->denoise);
+ self->denoise = tsk_object_ref(denoise);
+}
+
+/** Reset jitterbuffer */
+int tdav_consumer_audio_reset(tdav_consumer_audio_t* self){
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_safeobj_lock(self);
+ if(self->jb.jbuffer){
+ jb_reset_all(self->jb.jbuffer);
+ }
+ tsk_safeobj_unlock(self);
+
+ return 0;
+}
+
+/* tsk_safeobj_lock(self); */
+/* tsk_safeobj_unlock(self); */
+
+/** DeInitialize audio consumer */
+int tdav_consumer_audio_deinit(tdav_consumer_audio_t* self)
+{
+ int ret;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* base */
+ if((ret = tmedia_consumer_deinit(TMEDIA_CONSUMER(self)))){
+ /* return ret; */
+ }
+
+ /* self */
+ if(self->jb.jbuffer){
+ jb_destroy(self->jb.jbuffer);
+ }
+ TSK_OBJECT_SAFE_FREE(self->denoise);
+
+ tsk_safeobj_deinit(self);
+
+ return 0;
+}
+
diff --git a/tinyDAV/src/audio/tdav_jitterbuffer.c b/tinyDAV/src/audio/tdav_jitterbuffer.c
new file mode 100644
index 0000000..4794f80
--- /dev/null
+++ b/tinyDAV/src/audio/tdav_jitterbuffer.c
@@ -0,0 +1,1034 @@
+/* File from: http://cms.speakup.nl/tech/opensource/jitterbuffer/verslag-20051209.pdf/ */
+
+/*******************************************************
+* jitterbuffer:
+* an application-independent jitterbuffer, which tries
+* to achieve the maximum user perception during a call.
+* For more information look at:
+* http://www.speakup.nl/opensource/jitterbuffer/
+*
+* Copyright on this file is held by:
+* - Jesse Kaijen <jesse@speakup.nl>
+* - SpeakUp <info@speakup.nl>
+*
+* Contributors:
+* Jesse Kaijen <jesse@speakup.nl>
+*
+* This program is free software, distributed under the terms of:
+* - the GNU Lesser (Library) General Public License
+* - the Mozilla Public License
+*
+* if you are interested in an different licence type, please contact us.
+*
+* How to use the jitterbuffer, please look at the comments
+* in the headerfile.
+*
+* Further details on specific implementations,
+* please look at the comments in the code file.
+*/
+#include "tinydav/audio/tdav_jitterbuffer.h"
+
+#include "tsk_memory.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
+#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
+#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+
+//public functions
+jitterbuffer *jb_new();
+void jb_reset(jitterbuffer *jb);
+void jb_reset_all(jitterbuffer *jb);
+void jb_destroy(jitterbuffer *jb);
+void jb_set_settings(jitterbuffer *jb, jb_settings *settings);
+
+void jb_get_info(jitterbuffer *jb, jb_info *stats);
+void jb_get_settings(jitterbuffer *jb, jb_settings *settings);
+float jb_guess_mos(float p, long d, int codec);
+int jb_has_frames(jitterbuffer *jb);
+
+void jb_put(jitterbuffer *jb, void *data, int type, long ms, long ts, long now, int codec);
+int jb_get(jitterbuffer *jb, void **data, long now, long interpl);
+
+
+
+//private functions
+static void set_default_settings(jitterbuffer *jb);
+static void reset(jitterbuffer *jb);
+static long find_pointer(long *array, long max_index, long value); static void frame_free(jb_frame *frame);
+
+static void put_control(jitterbuffer *jb, void *data, int type, long ts);
+static void put_voice(jitterbuffer *jb, void *data, int type, long ms, long ts, int codec);
+static void put_history(jitterbuffer *jb, long ts, long now, long ms, int codec);
+static void calculate_info(jitterbuffer *jb, long ts, long now, int codec);
+
+static int get_control(jitterbuffer *jb, void **data);
+static int get_voice(jitterbuffer *jb, void **data, long now, long interpl);
+static int get_voicecase(jitterbuffer *jb, void **data, long now, long interpl, long diff);
+
+static int get_next_frametype(jitterbuffer *jb, long ts);
+static long get_next_framets(jitterbuffer *jb);
+static jb_frame *get_frame(jitterbuffer *jb, long ts);
+static jb_frame *get_all_frames(jitterbuffer *jb);
+
+//debug...
+static jb_output_function_t warnf, errf, dbgf;
+void jb_setoutput(jb_output_function_t warn, jb_output_function_t err, jb_output_function_t dbg) {
+ warnf = warn;
+ errf = err;
+ dbgf = dbg;
+}
+
+
+/***********
+ * create a new jitterbuffer
+ * return NULL if malloc doesn't work
+ * else return jb with default_settings.
+ */
+jitterbuffer *jb_new()
+{
+ jitterbuffer *jb;
+
+ jb_dbg("N");
+ jb = tsk_calloc(1, sizeof(jitterbuffer));
+ if (!jb) {
+ jb_err("cannot allocate jitterbuffer\n");
+ return NULL;
+ }
+ set_default_settings(jb);
+ reset(jb);
+ return jb;
+}
+
+
+/***********
+ * empty voice messages
+ * reset statistics
+ * keep the settings
+ */
+void jb_reset(jitterbuffer *jb)
+{
+ jb_frame *frame;
+
+ jb_dbg("R");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_reset()\n");
+ return;
+ }
+
+ //free voice
+ while(jb->voiceframes) {
+ frame = get_all_frames(jb);
+ frame_free(frame);
+ }
+ //reset stats
+ memset(&(jb->info),0,sizeof(jb_info) );
+ // set default settings
+ reset(jb);
+}
+
+
+/***********
+ * empty nonvoice messages
+ * empty voice messages
+ * reset statistics
+ * reset settings to default
+ */
+void jb_reset_all(jitterbuffer *jb)
+{
+ jb_frame *frame;
+
+ jb_dbg("r");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_reset_all()\n");
+ return;
+ }
+
+ // free nonvoice
+ while(jb->controlframes) {
+ frame = jb->controlframes;
+ jb->controlframes = frame->next;
+ frame_free(frame);
+ }
+ // free voice and reset statistics is done by jb_reset
+ jb_reset(jb);
+ set_default_settings(jb);
+}
+
+
+/***********
+ * destroy the jitterbuffer
+ * free all the [non]voice frames with reset_all
+ * free the jitterbuffer
+ */
+void jb_destroy(jitterbuffer *jb)
+{
+ jb_dbg("D");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_destroy()\n");
+ return;
+ }
+
+ jb_reset_all(jb);
+ free(jb);
+}
+
+
+/***********
+ * Set settings for the jitterbuffer.
+ * Only if a setting is defined it will be written
+ * in the jb->settings.
+ * This means that no setting can be set to zero
+ */
+void jb_set_settings(jitterbuffer *jb, jb_settings *settings)
+{
+ jb_dbg("S");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_set_settings()\n");
+ return;
+ }
+
+ if (settings->min_jb) {
+ jb->settings.min_jb = settings->min_jb;
+ }
+ if (settings->max_jb) {
+ jb->settings.max_jb = settings->max_jb;
+ }
+ if (settings->max_successive_interp) {
+ jb->settings.max_successive_interp = settings->max_successive_interp;
+ }
+ if (settings->extra_delay) {
+ jb->settings.extra_delay = settings->extra_delay;
+ }
+ if (settings->wait_grow) {
+ jb->settings.wait_grow = settings->wait_grow;
+ }
+ if (settings->wait_shrink) {
+ jb->settings.wait_shrink = settings->wait_shrink;
+ }
+ if (settings->max_diff) {
+ jb->settings.max_diff = settings->max_diff;
+ }
+}
+
+
+/***********
+ * validates the statistics
+ * the losspct due the jitterbuffer will be calculated.
+ * delay and delay_target will be calculated
+ * *stats = info
+ */
+void jb_get_info(jitterbuffer *jb, jb_info *stats)
+{
+ long max_index, pointer;
+
+ jb_dbg("I");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_get_info()\n");
+ return;
+ }
+
+ jb->info.delay = jb->current - jb->min;
+ jb->info.delay_target = jb->target - jb->min;
+
+ //calculate the losspct...
+ max_index = (jb->hist_pointer < JB_HISTORY_SIZE) ?
+jb->hist_pointer : JB_HISTORY_SIZE-1;
+ if (max_index>1) {
+ pointer = find_pointer(&jb->hist_sorted_delay[0], max_index,
+jb->current);
+ jb->info.losspct = ((max_index - pointer)*100/max_index);
+ if (jb->info.losspct < 0) {
+ jb->info.losspct = 0;
+ }
+ } else {
+ jb->info.losspct = 0;
+ }
+
+ *stats = jb->info;
+}
+
+
+/***********
+ * gives the settings for this jitterbuffer
+ * *settings = settings
+ */
+void jb_get_settings(jitterbuffer *jb, jb_settings *settings)
+{
+ jb_dbg("S");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_get_settings()\n");
+ return;
+ }
+
+ *settings = jb->settings;
+}
+
+
+/***********
+ * returns an estimate on the MOS with given loss, delay and codec
+ * if the formula is not present the default will be used
+ * please use the JB_CODEC_OTHER if you want to define your own formula
+ *
+ */
+float jb_guess_mos(float p, long d, int codec)
+{
+ float result;
+
+ switch (codec) {
+ case JB_CODEC_GSM_EFR:
+ result = (4.31f - 0.23f*p - 0.0071f*d);
+ break;
+
+ case JB_CODEC_G723_1:
+ result = (3.99f - 0.16f*p - 0.0071f*d);
+ break;
+
+ case JB_CODEC_G729:
+ case JB_CODEC_G729A:
+ result = (4.13f - 0.14f*p - 0.0071f*d);
+ break;
+
+ case JB_CODEC_G711x_PLC:
+ result = (4.42f - 0.087f*p - 0.0071f*d);
+ break;
+
+ case JB_CODEC_G711x:
+ result = (4.42f - 0.63f*p - 0.0071f*d);
+ break;
+
+ case JB_CODEC_OTHER:
+ default:
+ result = (4.42f - 0.63f*p - 0.0071f*d);
+
+ }
+ return result;
+}
+
+
+/***********
+ * if there are any frames left in JB returns JB_OK, otherwise returns JB_EMPTY
+ */
+int jb_has_frames(jitterbuffer *jb)
+{
+ jb_dbg("H");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_has_frames()\n");
+ return JB_NOJB;
+ }
+
+ if(jb->controlframes || jb->voiceframes) {
+ return JB_OK;
+ } else {
+ return JB_EMPTY;
+ }
+}
+
+
+/***********
+ * Put a packet into the jitterbuffers
+ * Only the timestamps of voicepackets are put in the history
+ * this because the jitterbuffer only works for voicepackets
+ * don't put packets twice in history and queue (e.g. transmitting every frame twice)
+ * keep track of statistics
+ */
+void jb_put(jitterbuffer *jb, void *data, int type, long ms, long ts, long now, int codec)
+{
+ long pointer, max_index;
+
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_put()\n");
+ return;
+ }
+
+ jb->info.frames_received++;
+
+ if (type == JB_TYPE_CONTROL) {
+ //put the packet into the contol-queue of the jitterbuffer
+ jb_dbg("pC");
+ put_control(jb,data,type,ts);
+
+ } else if (type == JB_TYPE_VOICE) {
+ // only add voice that aren't already in the buffer
+ max_index = (jb->hist_pointer < JB_HISTORY_SIZE) ? jb->hist_pointer : JB_HISTORY_SIZE-1;
+ pointer = find_pointer(&jb->hist_sorted_timestamp[0], max_index, ts);
+ if (jb->hist_sorted_timestamp[pointer]==ts) { //timestamp already in queue
+ jb_dbg("pT");
+ free(data);
+ jb->info.frames_dropped_twice++;
+ } else { //add
+ jb_dbg("pV");
+ /* add voicepacket to history */
+ put_history(jb,ts,now,ms,codec);
+ /*calculate jitterbuffer size*/
+ calculate_info(jb, ts, now, codec);
+ /*put the packet into the queue of the jitterbuffer*/
+ put_voice(jb,data,type,ms,ts,codec);
+ }
+
+ } else if (type == JB_TYPE_SILENCE){ //silence
+ jb_dbg("pS");
+ put_voice(jb,data,type,ms,ts,codec);
+
+ } else {//should NEVER happen
+ jb_err("jb_put(): type not known\n");
+ free(data);
+ }
+}
+
+
+/***********
+ * control frames have a higher priority then voice frames
+ * returns JB_OK if a frame is available and *data points to the packet
+ * returns JB_NOFRAME if it's no time to play voice and no control available
+ * returns JB_INTERP if interpolating is required
+ * returns JB_EMPTY if no voice frame is in the jitterbuffer (only during silence)
+ */
+int jb_get(jitterbuffer *jb, void **data, long now, long interpl)
+{
+ int result;
+
+ jb_dbg("A");
+ if (jb == NULL) {
+ jb_err("no jitterbuffer in jb_get()\n");
+ return JB_NOJB;
+ }
+
+ result = get_control(jb, data);
+ if (result != JB_OK ) { //no control message available maybe there is voice...
+ result = get_voice(jb, data, now, interpl);
+ }
+ return result;
+}
+
+
+/***********
+ * set all the settings to default
+ */
+static void set_default_settings(jitterbuffer *jb)
+{
+ jb->settings.min_jb = JB_MIN_SIZE;
+ jb->settings.max_jb = JB_MAX_SIZE;
+ jb->settings.max_successive_interp = JB_MAX_SUCCESSIVE_INTERP;
+ jb->settings.extra_delay = JB_ALLOW_EXTRA_DELAY;
+ jb->settings.wait_grow = JB_WAIT_GROW;
+ jb->settings.wait_shrink = JB_WAIT_SHRINK;
+ jb->settings.max_diff = JB_MAX_DIFF;
+}
+
+
+/***********
+ * reset the jitterbuffer so we can start in silence and
+ * we start with a new history
+ */
+static void reset(jitterbuffer *jb)
+{
+ jb->hist_pointer = 0; //start over
+ jb->silence_begin_ts = 0; //no begin_ts defined
+ jb->info.silence =1; //we always start in silence
+}
+
+
+/***********
+ * Search algorithm
+ * @REQUIRE max_index is within array
+ *
+ * Find the position of value in hist_sorted_delay
+ * if value doesn't exist return first pointer where array[low]>value
+ * int low; //the lowest index being examined
+ * int max_index; //the highest index being examined
+ * int mid; //the middle index between low and max_index.
+ * mid ==(low+max_index)/2
+ * at the end low is the position of value or where array[low]>value
+ */
+static long find_pointer(long *array, long max_index, long value)
+{
+ register long low, mid, high;
+ low = 0;
+ high = max_index;
+ while (low<=high) {
+ mid= (low+high)/2;
+ if (array[mid] < value) {
+ low = mid+1;
+ } else {
+ high = mid-1;
+ }
+ }
+ while(low < max_index && (array[low]==array[(low+1)]) ) {
+ low++;
+ }
+ return low;
+}
+
+
+/***********
+ * free the given frame, afterwards the framepointer is undefined
+ */
+static void frame_free(jb_frame *frame)
+{
+ if (frame->data) {
+ free(frame->data);
+ }
+ free(frame);
+}
+
+
+/***********
+ * put a nonvoice frame into the nonvoice queue
+ */
+static void put_control(jitterbuffer *jb, void *data, int type, long ts)
+{
+ jb_frame *frame, *p;
+
+ frame = malloc(sizeof(jb_frame));
+ if(!frame) {
+ jb_err("cannot allocate frame\n");
+ return;
+ }
+ frame->data = data;
+ frame->ts = ts;
+ frame->type = type;
+ frame->next = NULL;
+ data = NULL;//to avoid stealing memory
+
+ p = jb->controlframes;
+ if (p) { //there are already control messages
+ if (ts < p->ts) {
+ jb->controlframes = frame;
+ frame->next = p;
+ } else {
+ while (p->next && (ts >=p->next->ts)) {//sort on timestamps! so find place to put...
+ p = p->next;
+ }
+ if (p->next) {
+ frame->next = p->next;
+ }
+ p->next = frame;
+ }
+ } else {
+ jb->controlframes = frame;
+ }
+}
+
+
+/***********
+ * put a voice or silence frame into the jitterbuffer
+ */
+static void put_voice(jitterbuffer *jb, void *data, int type, long ms, long ts, int codec)
+{
+ jb_frame *frame, *p;
+ frame = malloc(sizeof(jb_frame));
+ if(!frame) {
+ jb_err("cannot allocate frame\n");
+ return;
+ }
+
+ frame->data = data;
+ frame->ts = ts;
+ frame->ms = ms;
+ frame->type = type;
+ frame->codec = codec;
+
+ data = NULL; //to avoid stealing the memory location
+ /*
+ * frames are a circular list, jb->voiceframes points to to the lowest ts,
+ * jb->voiceframes->prev points to the highest ts
+ */
+ if(!jb->voiceframes) { /* queue is empty */
+ jb->voiceframes = frame;
+ frame->next = frame;
+ frame->prev = frame;
+ } else {
+ p = jb->voiceframes;
+ if(ts < p->prev->ts) { //frame is out of order
+ jb->info.frames_ooo++;
+ }
+ if (ts < p->ts) { //frame is lowest, let voiceframes point to it!
+ jb->voiceframes = frame;
+ } else {
+ while(ts < p->prev->ts ) {
+ p = p->prev;
+ }
+ }
+ frame->next = p;
+ frame->prev = p->prev;
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+ }
+}
+
+
+/***********
+ * puts the timestamps of a received packet in the history of *jb
+ * for later calculations of the size of jitterbuffer *jb.
+ *
+ * summary of function:
+ * - calculate delay difference
+ * - delete old value from hist & sorted_history_delay & sorted_history_timestamp if needed
+ * - add new value to history & sorted_history_delay & sorted_history_timestamp
+ * - we keep sorted_history_delay for calculations
+ * - we keep sorted_history_timestamp for ensuring each timestamp isn't put twice in the buffer.
+ */
+static void put_history(jitterbuffer *jb, long ts, long now, long ms, int codec)
+{
+ jb_hist_element out, in;
+ long max_index, pointer, location;
+
+ // max_index is the highest possible index
+ max_index = (jb->hist_pointer < JB_HISTORY_SIZE) ? jb->hist_pointer : JB_HISTORY_SIZE-1;
+ location = (jb->hist_pointer % JB_HISTORY_SIZE);
+
+ // we want to delete a value from the jitterbuffer
+ // only when we are through the history.
+ if (jb->hist_pointer > JB_HISTORY_SIZE-1) {
+ /* the value we need to delete from sorted histories */
+ out = jb->hist[location];
+ //delete delay from hist_sorted_delay
+ pointer = find_pointer(&jb->hist_sorted_delay[0], max_index, out.delay);
+ /* move over pointer is the position of kicked*/
+ if (pointer<max_index) { //only move if we have something to move
+ memmove( &(jb->hist_sorted_delay[pointer]),
+ &(jb->hist_sorted_delay[pointer+1]),
+ ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
+ }
+
+ //delete timestamp from hist_sorted_timestamp
+ pointer = find_pointer(&jb->hist_sorted_timestamp[0], max_index, out.ts);
+ /* move over pointer is the position of kicked*/
+ if (pointer<max_index) { //only move if we have something to move
+ memmove( &(jb->hist_sorted_timestamp[pointer]),
+ &(jb->hist_sorted_timestamp[pointer+1]),
+ ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
+ }
+ }
+
+ in.delay = now - ts; //delay of current packet
+ in.ts = ts; //timestamp of current packet
+ in.ms = ms; //length of current packet
+ in.codec = codec; //codec of current packet
+
+ /* adding the new delay to the sorted history
+ * first special cases:
+ * - delay is the first history stamp
+ * - delay > highest history stamp
+ */
+ if (max_index==0 || in.delay >= jb->hist_sorted_delay[max_index-1]) {
+ jb->hist_sorted_delay[max_index] = in.delay;
+ } else {
+ pointer = find_pointer(&jb->hist_sorted_delay[0], (max_index-1), in.delay);
+ /* move over and add delay */
+ memmove( &(jb->hist_sorted_delay[pointer+1]),
+ &(jb->hist_sorted_delay[pointer]),
+ ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
+ jb->hist_sorted_delay[pointer] = in.delay;
+ }
+
+ /* adding the new timestamp to the sorted history
+ * first special cases:
+ * - timestamp is the first history stamp
+ * - timestamp > highest history stamp
+ */
+ if (max_index==0 || in.ts >= jb->hist_sorted_timestamp[max_index-1]) {
+ jb->hist_sorted_timestamp[max_index] = in.ts;
+ } else {
+
+ pointer = find_pointer(&jb->hist_sorted_timestamp[0], (max_index-1), in.ts);
+ /* move over and add timestamp */
+ memmove( &(jb->hist_sorted_timestamp[pointer+1]),
+ &(jb->hist_sorted_timestamp[pointer]),
+ ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
+ jb->hist_sorted_timestamp[pointer] = in.ts;
+ }
+
+ /* put the jb_hist_element in the history
+ * then increase hist_pointer for next time
+ */
+ jb->hist[location] = in;
+ jb->hist_pointer++;
+}
+
+
+/***********
+ * this tries to make a jitterbuffer that behaves like
+ * the jitterbuffer proposed in this article:
+ * Adaptive Playout Buffer Algorithm for Enhancing Perceived Quality of Streaming Applications
+ * by: Kouhei Fujimoto & Shingo Ata & Masayuki Murata
+ * http://www.nal.ics.es.osaka-u.ac.jp/achievements/web2002/pdf/journal/k-fujimo02TSJ-AdaptivePlayoutBuffer.pdf
+ *
+ * it calculates jitter and minimum delay
+ * get the best delay for the specified codec
+
+ */
+static void calculate_info(jitterbuffer *jb, long ts, long now, int codec)
+{
+ long diff, size, max_index, d, d1, d2, n;
+ float p, p1, p2, A, B;
+ //size = how many items there in the history
+ size = (jb->hist_pointer < JB_HISTORY_SIZE) ? jb->hist_pointer : JB_HISTORY_SIZE;
+ max_index = size-1;
+
+ /*
+ * the Inter-Quartile Range can be used for estimating jitter
+ * http://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#variable
+ * just take the square root of the iqr for jitter
+ */
+ jb->info.iqr = jb->hist_sorted_delay[max_index*3/4] - jb->hist_sorted_delay[max_index/4];
+
+
+ /*
+ * The RTP way of calculating jitter.
+ * This one is used at the moment, although it is not correct.
+ * But in this way the other side understands us.
+ */
+ diff = now - ts - jb->last_delay;
+ if (!jb->last_delay) {
+ diff = 0; //this to make sure we won't get odd jitter due first ts.
+ }
+ jb->last_delay = now - ts;
+ if (diff <0){
+ diff = -diff;
+ }
+ jb->info.jitter = jb->info.jitter + (diff - jb->info.jitter)/16;
+
+ /* jb->min is minimum delay in hist_sorted_delay, we don't look at the lowest 2% */
+ /* because sometimes there are odd delays in there */
+ jb->min = jb->hist_sorted_delay[(max_index*2/100)];
+
+ /*
+ * calculating the preferred size of the jitterbuffer:
+ * instead of calculating the optimum delay using the Pareto equation
+ * I use look at the array of sorted delays and choose my optimum from there
+ * always walk trough a percentage of the history this because imagine following tail:
+ * [...., 12, 300, 301 ,302]
+ * her we want to discard last three but that won't happen if we won't walk the array
+ * the number of frames we walk depends on how scattered the sorted delays are.
+ * For that we look at the iqr. The dependencies of the iqr are based on
+ * tests we've done here in the lab. But are not optimized.
+ */
+ //init:
+ //the higest delay..
+ d = d1= d2 = jb->hist_sorted_delay[max_index]- jb->min;
+ A=B=LONG_MIN;
+ p = p2 =0;
+ n=0;
+ p1 = 5; //always look at the top 5%
+ if (jb->info.iqr >200) { //with more jitter look at more delays
+ p1=25;
+ } else if (jb->info.iqr >100) {
+ p1=20;
+ } else if (jb->info.iqr >50){
+ p1=11;
+ }
+
+ //find the optimum delay..
+ while(max_index>10 && (B > A ||p2<p1)) { // By MDI: from ">=" to ">"
+ //the packetloss with this delay
+ p2 =(n*100.0f/size);
+ // estimate MOS-value
+ B = jb_guess_mos(p2,d2,codec);
+ if (B > A) {
+ p = p2;
+ d = d2;
+ A = B;
+ }
+ d1 = d2;
+ //find next delay != delay so the same delay isn't calculated twice
+ //don't look further if we have seen half of the history
+ while((d2>=d1) && ((n*2)<max_index) ) {
+ n++;
+ d2 = jb->hist_sorted_delay[(max_index-n)] - jb->min;
+ }
+ }
+ //the targeted size of the jitterbuffer
+ if (jb->settings.min_jb && (jb->settings.min_jb > d) ) {
+ jb->target = jb->min + jb->settings.min_jb;
+ } else if (jb->settings.max_jb && (jb->settings.max_jb > d) ){
+ jb->target = jb->min + jb->settings.max_jb;
+ } else {
+ jb->target = jb->min + d;
+ }
+}
+
+
+/***********
+ * if there is a nonvoice frame it will be returned [*data] and the frame
+ * will be made free
+ */
+static int get_control(jitterbuffer *jb, void **data)
+{
+ jb_frame *frame;
+ int result;
+
+ frame = jb->controlframes;
+ if (frame) {
+ jb_dbg("gC");
+ *data = frame->data;
+ frame->data = NULL;
+ jb->controlframes = frame->next;
+ frame_free(frame);
+ result = JB_OK;
+ } else {
+ result = JB_NOFRAME;
+ }
+ return result;
+}
+
+
+/***********
+ * returns JB_OK if a frame is available and *data points to the packet
+ * returns JB_NOFRAME if it's no time to play voice and or no frame available
+ * returns JB_INTERP if interpolating is required
+ * returns JB_EMPTY if no voice frame is in the jitterbuffer (only during silence)
+ *
+ * if the next frame is a silence frame we will go in silence-mode
+ * each new instance of the jitterbuffer will start in silence mode
+ * in silence mode we will set the jitterbuffer to the size we want
+ * when we are not in silence mode get_voicecase will handle the rest.
+ */
+static int get_voice(jitterbuffer *jb, void **data, long now, long interpl)
+{
+ jb_frame *frame;
+ long diff;
+ int result;
+
+ diff = jb->target - jb->current;
+
+ //if the next frame is a silence frame, go in silence mode...
+ if((get_next_frametype(jb, now - jb->current) == JB_TYPE_SILENCE) ) {
+ jb_dbg("gs");
+ frame = get_frame(jb, now - jb->current);
+ *data = frame->data;
+ frame->data = NULL;
+ jb->info.silence =1;
+ jb->silence_begin_ts = frame->ts;
+ frame_free(frame);
+ result = JB_OK;
+ } else {
+ if(jb->info.silence) { // we are in silence
+ /*
+ * During silence we can set the jitterbuffer size to the size
+ * we want...
+ */
+ if (diff) {
+ jb->current = jb->target;
+ }
+ frame = get_frame(jb, now - jb->current);
+ if (frame) {
+ if (jb->silence_begin_ts && frame->ts < jb->silence_begin_ts) {
+ jb_dbg("gL");
+ /* voice frame is late, next!*/
+ jb->info.frames_late++;
+ frame_free(frame);
+ result = get_voice(jb, data, now, interpl);
+ } else {
+ jb_dbg("gP");
+ /* voice frame */
+ jb->info.silence = 0;
+ jb->silence_begin_ts = 0;
+ jb->next_voice_time = frame->ts + frame->ms;
+ jb->info.last_voice_ms = frame->ms;
+ *data = frame->data;
+ frame->data = NULL;
+ frame_free(frame);
+ result = JB_OK;
+ }
+ } else { //no frame
+ jb_dbg("gS");
+ result = JB_EMPTY;
+ }
+ } else { //voice case
+ result = get_voicecase(jb,data,now,interpl,diff);
+ }
+ }
+ return result;
+}
+
+
+/***********
+ * The voicecase has four 'options'
+ * - difference is way off, reset
+ * - diff > 0, we may need to grow
+ * - diff < 0, we may need to shrink
+ * - everything else
+ */
+static int get_voicecase(jitterbuffer *jb, void **data, long now, long interpl, long diff)
+{
+ jb_frame *frame;
+ int result;
+
+ // * - difference is way off, reset
+ if (diff > jb->settings.max_diff || -diff > jb->settings.max_diff) {
+ jb_err("wakko diff in get_voicecase\n");
+ reset(jb); //reset hist because the timestamps are wakko.
+ result = JB_NOFRAME;
+ //- diff > 0, we may need to grow
+ } else if ((diff > 0) &&
+ (now > (jb->last_adjustment + jb->settings.wait_grow)
+ || (now + jb->current + interpl) < get_next_framets(jb) ) ) { //grow
+ /* first try to grow */
+ if (diff<interpl/2) {
+ jb_dbg("ag");
+ jb->current +=diff;
+ } else {
+ jb_dbg("aG");
+ /* grow by interp frame len */
+ jb->current += interpl;
+ }
+ jb->last_adjustment = now;
+ result = get_voice(jb, data, now, interpl);
+ //- diff < 0, we may need to shrink
+ } else if ( (diff < 0)
+ && (now > (jb->last_adjustment + jb->settings.wait_shrink))
+ && ((-diff) > jb->settings.extra_delay) ) {
+ /* now try to shrink
+ * if there is a frame shrink by frame length
+ * otherwise shrink by interpl
+ */
+ jb->last_adjustment = now;
+
+ frame = get_frame(jb, now - jb->current);
+ if(frame) {
+ jb_dbg("as");
+ /* shrink by frame size we're throwing out */
+ jb->info.frames_dropped++;
+ jb->current -= frame->ms;
+ frame_free(frame);
+ } else {
+ jb_dbg("aS");
+ /* shrink by interpl */
+ jb->current -= interpl;
+ }
+ result = get_voice(jb, data, now, interpl);
+ } else {
+ /* if it is not the time to play a result = JB_NOFRAME
+ * else We try to play a frame if a frame is available
+ * and not late it is played otherwise
+ * if available it is dropped and the next is tried
+ * last option is interpolating
+ */
+ if (now - jb->current < jb->next_voice_time) {
+ jb_dbg("aN");
+ result = JB_NOFRAME;
+ } else {
+ frame = get_frame(jb, now - jb->current);
+ if (frame) { //there is a frame
+ /* voice frame is late */
+ if(frame->ts < jb->next_voice_time) { //late
+ jb_dbg("aL");
+ jb->info.frames_late++;
+ frame_free(frame);
+ result = get_voice(jb, data, now, interpl);
+ } else {
+ jb_dbg("aP");
+ /* normal case; return the frame, increment stuff */
+ *data = frame->data;
+ frame->data = NULL;
+ jb->next_voice_time = frame->ts + frame->ms;
+ jb->cnt_successive_interp = 0;
+ frame_free(frame);
+ result = JB_OK;
+ }
+ } else { // no frame, thus interpolate
+ jb->cnt_successive_interp++;
+ /* assume silence instead of continuing to interpolate */
+ if (jb->settings.max_successive_interp && jb->cnt_successive_interp >= jb->settings.max_successive_interp) {
+ jb->info.silence = 1;
+ jb->silence_begin_ts = jb->next_voice_time;
+ }
+ jb_dbg("aI");
+ jb->next_voice_time += interpl;
+ result = JB_INTERP;
+ }
+ }
+ }
+ return result;
+
+}
+
+
+/***********
+ * if there are frames and next frame->ts is smaller or equal ts
+ * return type of next frame.
+ * else return 0
+ */
+static int get_next_frametype(jitterbuffer *jb, long ts)
+{
+ jb_frame *frame;
+ int result;
+
+ result = 0;
+ frame = jb->voiceframes;
+ if (frame && frame->ts <= ts) {
+ result = frame->type;
+ }
+ return result;
+}
+
+
+/***********
+ * returns ts from next frame in jb->voiceframes
+ * or returns LONG_MAX if there is no frame
+ */
+static long get_next_framets(jitterbuffer *jb)
+{
+ if (jb->voiceframes) {
+ return jb->voiceframes->ts;
+ }
+ return LONG_MAX;
+}
+
+
+/***********
+ * if there is a frame in jb->voiceframes and
+ * has a timestamp smaller/equal to ts
+ * this frame will be returned and
+ * removed from the queue
+ */
+static jb_frame *get_frame(jitterbuffer *jb, long ts)
+{
+ jb_frame *frame;
+
+ frame = jb->voiceframes;
+ if (frame && frame->ts <= ts) {
+ if(frame->next == frame) {
+ jb->voiceframes = NULL;
+ } else {
+ /* remove this frame */
+ frame->prev->next = frame->next;
+ frame->next->prev = frame->prev;
+ jb->voiceframes = frame->next;
+ }
+ return frame;
+ }
+ return NULL;
+}
+
+/***********
+ * if there is a frame in jb->voiceframes
+ * this frame will be unconditionally returned and
+ * removed from the queue
+ */
+static jb_frame *get_all_frames(jitterbuffer *jb)
+{
+ jb_frame *frame;
+
+ frame = jb->voiceframes;
+ if (frame) {
+ if(frame->next == frame) {
+ jb->voiceframes = NULL;
+ } else {
+ /* remove this frame */
+ frame->prev->next = frame->next;
+ frame->next->prev = frame->prev;
+ jb->voiceframes = frame->next;
+ }
+ return frame;
+ }
+ return NULL;
+}
+
+
+//EOF
diff --git a/tinyDAV/src/audio/tdav_producer_audio.c b/tinyDAV/src/audio/tdav_producer_audio.c
new file mode 100644
index 0000000..7efeb79
--- /dev/null
+++ b/tinyDAV/src/audio/tdav_producer_audio.c
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_audio.c
+ * @brief Base class for all Audio producers.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/tdav_producer_audio.h"
+
+#define TDAV_BITS_PER_SAMPLE_DEFAULT 16
+#define TDAV_CHANNELS_DEFAULT 1
+#define TDAV_RATE_DEFAULT 8000
+#define TDAV_PTIME_DEFAULT 20
+
+#include "tsk_debug.h"
+
+/** Initialize Audio producer
+* @param self The producer to initialize
+*/
+int tdav_producer_audio_init(tdav_producer_audio_t* self)
+{
+ int ret;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ /* base */
+ if((ret = tmedia_producer_init(TMEDIA_PRODUCER(self)))){
+ return ret;
+ }
+
+ /* self (should be update by prepare() by using the codec's info)*/
+ self->bits_per_sample = TDAV_BITS_PER_SAMPLE_DEFAULT;
+ self->channels = TDAV_CHANNELS_DEFAULT;
+ self->rate = TDAV_RATE_DEFAULT;
+ self->ptime = TDAV_PTIME_DEFAULT;
+
+ return 0;
+}
+
+/**
+* Generic function to compare two producers.
+* @param producer1 The first producer to compare.
+* @param producer2 The second producer to compare.
+* @retval Returns an integral value indicating the relationship between the two producers:
+* <0 : @a producer1 less than @a producer2.<br>
+* 0 : @a producer1 identical to @a producer2.<br>
+* >0 : @a producer1 greater than @a producer2.<br>
+*/
+int tdav_producer_audio_cmp(const tsk_object_t* producer1, const tsk_object_t* producer2)
+{
+ return (TDAV_PRODUCER_AUDIO(producer1) - TDAV_PRODUCER_AUDIO(producer2));
+}
+
+/** Deinitialize a producer
+*/
+int tdav_producer_audio_deinit(tdav_producer_audio_t* self)
+{
+ int ret;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* base */
+ if((ret = tmedia_producer_deinit(TMEDIA_PRODUCER(self)))){
+ return ret;
+ }
+
+ return ret;
+} \ No newline at end of file
diff --git a/tinyDAV/src/audio/tdav_session_audio.c b/tinyDAV/src/audio/tdav_session_audio.c
new file mode 100644
index 0000000..9a94b5c
--- /dev/null
+++ b/tinyDAV/src/audio/tdav_session_audio.c
@@ -0,0 +1,892 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_session_audio.c
+ * @brief Audio Session plugin.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/tdav_session_audio.h"
+
+#include "tinydav/codecs/dtmf/tdav_codec_dtmf.h"
+#include "tinydav/audio/tdav_consumer_audio.h"
+
+#include "tinymedia/tmedia_denoise.h"
+#include "tinymedia/tmedia_consumer.h"
+#include "tinymedia/tmedia_producer.h"
+
+#include "tinyrtp/trtp_manager.h"
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_timer.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define IS_DTMF_CODEC(codec) (TMEDIA_CODEC((codec))->plugin == tdav_codec_dtmf_plugin_def_t)
+
+static int _tdav_session_audio_dtmfe_timercb(const void* arg, tsk_timer_id_t timer_id);
+static struct tdav_session_audio_dtmfe_s* _tdav_session_audio_dtmfe_create(const tdav_session_audio_t* session, uint8_t event, uint16_t duration, uint32_t seq, uint32_t timestamp, uint8_t format, tsk_bool_t M, tsk_bool_t E);
+static const tmedia_codec_t* _tdav_first_best_neg_codec(const tdav_session_audio_t* session);
+
+
+/* DTMF event object */
+typedef struct tdav_session_audio_dtmfe_s
+{
+ TSK_DECLARE_OBJECT;
+
+ tsk_timer_id_t timer_id;
+ trtp_rtp_packet_t* packet;
+
+ const tdav_session_audio_t* session;
+}
+tdav_session_audio_dtmfe_t;
+extern const tsk_object_def_t *tdav_session_audio_dtmfe_def_t;
+
+// RTP/RTCP callback (From the network to the consumer)
+static int tdav_session_audio_rtp_cb(const void* callback_data, const struct trtp_rtp_packet_s* packet)
+{
+ tdav_session_audio_t* audio = (tdav_session_audio_t*)callback_data;
+
+ if(!audio || !packet){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(audio->consumer){
+ tsk_size_t out_size = 0;
+ tmedia_codec_t* codec;
+ tsk_istr_t format;
+
+ // Find the codec to use to decode the RTP payload
+ tsk_itoa(packet->header->payload_type, &format);
+ if(!(codec = tmedia_codec_find_by_format(TMEDIA_SESSION(audio)->neg_codecs, format)) || !codec->plugin || !codec->plugin->decode){
+ TSK_DEBUG_ERROR("%s is not a valid payload for this session", format);
+ TSK_OBJECT_SAFE_FREE(codec);
+ return -2;
+ }
+ // Open codec if not already done
+ if(!TMEDIA_CODEC(codec)->opened){
+ int ret;
+ tsk_safeobj_lock(audio);
+ if((ret = tmedia_codec_open(codec))){
+ tsk_safeobj_unlock(audio);
+ TSK_OBJECT_SAFE_FREE(codec);
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", codec->plugin->desc);
+ return ret;
+ }
+ tsk_safeobj_unlock(audio);
+ }
+ // Decode data
+ out_size = codec->plugin->decode(codec, packet->payload.data, packet->payload.size, &audio->decoder.buffer, &audio->decoder.buffer_size, packet->header);
+ if(out_size){
+ // Denoise (VAD, AGC, Noise suppression, ...)
+ // See tdav_consumer_audio.c::tdav_consumer_audio_get()
+ //if(audio->denoise && TMEDIA_DENOISE(audio->denoise)->opened){
+ // tmedia_denoise_echo_playback(TMEDIA_DENOISE(audio->denoise), audio->decoder.buffer);
+ //}
+ tmedia_consumer_consume(audio->consumer, &audio->decoder.buffer, out_size, packet->header);
+ if(!audio->decoder.buffer){
+ /* taken by the consumer */
+ audio->decoder.buffer_size = 0;
+ }
+ }
+ TSK_OBJECT_SAFE_FREE(codec);
+ }
+ return 0;
+}
+
+// Producer callback (From the producer to the network). Will encode() data before sending
+static int tdav_session_audio_producer_enc_cb(const void* callback_data, const void* buffer, tsk_size_t size)
+{
+ int ret;
+
+ tdav_session_audio_t* audio = (tdav_session_audio_t*)callback_data;
+
+ if(audio->rtp_manager){
+ /* encode */
+ tsk_size_t out_size = 0;
+
+ ret = 0;
+
+ //
+ // Find Encoder (call one time)
+ //
+ if(!audio->encoder.codec){
+ tsk_list_item_t* item;
+ tsk_list_foreach(item, TMEDIA_SESSION(audio)->neg_codecs){
+ if(!tsk_striequals(TMEDIA_CODEC(item->data)->neg_format, TMEDIA_CODEC_FORMAT_DTMF) &&
+ !tsk_striequals(TMEDIA_CODEC(item->data)->format, TMEDIA_CODEC_FORMAT_DTMF)){
+ audio->encoder.codec = tsk_object_ref(item->data);
+ trtp_manager_set_payload_type(audio->rtp_manager, audio->encoder.codec->neg_format ? atoi(audio->encoder.codec->neg_format) : atoi(audio->encoder.codec->format));
+ /* Denoise */
+ if(audio->denoise && !audio->denoise->opened){
+ ret = tmedia_denoise_open(audio->denoise,
+ TMEDIA_CODEC_PCM_FRAME_SIZE(audio->encoder.codec), //160 if 20ms at 8khz
+ TMEDIA_CODEC_RATE(audio->encoder.codec), tsk_true, 8000.0f, tsk_true, tsk_false);
+ }
+ break;
+ }
+ }
+ }
+ if(!audio->encoder.codec){
+ TSK_DEBUG_ERROR("Failed to find a valid codec");
+ return -3;
+ }
+
+ // Open codec if not already done
+ if(!audio->encoder.codec->opened){
+ tsk_safeobj_lock(audio);
+ if((ret = tmedia_codec_open(audio->encoder.codec))){
+ tsk_safeobj_unlock(audio);
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", audio->encoder.codec->plugin->desc);
+ return -4;
+ }
+ tsk_safeobj_unlock(audio);
+ }
+ // Denoise (VAD, AGC, Noise suppression, ...)
+ if(audio->denoise){
+ tsk_bool_t silence_or_noise = tsk_false;
+ ret = tmedia_denoise_process(TMEDIA_DENOISE(audio->denoise), (void*)buffer, &silence_or_noise);
+ if(silence_or_noise && (ret == 0)){
+ //FIXME:
+ TSK_DEBUG_INFO("Silence or Noise buffer");
+ return 0;
+ }
+ }
+
+ // Encode data
+ if((audio->encoder.codec = tsk_object_ref(audio->encoder.codec))){ /* Thread safeness (SIP reINVITE or UPDATE could update the encoder) */
+ out_size = audio->encoder.codec->plugin->encode(audio->encoder.codec, buffer, size, &audio->encoder.buffer, &audio->encoder.buffer_size);
+ if(out_size){
+ ret = trtp_manager_send_rtp(audio->rtp_manager, audio->encoder.buffer, out_size, TMEDIA_CODEC_PCM_FRAME_SIZE(audio->encoder.codec), tsk_false/*Marker*/, tsk_true/*lastPacket*/);
+ }
+ tsk_object_unref(audio->encoder.codec);
+ }
+ else{
+ TSK_DEBUG_WARN("No encoder");
+ }
+ }
+
+ return ret;
+}
+
+
+/* ============ Plugin interface ================= */
+
+int tdav_session_audio_set(tmedia_session_t* self, const tmedia_param_t* param)
+{
+ int ret = 0;
+ tdav_session_audio_t* audio;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ audio = (tdav_session_audio_t*)self;
+
+ if(param->plugin_type == tmedia_ppt_consumer){
+ TSK_DEBUG_WARN("Not implemented");
+ }
+ else if(param->plugin_type == tmedia_ppt_producer){
+ TSK_DEBUG_WARN("Not implemented");
+ }
+ else{
+ if(param->value_type == tmedia_pvt_pchar){
+ if(tsk_striequals(param->key, "remote-ip")){
+ /* only if no ip associated to the "m=" line */
+ if(param->value && !audio->remote_ip){
+ audio->remote_ip = tsk_strdup(param->value);
+ }
+ }
+ else if(tsk_striequals(param->key, "local-ip")){
+ tsk_strupdate(&audio->local_ip, param->value);
+ }
+ else if(tsk_striequals(param->key, "local-ipver")){
+ audio->useIPv6 = tsk_striequals(param->value, "ipv6");
+ }
+ }
+ else if(param->value_type == tmedia_pvt_pobject){
+ if(tsk_striequals(param->key, "natt-ctx")){
+ TSK_OBJECT_SAFE_FREE(audio->natt_ctx);
+ audio->natt_ctx = tsk_object_ref(param->value);
+ }
+ }
+ }
+
+ return ret;
+}
+
+int tdav_session_audio_prepare(tmedia_session_t* self)
+{
+ tdav_session_audio_t* audio;
+ int ret = 0;
+
+ audio = (tdav_session_audio_t*)self;
+
+ /* set local port */
+ if(!audio->rtp_manager){
+ if((audio->rtp_manager = trtp_manager_create(audio->rtcp_enabled, audio->local_ip, audio->useIPv6))){
+
+ ret = trtp_manager_set_rtp_callback(audio->rtp_manager, tdav_session_audio_rtp_cb, audio);
+ ret = trtp_manager_prepare(audio->rtp_manager);
+ if(audio->natt_ctx){
+ ret = trtp_manager_set_natt_ctx(audio->rtp_manager, audio->natt_ctx);
+ }
+ }
+ }
+
+ /* Consumer will be prepared in tdav_session_audio_start() */
+ /* Producer will be prepared in tdav_session_audio_start() */
+
+ return ret;
+}
+
+int tdav_session_audio_start(tmedia_session_t* self)
+{
+ tdav_session_audio_t* audio;
+ const tmedia_codec_t* codec;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ audio = (tdav_session_audio_t*)self;
+
+ if(!(codec = _tdav_first_best_neg_codec(audio))){
+ TSK_DEBUG_ERROR("No codec matched");
+ return -2;
+ }
+
+ if(audio->rtp_manager){
+ int ret;
+ /* RTP/RTCP manager: use latest information. */
+ ret = trtp_manager_set_rtp_remote(audio->rtp_manager, audio->remote_ip, audio->remote_port);
+ //trtp_manager_set_payload_type(audio->rtp_manager, codec->neg_format ? atoi(codec->neg_format) : atoi(codec->format));
+ ret = trtp_manager_start(audio->rtp_manager);
+
+ /* Consumer */
+ if(audio->consumer){
+ tmedia_consumer_prepare(audio->consumer, codec);
+ tmedia_consumer_start(audio->consumer);
+ }
+ /* Producer */
+ if(audio->producer){
+ tmedia_producer_prepare(audio->producer, codec);
+ tmedia_producer_start(audio->producer);
+ }
+ /* Denoise (AEC, Noise Suppression, AGC) */
+ if(audio->denoise && audio->encoder.codec){
+ tmedia_denoise_open(audio->denoise, TMEDIA_CODEC_PCM_FRAME_SIZE(audio->encoder.codec), TMEDIA_CODEC_RATE(audio->encoder.codec), tsk_true, 8000.0f, tsk_true, tsk_true);
+ }
+
+ /* for test */
+ //trtp_manager_send_rtp(audio->rtp_manager, "test", 4, tsk_true);
+ return ret;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid RTP/RTCP manager");
+ return -3;
+ }
+
+ return 0;
+}
+
+int tdav_session_audio_stop(tmedia_session_t* self)
+{
+ tdav_session_audio_t* audio;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ audio = (tdav_session_audio_t*)self;
+
+ /* RTP/RTCP manager */
+ if(audio->rtp_manager){
+ trtp_manager_stop(audio->rtp_manager);
+ }
+
+ /* Consumer */
+ if(audio->consumer){
+ tmedia_consumer_stop(audio->consumer);
+ }
+ /* Producer */
+ if(audio->producer){
+ tmedia_producer_stop(audio->producer);
+ }
+
+ return 0;
+}
+
+int tdav_session_audio_send_dtmf(tmedia_session_t* self, uint8_t event)
+{
+ tdav_session_audio_t* audio;
+ tmedia_codec_t* codec;
+ int ret, rate = 8000, ptime = 20;
+ uint16_t duration;
+ tdav_session_audio_dtmfe_t *dtmfe, *copy;
+ static uint32_t timestamp = 0x3200;
+ static uint32_t seq_num = 0;
+ int format = 101;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ audio = (tdav_session_audio_t*)self;
+
+ // Find the DTMF codec to use to use the RTP payload
+ if((codec = tmedia_codec_find_by_format(TMEDIA_SESSION(audio)->codecs, TMEDIA_CODEC_FORMAT_DTMF))){
+ rate = (int)codec->plugin->rate;
+ format = atoi(codec->neg_format ? codec->neg_format : codec->format);
+ TSK_OBJECT_SAFE_FREE(codec);
+ }
+
+ /* do we have an RTP manager? */
+ if(!audio->rtp_manager){
+ TSK_DEBUG_ERROR("No RTP manager associated to this session");
+ return -2;
+ }
+
+ /* Create Events list */
+ if(!audio->dtmf_events){
+ audio->dtmf_events = tsk_list_create();
+ }
+
+ /* Create global reference to the timer manager */
+ if(!audio->timer.created){
+ if((ret = tsk_timer_mgr_global_ref())){
+ TSK_DEBUG_ERROR("Failed to create Global Timer Manager");
+ return ret;
+ }
+ audio->timer.created = tsk_true;
+ }
+
+ /* Start the timer manager */
+ if(!audio->timer.started){
+ if((ret = tsk_timer_mgr_global_start())){
+ TSK_DEBUG_ERROR("Failed to start Global Timer Manager");
+ return ret;
+ }
+ audio->timer.started = tsk_true;
+ }
+
+
+ /* RFC 4733 - 5. Examples
+
+ +-------+-----------+------+--------+------+--------+--------+------+
+ | Time | Event | M | Time- | Seq | Event | Dura- | E |
+ | (ms) | | bit | stamp | No | Code | tion | bit |
+ +-------+-----------+------+--------+------+--------+--------+------+
+ | 0 | "9" | | | | | | |
+ | | starts | | | | | | |
+ | 50 | RTP | "1" | 0 | 1 | 9 | 400 | "0" |
+ | | packet 1 | | | | | | |
+ | | sent | | | | | | |
+ | 100 | RTP | "0" | 0 | 2 | 9 | 800 | "0" |
+ | | packet 2 | | | | | | |
+ | | sent | | | | | | |
+ | 150 | RTP | "0" | 0 | 3 | 9 | 1200 | "0" |
+ | | packet 3 | | | | | | |
+ | | sent | | | | | | |
+ | 200 | RTP | "0" | 0 | 4 | 9 | 1600 | "0" |
+ | | packet 4 | | | | | | |
+ | | sent | | | | | | |
+ | 200 | "9" ends | | | | | | |
+ | 250 | RTP | "0" | 0 | 5 | 9 | 1600 | "1" |
+ | | packet 4 | | | | | | |
+ | | first | | | | | | |
+ | | retrans- | | | | | | |
+ | | mission | | | | | | |
+ | 300 | RTP | "0" | 0 | 6 | 9 | 1600 | "1" |
+ | | packet 4 | | | | | | |
+ | | second | | | | | | |
+ | | retrans- | | | | | | |
+ | | mission | | | | | | |
+ =====================================================================
+ | 880 | First "1" | | | | | | |
+ | | starts | | | | | | |
+ | 930 | RTP | "1" | 7040 | 7 | 1 | 400 | "0" |
+ | | packet 5 | | | | | | |
+ | | sent | | | | | | |
+ */
+
+ // ref()(thread safeness)
+ audio = tsk_object_ref(audio);
+
+ duration = (rate * ptime)/1000;
+ /* Not mandatory but elegant */
+ timestamp += duration;
+
+ copy = dtmfe = _tdav_session_audio_dtmfe_create(audio, event, duration*1, ++seq_num, timestamp, (uint8_t)format, tsk_true, tsk_false);
+ tsk_list_push_back_data(audio->dtmf_events, (void**)&dtmfe);
+ tsk_timer_mgr_global_schedule(ptime*0, _tdav_session_audio_dtmfe_timercb, copy);
+ copy = dtmfe = _tdav_session_audio_dtmfe_create(audio, event, duration*2, ++seq_num, timestamp, (uint8_t)format, tsk_false, tsk_false);
+ tsk_list_push_back_data(audio->dtmf_events, (void**)&dtmfe);
+ tsk_timer_mgr_global_schedule(ptime*1, _tdav_session_audio_dtmfe_timercb, copy);
+ copy = dtmfe = _tdav_session_audio_dtmfe_create(audio, event, duration*3, ++seq_num, timestamp, (uint8_t)format, tsk_false, tsk_false);
+ tsk_list_push_back_data(audio->dtmf_events, (void**)&dtmfe);
+ tsk_timer_mgr_global_schedule(ptime*2, _tdav_session_audio_dtmfe_timercb, copy);
+
+ copy = dtmfe = _tdav_session_audio_dtmfe_create(audio, event, duration*4, ++seq_num, timestamp, (uint8_t)format, tsk_false, tsk_true);
+ tsk_list_push_back_data(audio->dtmf_events, (void**)&dtmfe);
+ tsk_timer_mgr_global_schedule(ptime*3, _tdav_session_audio_dtmfe_timercb, copy);
+ copy = dtmfe = _tdav_session_audio_dtmfe_create(audio, event, duration*4, seq_num, timestamp, (uint8_t)format, tsk_false, tsk_true);
+ tsk_list_push_back_data(audio->dtmf_events, (void**)&dtmfe);
+ tsk_timer_mgr_global_schedule(ptime*4, _tdav_session_audio_dtmfe_timercb, copy);
+ copy = dtmfe = _tdav_session_audio_dtmfe_create(audio, event, duration*4, seq_num, timestamp, (uint8_t)format, tsk_false, tsk_true);
+ tsk_list_push_back_data(audio->dtmf_events, (void**)&dtmfe);
+ tsk_timer_mgr_global_schedule(ptime*5, _tdav_session_audio_dtmfe_timercb, copy);
+
+ // unref()(thread safeness)
+ audio = tsk_object_unref(audio);
+
+ return 0;
+}
+
+int tdav_session_audio_pause(tmedia_session_t* self)
+{
+ tdav_session_audio_t* audio;
+
+ audio = (tdav_session_audio_t*)self;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* Consumer */
+ if(audio->consumer){
+ tmedia_consumer_pause(audio->consumer);
+ }
+ /* Producer */
+ if(audio->producer){
+ tmedia_producer_pause(audio->producer);
+ }
+
+ return 0;
+}
+
+const tsdp_header_M_t* tdav_session_audio_get_lo(tmedia_session_t* self)
+{
+ tdav_session_audio_t* audio;
+ tsk_bool_t changed = tsk_false;
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ audio = (tdav_session_audio_t*)self;
+
+ if(!audio->rtp_manager || !audio->rtp_manager->transport){
+ TSK_DEBUG_ERROR("RTP/RTCP manager in invalid");
+ return tsk_null;
+ }
+
+ if(self->ro_changed && self->M.lo){
+ /* Codecs */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "fmtp");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "rtpmap");
+ tsk_list_clear_items(self->M.lo->FMTs);
+
+ /* QoS */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "curr");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "des");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "conf");
+ }
+
+ changed = (self->ro_changed || !self->M.lo);
+
+ if(!self->M.lo){
+ if((self->M.lo = tsdp_header_M_create(self->plugin->media, audio->rtp_manager->rtp.public_port, "RTP/AVP"))){
+ /* If NATT is active, do not rely on the global IP address Connection line */
+ if(audio->natt_ctx){
+ tsdp_header_M_add_headers(self->M.lo,
+ TSDP_HEADER_C_VA_ARGS("IN", audio->useIPv6 ? "IP6" : "IP4", audio->rtp_manager->rtp.public_ip),
+ tsk_null);
+ }
+ /* 3GPP TS 24.229 - 6.1.1 General
+ In order to support accurate bandwidth calculations, the UE may include the "a=ptime" attribute for all "audio" media
+ lines as described in RFC 4566 [39]. If a UE receives an "audio" media line with "a=ptime" specified, the UE should
+ transmit at the specified packetization rate. If a UE receives an "audio" media line which does not have "a=ptime"
+ specified or the UE does not support the "a=ptime" attribute, the UE should transmit at the default codec packetization
+ rate as defined in RFC 3551 [55A]. The UE will transmit consistent with the resources available from the network.
+
+ For "video" and "audio" media types that utilize the RTP/RTCP, the UE shall specify the proposed bandwidth for each
+ media stream utilizing the "b=" media descriptor and the "AS" bandwidth modifier in the SDP.
+
+ The UE shall include the MIME subtype "telephone-event" in the "m=" media descriptor in the SDP for audio media
+ flows that support both audio codec and DTMF payloads in RTP packets as described in RFC 4733 [23].
+ */
+ tsdp_header_M_add_headers(self->M.lo,
+ TSDP_HEADER_A_VA_ARGS("ptime", "20"),
+ tsk_null);
+ // the "telephone-event" fmt/rtpmap is added below
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create lo");
+ return tsk_null;
+ }
+ }
+
+ /* from codecs to sdp */
+ if(changed){
+ tmedia_codecs_L_t* neg_codecs = tsk_null;
+
+ if(self->M.ro){
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+ /* update negociated codecs */
+ if((neg_codecs = tmedia_session_match_codec(self, self->M.ro))){
+ self->neg_codecs = neg_codecs;
+ TSK_OBJECT_SAFE_FREE(audio->encoder.codec);
+ }
+ /* from codecs to sdp */
+ if(TSK_LIST_IS_EMPTY(self->neg_codecs) || ((self->neg_codecs->tail == self->neg_codecs->head) && IS_DTMF_CODEC(TSK_LIST_FIRST_DATA(self->neg_codecs)))){
+ self->M.lo->port = 0; /* Keep the RTP transport and reuse it when we receive a reINVITE or UPDATE request */
+ goto DONE;
+ }
+ else{
+ tmedia_codec_to_sdp(self->neg_codecs, self->M.lo);
+ }
+ }
+ else{
+ /* from codecs to sdp */
+ tmedia_codec_to_sdp(self->codecs, self->M.lo);
+ }
+
+ /* Hold/Resume */
+ if(self->M.ro){
+ if(tsdp_header_M_is_held(self->M.ro, tsk_false)){
+ tsdp_header_M_hold(self->M.lo, tsk_false);
+ }
+ else{
+ tsdp_header_M_resume(self->M.lo, tsk_false);
+ }
+ }
+ ///* 3GPP TS 24.229 - 6.1.1 General
+ // The UE shall include the MIME subtype "telephone-event" in the "m=" media descriptor in the SDP for audio media
+ // flows that support both audio codec and DTMF payloads in RTP packets as described in RFC 4733 [23].
+ //*/
+ //tsdp_header_M_add_fmt(self->M.lo, TMEDIA_CODEC_FORMAT_DTMF);
+ //tsdp_header_M_add_headers(self->M.lo,
+ // TSDP_HEADER_A_VA_ARGS("fmtp", TMEDIA_CODEC_FORMAT_DTMF" 0-15"),
+ // tsk_null);
+ //tsdp_header_M_add_headers(self->M.lo,
+ // TSDP_HEADER_A_VA_ARGS("rtpmap", TMEDIA_CODEC_FORMAT_DTMF" telephone-event/8000"),
+ // tsk_null);
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ tmedia_qos_tline_to_sdp(self->qos, self->M.lo);
+ }
+DONE:;
+ }
+
+ return self->M.lo;
+}
+
+int tdav_session_audio_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ tdav_session_audio_t* audio;
+ tmedia_codecs_L_t* neg_codecs;
+
+ if(!self || !m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ audio = (tdav_session_audio_t*)self;
+
+ /* update remote offer */
+ TSK_OBJECT_SAFE_FREE(self->M.ro);
+ self->M.ro = tsk_object_ref((void*)m);
+
+ if(self->M.lo){
+ if((neg_codecs = tmedia_session_match_codec(self, m))){
+ /* update negociated codecs */
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+ self->neg_codecs = neg_codecs;
+ TSK_OBJECT_SAFE_FREE(audio->encoder.codec);
+ }
+ else{
+ TSK_DEBUG_ERROR("None Match");
+ return -1;
+ }
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ }
+ }
+
+ /* get connection associated to this media line
+ * If the connnection is global, then the manager will call tdav_session_audio_set() */
+ if(m->C && m->C->addr){
+ tsk_strupdate(&audio->remote_ip, m->C->addr);
+ audio->useIPv6 = tsk_striequals(m->C->addrtype, "IP6");
+ }
+ /* set remote port */
+ audio->remote_port = m->port;
+
+
+ return 0;
+}
+
+/* first best negotiated codec (ignore dtmf) */
+const tmedia_codec_t* _tdav_first_best_neg_codec(const tdav_session_audio_t* session)
+{
+ const tsk_list_item_t* item;
+ tsk_list_foreach(item, TMEDIA_SESSION(session)->neg_codecs){
+ if(!IS_DTMF_CODEC(item->data)){
+ return TMEDIA_CODEC(item->data);
+ }
+ }
+ return tsk_null;
+}
+
+
+/* Internal function used to create new DTMF event */
+tdav_session_audio_dtmfe_t* _tdav_session_audio_dtmfe_create(const tdav_session_audio_t* session, uint8_t event, uint16_t duration, uint32_t seq, uint32_t timestamp, uint8_t format, tsk_bool_t M, tsk_bool_t E)
+{
+ tdav_session_audio_dtmfe_t* dtmfe;
+ static uint8_t volume = 10;
+ static uint32_t ssrc = 0x5234A8;
+
+ uint8_t pay[4] = {0};
+
+ /* RFC 4733 - 2.3. Payload Format
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | event |E|R| volume | duration |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if(!(dtmfe = tsk_object_new(tdav_session_audio_dtmfe_def_t))){
+ TSK_DEBUG_ERROR("Failed to create new DTMF event");
+ return tsk_null;
+ }
+ dtmfe->session = session;
+
+ if(!(dtmfe->packet = trtp_rtp_packet_create((session && session->rtp_manager) ? session->rtp_manager->rtp.ssrc : ssrc, seq, timestamp, format, M))){
+ TSK_DEBUG_ERROR("Failed to create DTMF RTP packet");
+ TSK_OBJECT_SAFE_FREE(dtmfe);
+ return tsk_null;
+ }
+
+ pay[0] = event;
+ pay[1] |= ((E << 7) | (volume & 0x3F));
+ pay[2] = (duration >> 8);
+ pay[3] = (duration & 0xFF);
+
+ /* set data */
+ if((dtmfe->packet->payload.data = tsk_calloc(sizeof(pay), sizeof(uint8_t)))){
+ memcpy(dtmfe->packet->payload.data, pay, sizeof(pay));
+ dtmfe->packet->payload.size = sizeof(pay);
+ }
+
+ return dtmfe;
+}
+
+int _tdav_session_audio_dtmfe_timercb(const void* arg, tsk_timer_id_t timer_id)
+{
+ tdav_session_audio_dtmfe_t* dtmfe = (tdav_session_audio_dtmfe_t*)arg;
+ int ret;
+
+ if(!dtmfe || !dtmfe->session){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* Send the data */
+ TSK_DEBUG_INFO("Sending DTMF event");
+ ret = trtp_manager_send_rtp_2(dtmfe->session->rtp_manager, dtmfe->packet);
+
+ /* Remove and delete the event from the queue */
+ tsk_list_remove_item_by_data(dtmfe->session->dtmf_events, dtmfe);
+
+ return ret;
+}
+
+//=================================================================================================
+// Session Audio Plugin object definition
+//
+/* constructor */
+static tsk_object_t* tdav_session_audio_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_session_audio_t *session = self;
+ if(session){
+ /* init base: called by tmedia_session_create() */
+ /* init self */
+ tsk_safeobj_init(session);
+ if(!(session->consumer = tmedia_consumer_create(tdav_session_audio_plugin_def_t->type, TMEDIA_SESSION(session)->id))){
+ TSK_DEBUG_ERROR("Failed to create Audio consumer");
+ }
+ if((session->producer = tmedia_producer_create(tdav_session_audio_plugin_def_t->type, TMEDIA_SESSION(session)->id))){
+ tmedia_producer_set_enc_callback(session->producer, tdav_session_audio_producer_enc_cb, self);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create Audio producer");
+ }
+ if(!(session->denoise = tmedia_denoise_create())){
+ TSK_DEBUG_WARN("No Audio denoiser found");
+ }
+ else if(session->consumer){// IMPORTANT: This means that the consumer must be child of "tdav_consumer_audio_t" object.
+ tdav_consumer_audio_set_denoise(TDAV_CONSUMER_AUDIO(session->consumer), session->denoise);
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_session_audio_dtor(tsk_object_t * self)
+{
+ tdav_session_audio_t *session = self;
+ if(session){
+
+ // Do it in this order (deinit self first)
+
+ /* Timer manager */
+ if(session->timer.started){
+ if(session->dtmf_events){
+ /* Cancel all events */
+ tsk_list_item_t* item;
+ tsk_list_foreach(item, session->dtmf_events){
+ tsk_timer_mgr_global_cancel(((tdav_session_audio_dtmfe_t*)item->data)->timer_id);
+ }
+ }
+ tsk_timer_mgr_global_stop();
+ }
+ if(session->timer.created){
+ tsk_timer_mgr_global_unref();
+ }
+ /* CleanUp the DTMF events */
+ TSK_OBJECT_SAFE_FREE(session->dtmf_events);
+
+ /* deinit self (rtp manager should be destroyed after the producer) */
+ TSK_OBJECT_SAFE_FREE(session->consumer);
+ TSK_OBJECT_SAFE_FREE(session->producer);
+ TSK_OBJECT_SAFE_FREE(session->rtp_manager);
+ TSK_FREE(session->remote_ip);
+ TSK_FREE(session->local_ip);
+ TSK_OBJECT_SAFE_FREE(session->denoise);
+
+ TSK_OBJECT_SAFE_FREE(session->encoder.codec);
+ TSK_FREE(session->encoder.buffer);
+ TSK_FREE(session->decoder.buffer);
+
+ /* NAT Traversal context */
+ TSK_OBJECT_SAFE_FREE(session->natt_ctx);
+
+ tsk_safeobj_deinit(session);
+
+ /* deinit base */
+ tmedia_session_deinit(self);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_session_audio_def_s =
+{
+ sizeof(tdav_session_audio_t),
+ tdav_session_audio_ctor,
+ tdav_session_audio_dtor,
+ tmedia_session_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tdav_session_audio_plugin_def_s =
+{
+ &tdav_session_audio_def_s,
+
+ tmedia_audio,
+ "audio",
+
+ tdav_session_audio_set,
+ tdav_session_audio_prepare,
+ tdav_session_audio_start,
+ tdav_session_audio_pause,
+ tdav_session_audio_stop,
+
+ /* Audio part */
+ {
+ tdav_session_audio_send_dtmf
+ },
+
+ tdav_session_audio_get_lo,
+ tdav_session_audio_set_ro
+};
+const tmedia_session_plugin_def_t *tdav_session_audio_plugin_def_t = &tdav_session_audio_plugin_def_s;
+
+
+
+//=================================================================================================
+// DTMF event object definition
+//
+static tsk_object_t* tdav_session_audio_dtmfe_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_session_audio_dtmfe_t *event = self;
+ if(event){
+ event->timer_id = TSK_INVALID_TIMER_ID;
+ }
+ return self;
+}
+
+static tsk_object_t* tdav_session_audio_dtmfe_dtor(tsk_object_t * self)
+{
+ tdav_session_audio_dtmfe_t *event = self;
+ if(event){
+ TSK_OBJECT_SAFE_FREE(event->packet);
+ }
+
+ return self;
+}
+
+static int tdav_session_audio_dtmfe_cmp(const tsk_object_t *_e1, const tsk_object_t *_e2)
+{
+ const tdav_session_audio_dtmfe_t *e1 = _e1;
+ const tdav_session_audio_dtmfe_t *e2 = _e2;
+
+ return (e1 - e2);
+}
+
+static const tsk_object_def_t tdav_session_audio_dtmfe_def_s =
+{
+ sizeof(tdav_session_audio_dtmfe_t),
+ tdav_session_audio_dtmfe_ctor,
+ tdav_session_audio_dtmfe_dtor,
+ tdav_session_audio_dtmfe_cmp,
+};
+const tsk_object_def_t *tdav_session_audio_dtmfe_def_t = &tdav_session_audio_dtmfe_def_s;
diff --git a/tinyDAV/src/audio/tdav_speex_denoise.c b/tinyDAV/src/audio/tdav_speex_denoise.c
new file mode 100644
index 0000000..fd8c5df
--- /dev/null
+++ b/tinyDAV/src/audio/tdav_speex_denoise.c
@@ -0,0 +1,221 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_speex_denoise.c
+ * @brief Speex Denoiser (Noise suppression, AGC, AEC) Plugin
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/tdav_speex_denoise.h"
+
+#if HAVE_SPEEX_DSP
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define ECHO_TAIL 10
+
+int tdav_speex_denoise_open(tmedia_denoise_t* self, uint32_t frame_size, uint32_t sampling_rate, tsk_bool_t denoise, float agc_level, tsk_bool_t aec, tsk_bool_t vad)
+{
+ tdav_speex_denoise_t *denoiser = (tdav_speex_denoise_t *)self;
+ float f;
+ int i;
+
+ if(!denoiser->echo_state){
+ if((denoiser->echo_state = speex_echo_state_init(frame_size, ECHO_TAIL*frame_size))){
+ speex_echo_ctl(denoiser->echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampling_rate);
+ }
+ }
+
+ if(!denoiser->preprocess_state){
+ denoiser->vad_on = vad;
+ denoiser->frame_size = frame_size;
+
+ if((denoiser->preprocess_state = speex_preprocess_state_init(frame_size, sampling_rate))){
+
+ if(denoiser->echo_state){
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE, denoiser->echo_state);
+
+ i = -40;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS, &i);
+ i = -15;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE, &i);
+
+ TSK_FREE(denoiser->echo_output_frame);
+ denoiser->echo_output_frame = tsk_calloc(denoiser->frame_size, sizeof(spx_int16_t));
+ }
+
+ if(denoise){
+ i = 1;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_DENOISE, &i);
+ i = -30;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &i);
+ }
+ else{
+ i = 0;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_DENOISE, &i);
+ }
+
+ if(agc_level){
+ i = 1;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC, &i);
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &agc_level);
+ //speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC_TARGET, &agc_level);
+ i = 30;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &i);
+ i = 12;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &i);
+ i = -40;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC_DECREMENT, &i);
+ }
+ else{
+ i = 0, f = 8000.0f;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC, &i);
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
+ }
+ i = vad ? 1 : 2;
+ speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_VAD, &i);
+ //i=1;
+ //speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_DEREVERB, &i);
+ //i=1;
+ //speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &i);
+ //i=1;
+ //speex_preprocess_ctl(denoiser->preprocess_state, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &i);
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create Speex preprocessor state");
+ return -2;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_speex_denoise_echo_playback(tmedia_denoise_t* self, const void* echo_frame)
+{
+ tdav_speex_denoise_t *denoiser = (tdav_speex_denoise_t *)self;
+ if(denoiser->echo_state){
+ speex_echo_playback(denoiser->echo_state, echo_frame);
+ }
+ return 0;
+}
+
+int tdav_speex_denoise_process(tmedia_denoise_t* self, void* audio_frame, tsk_bool_t* silence_or_noise)
+{
+ tdav_speex_denoise_t *denoiser = (tdav_speex_denoise_t *)self;
+ int vad;
+
+ if(denoiser->preprocess_state){
+ if(denoiser->echo_state && denoiser->echo_output_frame){
+ speex_echo_capture(denoiser->echo_state, audio_frame, denoiser->echo_output_frame);
+ memcpy(audio_frame, denoiser->echo_output_frame, denoiser->frame_size*sizeof(spx_int16_t));
+ }
+ vad = speex_preprocess_run(denoiser->preprocess_state, audio_frame);
+ if(!vad && denoiser->vad_on){
+ *silence_or_noise = tsk_true;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_speex_denoise_close(tmedia_denoise_t* self)
+{
+ tdav_speex_denoise_t *denoiser = (tdav_speex_denoise_t *)self;
+
+ if(denoiser->preprocess_state){
+ speex_preprocess_state_destroy(denoiser->preprocess_state);
+ denoiser->preprocess_state = tsk_null;
+ }
+ if(denoiser->echo_state){
+ speex_echo_state_destroy(denoiser->echo_state);
+ denoiser->echo_state = tsk_null;
+ }
+ TSK_FREE(denoiser->echo_output_frame);
+
+ return 0;
+}
+
+
+
+//
+// Speex denoiser Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_speex_denoise_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_speex_denoise_t *denoise = self;
+ if(denoise){
+ /* init base */
+ tmedia_denoise_init(TMEDIA_DENOISE(denoise));
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_speex_denoise_dtor(tsk_object_t * self)
+{
+ tdav_speex_denoise_t *denoise = self;
+ if(denoise){
+ /* deinit base */
+ tmedia_denoise_deinit(TMEDIA_DENOISE(denoise));
+ /* deinit self */
+ if(denoise->preprocess_state){ // already done by close() ...but who know?
+ speex_preprocess_state_destroy(denoise->preprocess_state);
+ }
+ if(denoise->echo_state){
+ speex_echo_state_destroy(denoise->echo_state);
+ }
+ TSK_FREE(denoise->echo_output_frame);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_speex_denoise_def_s =
+{
+ sizeof(tdav_speex_denoise_t),
+ tdav_speex_denoise_ctor,
+ tdav_speex_denoise_dtor,
+ tsk_null,
+};
+/* plugin definition*/
+static const tmedia_denoise_plugin_def_t tdav_speex_denoise_plugin_def_s =
+{
+ &tdav_speex_denoise_def_s,
+
+ "Audio Denoiser based on Speex",
+
+ tdav_speex_denoise_open,
+ tdav_speex_denoise_echo_playback,
+ tdav_speex_denoise_process,
+ tdav_speex_denoise_close,
+};
+const tmedia_denoise_plugin_def_t *tdav_speex_denoise_plugin_def_t = &tdav_speex_denoise_plugin_def_s;
+
+
+#endif /* HAVE_SPEEX_DSP */ \ No newline at end of file
diff --git a/tinyDAV/src/audio/waveapi/tdav_consumer_waveapi.c b/tinyDAV/src/audio/waveapi/tdav_consumer_waveapi.c
new file mode 100644
index 0000000..21169bc
--- /dev/null
+++ b/tinyDAV/src/audio/waveapi/tdav_consumer_waveapi.c
@@ -0,0 +1,406 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_consumer_waveapi.c
+ * @brief Audio Consumer for Win32 and WinCE platforms.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/waveapi/tdav_consumer_waveapi.h"
+
+#if HAVE_WAVE_API
+
+#include "tsk_thread.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_WAVEAPI_CONSUMER_ERROR_BUFF_COUNT 0xFF
+
+#define tdav_consumer_waveapi_set tsk_null
+
+static void print_last_error(MMRESULT mmrError, const char* func)
+{
+ static char buffer_err[TDAV_WAVEAPI_CONSUMER_ERROR_BUFF_COUNT];
+
+ waveOutGetErrorTextA(mmrError, buffer_err, sizeof(buffer_err));
+ TSK_DEBUG_ERROR("%s() error: %s", func, buffer_err);
+}
+
+static int free_wavehdr(tdav_consumer_waveapi_t* consumer, tsk_size_t index)
+{
+ if(!consumer || index >= sizeof(consumer->hWaveHeaders)/sizeof(LPWAVEHDR)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TSK_FREE(consumer->hWaveHeaders[index]->lpData);
+ TSK_FREE(consumer->hWaveHeaders[index]);
+
+ return 0;
+}
+
+static int create_wavehdr(tdav_consumer_waveapi_t* consumer, tsk_size_t index)
+{
+ if(!consumer || index >= sizeof(consumer->hWaveHeaders)/sizeof(LPWAVEHDR)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(consumer->hWaveHeaders[index]){
+ free_wavehdr(consumer, index);
+ }
+
+ consumer->hWaveHeaders[index] = tsk_calloc(1, sizeof(WAVEHDR));
+ consumer->hWaveHeaders[index]->lpData = tsk_calloc(1, consumer->bytes_per_notif);
+ consumer->hWaveHeaders[index]->dwBufferLength = consumer->bytes_per_notif;
+ consumer->hWaveHeaders[index]->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
+ consumer->hWaveHeaders[index]->dwLoops = 0x01;
+ consumer->hWaveHeaders[index]->dwUser = index;
+
+ return 0;
+}
+
+static int write_wavehdr(tdav_consumer_waveapi_t* consumer, tsk_size_t index)
+{
+ MMRESULT result;
+
+ if(!consumer || !consumer->hWaveHeaders[index] || !consumer->hWaveOut){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ result = waveOutPrepareHeader(consumer->hWaveOut, consumer->hWaveHeaders[index], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveOutPrepareHeader");
+ return -2;
+ }
+
+ result = waveOutWrite(consumer->hWaveOut, consumer->hWaveHeaders[index], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveOutWrite");
+ return -3;
+ }
+
+ return 0;
+}
+
+static int play_wavehdr(tdav_consumer_waveapi_t* consumer, LPWAVEHDR lpHdr)
+{
+ MMRESULT result;
+ void* data;
+
+ if(!consumer || !lpHdr || !consumer->hWaveOut){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ result = waveOutUnprepareHeader(consumer->hWaveOut, lpHdr, sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveOutUnprepareHeader");
+ return -2;
+ }
+
+ //
+ //
+ // Fill lpHdr->Data with decoded data
+ //
+ //
+ if((data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer)))){
+ memcpy(lpHdr->lpData, data, lpHdr->dwBufferLength);
+ TSK_FREE(data);
+ }
+ else{
+ /* Put silence */
+ memset(lpHdr->lpData, 0, lpHdr->dwBufferLength);
+ }
+
+ if(!consumer->started){
+ return 0;
+ }
+
+ result = waveOutPrepareHeader(consumer->hWaveOut, lpHdr, sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveOutPrepareHeader");
+ return -3;
+ }
+
+ result = waveOutWrite(consumer->hWaveOut, lpHdr, sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveOutWrite");
+ return -4;
+ }
+
+ return 0;
+}
+
+static void *__playback_thread(void *param)
+{
+ tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)param;
+ DWORD dwEvent;
+ tsk_size_t i;
+
+ TSK_DEBUG_INFO("__playback_thread -- START");
+
+ SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS);
+
+ for(;;){
+ dwEvent = WaitForMultipleObjects(2, consumer->events, FALSE, INFINITE);
+
+ if (dwEvent == 1){
+ break;
+ }
+
+ else if (dwEvent == 0){
+ EnterCriticalSection(&consumer->cs);
+ for(i = 0; i< sizeof(consumer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ if(consumer->hWaveHeaders[i] && (consumer->hWaveHeaders[i]->dwFlags & WHDR_DONE)){
+ play_wavehdr(consumer, consumer->hWaveHeaders[i]);
+ }
+ }
+ LeaveCriticalSection(&consumer->cs);
+ }
+ }
+
+ TSK_DEBUG_INFO("__playback_thread -- STOP");
+
+
+ return tsk_null;
+}
+
+
+
+
+
+
+
+
+/* ============ Media Consumer Interface ================= */
+int tdav_consumer_waveapi_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec)
+{
+ tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
+ tsk_size_t i;
+
+ if(!consumer || !codec && codec->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TDAV_CONSUMER_AUDIO(consumer)->channels = codec->plugin->audio.channels;
+ TDAV_CONSUMER_AUDIO(consumer)->rate = codec->plugin->rate;
+ /* codec should have ptime */
+
+
+ /* Format */
+ ZeroMemory(&consumer->wfx, sizeof(WAVEFORMATEX));
+ consumer->wfx.wFormatTag = WAVE_FORMAT_PCM;
+ consumer->wfx.nChannels = TDAV_CONSUMER_AUDIO(consumer)->channels;
+ consumer->wfx.nSamplesPerSec = TDAV_CONSUMER_AUDIO(consumer)->rate;
+ consumer->wfx.wBitsPerSample = TDAV_CONSUMER_AUDIO(consumer)->bits_per_sample;
+ consumer->wfx.nBlockAlign = (consumer->wfx.nChannels * consumer->wfx.wBitsPerSample/8);
+ consumer->wfx.nAvgBytesPerSec = (consumer->wfx.nSamplesPerSec * consumer->wfx.nBlockAlign);
+
+ /* Average bytes (count) for each notification */
+ consumer->bytes_per_notif = ((consumer->wfx.nAvgBytesPerSec * TDAV_CONSUMER_AUDIO(consumer)->ptime)/1000);
+
+ /* create buffers */
+ for(i = 0; i< sizeof(consumer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ create_wavehdr(consumer, i);
+ }
+
+ return 0;
+}
+
+int tdav_consumer_waveapi_start(tmedia_consumer_t* self)
+{
+ tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
+ MMRESULT result;
+ tsk_size_t i;
+
+ if(!consumer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(consumer->started || consumer->hWaveOut){
+ TSK_DEBUG_WARN("Consumer already started");
+ return 0;
+ }
+
+ /* create events */
+ if(!consumer->events[0]){
+ consumer->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+ if(!consumer->events[1]){
+ consumer->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+
+ /* open */
+ result = waveOutOpen((HWAVEOUT *)&consumer->hWaveOut, WAVE_MAPPER, &consumer->wfx, (DWORD)consumer->events[0], (DWORD_PTR)consumer, CALLBACK_EVENT);
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveOutOpen");
+ return -2;
+ }
+
+ /* start thread */
+ tsk_thread_create(&consumer->tid[0], __playback_thread, consumer);
+
+ /* write */
+ for(i = 0; i< sizeof(consumer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ write_wavehdr(consumer, i);
+ }
+
+ consumer->started = tsk_true;
+
+ return 0;
+}
+
+int tdav_consumer_waveapi_consume(tmedia_consumer_t* self, void** buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
+{
+ tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
+
+ if(!consumer || !buffer || !*buffer || !size){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ /* buffer is already decoded */
+ return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(consumer), buffer, size, proto_hdr);
+}
+
+int tdav_consumer_waveapi_pause(tmedia_consumer_t* self)
+{
+ tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
+
+ if(!consumer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tdav_consumer_waveapi_stop(tmedia_consumer_t* self)
+{
+ tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
+ MMRESULT result;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!consumer->started){
+ TSK_DEBUG_WARN("Consumer not started");
+ return 0;
+ }
+
+ /* stop thread */
+ if(consumer->tid[0]){
+ SetEvent(consumer->events[1]);
+ tsk_thread_join(&(consumer->tid[0]));
+ }
+
+ /* should be done here */
+ consumer->started = tsk_false;
+
+ if(consumer->hWaveOut && ((result = waveOutReset(consumer->hWaveOut)) != MMSYSERR_NOERROR)){
+ print_last_error(result, "waveOutReset");
+ }
+
+ return 0;
+}
+
+
+//
+// WaveAPI consumer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_consumer_waveapi_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_consumer_waveapi_t *consumer = self;
+ if(consumer){
+ /* init base */
+ tdav_consumer_audio_init(TDAV_CONSUMER_AUDIO(consumer));
+ /* init self */
+ InitializeCriticalSection(&consumer->cs);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_consumer_waveapi_dtor(tsk_object_t * self)
+{
+ tdav_consumer_waveapi_t *consumer = self;
+ if(consumer){
+ tsk_size_t i;
+
+ /* stop */
+ if(consumer->started){
+ tdav_consumer_waveapi_stop(self);
+ }
+
+ /* deinit base */
+ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(consumer));
+ /* deinit self */
+ for(i = 0; i< sizeof(consumer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ free_wavehdr(consumer, i);
+ }
+ if(consumer->hWaveOut){
+ waveOutClose(consumer->hWaveOut);
+ }
+ if(consumer->events[0]){
+ CloseHandle(consumer->events[0]);
+ }
+ if(consumer->events[1]){
+ CloseHandle(consumer->events[1]);
+ }
+ DeleteCriticalSection(&consumer->cs);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_consumer_waveapi_def_s =
+{
+ sizeof(tdav_consumer_waveapi_t),
+ tdav_consumer_waveapi_ctor,
+ tdav_consumer_waveapi_dtor,
+ tdav_consumer_audio_cmp,
+};
+/* plugin definition*/
+static const tmedia_consumer_plugin_def_t tdav_consumer_waveapi_plugin_def_s =
+{
+ &tdav_consumer_waveapi_def_s,
+
+ tmedia_audio,
+ "Microsoft WaveAPI consumer",
+
+ tdav_consumer_waveapi_set,
+ tdav_consumer_waveapi_prepare,
+ tdav_consumer_waveapi_start,
+ tdav_consumer_waveapi_consume,
+ tdav_consumer_waveapi_pause,
+ tdav_consumer_waveapi_stop
+};
+const tmedia_consumer_plugin_def_t *tdav_consumer_waveapi_plugin_def_t = &tdav_consumer_waveapi_plugin_def_s;
+
+#endif /* HAVE_WAVE_API */ \ No newline at end of file
diff --git a/tinyDAV/src/audio/waveapi/tdav_producer_waveapi.c b/tinyDAV/src/audio/waveapi/tdav_producer_waveapi.c
new file mode 100644
index 0000000..88fed78
--- /dev/null
+++ b/tinyDAV/src/audio/waveapi/tdav_producer_waveapi.c
@@ -0,0 +1,393 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_producer_waveapi.c
+ * @brief Audio Producer for Win32 and WinCE platforms.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/audio/waveapi/tdav_producer_waveapi.h"
+
+#if HAVE_WAVE_API
+
+#include "tsk_thread.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_WAVEAPI_PRODUCER_ERROR_BUFF_COUNT 0xFF
+
+#define tdav_producer_waveapi_set tsk_null
+
+static void print_last_error(MMRESULT mmrError, const char* func)
+{
+ static char buffer_err[TDAV_WAVEAPI_PRODUCER_ERROR_BUFF_COUNT];
+
+ waveInGetErrorTextA(mmrError, buffer_err, sizeof(buffer_err));
+ TSK_DEBUG_ERROR("%s() error: %s", func, buffer_err);
+}
+
+static int free_wavehdr(tdav_producer_waveapi_t* producer, tsk_size_t index)
+{
+ if(!producer || index >= sizeof(producer->hWaveHeaders)/sizeof(LPWAVEHDR)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TSK_FREE(producer->hWaveHeaders[index]->lpData);
+ TSK_FREE(producer->hWaveHeaders[index]);
+
+ return 0;
+}
+
+static int create_wavehdr(tdav_producer_waveapi_t* producer, tsk_size_t index)
+{
+ if(!producer || index >= sizeof(producer->hWaveHeaders)/sizeof(LPWAVEHDR)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(producer->hWaveHeaders[index]){
+ free_wavehdr(producer, index);
+ }
+
+ producer->hWaveHeaders[index] = tsk_calloc(1, sizeof(WAVEHDR));
+ producer->hWaveHeaders[index]->lpData = tsk_calloc(1, producer->bytes_per_notif);
+ producer->hWaveHeaders[index]->dwBufferLength = producer->bytes_per_notif;
+ producer->hWaveHeaders[index]->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
+ producer->hWaveHeaders[index]->dwLoops = 0x01;
+ producer->hWaveHeaders[index]->dwUser = index;
+
+ return 0;
+}
+
+static int add_wavehdr(tdav_producer_waveapi_t* producer, tsk_size_t index)
+{
+ MMRESULT result;
+
+ if(!producer || !producer->hWaveHeaders[index] || !producer->hWaveIn){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ result = waveInPrepareHeader(producer->hWaveIn, producer->hWaveHeaders[index], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInPrepareHeader");
+ return -2;
+ }
+
+ result = waveInAddBuffer(producer->hWaveIn, producer->hWaveHeaders[index], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInAddBuffer");
+ return -3;
+ }
+
+ return 0;
+}
+
+static int record_wavehdr(tdav_producer_waveapi_t* producer, LPWAVEHDR lpHdr)
+{
+ MMRESULT result;
+
+ if(!producer || !lpHdr || !producer->hWaveIn){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ //
+ // Alert the session that there is new data to send over the network
+ //
+ if(TMEDIA_PRODUCER(producer)->callback){
+ TMEDIA_PRODUCER(producer)->callback(TMEDIA_PRODUCER(producer)->callback_data, lpHdr->lpData, (lpHdr->dwBytesRecorded/2));
+ }
+
+
+ if(!producer->started){
+ return 0;
+ }
+
+ result = waveInUnprepareHeader(producer->hWaveIn, lpHdr, sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInUnprepareHeader");
+ return -2;
+ }
+
+ result = waveInPrepareHeader(producer->hWaveIn, lpHdr, sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInPrepareHeader");
+ return -3;
+ }
+
+ result = waveInAddBuffer(producer->hWaveIn, lpHdr, sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInAddBuffer");
+ return -4;
+ }
+
+ return 0;
+}
+
+static void *__record_thread(void *param)
+{
+ tdav_producer_waveapi_t* producer = (tdav_producer_waveapi_t*)param;
+ DWORD dwEvent;
+ tsk_size_t i;
+
+ TSK_DEBUG_INFO("__record_thread -- START");
+
+ SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS);
+
+ for(;;){
+ dwEvent = WaitForMultipleObjects(2, producer->events, FALSE, INFINITE);
+
+ if (dwEvent == 1){
+ break;
+ }
+
+ else if (dwEvent == 0){
+ EnterCriticalSection(&producer->cs);
+ for(i = 0; i< sizeof(producer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ if(producer->hWaveHeaders[i] && (producer->hWaveHeaders[i]->dwFlags & WHDR_DONE)){
+ record_wavehdr(producer, producer->hWaveHeaders[i]);
+ }
+ }
+ LeaveCriticalSection(&producer->cs);
+ }
+ }
+
+ TSK_DEBUG_INFO("__record_thread() -- STOP");
+
+
+ return tsk_null;
+}
+
+
+
+
+
+
+
+
+/* ============ Media Producer Interface ================= */
+int tdav_producer_waveapi_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec)
+{
+ tdav_producer_waveapi_t* producer = (tdav_producer_waveapi_t*)self;
+ tsk_size_t i;
+
+ if(!producer || !codec && codec->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TDAV_PRODUCER_AUDIO(producer)->channels = codec->plugin->audio.channels;
+ TDAV_PRODUCER_AUDIO(producer)->rate = codec->plugin->rate;
+ /* codec should have ptime */
+
+
+ /* Format */
+ ZeroMemory(&producer->wfx, sizeof(WAVEFORMATEX));
+ producer->wfx.wFormatTag = WAVE_FORMAT_PCM;
+ producer->wfx.nChannels = TDAV_PRODUCER_AUDIO(producer)->channels;
+ producer->wfx.nSamplesPerSec = TDAV_PRODUCER_AUDIO(producer)->rate;
+ producer->wfx.wBitsPerSample = TDAV_PRODUCER_AUDIO(producer)->bits_per_sample;
+ producer->wfx.nBlockAlign = (producer->wfx.nChannels * producer->wfx.wBitsPerSample/8);
+ producer->wfx.nAvgBytesPerSec = (producer->wfx.nSamplesPerSec * producer->wfx.nBlockAlign);
+
+ /* Average bytes (count) for each notification */
+ producer->bytes_per_notif = ((producer->wfx.nAvgBytesPerSec * TDAV_PRODUCER_AUDIO(producer)->ptime)/1000);
+
+ /* create buffers */
+ for(i = 0; i< sizeof(producer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ create_wavehdr(producer, i);
+ }
+
+ return 0;
+}
+
+int tdav_producer_waveapi_start(tmedia_producer_t* self)
+{
+ tdav_producer_waveapi_t* producer = (tdav_producer_waveapi_t*)self;
+ MMRESULT result;
+ tsk_size_t i;
+
+ if(!producer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(producer->started || producer->hWaveIn){
+ TSK_DEBUG_WARN("Producer already started");
+ return 0;
+ }
+
+ /* create events */
+ if(!producer->events[0]){
+ producer->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+ if(!producer->events[1]){
+ producer->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+
+ /* open */
+ result = waveInOpen((HWAVEIN *)&producer->hWaveIn, /*WAVE_MAPPER*/0, &producer->wfx, (DWORD)producer->events[0], (DWORD_PTR)producer, CALLBACK_EVENT);
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInOpen");
+ return -2;
+ }
+
+ /* start */
+ result = waveInStart(producer->hWaveIn);
+ if(result != MMSYSERR_NOERROR){
+ print_last_error(result, "waveInStart");
+ return -2;
+ }
+
+ /* start thread */
+ tsk_thread_create(&producer->tid[0], __record_thread, producer);
+
+ /* write */
+ for(i = 0; i< sizeof(producer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ add_wavehdr(producer, i);
+ }
+
+ producer->started = tsk_true;
+
+ return 0;
+}
+
+int tdav_producer_waveapi_pause(tmedia_producer_t* self)
+{
+ tdav_producer_waveapi_t* producer = (tdav_producer_waveapi_t*)self;
+
+ if(!producer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tdav_producer_waveapi_stop(tmedia_producer_t* self)
+{
+ tdav_producer_waveapi_t* producer = (tdav_producer_waveapi_t*)self;
+ MMRESULT result;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!producer->started){
+ TSK_DEBUG_WARN("Producer not started");
+ return 0;
+ }
+
+ /* stop thread */
+ if(producer->tid[0]){
+ SetEvent(producer->events[1]);
+ tsk_thread_join(&(producer->tid[0]));
+ }
+
+ /* should be done here */
+ producer->started = tsk_false;
+
+ if(producer->hWaveIn && (((result = waveInReset(producer->hWaveIn)) != MMSYSERR_NOERROR) || ((result = waveInClose(producer->hWaveIn)) != MMSYSERR_NOERROR))){
+ print_last_error(result, "waveInReset/waveInClose");
+ }
+
+ return 0;
+}
+
+
+//
+// WaveAPI producer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_producer_waveapi_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_producer_waveapi_t *producer = self;
+ if(producer){
+ /* init base */
+ tdav_producer_audio_init(TDAV_PRODUCER_AUDIO(producer));
+ /* init self */
+ InitializeCriticalSection(&producer->cs);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_producer_waveapi_dtor(tsk_object_t * self)
+{
+ tdav_producer_waveapi_t *producer = self;
+ if(producer){
+ tsk_size_t i;
+
+ /* stop */
+ if(producer->started){
+ tdav_producer_waveapi_stop(self);
+ }
+
+ /* deinit base */
+ tdav_producer_audio_deinit(TDAV_PRODUCER_AUDIO(producer));
+ /* deinit self */
+ for(i = 0; i< sizeof(producer->hWaveHeaders)/sizeof(LPWAVEHDR); i++){
+ free_wavehdr(producer, i);
+ }
+ if(producer->hWaveIn){
+ waveInClose(producer->hWaveIn);
+ }
+ if(producer->events[0]){
+ CloseHandle(producer->events[0]);
+ }
+ if(producer->events[1]){
+ CloseHandle(producer->events[1]);
+ }
+ DeleteCriticalSection(&producer->cs);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_producer_waveapi_def_s =
+{
+ sizeof(tdav_producer_waveapi_t),
+ tdav_producer_waveapi_ctor,
+ tdav_producer_waveapi_dtor,
+ tdav_producer_audio_cmp,
+};
+/* plugin definition*/
+static const tmedia_producer_plugin_def_t tdav_producer_waveapi_plugin_def_s =
+{
+ &tdav_producer_waveapi_def_s,
+
+ tmedia_audio,
+ "Microsoft WaveAPI producer",
+
+ tdav_producer_waveapi_set,
+ tdav_producer_waveapi_prepare,
+ tdav_producer_waveapi_start,
+ tdav_producer_waveapi_pause,
+ tdav_producer_waveapi_stop
+};
+const tmedia_producer_plugin_def_t *tdav_producer_waveapi_plugin_def_t = &tdav_producer_waveapi_plugin_def_s;
+
+#endif /* HAVE_WAVE_API */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/amr/tdav_codec_amr.c b/tinyDAV/src/codecs/amr/tdav_codec_amr.c
new file mode 100644
index 0000000..71de9ee
--- /dev/null
+++ b/tinyDAV/src/codecs/amr/tdav_codec_amr.c
@@ -0,0 +1,810 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_amr.c
+ * @brief AMR-NB and AMR-WB codecs.
+ * RTP payloader/depayloader are based on RFC 4867
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/amr/tdav_codec_amr.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <stdlib.h> /* atoi() */
+
+#if HAVE_OPENCORE_AMR
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "..\\thirdparties\\win32\\lib\\opencore\\libopencore-amrnb.a")
+#endif
+
+#define NO_DATA 15
+#define DEFAULT_ENC_MODE ((enum Mode)MR122) /* Higher, could be changed by remote party by using CMR */
+
+/* From WmfDecBytesPerFrame in dec_input_format_tab.cpp */
+static const int tdav_codec_amr_nb_sizes[] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0 };
+/* From pvamrwbdecoder_api.h, by dividing by 8 and rounding up */
+static const int tdav_codec_amr_wb_sizes[] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, -1, -1, -1, -1, -1, -1 };
+
+/* ============ Common ================= */
+int tdav_codec_amr_init(tdav_codec_amr_t* self, tdav_codec_amr_type_t type, tdav_codec_amr_mode_t mode);
+int tdav_codec_amr_deinit(tdav_codec_amr_t* self);
+tdav_codec_amr_mode_t tdav_codec_amr_get_mode(const char* fmtp);
+int tdav_codec_amr_parse_fmtp(tdav_codec_amr_t* self, const char* fmtp);
+tsk_size_t tdav_codec_amr_oa_decode(tdav_codec_amr_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr);
+tsk_size_t tdav_codec_amr_be_decode(tdav_codec_amr_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr);
+tsk_size_t tdav_codec_amr_be_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size);
+tsk_size_t tdav_codec_amr_oa_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size);
+uint8_t tdav_codec_amr_bitbuffer_read(const void* bits, tsk_size_t size, tsk_size_t start, tsk_size_t count);
+
+/* ============ AMR-NB Plugin interface =================
+ The AMR codec was originally developed and standardized by the
+ European Telecommunications Standards Institute (ETSI) for GSM
+ cellular systems. It is now chosen by the Third Generation
+ Partnership Project (3GPP) as the mandatory codec for third
+ generation (3G) cellular systems [1].
+
+ The AMR codec is a multi-mode codec that supports eight narrow band
+ speech encoding modes with bit rates between 4.75 and 12.2 kbps. The
+ sampling frequency used in AMR is 8000 Hz and the speech encoding is
+ performed on 20 ms speech frames. Therefore, each encoded AMR speech
+ frame represents 160 samples of the original speech.
+
+ Among the eight AMR encoding modes, three are already separately
+ adopted as standards of their own. Particularly, the 6.7 kbps mode
+ is adopted as PDC-EFR [18], the 7.4 kbps mode as IS-641 codec in TDMA
+ [17], and the 12.2 kbps mode as GSM-EFR [16].
+*/
+
+#define tdav_codec_amrnb_fmtp_set tsk_null
+
+int tdav_codec_amrnb_open(tmedia_codec_t* self)
+{
+ tdav_codec_amr_t* amrnb = (tdav_codec_amr_t*)self;
+
+ if(!TDAV_CODEC_AMR(amrnb)->encoder){
+ if(!(TDAV_CODEC_AMR(amrnb)->encoder = Encoder_Interface_init(0))){
+ TSK_DEBUG_ERROR("Failed to initialize AMR-NB encoder");
+ return -2;
+ }
+ }
+
+ if(!TDAV_CODEC_AMR(amrnb)->decoder){
+ if(!(TDAV_CODEC_AMR(amrnb)->decoder = Decoder_Interface_init())){
+ TSK_DEBUG_ERROR("Failed to initialize AMR-NB encoder");
+ return -2;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_codec_amrnb_close(tmedia_codec_t* self)
+{
+ tdav_codec_amr_t* amrnb = (tdav_codec_amr_t*)self;
+
+ if(TDAV_CODEC_AMR(amrnb)->encoder){
+ Encoder_Interface_exit(TDAV_CODEC_AMR(amrnb)->encoder);
+ TDAV_CODEC_AMR(amrnb)->encoder = tsk_null;
+ }
+
+ if(TDAV_CODEC_AMR(amrnb)->decoder){
+ Decoder_Interface_exit(TDAV_CODEC_AMR(amrnb)->decoder);
+ TDAV_CODEC_AMR(amrnb)->decoder = tsk_null;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_amrnb_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tdav_codec_amr_t* amr = (tdav_codec_amr_t*)self;
+
+ switch(amr->mode){
+ case tdav_codec_amr_mode_be:
+ return tdav_codec_amr_be_encode(amr, in_data, in_size, out_data, out_max_size);
+ default:
+ return tdav_codec_amr_oa_encode(amr, in_data, in_size, out_data, out_max_size);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_amrnb_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tdav_codec_amr_t* amr = (tdav_codec_amr_t*)self;
+
+ switch(amr->mode){
+ case tdav_codec_amr_mode_be:
+ return tdav_codec_amr_be_decode(amr, in_data, in_size, out_data, out_max_size, proto_hdr);
+ default:
+ return tdav_codec_amr_oa_decode(amr, in_data, in_size, out_data, out_max_size, proto_hdr);
+ }
+}
+
+char* tdav_codec_amrnb_fmtp_get(const tmedia_codec_t* codec)
+{
+ const tdav_codec_amr_t* amr = (const tdav_codec_amr_t*)codec;
+
+ /* We support all modes, all ... */
+ if(amr){
+ switch(amr->mode){
+ case tdav_codec_amr_mode_be:
+ return tsk_strdup("octet-align=0");
+ default:
+ return tsk_strdup("octet-align=1");
+ }
+ }
+ return tsk_null;
+}
+
+tsk_bool_t tdav_codec_amrnb_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tdav_codec_amr_t* amr = (tdav_codec_amr_t*)codec;
+
+ if(amr){
+ /* Match mode */
+ if(tdav_codec_amr_get_mode(fmtp) != amr->mode){
+ TSK_DEBUG_INFO("Failed to match [%s]", fmtp);
+ return tsk_false;
+ }
+ /* check parameters validity */
+ if(tdav_codec_amr_parse_fmtp(amr, fmtp)){
+ TSK_DEBUG_INFO("Failed to match [%s]", fmtp);
+ return tsk_false;
+ }
+
+ return tsk_true;
+ }
+ return tsk_false;
+}
+
+
+//
+// AMR-NB OA Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_amrnb_oa_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_amr_t *amrnb_oa = self;
+ if(amrnb_oa){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_amr_init(TDAV_CODEC_AMR(amrnb_oa), tdav_codec_amr_type_nb, tdav_codec_amr_mode_oa);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_amrnb_oa_dtor(tsk_object_t * self)
+{
+ tdav_codec_amr_t *amrnb_oa = self;
+ if(amrnb_oa){
+ /* deinit base */
+ tmedia_codec_audio_deinit(amrnb_oa);
+ /* deinit self */
+ tdav_codec_amr_deinit(TDAV_CODEC_AMR(amrnb_oa));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_amrnb_oa_def_s =
+{
+ sizeof(tdav_codec_amr_t),
+ tdav_codec_amrnb_oa_ctor,
+ tdav_codec_amrnb_oa_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_amrnb_oa_plugin_def_s =
+{
+ &tdav_codec_amrnb_oa_def_s,
+
+ tmedia_audio,
+ "AMR",
+ "AMR Narrow Band - Octet Aligned",
+ TMEDIA_CODEC_FORMAT_AMR_NB_OA,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_amrnb_open,
+ tdav_codec_amrnb_close,
+ tdav_codec_amrnb_encode,
+ tdav_codec_amrnb_decode,
+ tdav_codec_amrnb_fmtp_match,
+ tdav_codec_amrnb_fmtp_get,
+ tdav_codec_amrnb_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_amrnb_oa_plugin_def_t = &tdav_codec_amrnb_oa_plugin_def_s;
+
+//
+// AMR-NB BE Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_amrnb_be_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_amr_t *amrnb_be = self;
+ if(amrnb_be){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_amr_init(TDAV_CODEC_AMR(amrnb_be), tdav_codec_amr_type_nb, tdav_codec_amr_mode_be);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_amrnb_be_dtor(tsk_object_t * self)
+{
+ tdav_codec_amr_t *amrnb_be = self;
+ if(amrnb_be){
+ /* deinit base */
+ tmedia_codec_audio_deinit(amrnb_be);
+ /* deinit self */
+ tdav_codec_amr_deinit(TDAV_CODEC_AMR(amrnb_be));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_amrnb_be_def_s =
+{
+ sizeof(tdav_codec_amr_t),
+ tdav_codec_amrnb_be_ctor,
+ tdav_codec_amrnb_be_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_amrnb_be_plugin_def_s =
+{
+ &tdav_codec_amrnb_be_def_s,
+
+ tmedia_audio,
+ "AMR",
+ "AMR Narrow Band - Bandwidth-Efficient",
+ TMEDIA_CODEC_FORMAT_AMR_NB_BE,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_amrnb_open,
+ tdav_codec_amrnb_close,
+ tdav_codec_amrnb_encode,
+ tdav_codec_amrnb_decode,
+ tdav_codec_amrnb_fmtp_match,
+ tdav_codec_amrnb_fmtp_get,
+ tdav_codec_amrnb_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_amrnb_be_plugin_def_t = &tdav_codec_amrnb_be_plugin_def_s;
+
+
+
+
+
+
+
+
+
+//
+// Common functions
+//
+
+int tdav_codec_amr_init(tdav_codec_amr_t* self, tdav_codec_amr_type_t type, tdav_codec_amr_mode_t mode)
+{
+ if(self){
+ self->type = type;
+ self->mode = mode;
+ self->encoder_mode = DEFAULT_ENC_MODE;
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+}
+
+int tdav_codec_amr_deinit(tdav_codec_amr_t* self)
+{
+ if(self){
+ switch(self->type){
+ case tdav_codec_amr_type_nb:
+ { /* AMR-NB */
+ if(self->encoder){
+ Encoder_Interface_exit(self->encoder);
+ self->encoder = tsk_null;
+ }
+ if(self->decoder){
+ Decoder_Interface_exit(self->decoder);
+ self->decoder = tsk_null;
+ }
+ break;
+ }
+ case tdav_codec_amr_type_wb:
+ { /* AMR-WB */
+ break;
+ }
+ }
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+}
+
+tdav_codec_amr_mode_t tdav_codec_amr_get_mode(const char* fmtp)
+{
+ /* RFC 4867 - 8.1. AMR Media Type Registration
+ octet-align: Permissible values are 0 and 1. If 1, octet-aligned
+ operation SHALL be used. If 0 or if not present, bandwidth-efficient operation is employed.
+ */
+ tdav_codec_amr_mode_t mode = tdav_codec_amr_mode_be;
+ tsk_size_t size = tsk_strlen(fmtp);
+ int start, end;
+
+ if((start = tsk_strindexOf(fmtp, size, "octet-align")) !=-1){
+ tsk_param_t* param;
+ if((end = tsk_strindexOf((fmtp+start), (size-start), ";")) == -1){
+ end = size;
+ }
+ if((param = tsk_params_parse_param((fmtp+start), (end-start)))){
+ if(param->value && tsk_strequals(param->value, "1")){
+ mode = tdav_codec_amr_mode_oa;
+ }
+ TSK_OBJECT_SAFE_FREE(param);
+ }
+ }
+ return mode;
+}
+
+int tdav_codec_amr_parse_fmtp(tdav_codec_amr_t* self, const char* fmtp)
+{
+ int ret = 0;
+ int val_int;
+ const char* val_str;
+ //--tdav_codec_amr_mode_t mode = self->mode;
+ tsk_params_L_t* params = tsk_null;
+
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ /* Do not check "octet-align" => already done by the caller of this function */
+
+ /* === mode-set ===*/
+ if((val_str = tsk_params_get_param_value(params, "mode-set"))){
+ char* modes = tsk_strdup(val_str);
+ char* pch;
+ int mode_int;
+ pch = strtok(modes, ", ");
+ while(pch){
+ mode_int = atoi(pch);
+ self->modes |= 0x0001 << mode_int;
+ pch = strtok(tsk_null, ", ");
+ }
+
+ TSK_FREE(modes);
+ }
+ else{
+ self->modes = 0xFFFF;
+ }
+
+ /* === interleaving ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "interleaving")) != -1){
+ TSK_DEBUG_WARN("interleaving not supported");
+ ret = -1; goto bail;
+ }
+ /* === mode-change-period ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "mode-change-period")) != -1){
+ if(val_int != 1 && val_int != 2){
+ TSK_DEBUG_ERROR("Invalid [mode-change-period]");
+ ret = -1; goto bail;
+ }
+ self->mcp = (unsigned)val_int;
+ }
+ /* === mode-change-capability ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "mode-change-capability")) != -1){
+ if(val_int != 1 && val_int != 2){
+ TSK_DEBUG_ERROR("Invalid [mode-change-capability]");
+ ret = -1; goto bail;
+ }
+ self->mcc = (unsigned)val_int;
+ }
+ /* === mode-change-neighbor ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "mode-change-neighbor")) != -1){
+ if(val_int != 0 && val_int != 1){
+ TSK_DEBUG_ERROR("Invalid [mode-change-neighbor]");
+ ret = -1; goto bail;
+ }
+ self->mcn = (unsigned)val_int;
+ }
+ }
+
+bail:
+ TSK_OBJECT_SAFE_FREE(params);
+ return ret;
+}
+
+
+/* RFC 4867 - 4.2. Payload Structure
+ +----------------+-------------------+----------------
+ | payload header | table of contents | speech data ...
+ +----------------+-------------------+----------------
+*/
+/* RFC 4867 - 4.4.2. The Payload Table of Contents and Frame CRCs
+ The table of contents (ToC) consists of a list of ToC entries, each representing a speech frame.
+ +---------------------+
+ | list of ToC entries |
+ +---------------------+
+ | list of frame CRCs | (optional)
+ - - - - - - - - - - -
+ Note, for ToC entries with FT=14 or 15, there will be no
+ corresponding speech frame or frame CRC present in the payload.
+*/
+
+
+tsk_size_t tdav_codec_amr_be_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size = 0, i;
+ int ret_size;
+ uint8_t ToC;
+ static uint8_t CMR = NO_DATA /* No interleaving */;
+
+ uint8_t outbuf[60 + 1]; /* enought for both NB and WB at ptime=20ms */
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_be)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Encode */
+ if((ret_size = Encoder_Interface_Encode(amr->encoder, amr->encoder_mode, in_data, outbuf, 0)) <= 0){
+ TSK_DEBUG_ERROR("Encoder_Interface_Encode() failed");
+ goto bail;
+ }
+
+
+ /* allocate output buffer */
+ if((int)*out_max_size <ret_size){
+ if(!(*out_data = tsk_realloc(*out_data, ret_size))){
+ *out_max_size = 0;
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ goto bail;
+ }
+ *out_max_size = ret_size;
+ }
+
+ out_size = ret_size;
+
+ /* CMR (4bits) */
+ ((uint8_t*)*out_data)[0] = (CMR<<4);
+ /* ToC (Always ONE Frame, don't need to test for [F]) (6bits)*/
+ ToC = outbuf[0]>>2/*2*[P]*/;
+ ((uint8_t*)*out_data)[0] |= (ToC >> 2/*[Q],[1-FT]*/) & 0xF; /* 4bits */
+ ((uint8_t*)*out_data)[1] = (ToC & 0x3/*[1-FT],[Q]*/)<<6; /* 2bits */
+
+ /* === THERE ARE 2 EXTRA BITS === */
+
+ for(i=1; i<out_size-1; i++){
+ ((uint8_t*)*out_data)[i] |= outbuf[i]>>2;/* 6bits */
+ ((uint8_t*)*out_data)[i+1] = outbuf[i]<<6;/* 2bits */
+ }
+
+bail:
+ return out_size;
+}
+
+tsk_size_t tdav_codec_amr_be_decode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size = 0, pcm_frame_size = 0, index = 0;
+ const uint8_t* pdata = (const uint8_t*)in_data;
+ //--const uint8_t* pend = (pdata + in_size);
+ uint8_t CMR;
+ int toc_entries = 0, i, k; // ToC entries count
+
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_be)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* compute PCM frame size */
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ pcm_frame_size = 160 * sizeof(short);
+ break;
+ case tdav_codec_amr_type_wb:
+ pcm_frame_size = 320 * sizeof(short);
+ break;
+ default:
+ TSK_DEBUG_ERROR("Invalid AMR type");
+ return 0;
+ }
+
+ /* CMR (4bits) */
+ CMR = tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), index, 4);
+ index += 4;
+ if(CMR != NO_DATA){
+ amr->encoder_mode = (enum Mode)CMR;
+ }
+
+ /* F(1bit), FT(4bits), Q(1bit) */
+ /* count ToC entries */
+ do{ /* At least ONE ToC */
+ ++toc_entries;
+ ++pdata;
+ index += 6;
+ }
+ while((index < (in_size*8)) && (tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), (index-6), 1)/* F */));
+
+ for(i = 0; (i<toc_entries && (in_size < (in_size*8))) ; i++){
+ int size = -1;
+ uint8_t* speech_data = tsk_null;
+ //--int speech_data_size = 0;
+ uint8_t ToC = tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), 4/*CMR*/ + (i*6), 6);
+
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ size = tdav_codec_amr_nb_sizes[(ToC>>1)&0x0F/* FT */];
+ break;
+ case tdav_codec_amr_type_wb:
+ size = tdav_codec_amr_wb_sizes[(ToC>>1)&0x0F/* FT */];
+ break;
+ }
+
+ if((speech_data = tsk_calloc((size + 2/* ToC + '\0' */), sizeof(uint8_t)))){
+ /* copy ToC */
+ speech_data[0] = (ToC & 0x1F)<<2/* 2*[P] */; /* ToC as OA layout */
+ /* copy speech data */
+ for(k=0; k<size; k++){
+ speech_data[1 + k] = tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), index, 8);
+ index+=8;
+ if((k==size-1) && (index%8)){
+ speech_data[1 + k] <<= (8-(index%8)); //clean
+ }
+ }
+
+ /* allocate/reallocate speech data */
+ if(*out_max_size <(out_size + pcm_frame_size)){
+ if(!(*out_data = tsk_realloc(*out_data, (out_size + pcm_frame_size)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ TSK_FREE(speech_data);
+ goto bail;
+ }
+ *out_max_size = out_size + pcm_frame_size;
+ }
+
+ /* decode speech data */
+ Decoder_Interface_Decode(amr->decoder, speech_data, &((short*)*out_data)[out_size/sizeof(short)], 0);
+ out_size += pcm_frame_size, pdata+= size;
+
+ TSK_FREE(speech_data);
+ }
+ }
+
+bail:
+ return out_size;
+}
+
+tsk_size_t tdav_codec_amr_oa_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size = 0;
+ int ret_size;
+ static uint8_t CMR = NO_DATA /* No interleaving */;
+
+ uint8_t outbuf[60 + 1]; /* enought for both NB and WB at ptime=20ms */
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_oa)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Encode */
+ if((ret_size = Encoder_Interface_Encode(amr->encoder, amr->encoder_mode, in_data, outbuf, 0)) <= 0){
+ TSK_DEBUG_ERROR("Encoder_Interface_Encode() failed");
+ goto bail;
+ }
+
+ out_size = ret_size + 1 /* CMR without interleaving */;
+ /* allocate output buffer */
+ if(*out_max_size <out_size){
+ if(!(*out_data = tsk_realloc(*out_data, out_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = out_size = 0;
+ goto bail;
+ }
+ *out_max_size = out_size;
+ }
+
+ /* CMR */
+ ((uint8_t*)*out_data)[0] = (CMR << 4);
+ /* Only ONE ToC --> believe me */
+ memcpy(&((uint8_t*)*out_data)[1], outbuf, ret_size);
+
+bail:
+ return out_size;
+}
+
+tsk_size_t tdav_codec_amr_oa_decode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size = 0, pcm_frame_size = 0;
+ const uint8_t* pdata = (const uint8_t*)in_data;
+ const uint8_t* pend = (pdata + in_size);
+ uint8_t CMR;
+ int toc_entries = 0, i; // ToC entries count
+
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_oa)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* compute PCM frame size */
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ pcm_frame_size = 160 * sizeof(short);
+ break;
+ case tdav_codec_amr_type_wb:
+ pcm_frame_size = 320 * sizeof(short);
+ break;
+ default:
+ TSK_DEBUG_ERROR("Invalid AMR type");
+ return 0;
+ }
+
+ /* RFC 4867 - 4.4. Octet-Aligned Mode
+ In octet-aligned mode, the payload header consists of a 4-bit CMR, 4
+ reserved bits, and optionally, an 8-bit interleaving header, as shown
+ below:
+
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+- - - - - - - -
+ | CMR |R|R|R|R| ILL | ILP |
+ +-+-+-+-+-+-+-+-+- - - - - - - -
+
+ CMR (4 bits): same as defined in Section 4.3.1.
+
+ "interleaving" not supported ==> could ignore ILL and ILP (wich are optional)
+ */
+
+ CMR = (*pdata++ >> 4);
+ if(CMR != NO_DATA){
+ /* The codec mode request received in the CMR field is valid until the
+ next codec mode request is received, i.e., a newly received CMR value
+ corresponding to a speech mode, or NO_DATA overrides the previously
+ received CMR value corresponding to a speech mode or NO_DATA. */
+ amr->encoder_mode = (enum Mode)CMR; // As we support all modes, do not check for validity
+ }
+
+ /*
+ A ToC entry takes the following format in octet-aligned mode:
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |F| FT |Q|P|P|
+ +-+-+-+-+-+-+-+-+
+
+ F (1 bit): see definition in Section 4.3.2.
+ FT (4 bits, unsigned integer): see definition in Section 4.3.2.
+ Q (1 bit): see definition in Section 4.3.2.
+ P bits: padding bits, MUST be set to zero, and MUST be ignored on reception.
+ */
+
+ /* count ToC entries */
+ do{ /* At least ONE ToC */
+ ++toc_entries;
+ ++pdata;
+ }
+ while(pdata && (pdata < pend) && (pdata[-1] >> 7/* F */));
+
+ for(i = 0; (i<toc_entries && (pdata < pend)) ; i++){
+ int size = -1;
+ uint8_t* speech_data = tsk_null;
+ //--int speech_data_size = 0;
+ uint8_t ToC = ((const uint8_t*)in_data)[1/*CMR...*/ + i];
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ size = tdav_codec_amr_nb_sizes[(ToC>>3) & 0x0F/* FT */];
+ break;
+ case tdav_codec_amr_type_wb:
+ size = tdav_codec_amr_wb_sizes[(ToC>>3) & 0x0F/* FT */];
+ break;
+ }
+
+ /* check size */
+ if(size <0 || ((pdata + size) > pend)){
+ TSK_DEBUG_ERROR("Invalid size");
+ break;
+ }
+
+ if((speech_data = tsk_calloc((size + 2/* ToC + '\0' */), sizeof(uint8_t)))){
+ /* copy ToC */
+ *speech_data = ToC & 0x7F/* with 'F'=0 */;
+ /* copy speech data */
+ memcpy((speech_data + 1), pdata, size);
+ /* allocate/reallocate speech data */
+ if(*out_max_size <(out_size + pcm_frame_size)){
+ if(!(*out_data = tsk_realloc(*out_data, (out_size + pcm_frame_size)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ TSK_FREE(speech_data);
+ goto bail;
+ }
+ *out_max_size = (out_size + pcm_frame_size);
+ }
+ /* decode speech data */
+ Decoder_Interface_Decode(amr->decoder, speech_data, &((short*)*out_data)[out_size/sizeof(short)], 0);
+ out_size += pcm_frame_size, pdata+= size;
+
+ TSK_FREE(speech_data);
+ }
+ }
+
+bail:
+ return out_size;
+}
+
+
+uint8_t tdav_codec_amr_bitbuffer_read(const void* bits, tsk_size_t size, tsk_size_t start, tsk_size_t count)
+{
+ uint8_t byte, left, right, pad;
+
+ if(!bits || !size || count>8){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if((start + count) > size){
+ count = (size - start);
+ }
+
+ pad = start ? (8 - (start % 8)) : count;
+ left = ((uint8_t*)bits)[start/8] << (8-pad);
+ right = ((uint8_t*)bits)[((start+count)<size ? (start+count) : start)/8] >> pad;
+
+ if((start && (start % 8) != ((start+count)%8)) || (!start && count>8)){
+ /* overlap */
+ byte = (left | right) >> (8-count);
+ }
+ else{
+ byte = (left | right) & (0xFF >> (8-count));
+ }
+
+ return byte;
+}
+
+#endif /* HAVE_OPENCORE_AMR */
diff --git a/tinyDAV/src/codecs/bv/tdav_codec_bv16.c b/tinyDAV/src/codecs/bv/tdav_codec_bv16.c
new file mode 100644
index 0000000..f78f526
--- /dev/null
+++ b/tinyDAV/src/codecs/bv/tdav_codec_bv16.c
@@ -0,0 +1,249 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_bv16.c
+ * @brief BroadVoice16 codec
+ * The payloader/depayloader follow RFC 4298
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/bv/tdav_codec_bv16.h"
+
+#if HAVE_BV16
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "..\\thirdparties\\win32\\lib\\BroadVoice16\\libbv16.a")
+#endif
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include "typedef.h"
+#include "bvcommon.h"
+#include "bv16cnst.h"
+#include "bv16strct.h"
+#include "bv16.h"
+#include "utility.h"
+#if G192BITSTREAM
+#include "g192.h"
+#else
+#include "bitpack.h"
+#endif
+#include "memutil.h"
+
+/* RFC 4298 - 3.1. BroadVoice16 Bit Stream Definition */
+#define TDAV_BV16_FRAME_SIZE 10
+#define FRSZ_IN_U8 (FRSZ*2)
+
+/* ============ BV16 Plugin interface ================= */
+
+#define tdav_codec_bv16_fmtp_get tsk_null
+#define tdav_codec_bv16_fmtp_set tsk_null
+
+static int sizestate = sizeof(struct BV16_Encoder_State);
+static int sizebitstream = sizeof(struct BV16_Bit_Stream);
+
+int tdav_codec_bv16_open(tmedia_codec_t* self)
+{
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+
+ if(!bv16->encoder.state){
+ bv16->encoder.state = allocWord16(0, sizestate/2-1);
+ Reset_BV16_Encoder((struct BV16_Encoder_State*)bv16->encoder.state);
+ }
+ if(!bv16->encoder.bs){
+ bv16->encoder.bs = allocWord16(0, sizebitstream/2-1);
+ }
+ if(!bv16->encoder.x){
+ bv16->encoder.x = allocWord16(0, FRSZ-1);
+ }
+
+ if(!bv16->decoder.state){
+ bv16->decoder.state = allocWord16(0, sizestate/2-1);
+ Reset_BV16_Decoder((struct BV16_Decoder_State*)bv16->decoder.state);
+ }
+ if(!bv16->decoder.bs){
+ bv16->decoder.bs = allocWord16(0, sizebitstream/2-1);
+ }
+ if(!bv16->decoder.x){
+ bv16->decoder.x = allocWord16(0, FRSZ-1);
+ }
+
+ return 0;
+}
+
+int tdav_codec_bv16_close(tmedia_codec_t* self)
+{
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+
+ if(bv16->encoder.state){
+ deallocWord16(bv16->encoder.state, 0, sizestate/2-1);
+ bv16->encoder.state = tsk_null;
+ }
+ if(bv16->encoder.bs){
+ deallocWord16(bv16->encoder.bs, 0, sizebitstream/2-1);
+ bv16->encoder.bs = tsk_null;
+ }
+ if(bv16->encoder.x){
+ deallocWord16(bv16->encoder.x, 0, FRSZ-1);
+ bv16->encoder.x = tsk_null;
+ }
+
+ if(bv16->decoder.state){
+ deallocWord16(bv16->decoder.state, 0, sizestate/2-1);
+ bv16->decoder.state = tsk_null;
+ }
+ if(bv16->encoder.bs){
+ deallocWord16(bv16->decoder.bs, 0, sizebitstream/2-1);
+ bv16->decoder.bs = tsk_null;
+ }
+ if(bv16->decoder.x){
+ deallocWord16(bv16->decoder.x, 0, FRSZ-1);
+ bv16->decoder.x = tsk_null;
+ }
+
+
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_bv16_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ //tsk_size_t out_size = 0;
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_bv16_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size = 0;
+ int i;
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+ uint8_t mama[600];
+
+ if(!self || !in_data || !in_size || !out_data || (in_size % TDAV_BV16_FRAME_SIZE)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ for(i=0; i<(int)in_size; i+=TDAV_BV16_FRAME_SIZE){
+ BV16_BitUnPack(mama, (struct BV16_Bit_Stream*)bv16->decoder.bs);
+ //BV16_BitUnPack(&((UWord8 *)in_data)[i], (struct BV16_Bit_Stream*)bv16->decoder.bs);
+ BV16_Decode((struct BV16_Bit_Stream*)bv16->decoder.bs, (struct BV16_Decoder_State*)bv16->decoder.state, bv16->decoder.x);
+
+
+ if(*out_max_size<(out_size + FRSZ_IN_U8)){
+ if(!(*out_data = tsk_realloc(*out_data, (out_size + FRSZ_IN_U8)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = (out_size + FRSZ_IN_U8);
+ }
+ memcpy(&((uint8_t*)* out_data)[out_size], bv16->decoder.x, FRSZ_IN_U8);
+ out_size += FRSZ_IN_U8;
+ }
+
+
+ return out_size;
+}
+
+tsk_bool_t tdav_codec_bv16_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// BV16 Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_bv16_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_bv16_t *bv16 = self;
+ if(bv16){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_bv16_dtor(tsk_object_t * self)
+{
+ tdav_codec_bv16_t *bv16 = self;
+ if(bv16){
+ /* deinit base */
+ tmedia_codec_audio_deinit(bv16);
+ /* deinit self */
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_bv16_def_s =
+{
+ sizeof(tdav_codec_bv16_t),
+ tdav_codec_bv16_ctor,
+ tdav_codec_bv16_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_bv16_plugin_def_s =
+{
+ &tdav_codec_bv16_def_s,
+
+ tmedia_audio,
+ "BV16",
+ "BroadVoice16 Rate",
+ TMEDIA_CODEC_FORMAT_BV16,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_bv16_open,
+ tdav_codec_bv16_close,
+ tdav_codec_bv16_encode,
+ tdav_codec_bv16_decode,
+ tdav_codec_bv16_fmtp_match,
+ tdav_codec_bv16_fmtp_get,
+ tdav_codec_bv16_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_bv16_plugin_def_t = &tdav_codec_bv16_plugin_def_s;
+
+
+#endif /* HAVE_BV16 */
diff --git a/tinyDAV/src/codecs/bv/tdav_codec_bv32.c b/tinyDAV/src/codecs/bv/tdav_codec_bv32.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyDAV/src/codecs/bv/tdav_codec_bv32.c
diff --git a/tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c b/tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c
new file mode 100644
index 0000000..cbd9a03
--- /dev/null
+++ b/tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_dtmf.c
+ * @brief DTMF (RFC 4733) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/dtmf/tdav_codec_dtmf.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+
+/* ============ DTMF Plugin interface ================= */
+
+#define tdav_codec_dtmf_fmtp_set tsk_null
+
+tsk_size_t tdav_codec_dtmf_fmtp_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ return 0;
+}
+
+tsk_size_t tdav_codec_dtmf_fmtp_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ return 0;
+}
+
+char* tdav_codec_dtmf_fmtp_get(const tmedia_codec_t* self)
+{
+ return tsk_strdup("0-15");
+}
+
+tsk_bool_t tdav_codec_dtmf_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// DTMF Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_dtmf_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_dtmf_t *dtmf = self;
+ if(dtmf){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_dtmf_dtor(tsk_object_t * self)
+{
+ tdav_codec_dtmf_t *dtmf = self;
+ if(dtmf){
+ /* deinit base */
+ tmedia_codec_audio_deinit(dtmf);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_dtmf_def_s =
+{
+ sizeof(tdav_codec_dtmf_t),
+ tdav_codec_dtmf_ctor,
+ tdav_codec_dtmf_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_dtmf_plugin_def_s =
+{
+ &tdav_codec_dtmf_def_s,
+
+ tmedia_audio,
+ "telephone-event",
+ "DTMF Codec (RFC 4733)",
+ TMEDIA_CODEC_FORMAT_DTMF,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // open
+ tsk_null, // close
+ tdav_codec_dtmf_fmtp_encode,
+ tdav_codec_dtmf_fmtp_decode,
+ tdav_codec_dtmf_fmtp_match,
+ tdav_codec_dtmf_fmtp_get,
+ tdav_codec_dtmf_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_dtmf_plugin_def_t = &tdav_codec_dtmf_plugin_def_s;
diff --git a/tinyDAV/src/codecs/g711/g711.c b/tinyDAV/src/codecs/g711/g711.c
new file mode 100644
index 0000000..744fad1
--- /dev/null
+++ b/tinyDAV/src/codecs/g711/g711.c
@@ -0,0 +1,295 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "tinydav/codecs/g711/g711.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static short search(short val, short *table, short size)
+{
+ short i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char linear2alaw(short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char aval;
+
+ pcm_val = pcm_val >> 3;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+short alaw2linear(unsigned char a_val)
+{
+ short t;
+ short seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char linear2ulaw(short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ pcm_val = pcm_val >> 2;
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+short ulaw2linear(unsigned char u_val)
+{
+ short t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char alaw2ulaw(unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char ulaw2alaw(unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
diff --git a/tinyDAV/src/codecs/g711/tdav_codec_g711.c b/tinyDAV/src/codecs/g711/tdav_codec_g711.c
new file mode 100644
index 0000000..4f2dbbe
--- /dev/null
+++ b/tinyDAV/src/codecs/g711/tdav_codec_g711.c
@@ -0,0 +1,304 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_g711.c
+ * @brief G.711u and G.711a (a.k.a PCMU and PCMA) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/g711/tdav_codec_g711.h"
+
+#include "tinydav/codecs/g711/g711.h" /* alforithms */
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ G.711u Plugin interface ================= */
+
+#define tdav_codec_g711u_fmtp_get tsk_null
+#define tdav_codec_g711u_fmtp_set tsk_null
+
+tsk_size_t tdav_codec_g711u_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t i;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(*out_max_size <in_size/2){
+ if(!(*out_data = tsk_realloc(*out_data, in_size/2))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = in_size/2;
+ }
+
+ for(i = 0; i<(in_size/2); i++){
+ ((uint8_t*)*out_data)[i] = linear2ulaw(((short*)in_data)[i]);
+ }
+
+ return (in_size/2);
+}
+
+tsk_size_t tdav_codec_g711u_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t i;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* allocate new buffer */
+ if(*out_max_size<(in_size*2)){
+ if(!(*out_data = tsk_calloc(in_size, sizeof(short)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = in_size*2;
+ }
+
+ for(i = 0; i<in_size; i++){
+ ((short*)*out_data)[i] = ulaw2linear(((uint8_t*)in_data)[i]);
+ }
+
+ return (in_size*2);
+}
+
+tsk_bool_t tdav_codec_g711u_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// G.711u Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g711u_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g711u_t *g711u = self;
+ if(g711u){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g711u_dtor(tsk_object_t * self)
+{
+ tdav_codec_g711u_t *g711u = self;
+ if(g711u){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g711u);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g711u_def_s =
+{
+ sizeof(tdav_codec_g711u_t),
+ tdav_codec_g711u_ctor,
+ tdav_codec_g711u_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g711u_plugin_def_s =
+{
+ &tdav_codec_g711u_def_s,
+
+ tmedia_audio,
+ "PCMU",
+ "G.711u codec (From tinyDAV)",
+ TMEDIA_CODEC_FORMAT_G711u,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // open
+ tsk_null, // close
+ tdav_codec_g711u_encode,
+ tdav_codec_g711u_decode,
+ tdav_codec_g711u_fmtp_match,
+ tdav_codec_g711u_fmtp_get,
+ tdav_codec_g711u_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g711u_plugin_def_t = &tdav_codec_g711u_plugin_def_s;
+
+
+/* ============ G.711a Plugin interface ================= */
+
+#define tdav_codec_g711a_fmtp_get tsk_null
+#define tdav_codec_g711a_fmtp_set tsk_null
+
+tsk_size_t tdav_codec_g711a_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t i;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(*out_max_size <in_size/2){
+ if(!(*out_data = tsk_realloc(*out_data, in_size/2))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = in_size/2;
+ }
+
+ for(i = 0; i<(in_size/2); i++){
+ ((uint8_t*)*out_data)[i] = linear2alaw(((short*)in_data)[i]);
+ }
+
+ return (in_size/2);
+}
+
+#if 0
+FILE* file = tsk_null;
+int count = 0;
+#endif
+tsk_size_t tdav_codec_g711a_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t i;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+#if 0
+ if(!file && count<=1000){
+ file = fopen("./g711a.pcm", "wb");
+ }
+#endif
+ /* allocate new buffer */
+ if(*out_max_size<(in_size*2)){
+ if(!(*out_data = tsk_realloc(*out_data, in_size*2))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = in_size*2;
+ }
+
+ for(i = 0; i<in_size; i++){
+ ((short*)*out_data)[i] = alaw2linear(((uint8_t*)in_data)[i]);
+ }
+#if 0
+ if(++count<=1000){
+ fwrite(*out_data, sizeof(short), in_size, file);
+ }
+ else if(file){
+ fclose(file);
+ file = tsk_null;
+ }
+#endif
+ return (in_size*2);
+}
+
+tsk_bool_t tdav_codec_g711a_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// G.711a Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g711a_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g711a_t *g711a = self;
+ if(g711a){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g711a_dtor(tsk_object_t * self)
+{
+ tdav_codec_g711a_t *g711a = self;
+ if(g711a){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g711a);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g711a_def_s =
+{
+ sizeof(tdav_codec_g711a_t),
+ tdav_codec_g711a_ctor,
+ tdav_codec_g711a_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g711a_plugin_def_s =
+{
+ &tdav_codec_g711a_def_s,
+
+ tmedia_audio,
+ "PCMA",
+ "G.711a codec (From tinyDAV)",
+ TMEDIA_CODEC_FORMAT_G711a,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20, // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // open
+ tsk_null, // close
+ tdav_codec_g711a_encode,
+ tdav_codec_g711a_decode,
+ tdav_codec_g711a_fmtp_match,
+ tdav_codec_g711a_fmtp_get,
+ tdav_codec_g711a_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g711a_plugin_def_t = &tdav_codec_g711a_plugin_def_s;
diff --git a/tinyDAV/src/codecs/g729/tdav_codec_g729.c b/tinyDAV/src/codecs/g729/tdav_codec_g729.c
new file mode 100644
index 0000000..3ca6fc7
--- /dev/null
+++ b/tinyDAV/src/codecs/g729/tdav_codec_g729.c
@@ -0,0 +1,454 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_g729.c
+ * @brief G729ab codec.
+ * Source from: http://www.itu.int/rec/T-REC-G.729-199610-S!AnnB/en
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/g729/tdav_codec_g729.h"
+
+#if HAVE_G729
+
+#include "g729b/dtx.h"
+#include "g729b/octet.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "../thirdparties/win32/lib/g729b/g729b.a")
+#endif
+
+Word16 bad_lsf; /* bad LSF indicator */
+
+#ifndef G729_ENABLE_VAD
+# define G729_ENABLE_VAD 1
+#endif
+
+static Word16 bin2int(Word16 no_of_bits, const Word16 *bitstream);
+static void int2bin(Word16 value, Word16 no_of_bits, Word16 *bitstream);
+
+static void unpack_G729(const uint8_t bitstream[], Word16 bits[], int len);
+static void unpack_SID(const uint8_t bitstream[], Word16 bits[]);
+
+static void pack_G729(const Word16 ituBits[], uint8_t bitstream[]);
+static void pack_SID(const Word16 ituBits[], uint8_t bitstream[]);
+
+/* ============ G.729ab Plugin interface ================= */
+
+#define tdav_codec_g729ab_fmtp_set tsk_null
+
+int tdav_codec_g729ab_open(tmedia_codec_t* self)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+
+ // Initialize the decoder
+ bad_lsf = 0;
+ g729a->decoder.synth = (g729a->decoder.synth_buf + M);
+
+ Init_Decod_ld8a();
+ Init_Post_Filter();
+ Init_Post_Process();
+ /* for G.729B */
+ Init_Dec_cng();
+
+ // Initialize the encoder
+ Init_Pre_Process();
+ Init_Coder_ld8a();
+ Set_zero(g729a->encoder.prm, PRM_SIZE + 1);
+ /* for G.729B */
+ Init_Cod_cng();
+
+
+ return 0;
+}
+
+int tdav_codec_g729ab_close(tmedia_codec_t* self)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_g729ab_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t ex_size, out_size = 0;
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+ int i, frame_count = (in_size / 160);
+
+
+ if(!self || !in_data || !in_size || !out_data || (in_size % 160)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ ex_size = (frame_count * 10);
+
+ // allocate new buffer if needed
+ if(*out_max_size <ex_size){
+ if(!(*out_data = tsk_realloc(*out_data, ex_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = ex_size;
+ }
+
+ for(i=0; i<frame_count; i++){
+ extern Word16 *new_speech;
+
+ if(g729a->encoder.frame == 32767){
+ g729a->encoder.frame = 256;
+ }
+ else{
+ g729a->encoder.frame++;
+ }
+
+ memcpy(new_speech, &((uint8_t*)in_data)[i*L_FRAME*sizeof(Word16)], sizeof(Word16)*L_FRAME);
+
+ Pre_Process(new_speech, L_FRAME);
+ Coder_ld8a(g729a->encoder.prm, g729a->encoder.frame, g729a->encoder.vad_enable);
+ prm2bits_ld8k(g729a->encoder.prm, g729a->encoder.serial);
+
+ if(g729a->encoder.serial[1] == RATE_8000){
+ pack_G729(&g729a->encoder.serial[2], &((uint8_t*)(*out_data))[out_size]);
+ out_size += 10;
+ }
+ else if(g729a->encoder.serial[1] == RATE_SID_OCTET){
+ pack_SID(&g729a->encoder.serial[2], &((uint8_t*)(*out_data))[out_size]);
+ out_size += 2;
+ }
+ else{ // RATE_0
+ //TSK_DEBUG_INFO("G729_RATE_0 - Not transmitted");
+ return 0;
+ }
+ }
+
+ return out_size;
+}
+
+tsk_size_t tdav_codec_g729ab_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size = 0;
+ int i, frame_count;
+ const uint8_t* data_start = (const uint8_t*)in_data;
+ const uint8_t* data_end;
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data || ((in_size % 10) && (in_size % 10 != 2))){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ data_end = (data_start + in_size);
+
+ frame_count = (in_size/10) + ((in_size % 10) ? 1 : 0);
+
+ out_size = 160*frame_count;
+
+ /* allocate new buffer if needed */
+ if(*out_max_size <out_size){
+ if(!(*out_data = tsk_realloc(*out_data, out_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ for(i=0; i<frame_count; i++){
+ memset(g729a->decoder.synth_buf, 0, M);
+ g729a->decoder.synth = g729a->decoder.synth_buf + M;
+
+ if((data_end - data_start) == 2){
+ unpack_SID(data_start, g729a->decoder.serial);
+ data_start += 2;
+ }
+ else{
+ unpack_G729(data_start, g729a->decoder.serial, 10);
+ data_start += 10;
+ }
+
+ bits2prm_ld8k(&g729a->decoder.serial[1], g729a->decoder.parm);
+
+ /* This part was modified for version V1.3 */
+ /* for speech and SID frames, the hardware detects frame erasures
+ by checking if all bits are set to zero */
+ /* for untransmitted frames, the hardware detects frame erasures
+ by testing serial[0] */
+
+ g729a->decoder.parm[0] = 0; /* No frame erasure */
+ if(g729a->decoder.serial[1] != 0) {
+ int j;
+ for (j=0; j < g729a->decoder.serial[1]; j++){
+ if (g729a->decoder.serial[j+2] == 0){
+ g729a->decoder.parm[0] = 1; /* frame erased */
+ break;
+ }
+ }
+ }
+ else if(g729a->decoder.serial[0] != SYNC_WORD){
+ g729a->decoder.parm[0] = 1;
+ }
+ if(g729a->decoder.parm[1] == 1) {
+ /* check parity and put 1 in parm[5] if parity error */
+ g729a->decoder.parm[5] = Check_Parity_Pitch(g729a->decoder.parm[4], g729a->decoder.parm[5]);
+ }
+
+ Decod_ld8a(g729a->decoder.parm, g729a->decoder.synth, g729a->decoder.Az_dec, g729a->decoder.T2, &g729a->decoder.Vad);
+ Post_Filter(g729a->decoder.synth, g729a->decoder.Az_dec, g729a->decoder.T2, g729a->decoder.Vad); /* Post-filter */
+ Post_Process(g729a->decoder.synth, L_FRAME);
+
+ memcpy(&((uint8_t*)*out_data)[160*i], g729a->decoder.synth, 160);
+ }
+
+
+ return out_size;
+}
+
+tsk_bool_t tdav_codec_g729ab_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tsk_params_L_t* params = tsk_null;
+ const char* val_str;
+
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)codec;
+
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ if((val_str = tsk_params_get_param_value(params, "annexb"))){
+ g729a->encoder.vad_enable = tsk_strequals(val_str, "yes");
+ }
+
+ TSK_OBJECT_SAFE_FREE(params);
+ }
+
+ return tsk_true;
+}
+
+char* tdav_codec_g729ab_fmtp_get(const tmedia_codec_t* codec)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)codec;
+
+ if(g729a->encoder.vad_enable){
+ return tsk_strdup("annexb=yes");
+ }
+ else{
+ return tsk_strdup("annexb=no");
+ }
+}
+
+
+
+
+
+
+/* ============ Internal functions ================= */
+
+
+/**
+* Converts from bitstream (ITU bits) to word16 value
+* @param no_of_bits number of bits to read
+* @param bitstream array containing bits
+* @retval decimal value of bit pattern
+*/
+static Word16 bin2int(Word16 no_of_bits, const Word16 *bitstream)
+{
+ Word16 value, i;
+ Word16 bit;
+
+ value = 0;
+ for(i = 0; i < no_of_bits; i++){
+ value <<= 1;
+ bit = *bitstream++;
+ if (bit == BIT_1){
+ value += 1;
+ }
+ }
+ return(value);
+}
+
+/*----------------------------------------------------------------------------
+ * int2bin convert integer to binary and write the bits bitstream array
+ *----------------------------------------------------------------------------
+ */
+
+/**
+* Writes Word16 value to bitstream
+* @param value decimal value to write
+* @param no_of_bits number of bits from value to write
+* @param bitstream pointer to the destination stream (ITU bits)
+*/
+static void int2bin(Word16 value, Word16 no_of_bits, Word16 *bitstream)
+{
+ Word16 *pt_bitstream;
+ Word16 i, bit;
+
+ pt_bitstream = bitstream + no_of_bits;
+
+ for (i = 0; i < no_of_bits; i++){
+ bit = value & (Word16)0x0001; /* get lsb */
+ if (bit == 0){
+ *--pt_bitstream = BIT_0;
+ }
+ else{
+ *--pt_bitstream = BIT_1;
+ }
+ value >>= 1;
+ }
+}
+
+/**
+* UnPack RTP bitstream as unpacked ITU stream
+* @param bitstream RTP bitstream to unpack
+* @param bits ITU bitstream used as destination (0 - BIT_0, 1 - BIT_1)
+* @param len length of the RTP bitstream
+*/
+static void unpack_G729(const uint8_t bitstream[], Word16 bits[], int len)
+{
+ Word16 i;
+ *bits++ = SYNC_WORD; /* bit[0], at receiver this bits indicates BFI */
+ switch(len){
+ case 10:
+ *bits++ = SIZE_WORD;
+ break;
+ case 8: // RATE_6400
+ case 15: //RATE_11800
+ default:
+ TSK_DEBUG_ERROR("%d is an invalid lenght value", len);
+ return;
+ }
+
+ for(i=0; i<len; i++){
+ int2bin(bitstream[i], 8, &bits[i*8]);
+ }
+}
+
+/**
+* UnPack RTP bitstream containing SID frame as unpacked ITU stream
+* @param bitstream RTP bitstream to unpack
+* @param bits ITU bitstream used as destination (0 - BIT_0, 1 - BIT_1)
+*/
+static void unpack_SID(const uint8_t bitstream[], Word16 bits[])
+{
+ *bits++ = SYNC_WORD;
+ *bits++ = RATE_SID_OCTET;
+ int2bin((Word16)bitstream[0], 8, &bits[0]);
+ int2bin((Word16)bitstream[1], 8, &bits[8]);
+}
+
+/**
+* Pack ITU bits into RTP stream
+* @param ituBits ITU stream to pack (80 shorts)
+* @param bitstream RTP bitstream (80 bits, 5 shorts, 10 bytes)
+*/
+static void pack_G729(const Word16 ituBits[], uint8_t bitstream[])
+{
+ Word16 word16, i;
+ for(i=0; i<5; i++){
+ word16 = bin2int(16, (Word16*)&ituBits[i*16]);
+ bitstream[i*2] = word16>>8, bitstream[(i*2)+1] = (word16 & 0xFF);
+ }
+}
+
+/**
+* Pack ITU bits containing SID frame as RTP stream
+* @param ituBits ITU stream to pack
+* @param bitstream RTP bitstream (15 bits, 1 short, 2 bytes)
+*/
+static void pack_SID(const Word16 ituBits[], uint8_t bitstream[])
+{
+ Word16 word16 = bin2int(16, ituBits);
+ bitstream[0] = word16>>8, bitstream[1] = (word16 & 0xFF);
+}
+
+
+//
+// g729ab Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g729ab_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g729ab_t *g729a = self;
+ if(g729a){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ g729a->encoder.vad_enable = G729_ENABLE_VAD; // AnnexB
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g729ab_dtor(tsk_object_t * self)
+{
+ tdav_codec_g729ab_t *g729a = self;
+ if(g729a){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g729a);
+ /* deinit self */
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g729ab_def_s =
+{
+ sizeof(tdav_codec_g729ab_t),
+ tdav_codec_g729ab_ctor,
+ tdav_codec_g729ab_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g729ab_plugin_def_s =
+{
+ &tdav_codec_g729ab_def_s,
+
+ tmedia_audio,
+ "g729",
+ "g729ab Codec",
+ TMEDIA_CODEC_FORMAT_G729,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_g729ab_open,
+ tdav_codec_g729ab_close,
+ tdav_codec_g729ab_encode,
+ tdav_codec_g729ab_decode,
+ tdav_codec_g729ab_fmtp_match,
+ tdav_codec_g729ab_fmtp_get,
+ tdav_codec_g729ab_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g729ab_plugin_def_t = &tdav_codec_g729ab_plugin_def_s;
+
+#endif /* HAVE_G729 */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/gsm/tdav_codec_gsm.c b/tinyDAV/src/codecs/gsm/tdav_codec_gsm.c
new file mode 100644
index 0000000..56748da
--- /dev/null
+++ b/tinyDAV/src/codecs/gsm/tdav_codec_gsm.c
@@ -0,0 +1,208 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_gsm.c
+ * @brief GSM Full Rate Codec (Based on libgsm)
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/gsm/tdav_codec_gsm.h"
+
+#if HAVE_LIBGSM
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_GSM_FRAME_SIZE 33
+
+/* ============ GSM Plugin interface ================= */
+
+#define tdav_codec_gsm_fmtp_get tsk_null
+#define tdav_codec_gsm_fmtp_set tsk_null
+
+int tdav_codec_gsm_open(tmedia_codec_t* self)
+{
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(!gsm->encoder && !(gsm->encoder = gsm_create())){
+ TSK_DEBUG_ERROR("Failed to create GSM encoder");
+ return -2;
+ }
+ if(!gsm->decoder && !(gsm->decoder = gsm_create())){
+ TSK_DEBUG_ERROR("Failed to create GSM decoder");
+ return -3;
+ }
+
+ return 0;
+}
+
+int tdav_codec_gsm_close(tmedia_codec_t* self)
+{
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(gsm->encoder){
+ gsm_destroy(gsm->encoder);
+ gsm->encoder = tsk_null;
+ }
+ if(gsm->decoder){
+ gsm_destroy(gsm->decoder);
+ gsm->decoder = tsk_null;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_gsm_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size;
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = ((in_size / (TMEDIA_CODEC_PCM_FRAME_SIZE(self) * sizeof(short))) * TDAV_GSM_FRAME_SIZE);
+
+ /* allocate new buffer if needed */
+ if(*out_max_size <out_size){
+ if(!(*out_data = tsk_realloc(*out_data, out_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ gsm_encode(gsm->encoder, (gsm_signal*)in_data, (gsm_byte*)*out_data);
+
+ return out_size;
+}
+
+tsk_size_t tdav_codec_gsm_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size;
+ int ret;
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data || (in_size % TDAV_GSM_FRAME_SIZE)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = (in_size / TDAV_GSM_FRAME_SIZE) * (TMEDIA_CODEC_PCM_FRAME_SIZE(self) * sizeof(short));
+
+ /* allocate new buffer if needed */
+ if(*out_max_size <out_size){
+ if(!(*out_data = tsk_realloc(*out_data, out_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ ret = gsm_decode(gsm->decoder, (gsm_byte*)in_data, (gsm_signal*)*out_data);
+
+ return out_size;
+}
+
+tsk_bool_t tdav_codec_gsm_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// GSM Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_gsm_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_gsm_t *gsm = self;
+ if(gsm){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_gsm_dtor(tsk_object_t * self)
+{
+ tdav_codec_gsm_t *gsm = self;
+ if(gsm){
+ /* deinit base */
+ tmedia_codec_audio_deinit(gsm);
+ /* deinit self */
+ if(gsm->encoder){
+ gsm_destroy(gsm->encoder);
+ }
+ if(gsm->decoder){
+ gsm_destroy(gsm->decoder);
+ }
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_gsm_def_s =
+{
+ sizeof(tdav_codec_gsm_t),
+ tdav_codec_gsm_ctor,
+ tdav_codec_gsm_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_gsm_plugin_def_s =
+{
+ &tdav_codec_gsm_def_s,
+
+ tmedia_audio,
+ "GSM",
+ "GSM Full Rate",
+ TMEDIA_CODEC_FORMAT_GSM,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_gsm_open,
+ tdav_codec_gsm_close,
+ tdav_codec_gsm_encode,
+ tdav_codec_gsm_decode,
+ tdav_codec_gsm_fmtp_match,
+ tdav_codec_gsm_fmtp_get,
+ tdav_codec_gsm_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_gsm_plugin_def_t = &tdav_codec_gsm_plugin_def_s;
+
+
+#endif /* HAVE_LIBGSM */
diff --git a/tinyDAV/src/codecs/h261/tdav_codec_h261.c b/tinyDAV/src/codecs/h261/tdav_codec_h261.c
new file mode 100644
index 0000000..780f2be
--- /dev/null
+++ b/tinyDAV/src/codecs/h261/tdav_codec_h261.c
@@ -0,0 +1,539 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h261.c
+ * @brief H.261 codec plugin.
+ * RTP payloader follows RFC 4587
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/h261/tdav_codec_h261.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tnet_endianness.h"
+
+#include "tsk_time.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define RTP_PAYLOAD_SIZE 700
+#define H261_HEADER_SIZE 4
+
+static void *run(void* self);
+static void tdav_codec_h261_rtp_callback(tdav_codec_h261_t *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+static void tdav_codec_h261_encap(const tdav_codec_h261_t* h261, const uint8_t* pdata, tsk_size_t size);
+
+/* ============ H.261 Plugin interface ================= */
+
+//
+// H.261 object definition
+//
+int tdav_codec_h261_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+
+ if(!h261){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ //
+ // Encoder
+ //
+ if(!(h261->encoder.codec = avcodec_find_encoder(CODEC_ID_H261))){
+ TSK_DEBUG_ERROR("Failed to find H.261 encoder");
+ return -2;
+ }
+ h261->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h261->encoder.context);
+
+ h261->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h261->encoder.context->time_base.num = 1;
+ h261->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(h261)->fps;
+ h261->encoder.context->width = TMEDIA_CODEC_VIDEO(h261)->width;
+ h261->encoder.context->height = TMEDIA_CODEC_VIDEO(h261)->height;
+
+ h261->encoder.context->mb_qmin = h261->encoder.context->qmin = 4;
+ h261->encoder.context->mb_qmax = h261->encoder.context->qmax = 31;
+ h261->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+
+ h261->encoder.context->thread_count = 1;
+ h261->encoder.context->rtp_payload_size = RTP_PAYLOAD_SIZE;
+ h261->encoder.context->opaque = tsk_null;
+ h261->encoder.context->bit_rate = (float) (500000) * 0.80f;
+ h261->encoder.context->bit_rate_tolerance = (int) (500000 * 0.20f);
+ h261->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(h261)->fps*4; /* each 4 seconds */
+
+ // Picture (YUV 420)
+ if(!(h261->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h261->encoder.picture);
+ //if((ret = avpicture_alloc((AVPicture*)h261->encoder.picture, PIX_FMT_YUV420P, h261->encoder.context->width, h261->encoder.context->height))){
+ // TSK_DEBUG_ERROR("Failed to allocate encoder picture");
+ // return ret;
+ //}
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h261->encoder.context->width, h261->encoder.context->height);
+ if(!(h261->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(h261->encoder.context, h261->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open H.261 encoder");
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ if(!(h261->decoder.codec = avcodec_find_decoder(CODEC_ID_H261))){
+ TSK_DEBUG_ERROR("Failed to find H.261 decoder");
+ }
+ h261->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h261->decoder.context);
+
+ h261->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h261->decoder.context->width = TMEDIA_CODEC_VIDEO(h261)->width;
+ h261->decoder.context->height = TMEDIA_CODEC_VIDEO(h261)->height;
+
+ // Picture (YUV 420)
+ if(!(h261->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h261->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h261->decoder.context->width, h261->decoder.context->height);
+ if(!(h261->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(h261->decoder.context, h261->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open H.261 decoder");
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_h261_close(tmedia_codec_t* self)
+{
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+
+ if(!h261){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ //
+ // Encoder
+ //
+ if(h261->encoder.context){
+ avcodec_close(h261->encoder.context);
+ av_free(h261->encoder.context);
+ h261->encoder.context = tsk_null;
+ }
+ if(h261->encoder.picture){
+ av_free(h261->encoder.picture);
+ }
+ if(h261->encoder.buffer){
+ TSK_FREE(h261->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(h261->decoder.context){
+ avcodec_close(h261->decoder.context);
+ av_free(h261->decoder.context);
+ h261->decoder.context = tsk_null;
+ }
+ if(h261->decoder.picture){
+ av_free(h261->decoder.picture);
+ h261->decoder.picture = tsk_null;
+ }
+ if(h261->decoder.accumulator){
+ TSK_FREE(h261->decoder.accumulator);
+ h261->decoder.accumulator_pos = 0;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h261_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // delete old buffer
+ if(*out_data){
+ TSK_FREE(*out_data);
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)h261->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, h261->encoder.context->width, h261->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+ /* Flip */
+#if FLIP_ENCODED_PICT
+ tdav_converter_video_flip(h261->encoder.picture, h261->encoder.context->height);
+#endif
+
+ // Encode data
+ h261->encoder.picture->pts = AV_NOPTS_VALUE;
+ //h261->encoder.picture->pict_type = FF_I_TYPE;
+ ret = avcodec_encode_video(h261->encoder.context, h261->encoder.buffer, size, h261->encoder.picture);
+ if(ret > 0){
+ tdav_codec_h261_encap(h261, h261->encoder.buffer, (tsk_size_t)ret);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h261_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ uint8_t sbit, ebit;
+ const uint8_t* pdata = in_data;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || !in_size || !out_data || !h261->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* RFC 4587
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ sbit = *pdata >> 5;
+ ebit = (*pdata >> 2) & 0x07;
+
+ /* Check size */
+ if(in_size < H261_HEADER_SIZE){
+ TSK_DEBUG_ERROR("Too short");
+ return 0;
+ }
+
+ pay_ptr = (pdata + H261_HEADER_SIZE);
+ pay_size = (in_size - H261_HEADER_SIZE);
+ xsize = avpicture_get_size(h261->decoder.context->pix_fmt, h261->decoder.context->width, h261->decoder.context->height);
+
+ /* Packet lost? */
+ if(h261->decoder.last_seq != (rtp_hdr->seq_num - 1) && h261->decoder.last_seq){
+ TSK_DEBUG_INFO("Packet lost");
+ }
+ h261->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((int)(h261->decoder.accumulator_pos + pay_size) <= xsize){
+
+ if((h261->decoder.ebit + sbit) == 8){ /* Perfect one Byte to clean up */
+ if(h261->decoder.accumulator_pos){
+ ((uint8_t*)h261->decoder.accumulator)[h261->decoder.accumulator_pos-1] =
+ (((uint8_t*)h261->decoder.accumulator)[h261->decoder.accumulator_pos-1] & (0xFF << h261->decoder.ebit)) |
+ (*pay_ptr << sbit);
+ }
+ pay_ptr++, pay_size--;
+ }
+ h261->decoder.ebit = ebit;
+
+ memcpy(&((uint8_t*)h261->decoder.accumulator)[h261->decoder.accumulator_pos], pay_ptr, pay_size);
+ h261->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h261->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size <xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ h261->decoder.accumulator_pos = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ /* decode the picture */
+ av_init_packet(&packet);
+ packet.size = h261->decoder.accumulator_pos;
+ packet.data = h261->decoder.accumulator;
+ ret = avcodec_decode_video2(h261->decoder.context, h261->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(h261->decoder.picture, h261->decoder.context->height);
+#endif
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)h261->decoder.picture, h261->decoder.context->pix_fmt, h261->decoder.context->width, h261->decoder.context->height,
+ *out_data, retsize);
+ }
+ /* in all cases: reset accumulator */
+ h261->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_h261_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ int ret;
+ unsigned maxbr, fps, width, height;
+ tmedia_codec_video_t* h261 = (tmedia_codec_video_t*)codec;
+
+ if(!(ret = tmedia_codec_parse_fmtp(fmtp, &maxbr, &fps, &width, &height))){
+ h261->max_br = maxbr * 1000;
+ h261->fps = fps;
+ h261->width = width;
+ h261->height = height;
+ return tsk_true;
+ }
+ else{
+ TSK_DEBUG_WARN("Failed to match fmtp [%s]", fmtp);
+ return tsk_false;
+ }
+}
+
+char* tdav_codec_h261_fmtp_get(const tmedia_codec_t* self)
+{
+#if 0
+ return tsk_strdup("CIF=2/MaxBR=3840;QCIF=2/MaxBR=1920");
+#else
+ return tsk_strdup("QCIF=2");
+#endif
+}
+
+int tdav_codec_h261_fmtp_set(tmedia_codec_t* self, const char* fmtp)
+{
+ TSK_DEBUG_INFO("remote fmtp=%s", fmtp);
+ return 0;
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h261_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h261_t *h261 = self;
+ if(h261){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h261_dtor(tsk_object_t * self)
+{
+ tdav_codec_h261_t *h261 = self;
+ if(h261){
+ /* deinit base */
+ tmedia_codec_video_deinit(h261); // will call close()
+ /* deinit self */
+ TSK_FREE(h261->rtp.ptr);
+ h261->rtp.size = 0;
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h261_def_s =
+{
+ sizeof(tdav_codec_h261_t),
+ tdav_codec_h261_ctor,
+ tdav_codec_h261_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h261_plugin_def_s =
+{
+ &tdav_codec_h261_def_s,
+
+ tmedia_video,
+ "H261",
+ "H261 codec",
+ TMEDIA_CODEC_FORMAT_H261,
+ tsk_false,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h261_open,
+ tdav_codec_h261_close,
+ tdav_codec_h261_encode,
+ tdav_codec_h261_decode,
+ tdav_codec_h261_fmtp_match,
+ tdav_codec_h261_fmtp_get,
+ tdav_codec_h261_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h261_plugin_def_t = &tdav_codec_h261_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ Callbacks ================= */
+
+static void tdav_codec_h261_encap(const tdav_codec_h261_t* h261, const uint8_t* pdata, tsk_size_t size)
+{
+ uint32_t i, last_index = 0;
+
+ if(size < RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+
+ for(i = 4; i<(size - 4); i++){
+ if(pdata[i] == 0x00 && pdata[i+1] == 0x00 && pdata[i+2]>=0x80){ /* PSC or (GBSC) found */
+ if((i - last_index) >= RTP_PAYLOAD_SIZE){
+ tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata+last_index,
+ (i - last_index), (last_index == size));
+ }
+ last_index = i;
+ }
+ }
+last:
+ if(last_index < size - 3/*PSC/GBSC size*/){
+ tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata + last_index,
+ (size - last_index), tsk_true);
+ }
+}
+
+//static void *run(void* self)
+//{
+// uint32_t i, last_index;
+// tsk_list_item_t *curr;
+//
+// const uint8_t* pdata;
+// tsk_size_t size;
+//
+// const tdav_codec_h261_t* h261 = ((tdav_runnable_video_t*)self)->userdata;
+//
+// TSK_DEBUG_INFO("H261 thread === START");
+//
+// TSK_RUNNABLE_RUN_BEGIN(self);
+//
+// if((curr = TSK_RUNNABLE_POP_FIRST(self))){
+// /* 4 is sizeof(uint32_t) */
+// pdata = ((const tsk_buffer_t*)curr->data)->data;
+// size = ((const tsk_buffer_t*)curr->data)->size;
+// last_index = 0;
+//
+// if(size < RTP_PAYLOAD_SIZE){
+// goto last;
+// }
+//
+// for(i = 4; i<(size - 4); i++){
+// if(pdata[i] == 0x00 && pdata[i+1] == 0x00 && pdata[i+2]>=0x80){ /* PSC or (GBSC) found */
+// if((i - last_index) >= RTP_PAYLOAD_SIZE){
+// tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata+last_index,
+// (i - last_index), (last_index == size));
+// }
+// last_index = i;
+// }
+// }
+//last:
+// if(last_index < size - 3/*PSC/GBSC size*/){
+// tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata + last_index,
+// (size - last_index), tsk_true);
+// }
+//
+// tsk_object_unref(curr);
+// }
+//
+// TSK_RUNNABLE_RUN_END(self);
+//
+// TSK_DEBUG_INFO("H261 thread === STOP");
+//
+// return tsk_null;
+//}
+
+static void tdav_codec_h261_rtp_callback(tdav_codec_h261_t *self, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+
+}
+
+
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/h263/tdav_codec_h263.c b/tinyDAV/src/codecs/h263/tdav_codec_h263.c
new file mode 100644
index 0000000..9b247d1
--- /dev/null
+++ b/tinyDAV/src/codecs/h263/tdav_codec_h263.c
@@ -0,0 +1,1230 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h263.c
+ * @brief H.263-1996 and H.263-1998 codec plugins.
+ * RTP payloader follows RFC 4629 for H263+ and RFC 2190 for H263.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/h263/tdav_codec_h263.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tnet_endianness.h"
+
+#include "tsk_time.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define RTP_PAYLOAD_SIZE 750
+
+#define H263P_HEADER_SIZE 2
+#define H263_HEADER_MODE_A_SIZE 4
+#define H263_HEADER_MODE_B_SIZE 8
+#define H263_HEADER_MODE_C_SIZE 12
+
+static void tdav_codec_h263_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+static void tdav_codec_h263p_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t frag, tsk_bool_t marker);
+
+static void tdav_codec_h263_encap(const tdav_codec_h263_t* h263, const uint8_t* pdata, tsk_size_t size);
+
+/* ============ Common To all H263 codecs ================= */
+
+int tdav_codec_h263_init(tdav_codec_h263_t* self, tdav_codec_h263_type_t type, enum CodecID encoder, enum CodecID decoder)
+{
+ int ret = 0;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->type = type;
+
+ if(!(self->encoder.codec = avcodec_find_encoder(encoder))){
+ TSK_DEBUG_ERROR("Failed to find [%d]encoder", encoder);
+ ret = -2;
+ }
+
+ if(!(self->decoder.codec = avcodec_find_decoder(decoder))){
+ TSK_DEBUG_ERROR("Failed to find [%d] decoder", decoder);
+ ret = -3;
+ }
+
+ /* allocations MUST be done by open() */
+ return ret;
+}
+
+int tdav_codec_h263_deinit(tdav_codec_h263_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->encoder.codec = tsk_null;
+ self->decoder.codec = tsk_null;
+
+ // FFMpeg resources are destroyed by close()
+
+
+
+ TSK_FREE(self->rtp.ptr);
+ self->rtp.size = 0;
+
+ return 0;
+}
+
+
+
+/* ============ H.263-1996 Plugin interface ================= */
+
+//
+// H.263-1996 object definition
+//
+int tdav_codec_h263_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+ float bitRate = 64000.f;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+
+ if(!h263){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ //
+ // Encoder
+ //
+ h263->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h263->encoder.context);
+
+ h263->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h263->encoder.context->time_base.num = 1;
+ h263->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(h263)->fps;
+ h263->encoder.context->width = TMEDIA_CODEC_VIDEO(h263)->width;
+ h263->encoder.context->height = TMEDIA_CODEC_VIDEO(h263)->height;
+
+ /*h263->encoder.context->mb_qmin =*/ h263->encoder.context->qmin = 4;
+ /*h263->encoder.context->mb_qmax =*/ h263->encoder.context->qmax = 31;
+ h263->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+ //h263->encoder.context->me_method = ME_EPZS;
+ //h263->encoder.context->flags |= CODEC_FLAG_INPUT_PRESERVED | CODEC_FLAG_PASS1;
+
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ bitRate = 64000.f;
+ break;
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ bitRate = 128000.f;
+ break;
+ }
+
+ h263->encoder.context->thread_count = 1;
+ h263->encoder.context->rtp_payload_size = RTP_PAYLOAD_SIZE;
+ h263->encoder.context->opaque = tsk_null;
+ h263->encoder.context->bit_rate = (int)(bitRate * 0.80f);
+ h263->encoder.context->bit_rate_tolerance = (int) (bitRate * 0.20f);
+ h263->encoder.context->rc_min_rate = 0;
+ h263->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(h263)->fps*3; /* each 3 seconds */
+
+
+ // Picture (YUV 420)
+ if(!(h263->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h263->encoder.picture);
+ //if((ret = avpicture_alloc((AVPicture*)h263->encoder.picture, PIX_FMT_YUV420P, h263->encoder.context->width, h263->encoder.context->height))){
+ // TSK_DEBUG_ERROR("Failed to allocate encoder picture");
+ // return ret;
+ //}
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h263->encoder.context->width, h263->encoder.context->height);
+ if(!(h263->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+
+ // RTP Callback
+ switch(h263->type){
+ case tdav_codec_h263_1996:
+ { /* H263 - 1996 */
+ break;
+ }
+ case tdav_codec_h263_1998:
+ { /* H263 - 1998 */
+ h263->encoder.context->flags |= CODEC_FLAG_H263P_UMV; /* Annex D+ */
+ h263->encoder.context->flags |= CODEC_FLAG_AC_PRED; /* Annex I and T */
+ h263->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER; /* Annex J */
+ h263->encoder.context->flags |= CODEC_FLAG_H263P_SLICE_STRUCT; /* Annex K */
+ h263->encoder.context->flags |= CODEC_FLAG_H263P_AIV; /* Annex S */
+ break;
+ }
+ case tdav_codec_h263_2000:
+ { /* H263 - 2000 */
+ h263->encoder.context->flags |= CODEC_FLAG_H263P_UMV; /* Annex D+ */
+ h263->encoder.context->flags |= CODEC_FLAG_AC_PRED; /* Annex I and T */
+ h263->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER; /* Annex J */
+ h263->encoder.context->flags |= CODEC_FLAG_H263P_SLICE_STRUCT; /* Annex K */
+ h263->encoder.context->flags |= CODEC_FLAG_H263P_AIV; /* Annex S */
+ break;
+ }
+ }
+ // Open encoder
+ if((ret = avcodec_open(h263->encoder.context, h263->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h263)->plugin->desc);
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ h263->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h263->decoder.context);
+
+ h263->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h263->decoder.context->width = TMEDIA_CODEC_VIDEO(h263)->width;
+ h263->decoder.context->height = TMEDIA_CODEC_VIDEO(h263)->height;
+
+ // Picture (YUV 420)
+ if(!(h263->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h263->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h263->decoder.context->width, h263->decoder.context->height);
+ if(!(h263->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(h263->decoder.context, h263->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h263)->plugin->desc);
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_h263_close(tmedia_codec_t* self)
+{
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+
+ if(!h263){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ //
+ // Encoder
+ //
+ if(h263->encoder.context){
+ avcodec_close(h263->encoder.context);
+ av_free(h263->encoder.context);
+ }
+ if(h263->encoder.picture){
+ av_free(h263->encoder.picture);
+ }
+ if(h263->encoder.buffer){
+ TSK_FREE(h263->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(h263->decoder.context){
+ avcodec_close(h263->decoder.context);
+ av_free(h263->decoder.context);
+ }
+ if(h263->decoder.picture){
+ av_free(h263->decoder.picture);
+ }
+ if(h263->decoder.accumulator){
+ TSK_FREE(h263->decoder.accumulator);
+ h263->decoder.accumulator_pos = 0;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h263_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)h263->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, h263->encoder.context->width, h263->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+ /* Flip */
+#if FLIP_ENCODED_PICT
+ tdav_converter_video_flip(h263->encoder.picture, h263->encoder.context->height);
+#endif
+
+ h263->encoder.picture->pts = AV_NOPTS_VALUE;
+ ret = avcodec_encode_video(h263->encoder.context, h263->encoder.buffer, size, h263->encoder.picture);
+ if(ret > 0){
+ tdav_codec_h263_encap(h263, h263->encoder.buffer, (tsk_size_t)ret);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h263_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ uint8_t F, P, sbit, ebit;
+ const uint8_t* pdata = in_data;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ tsk_size_t hdr_size;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || !in_size || !out_data || !h263->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* RFC 2190
+ get F and P bits, used to determine the header Mode (A, B or C)
+ F: 1 bit
+ The flag bit indicates the mode of the payload header. F=0, mode A;
+ F=1, mode B or mode C depending on P bit defined below.
+ P: 1 bit
+ Optional PB-frames mode as defined by the H.263 [4]. "0" implies
+ normal I or P frame, "1" PB-frames. When F=1, P also indicates modes:
+ mode B if P=0, mode C if P=1.
+ */
+ F = *pdata >> 7;
+ P = (*pdata >> 6) & 0x01;
+
+ /* SBIT and EBIT */
+ sbit = (*pdata >> 3) & 0x0F;
+ ebit = (*pdata & 0x07);
+
+ if(F == 0){
+ /* MODE A
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC |I|U|S|A|R |DBQ| TRB | TR |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ hdr_size = H263_HEADER_MODE_A_SIZE;
+ }
+ else if(P == 0){ // F=1 and P=0
+ /* MODE B
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC | QUANT | GOBN | MBA |R |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |I|U|S|A| HMV1 | VMV1 | HMV2 | VMV2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ hdr_size = H263_HEADER_MODE_B_SIZE;
+ }
+ else{ // F=1 and P=1
+ /* MODE C
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC | QUANT | GOBN | MBA |R |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |I|U|S|A| HMV1 | VMV1 | HMV2 | VMV2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RR |DBQ| TRB | TR |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ hdr_size = H263_HEADER_MODE_C_SIZE;
+ }
+
+ /* Check size */
+ if(in_size < hdr_size){
+ TSK_DEBUG_ERROR("Too short");
+ return 0;
+ }
+
+ pay_ptr = (pdata + hdr_size);
+ pay_size = (in_size - hdr_size);
+ xsize = avpicture_get_size(h263->decoder.context->pix_fmt, h263->decoder.context->width, h263->decoder.context->height);
+
+ /* Packet lost? */
+ if(h263->decoder.last_seq != (rtp_hdr->seq_num - 1) && h263->decoder.last_seq){
+ if(h263->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ h263->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((int)(h263->decoder.accumulator_pos + pay_size) <= xsize){
+ if((h263->decoder.ebit + sbit) == 8){ /* Perfect one Byte to clean up */
+ if(h263->decoder.accumulator_pos){
+ ((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos-1] = (((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos-1] & (0xFF << h263->decoder.ebit)) |
+ (*pay_ptr & (0xFF >> sbit));
+ }
+ pay_ptr++, pay_size--;
+ }
+ h263->decoder.ebit = ebit;
+
+ memcpy(&((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos], pay_ptr, pay_size);
+ h263->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h263->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size <xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ h263->decoder.accumulator_pos = 0;
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ av_init_packet(&packet);
+ packet.size = h263->decoder.accumulator_pos;
+ packet.data = h263->decoder.accumulator;
+ ret = avcodec_decode_video2(h263->decoder.context, h263->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+ /* flip */
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(h263->decoder.picture, h263->decoder.context->height);
+#endif
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)h263->decoder.picture, h263->decoder.context->pix_fmt, h263->decoder.context->width, h263->decoder.context->height,
+ *out_data, retsize);
+ }
+ /* in all cases: reset accumulator */
+ h263->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_h263_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tsk_bool_t ret = tsk_false;
+ tmedia_codec_video_t* h263 = (tmedia_codec_video_t*)codec;
+ tsk_params_L_t* params = tsk_null;
+
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ switch(codec->bl){
+ case tmedia_bl_low:
+ default:
+ if(tsk_params_have_param(params, "QCIF")){
+ h263->width = 176, h263->height = 144;
+ ret = tsk_true;
+ }
+ else if(tsk_params_have_param(params, "SQCIF")){
+ h263->width = 128, h263->height = 96;
+ ret = tsk_true;
+ }
+ break;
+
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ if(tsk_params_have_param(params, "CIF")){
+ h263->width = 352, h263->height = 288;
+ ret = tsk_true;
+ }
+ else if(tsk_params_have_param(params, "QCIF")){
+ h263->width = 176, h263->height = 144;
+ ret = tsk_true;
+ }
+ else if(tsk_params_have_param(params, "SQCIF")){
+ h263->width = 128, h263->height = 96;
+ ret = tsk_true;
+ }
+ break;
+ }
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+
+ return ret;
+}
+
+char* tdav_codec_h263_fmtp_get(const tmedia_codec_t* self)
+{
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ return tsk_strdup("QCIF=2;SQCIF=2");
+ break;
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ return tsk_strdup("CIF=2;QCIF=2;SQCIF=2");
+ break;
+ }
+}
+
+int tdav_codec_h263_fmtp_set(tmedia_codec_t* self, const char* fmtp)
+{
+ TSK_DEBUG_INFO("remote fmtp=%s", fmtp);
+ return 0;
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h263_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h263_t *h263 = self;
+ if(h263){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h263_init(TDAV_CODEC_H263(self), tdav_codec_h263_1996, CODEC_ID_H263, CODEC_ID_H263);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h263_dtor(tsk_object_t * self)
+{
+ tdav_codec_h263_t *h263 = self;
+ if(h263){
+ /* deinit base */
+ tmedia_codec_video_deinit(h263);
+ /* deinit self */
+ tdav_codec_h263_deinit(TDAV_CODEC_H263(self));
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h263_def_s =
+{
+ sizeof(tdav_codec_h263_t),
+ tdav_codec_h263_ctor,
+ tdav_codec_h263_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h263_plugin_def_s =
+{
+ &tdav_codec_h263_def_s,
+
+ tmedia_video,
+ "H263",
+ "H263-1996 codec",
+ TMEDIA_CODEC_FORMAT_H263,
+ tsk_false,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h263_open,
+ tdav_codec_h263_close,
+ tdav_codec_h263_encode,
+ tdav_codec_h263_decode,
+ tdav_codec_h263_fmtp_match,
+ tdav_codec_h263_fmtp_get,
+ tdav_codec_h263_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h263_plugin_def_t = &tdav_codec_h263_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ H.263-1998 Plugin interface ================= */
+
+//
+// H.263-1998 object definition
+//
+
+int tdav_codec_h263p_open(tmedia_codec_t* self)
+{
+ return tdav_codec_h263_open(self);
+}
+
+int tdav_codec_h263p_close(tmedia_codec_t* self)
+{
+ return tdav_codec_h263_close(self);
+}
+
+tsk_size_t tdav_codec_h263p_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ return tdav_codec_h263_encode(self, in_data, in_size, out_data, out_max_size);
+}
+
+tsk_size_t tdav_codec_h263p_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ uint8_t P, V, PLEN, PEBIT;
+ uint8_t* pdata = (uint8_t*)in_data;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ int hdr_size = H263P_HEADER_SIZE;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || !in_size || ((int)in_size <= hdr_size) || !out_data || !h263->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+/*
+ 5.1. General H.263+ Payload Header
+
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RR |P|V| PLEN |PEBIT|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+ P = (pdata[0] & 0x04)>>2;
+ V = (pdata[0] & 0x02)>>1;
+ PLEN = (((pdata[0] & 0x01)<<5) | pdata[1]>>3);
+ PEBIT = pdata[1] & 0x07;
+
+ if(V){
+ /*
+ Indicates the presence of an 8-bit field containing information
+ for Video Redundancy Coding (VRC), which follows immediately after
+ the initial 16 bits of the payload header, if present. For syntax
+ and semantics of that 8-bit VRC field, see Section 5.2.
+ */
+ }
+ if(PLEN){
+ /*
+ Length, in bytes, of the extra picture header. If no extra
+ picture header is attached, PLEN is 0. If PLEN>0, the extra
+ picture header is attached immediately following the rest of the
+ payload header. Note that the length reflects the omission of the
+ first two bytes of the picture start code (PSC). See Section 6.1.
+ */
+ hdr_size += PLEN;
+ if(PEBIT){
+ /*
+ Indicates the number of bits that shall be ignored in the last
+ byte of the picture header. If PLEN is not zero, the ignored bits
+ shall be the least significant bits of the byte. If PLEN is zero,
+ then PEBIT shall also be zero.
+ */
+ TSK_DEBUG_WARN("PEBIT ignored");
+ }
+ }
+ if(P){ /* MUST be done after PLEN and PEBIT */
+ /*
+ Indicates the picture start or a picture segment (GOB/Slice) start
+ or a video sequence end (EOS or EOSBS). Two bytes of zero bits
+ then have to be prefixed to the payload of such a packet to
+ compose a complete picture/GOB/slice/EOS/EOSBS start code. This
+ bit allows the omission of the two first bytes of the start codes,
+ thus improving the compression ratio.
+ */
+ hdr_size -= 2;
+ pdata[hdr_size] = 0x00, pdata[hdr_size + 1] = 0x00;
+ }
+
+ pay_ptr = (pdata + hdr_size);
+ pay_size = (in_size - hdr_size);
+ xsize = avpicture_get_size(h263->decoder.context->pix_fmt, h263->decoder.context->width, h263->decoder.context->height);
+
+ /* Packet lost? */
+ if(h263->decoder.last_seq != (rtp_hdr->seq_num - 1) && h263->decoder.last_seq){
+ if(h263->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ h263->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((int)(h263->decoder.accumulator_pos + pay_size) <= xsize){
+ /* PEBIT is ignored */
+ memcpy(&((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos], pay_ptr, pay_size);
+ h263->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h263->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size < xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ h263->decoder.accumulator_pos = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ /* decode the picture */
+ av_init_packet(&packet);
+ packet.size = h263->decoder.accumulator_pos;
+ packet.data = h263->decoder.accumulator;
+ ret = avcodec_decode_video2(h263->decoder.context, h263->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+ /* flip */
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(h263->decoder.picture, h263->decoder.context->height);
+#endif
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)h263->decoder.picture, h263->decoder.context->pix_fmt, h263->decoder.context->width, h263->decoder.context->height,
+ *out_data, retsize);
+ }
+ /* in all cases: reset accumulator */
+ h263->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_h263p_fmtp_match(const tmedia_codec_t* self, const char* fmtp)
+{
+ return tdav_codec_h263_fmtp_match(self, fmtp);
+}
+
+char* tdav_codec_h263p_fmtp_get(const tmedia_codec_t* self)
+{
+ return tdav_codec_h263_fmtp_get(self);
+}
+
+int tdav_codec_h263p_fmtp_set(tmedia_codec_t* self, const char* fmtp)
+{
+ TSK_DEBUG_INFO("remote fmtp=%s", fmtp);
+ return 0;
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h263p_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h263p_t *h263p = self;
+ if(h263p){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h263_init(TDAV_CODEC_H263(self), tdav_codec_h263_1998, CODEC_ID_H263P, CODEC_ID_H263);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h263p_dtor(tsk_object_t * self)
+{
+ tdav_codec_h263p_t *h263p = self;
+ if(h263p){
+ /* deinit base */
+ tmedia_codec_video_deinit(h263p);
+ /* deinit self */
+ tdav_codec_h263_deinit(TDAV_CODEC_H263(self));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h263p_def_s =
+{
+ sizeof(tdav_codec_h263p_t),
+ tdav_codec_h263p_ctor,
+ tdav_codec_h263p_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h263p_plugin_def_s =
+{
+ &tdav_codec_h263p_def_s,
+
+ tmedia_video,
+ "H263-1998",
+ "H263-1998 codec",
+ TMEDIA_CODEC_FORMAT_H263_1998,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h263p_open,
+ tdav_codec_h263p_close,
+ tdav_codec_h263p_encode,
+ tdav_codec_h263p_decode,
+ tdav_codec_h263p_fmtp_match,
+ tdav_codec_h263p_fmtp_get,
+ tdav_codec_h263p_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h263p_plugin_def_t = &tdav_codec_h263p_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ H.263-2000 Plugin interface ================= */
+
+//
+// H.263-2000 object definition
+//
+
+int tdav_codec_h263pp_open(tmedia_codec_t* self)
+{
+ return tdav_codec_h263_open(self);
+}
+
+int tdav_codec_h263pp_close(tmedia_codec_t* self)
+{
+ return tdav_codec_h263_close(self);
+}
+
+tsk_size_t tdav_codec_h263pp_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ return tdav_codec_h263_encode(self, in_data, in_size, out_data, out_max_size);
+}
+
+tsk_size_t tdav_codec_h263pp_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ return tdav_codec_h263p_decode(self, in_data, in_size, out_data, out_max_size, proto_hdr);
+}
+
+tsk_bool_t tdav_codec_h263pp_fmtp_match(const tmedia_codec_t* self, const char* fmtp)
+{
+ return tdav_codec_h263_fmtp_match(self, fmtp);
+}
+
+char* tdav_codec_h263pp_fmtp_get(const tmedia_codec_t* self)
+{
+ return tdav_codec_h263_fmtp_get(self);
+}
+
+int tdav_codec_h263pp_fmtp_set(tmedia_codec_t* self, const char* fmtp)
+{
+ TSK_DEBUG_INFO("remote fmtp=%s", fmtp);
+ return 0;
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h263pp_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h263pp_t *h263pp = self;
+ if(h263pp){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h263_init(TDAV_CODEC_H263(self), tdav_codec_h263_2000, CODEC_ID_H263P, CODEC_ID_H263);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h263pp_dtor(tsk_object_t * self)
+{
+ tdav_codec_h263pp_t *h263pp = self;
+ if(h263pp){
+ /* deinit base */
+ tmedia_codec_video_deinit(h263pp);
+ /* deinit self */
+ tdav_codec_h263_deinit(TDAV_CODEC_H263(self));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h263pp_def_s =
+{
+ sizeof(tdav_codec_h263pp_t),
+ tdav_codec_h263pp_ctor,
+ tdav_codec_h263pp_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h263pp_plugin_def_s =
+{
+ &tdav_codec_h263pp_def_s,
+
+ tmedia_video,
+ "H263-2000",
+ "H263-2000 codec",
+ TMEDIA_CODEC_FORMAT_H263_2000,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h263pp_open,
+ tdav_codec_h263pp_close,
+ tdav_codec_h263pp_encode,
+ tdav_codec_h263pp_decode,
+ tdav_codec_h263pp_fmtp_match,
+ tdav_codec_h263pp_fmtp_get,
+ tdav_codec_h263pp_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h263pp_plugin_def_t = &tdav_codec_h263pp_plugin_def_s;
+
+
+
+/* ============ Callbacks ================= */
+
+static void tdav_codec_h263_encap(const tdav_codec_h263_t* h263, const uint8_t* pdata, tsk_size_t size)
+{
+ tsk_bool_t frag = tsk_false;
+ register uint32_t i, last_index = 0;
+
+ if(size < RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+
+ for(i = 4; i<(size - 4); i++){
+ if(pdata[i] == 0x00 && pdata[i+1] == 0x00 && pdata[i+2]>=0x80){ /* PSC or (GBSC) found */
+ if((i - last_index) >= RTP_PAYLOAD_SIZE || tsk_true/* FIXME */){
+ switch(h263->type){
+ case tdav_codec_h263_1996:
+ tdav_codec_h263_rtp_callback((tdav_codec_h263_t*) h263, pdata+last_index,
+ (i - last_index), (last_index == size));
+ break;
+ default:
+ tdav_codec_h263p_rtp_callback((tdav_codec_h263_t*) h263, pdata + last_index,
+ (i - last_index), frag, (last_index == size));
+ frag = tsk_true;
+ break;
+ }
+ last_index = i;
+ }
+ }
+ }
+last:
+ if(last_index < size){
+ switch(h263->type){
+ case tdav_codec_h263_1996:
+ tdav_codec_h263_rtp_callback((tdav_codec_h263_t*) h263, pdata + last_index,
+ (size - last_index), tsk_true);
+ break;
+ default:
+ tdav_codec_h263p_rtp_callback((tdav_codec_h263_t*) h263, pdata + last_index,
+ (size - last_index), frag, tsk_true);
+ break;
+ }
+ }
+}
+
+
+static void tdav_codec_h263_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+ uint8_t* pdata = (uint8_t*)data;
+
+ if(self->rtp.size < (size + H263_HEADER_MODE_A_SIZE)){
+ if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (size + H263_HEADER_MODE_A_SIZE)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return;
+ }
+ self->rtp.size = (size + H263_HEADER_MODE_A_SIZE);
+ }
+ memcpy((self->rtp.ptr + H263_HEADER_MODE_A_SIZE), data, size);
+
+ /* http://eu.sabotage.org/www/ITU/H/H0263e.pdf section 5.1
+ * 5.1.1 Picture Start Code (PSC) (22 bits) - PSC is a word of 22 bits. Its value is 0000 0000 0000 0000 1 00000.
+
+ *
+ * 5.1.1 Picture Start Code (PSC) (22 bits)
+ * 5.1.2 Temporal Reference (TR) (8 bits)
+ * 5.1.3 Type Information (PTYPE) (Variable Length)
+ * – Bit 1: Always "1", in order to avoid start code emulation.
+ * – Bit 2: Always "0", for distinction with Recommendation H.261.
+
+ * – Bit 3: Split screen indicator, "0" off, "1" on.
+ * – Bit 4: Document camera indicator, "0" off, "1" on.
+ * – Bit 5: Full Picture Freeze Release, "0" off, "1" on.
+ * – Bits 6-8: Source Format, "000" forbidden, "001" sub-QCIF, "010" QCIF, "011" CIF,
+ "100" 4CIF, "101" 16CIF, "110" reserved, "111" extended PTYPE.
+ If bits 6-8 are not equal to "111", which indicates an extended PTYPE (PLUSPTYPE), the following
+ five bits are also present in PTYPE:
+ – Bit 9: Picture Coding Type, "0" INTRA (I-picture), "1" INTER (P-picture).
+ – Bit 10: Optional Unrestricted Motion Vector mode (see Annex D), "0" off, "1" on.
+ – Bit 11: Optional Syntax-based Arithmetic Coding mode (see Annex E), "0" off, "1" on.
+ – Bit 12: Optional Advanced Prediction mode (see Annex F), "0" off, "1" on.
+ – Bit 13: Optional PB-frames mode (see Annex G), "0" normal I- or P-picture, "1" PB-frame.
+ */
+ if(pdata[0] == 0x00 && pdata[1] == 0x00 && (pdata[2] & 0xfc)==0x80){ /* PSC */
+ /* RFC 2190 -5.1 Mode A
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC |I|U|S|A|R |DBQ| TRB | TR |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ SRC : 3 bits
+ Source format, bit 6,7 and 8 in PTYPE defined by H.263 [4], specifies
+ the resolution of the current picture.
+
+ I: 1 bit.
+ Picture coding type, bit 9 in PTYPE defined by H.263[4], "0" is
+ intra-coded, "1" is inter-coded.
+ */
+
+ // PDATA[4] ======> Bits 3-10 of PTYPE
+ uint32_t rtp_hdr = 0;
+ uint8_t format, pict_type;
+
+ // Source Format = 4,5,6
+ format = (pdata[4] & 0x3C)>>2;
+ // Picture Coding Type = 7
+ pict_type = (pdata[4] & 0x02)>>1;
+ // RTP mode A header
+ ((uint8_t*)&rtp_hdr)[1] = (format <<5) | (pict_type << 4);
+ //rtp_hdr = tnet_htonl(rtp_hdr);
+ memcpy(self->rtp.ptr, &rtp_hdr, sizeof(rtp_hdr));
+ }
+
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->callback){
+ TMEDIA_CODEC_VIDEO(self)->callback(TMEDIA_CODEC_VIDEO(self)->callback_data, self->rtp.ptr, (size + H263_HEADER_MODE_A_SIZE), (3003* (30/TMEDIA_CODEC_VIDEO(self)->fps)), marker);
+ }
+}
+
+static void tdav_codec_h263p_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t frag, tsk_bool_t marker)
+{
+ uint8_t* pdata = (uint8_t*)data;
+ //uint8_t rtp_hdr[2] = {0x00, 0x00};
+ //tsk_bool_t eos = tsk_false;
+
+ const void* _ptr = tsk_null;
+ tsk_size_t _size = 0;
+ //static tsk_bool_t frag = tsk_false;
+ //tsk_bool_t found_gob = tsk_false;
+
+ /* RFC 4629 - 5.1. General H.263+ Payload Header
+ The H.263+ payload header is structured as follows:
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RR |P|V| PLEN |PEBIT|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ /* http://eu.sabotage.org/www/ITU/H/H0263e.pdf
+ *
+ * 5.1.1 Picture Start Code (PSC) (22 bits)
+ * ->PSC is a word of 22 bits. Its value is 0000 0000 0000 0000 1 00000.
+ * 5.1.27 End Of Sequence (EOS) (22 bits)
+ * ->A codeword of 22 bits. Its value is 0000 0000 0000 0000 1 11111
+ * 5.2.2 Group of Block Start Code (GBSC) (17 bits)
+ * ->A word of 17 bits. Its value is 0000 0000 0000 0000 1
+ * C.4.1 End Of Sub-Bitstream code (EOSBS) (23 bits)
+ * ->The EOSBS code is a codeword of 23 bits. Its value is 0000 0000 0000 0000 1 11110 0
+ *
+ *
+ * 5.2.3 Group Number (GN) (5 bits)
+ * -> last 5 bits
+ */
+ //if(pdata[0] == 0x00 && pdata[1] == 0x00 && pdata[2] >= 0x80){ /* PSC or EOS or GBSC */
+ // uint8_t GN = ((pdata[2]>>2) & 0x1F);
+ // found_gob = tsk_true;
+ // //TSK_DEBUG_INFO("GN=%u", pdata[2]);
+ //
+ // /* RFC 4629 - 6.1.1. Packets that begin with a Picture Start Code
+ // A packet that begins at the location of a Picture, GOB, slice, EOS,
+ // or EOSBS start code shall omit the first two (all zero) bytes from
+ // the H.263+ bitstream and signify their presence by setting P=1 in the
+ // payload header.
+ // */
+
+ // if(GN == 0x00){ /* PSC 00000 */
+ // /* Use the two first bytes as RTP header */
+ // //pdata[0] |= 0x04; // P=1
+
+ // /*
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | RR |1|V|0|0|0|0|0|0|0|0|0| bitstream data without the :
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // : first two 0 bytes of the PSC
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // */
+
+ // //TSK_DEBUG_INFO("H263 - PSC");
+ // }
+ // else if(GN == 0x1F){ /* EOS 11111 */
+ // /* Use the two first bytes as RTP header */
+ // //pdata[0] |= 0x04; // P=1
+ // eos = tsk_true;
+ // /* RFC 4629 - 6.1.3. Packets that begin with an EOS or EOSBS Code
+ // 0 1 2
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | RR |1|V|0|0|0|0|0|0|0|0|0|1|1|1|1|1|1|0|0|
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // */
+ // //TSK_DEBUG_INFO("H263 - EOS");
+ // }
+ // else /*if((GN >> 4) == 0x01)*/{ /* GBSC 10000 */
+ // /* Use the two first bytes as RTP header */
+ // //pdata[0] |= 0x04; // P=1
+ //
+ // /* RFC 4629 - 6.1.2. Packets that begin with GBSC or SSC
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | RR |1|V|0 0 1 0 0 1|PEBIT|1 0 0 0 0 0| picture header :
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // : starting with TR, PTYPE ... |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ... | bitstream :
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // : data starting with GBSC/SSC without its first two 0 bytes
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // */
+ // //TSK_DEBUG_INFO("H263 - GBSC");
+ // found_gob = tsk_false;
+ // }
+ // //else if(EOSBS) -> Not Supported
+ //}
+ //else{
+ // /* 6.2. Encapsulating Follow-on Packet (P=0) */
+ // int i = 0;
+ // i++;
+ //}
+
+ //if(/*eos*/!found_gob && frag){
+ // if(self->rtp.size < (size + 2/* H263+ Header size */)){
+ // if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (size + 2)))){
+ // TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ // return;
+ // }
+ // self->rtp.size = (size + 2);
+ // }
+ // /* RFC 4629 - 6. Packetization Schemes */
+ // //rtp_hdr[0] |= 0x00;
+ // //memcpy(self->rtp.ptr, rtp_hdr/* zeros-> is it corretc? */, 2);
+ // //memcpy((self->rtp.ptr + 2), pdata, size);
+ // //_ptr = self->rtp.ptr;
+ // //_size = (size + 2);
+
+ // pdata[0] |= pdata[2] > 0x80 ? 0x04 : 0x04;
+ // _ptr = pdata;
+ // _size = size;
+ //}
+ //else{
+ // pdata[0] |= pdata[2] > 0x80 ? 0x04 : 0x04;
+ // _ptr = pdata;
+ // _size = size;
+ //}
+
+// FIXME
+ pdata[0] |= pdata[2] > 0x80 ? 0x04 : 0x04;
+ _ptr = pdata;
+ _size = size;
+
+
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->callback){
+ TMEDIA_CODEC_VIDEO(self)->callback(TMEDIA_CODEC_VIDEO(self)->callback_data, _ptr, _size, (3003* (30/TMEDIA_CODEC_VIDEO(self)->fps)), marker);
+ }
+}
+
+
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264.c b/tinyDAV/src/codecs/h264/tdav_codec_h264.c
new file mode 100644
index 0000000..1f4be60
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264.c
@@ -0,0 +1,871 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h264.c
+ * @brief H.264 codec plugin
+ * RTP payloader/depayloader follows RFC 3984
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/h264/tdav_codec_h264.h"
+
+#if HAVE_FFMPEG && (!defined(HAVE_H264) || HAVE_H264)
+
+#include "tinydav/codecs/h264/tdav_codec_h264_rtp.h"
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define H264_PACKETIZATION_MODE Non_Interleaved_Mode
+#define H264_MAX_BR 452
+#define H264_MAX_MBPS 11880
+
+int tdav_codec_h264_init(tdav_codec_h264_t* self, tdav_codec_h264_profile_t profile);
+int tdav_codec_h264_deinit(tdav_codec_h264_t* self);
+tdav_codec_h264_profile_t tdav_codec_h264_get_profile(const char* fmtp);
+
+static void tdav_codec_h264_encap(const tdav_codec_h264_t* h264, const uint8_t* pdata, tsk_size_t size);
+
+/* ============ H.264 Base Profile X.X Plugin interface functions ================= */
+
+#define tdav_codec_h264_fmtp_set tsk_null /* FIXME: should be removed from all plugins (useless) */
+
+int tdav_codec_h264_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+ float bitRate;
+
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+
+ //
+ // Encoder
+ //
+ h264->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h264->encoder.context);
+
+#if TDAV_UNDER_WINDOWS
+ h264->encoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);
+#endif
+
+ h264->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h264->encoder.context->time_base.num = 1;
+ h264->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(h264)->fps;
+ h264->encoder.context->width = TMEDIA_CODEC_VIDEO(h264)->width;
+ h264->encoder.context->height = TMEDIA_CODEC_VIDEO(h264)->height;
+
+ h264->encoder.context->rc_lookahead = 0;
+
+ //h264->encoder.context->refs = 1;
+ h264->encoder.context->scenechange_threshold = 0;
+ h264->encoder.context->me_subpel_quality = 0;
+ h264->encoder.context->partitions = X264_PART_I4X4 | X264_PART_I8X8 | X264_PART_P8X8 | X264_PART_B8X8;
+ h264->encoder.context->me_method = ME_EPZS;
+ h264->encoder.context->trellis = 0;
+
+ h264->encoder.context->me_range = 16;
+ h264->encoder.context->max_qdiff = 4;
+ h264->encoder.context->mb_qmin = h264->encoder.context->qmin = 10;
+ h264->encoder.context->mb_qmax = h264->encoder.context->qmax = 51;
+ h264->encoder.context->qcompress = 0.6f;
+ h264->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+ h264->encoder.context->flags2 |= CODEC_FLAG2_FASTPSKIP;
+ h264->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER;
+ h264->encoder.context->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ h264->encoder.context->max_b_frames = 0;
+ h264->encoder.context->b_frame_strategy = 1;
+ h264->encoder.context->chromaoffset = 0;
+
+ switch(h264->profile){
+ case tdav_codec_h264_bp10:
+ default:
+ h264->encoder.context->profile = FF_PROFILE_H264_BASELINE;
+ h264->encoder.context->level = 10;
+ bitRate = 128000.f;
+ break;
+ case tdav_codec_h264_bp20:
+ h264->encoder.context->profile = FF_PROFILE_H264_BASELINE;
+ h264->encoder.context->level = 20;
+ bitRate = 491400.f;
+ break;
+ case tdav_codec_h264_bp30:
+ h264->encoder.context->profile = FF_PROFILE_H264_BASELINE;
+ h264->encoder.context->level = 30;
+ bitRate = 2725300.f;
+ break;
+ }
+
+ h264->encoder.context->crf = 22;
+ h264->encoder.context->thread_count = 0;
+ h264->encoder.context->rtp_payload_size = H264_RTP_PAYLOAD_SIZE;
+ h264->encoder.context->opaque = tsk_null;
+ //h264->encoder.context->bit_rate = (int) (bitRate * 0.80f);
+ //h264->encoder.context->bit_rate_tolerance = (int) (bitRate * 0.20f);
+ h264->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(h264)->fps*4; // Each 4 second(s)
+
+
+ // Picture (YUV 420)
+ if(!(h264->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h264->encoder.picture);
+
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h264->encoder.context->width, h264->encoder.context->height);
+ if(!(h264->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(h264->encoder.context, h264->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ h264->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h264->decoder.context);
+
+ h264->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h264->decoder.context->flags2 |= CODEC_FLAG2_FAST;
+ h264->decoder.context->width = TMEDIA_CODEC_VIDEO(h264)->width;
+ h264->decoder.context->height = TMEDIA_CODEC_VIDEO(h264)->height;
+
+#if TDAV_UNDER_WINDOWS
+ h264->decoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);
+#endif
+
+ // Picture (YUV 420)
+ if(!(h264->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h264->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h264->decoder.context->width, h264->decoder.context->height);
+ if(!(h264->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(h264->decoder.context, h264->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_h264_close(tmedia_codec_t* self)
+{
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ //
+ // Encoder
+ //
+ if(h264->encoder.context){
+ avcodec_close(h264->encoder.context);
+ av_free(h264->encoder.context);
+ }
+ if(h264->encoder.picture){
+ av_free(h264->encoder.picture);
+ }
+ if(h264->encoder.buffer){
+ TSK_FREE(h264->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(h264->decoder.context){
+ avcodec_close(h264->decoder.context);
+ av_free(h264->decoder.context);
+ }
+ if(h264->decoder.picture){
+ av_free(h264->decoder.picture);
+ }
+ if(h264->decoder.accumulator){
+ TSK_FREE(h264->decoder.accumulator);
+ h264->decoder.accumulator_pos = 0;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h264_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret = 0;
+ int size;
+
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Codec not opened");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)h264->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, h264->encoder.context->width, h264->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+ /* Flip */
+#if FLIP_ENCODED_PICT
+ tdav_converter_video_flip(h264->encoder.picture, h264->encoder.context->height);
+#endif
+
+ if((h264->encoder.frame_count < (int)TMEDIA_CODEC_VIDEO(h264)->fps*3)
+ && ((h264->encoder.frame_count++%TMEDIA_CODEC_VIDEO(h264)->fps)==0)){
+
+ // You must patch FFmpeg to switch from X264_TYPE_AUTO to X264_TYPE_IDR
+ h264->encoder.picture->pict_type = FF_I_TYPE;
+ tdav_codec_h264_encap(h264, h264->encoder.context->extradata, (tsk_size_t)h264->encoder.context->extradata_size);
+ }
+ else{
+ // Encode data
+ h264->encoder.picture->pts = AV_NOPTS_VALUE;
+ ret = avcodec_encode_video(h264->encoder.context, h264->encoder.buffer, size, h264->encoder.picture);
+ if(ret >0){
+ tdav_codec_h264_encap(h264, h264->encoder.buffer, (tsk_size_t)ret);
+ }
+ h264->encoder.picture->pict_type = 0;//reset
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h264_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ const uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size = 0;
+ int ret;
+ tsk_bool_t append_scp;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+
+ if(!h264 || !in_data || !in_size || !out_data || !h264->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ //TSK_DEBUG_INFO("SeqNo=%hu", rtp_hdr->seq_num);
+
+ /* Packet lost? */
+ if(h264->decoder.last_seq != (rtp_hdr->seq_num - 1) && h264->decoder.last_seq){
+ if(h264->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ h264->decoder.last_seq = rtp_hdr->seq_num;
+
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ if(*((uint8_t*)in_data) >> 7){
+ TSK_DEBUG_WARN("F=1");
+ /* reset accumulator */
+ h264->decoder.accumulator = 0;
+ return 0;
+ }
+
+ /* get payload */
+ if((ret = tdav_codec_h264_get_pay(in_data, in_size, (const void**)&pay_ptr, &pay_size, &append_scp)) || !pay_ptr || !pay_size){
+ TSK_DEBUG_ERROR("Depayloader failed to get H.264 content");
+ return 0;
+ }
+ xsize = avpicture_get_size(h264->decoder.context->pix_fmt, h264->decoder.context->width, h264->decoder.context->height);
+
+ if((int)(h264->decoder.accumulator_pos + pay_size) <= xsize){
+ if(append_scp){
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], H264_START_CODE_PREFIX, sizeof(H264_START_CODE_PREFIX));
+ h264->decoder.accumulator_pos += sizeof(H264_START_CODE_PREFIX);
+ }
+
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], pay_ptr, pay_size);
+ h264->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h264->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+
+
+ /* decode the picture */
+ av_init_packet(&packet);
+ packet.size = h264->decoder.accumulator_pos;
+ packet.data = h264->decoder.accumulator;
+ ret = avcodec_decode_video2(h264->decoder.context, h264->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0){
+ TSK_DEBUG_ERROR("=============Failed to decode the buffer");
+ }
+ else if(got_picture_ptr){
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(h264->decoder.picture, h264->decoder.context->height);
+#endif
+ /* fill out */
+ if(*out_max_size<xsize){
+ if((*out_data = tsk_realloc(*out_data, xsize))){
+ *out_max_size = xsize;
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+ retsize = xsize;
+ avpicture_layout((AVPicture *)h264->decoder.picture, h264->decoder.context->pix_fmt, h264->decoder.context->width, h264->decoder.context->height,
+ *out_data, retsize);
+ }
+ h264->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_h264_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)codec;
+ tsk_params_L_t* params = tsk_null;
+ int val_int;
+ const char* val_str;
+ tsk_bool_t ret = tsk_true;
+ tdav_codec_h264_profile_t profile;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ TSK_DEBUG_INFO("Trying to match [%s]", fmtp);
+
+ /* Check whether the profile match (If the profile is missing, then we consider that it's ok) */
+ if(((profile = tdav_codec_h264_get_profile(fmtp)) != tdav_codec_h264_bp99) && (profile != h264->profile)){
+ TSK_DEBUG_INFO("Profile not matching");
+ return tsk_false;
+ }
+
+ /* e.g. profile-level-id=42e00a; packetization-mode=1; max-br=452; max-mbps=11880 */
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+
+ /* === max-br ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "max-br")) != -1){
+ // should compare "max-br"?
+ TMEDIA_CODEC_VIDEO(h264)->max_br = val_int*1000;
+ }
+
+ /* === max-mbps ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "max-mbps")) != -1){
+ // should compare "max-mbps"?
+ TMEDIA_CODEC_VIDEO(h264)->max_mbps = val_int*1000;
+ }
+
+ /* === packetization-mode ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "packetization-mode")) != -1){
+ if((packetization_mode_t)val_int == Single_NAL_Unit_Mode || (packetization_mode_t)val_int == Non_Interleaved_Mode){
+ h264->pack_mode = (packetization_mode_t)val_int;
+ }
+ else{
+ TSK_DEBUG_INFO("packetization-mode not matching");
+ ret = tsk_false;
+ goto bail;
+ }
+ }
+
+ /* === profile-level-id ===*/
+ if((val_str = tsk_params_get_param_value(params, "profile-level-id"))){
+ level_idc_t l_idc;
+ /* profile-idc and level-idc already tested by tdav_codec_h264_get_profile() */
+ tdav_codec_h264_parse_profile(val_str, tsk_null, tsk_null, &l_idc);
+ switch(l_idc){
+ case level_idc_1_0:
+ case level_idc_1_b:
+ case level_idc_1_1:
+ TMEDIA_CODEC_VIDEO(h264)->width = 176, TMEDIA_CODEC_VIDEO(h264)->height = 144;
+ break;
+ default:
+ TMEDIA_CODEC_VIDEO(h264)->width = 352, TMEDIA_CODEC_VIDEO(h264)->height = 288;
+ //TMEDIA_CODEC_VIDEO(h264)->width = 704, TMEDIA_CODEC_VIDEO(h264)->height = 480;
+ break;
+ }
+ }
+ }
+
+bail:
+ TSK_OBJECT_SAFE_FREE(params);
+ return ret;
+}
+
+char* tdav_codec_h264_fmtp_get(const tmedia_codec_t* self)
+{
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+ char* fmtp = tsk_null;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ switch(h264->profile){
+ case tdav_codec_h264_bp10:
+ fmtp = tsk_strdup("profile-level-id=42e00a");
+ break;
+ case tdav_codec_h264_bp20:
+ fmtp = tsk_strdup("profile-level-id=42e014");
+ break;
+ case tdav_codec_h264_bp30:
+ fmtp = tsk_strdup("profile-level-id=42e01e");
+ break;
+ }
+
+ if(fmtp){
+ tsk_strcat_2(&fmtp, "; packetization-mode=%d; max-br=%d; max-mbps=%d",
+ h264->pack_mode, TMEDIA_CODEC_VIDEO(h264)->max_br/1000, TMEDIA_CODEC_VIDEO(h264)->max_mbps/1000);
+ }
+
+ return fmtp;
+}
+
+
+
+
+/* ============ H.264 Base Profile 1.0 Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_bp10_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_t *h264 = self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h264_init(h264, tdav_codec_h264_bp10);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_bp10_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_t *h264 = self;
+ if(h264){
+ /* deinit base */
+ tmedia_codec_video_deinit(self);
+ /* deinit self */
+ tdav_codec_h264_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_bp10_def_s =
+{
+ sizeof(tdav_codec_h264_t),
+ tdav_codec_h264_bp10_ctor,
+ tdav_codec_h264_bp10_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_bp10_plugin_def_s =
+{
+ &tdav_codec_h264_bp10_def_s,
+
+ tmedia_video,
+ "H264",
+ "H264 Base Profile 1.0",
+ TMEDIA_CODEC_FORMAT_H264_BP10,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h264_open,
+ tdav_codec_h264_close,
+ tdav_codec_h264_encode,
+ tdav_codec_h264_decode,
+ tdav_codec_h264_fmtp_match,
+ tdav_codec_h264_fmtp_get,
+ tdav_codec_h264_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_bp10_plugin_def_t = &tdav_codec_h264_bp10_plugin_def_s;
+
+
+/* ============ H.264 Base Profile 2.0 Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_bp20_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_t *h264 = self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h264_init(h264, tdav_codec_h264_bp20);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_bp20_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_t *h264 = self;
+ if(h264){
+ /* deinit base */
+ tmedia_codec_video_deinit(self);
+ /* deinit self */
+ tdav_codec_h264_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_bp20_def_s =
+{
+ sizeof(tdav_codec_h264_t),
+ tdav_codec_h264_bp20_ctor,
+ tdav_codec_h264_bp20_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_bp20_plugin_def_s =
+{
+ &tdav_codec_h264_bp20_def_s,
+
+ tmedia_video,
+ "H264",
+ "H264 Base Profile 2.0",
+ TMEDIA_CODEC_FORMAT_H264_BP20,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {352, 288, 15},
+
+ tdav_codec_h264_open,
+ tdav_codec_h264_close,
+ tdav_codec_h264_encode,
+ tdav_codec_h264_decode,
+ tdav_codec_h264_fmtp_match,
+ tdav_codec_h264_fmtp_get,
+ tdav_codec_h264_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_bp20_plugin_def_t = &tdav_codec_h264_bp20_plugin_def_s;
+
+
+/* ============ H.264 Base Profile 3.0 Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_bp30_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_t *h264 = self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h264_init(h264, tdav_codec_h264_bp30);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_bp30_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_t *h264 = self;
+ if(h264){
+ /* deinit base */
+ tmedia_codec_video_deinit(self);
+ /* deinit self */
+ tdav_codec_h264_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_bp30_def_s =
+{
+ sizeof(tdav_codec_h264_t),
+ tdav_codec_h264_bp30_ctor,
+ tdav_codec_h264_bp30_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_bp30_plugin_def_s =
+{
+ &tdav_codec_h264_bp30_def_s,
+
+ tmedia_video,
+ "H264",
+ "H264 Base Profile 3.0",
+ TMEDIA_CODEC_FORMAT_H264_BP30,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {352, 288, 15},
+
+ tdav_codec_h264_open,
+ tdav_codec_h264_close,
+ tdav_codec_h264_encode,
+ tdav_codec_h264_decode,
+ tdav_codec_h264_fmtp_match,
+ tdav_codec_h264_fmtp_get,
+ tdav_codec_h264_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_bp30_plugin_def_t = &tdav_codec_h264_bp30_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ Common To all H264 codecs ================= */
+
+int tdav_codec_h264_init(tdav_codec_h264_t* self, tdav_codec_h264_profile_t profile)
+{
+ int ret = 0;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->pack_mode = H264_PACKETIZATION_MODE;
+ self->profile = profile;
+ TMEDIA_CODEC_VIDEO(self)->max_mbps = H264_MAX_MBPS*1000;
+ TMEDIA_CODEC_VIDEO(self)->max_br = H264_MAX_BR*1000;
+
+ // At this time self->plugin is Null
+ TMEDIA_CODEC_VIDEO(self)->width = 176;
+ TMEDIA_CODEC_VIDEO(self)->height = 144;
+ TMEDIA_CODEC_VIDEO(self)->fps = 15;
+
+ if(!(self->encoder.codec = avcodec_find_encoder(CODEC_ID_H264))){
+ TSK_DEBUG_ERROR("Failed to find H.264 encoder");
+ ret = -2;
+ }
+
+ if(!(self->decoder.codec = avcodec_find_decoder(CODEC_ID_H264))){
+ TSK_DEBUG_ERROR("Failed to find H.264 decoder");
+ ret = -3;
+ }
+
+ /* allocations MUST be done by open() */
+ return ret;
+}
+
+int tdav_codec_h264_deinit(tdav_codec_h264_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->encoder.codec = tsk_null;
+ self->decoder.codec = tsk_null;
+
+ // FFMpeg resources are destroyed by close()
+
+
+ TSK_FREE(self->rtp.ptr);
+ self->rtp.size = 0;
+
+ return 0;
+}
+
+tdav_codec_h264_profile_t tdav_codec_h264_get_profile(const char* fmtp)
+{
+ tdav_codec_h264_profile_t profile = tdav_codec_h264_bp99;
+ tsk_size_t size = tsk_strlen(fmtp);
+ int start, end;
+
+ if((start = tsk_strindexOf(fmtp, size, "profile-level-id")) !=-1){
+ tsk_param_t* param;
+ if((end = tsk_strindexOf((fmtp+start), (size-start), ";")) == -1){
+ end = size;
+ }
+
+ if((param = tsk_params_parse_param((fmtp+start), (end-start)))){
+ profile_idc_t p_idc;
+ level_idc_t l_idc;
+ if(param->value){
+ tsk_strtrim_both(&param->value);
+ }
+
+ tdav_codec_h264_parse_profile(param->value, &p_idc, tsk_null, &l_idc);
+
+ switch(p_idc){
+ case profile_idc_baseline:
+ switch(l_idc){
+ case level_idc_1_0:
+ case level_idc_1_b:
+ case level_idc_1_1:
+ case level_idc_1_2:
+ case level_idc_1_3:
+ profile = tdav_codec_h264_bp10;
+ break;
+ case level_idc_2_0:
+ case level_idc_2_1:
+ case level_idc_2_2:
+ profile = tdav_codec_h264_bp20;
+ break;
+ case level_idc_3_0:
+ profile = tdav_codec_h264_bp30;
+ break;
+ }
+ break;
+ case profile_idc_extended:
+ case profile_idc_main:
+ case profile_idc_high:
+ default:
+ /* Not supported */
+ break;
+ }
+
+ TSK_OBJECT_SAFE_FREE(param);
+ }
+ }
+ return profile;
+}
+
+static void tdav_codec_h264_encap(const tdav_codec_h264_t* h264, const uint8_t* pdata, tsk_size_t size)
+{
+ register uint32_t i;
+ uint32_t last_scp, prev_scp;
+ static uint32_t size_of_scp = sizeof(H264_START_CODE_PREFIX); /* we know it's equal to 4 ..but */
+
+ if(!pdata || !size){
+ return;
+ }
+
+ last_scp = 0, prev_scp = 0;
+/*
+#if 1
+ if(size < H264_RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+#else
+ goto last;
+#endif
+*/
+ for(i = size_of_scp; i<(size - size_of_scp); i++){
+ if(pdata[i] == H264_START_CODE_PREFIX[0] && pdata[i+1] == H264_START_CODE_PREFIX[1] && pdata[i+2] == H264_START_CODE_PREFIX[2] && pdata[i+3] == H264_START_CODE_PREFIX[3]){ /* Found Start Code Prefix */
+ prev_scp = last_scp;
+ if((i - last_scp) >= H264_RTP_PAYLOAD_SIZE || 1){
+ tdav_codec_h264_rtp_callback((tdav_codec_h264_t*) h264, pdata + prev_scp,
+ (i - prev_scp), (prev_scp == size));
+ }
+ last_scp = i;
+ }
+ }
+//last:
+ if(last_scp < size){
+ tdav_codec_h264_rtp_callback((tdav_codec_h264_t*) h264, pdata + last_scp,
+ (size - last_scp), tsk_true);
+ }
+}
+
+#endif /* HAVE_FFMPEG */
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c b/tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c
new file mode 100644
index 0000000..a750ccf
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c
@@ -0,0 +1,356 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_h264_rtp.c
+ * @brief H.264 payloader/depayloder as per RFC 3984
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/h264/tdav_codec_h264_rtp.h"
+
+#include "tinydav/codecs/h264/tdav_codec_h264.h"
+
+#if HAVE_FFMPEG && (!defined(HAVE_H264) || HAVE_H264)
+
+#include "tinymedia/tmedia_codec.h"
+
+#include "tsk_string.h"
+#include "tsk_debug.h"
+
+#include "tsk_memory.h"
+#include <string.h> /* strlen() */
+#include <stdlib.h> /* strtol() */
+
+/*
+* ITU H.264 - http://www.itu.int/rec/T-REC-H.264-200903-S/en
+*/
+
+uint8_t H264_START_CODE_PREFIX[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+#define H264_NAL_UNIT_TYPE_HEADER_SIZE 1
+#define H264_F_UNIT_TYPE_HEADER_SIZE 1
+#define H264_FUA_HEADER_SIZE 2
+#define H264_FUB_HEADER_SIZE 4
+#define H264_NAL_AGG_MAX_SIZE 65535
+
+int tdav_codec_h264_get_fua_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp);
+int tdav_codec_h264_get_nalunit_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size);
+
+// profile_level_id MUST be a "null-terminated" string
+int tdav_codec_h264_parse_profile(const char* profile_level_id, profile_idc_t *p_idc, profile_iop_t *p_iop, level_idc_t *l_idc)
+{
+ uint32_t value;
+
+ if(tsk_strlen(profile_level_id) != 6){
+ TSK_DEBUG_ERROR("I say [%s] is an invalid profile-level-id", profile_level_id);
+ return -1;
+ }
+
+ value = strtol(profile_level_id, tsk_null, 16);
+
+ /* profile-idc */
+ if(p_idc){
+ switch((value >> 16)){
+ case profile_idc_baseline:
+ *p_idc = profile_idc_baseline;
+ break;
+ case profile_idc_extended:
+ *p_idc = profile_idc_extended;
+ break;
+ case profile_idc_main:
+ *p_idc = profile_idc_main;
+ break;
+ case profile_idc_high:
+ *p_idc = profile_idc_high;
+ break;
+ default:
+ *p_idc = profile_idc_none;
+ break;
+ }
+ }
+
+ /* profile-iop */
+ if(p_iop){
+ p_iop->constraint_set0_flag = ((value >> 8) & 0x80)>>7;
+ p_iop->constraint_set1_flag = ((value >> 8) & 0x40)>>6;
+ p_iop->constraint_set2_flag = ((value >> 8) & 0x20)>>5;
+ p_iop->reserved_zero_5bits = ((value >> 8) & 0x1F);
+ }
+
+ /* level-idc */
+ if(l_idc){
+ switch((value & 0xFF)){
+ case level_idc_1_0:
+ *l_idc = level_idc_1_0;
+ break;
+ case level_idc_1_b:
+ *l_idc = level_idc_1_b;
+ break;
+ case level_idc_1_1:
+ *l_idc = level_idc_1_1;
+ break;
+ case level_idc_1_2:
+ *l_idc = level_idc_1_2;
+ break;
+ case level_idc_1_3:
+ *l_idc = level_idc_1_3;
+ break;
+ case level_idc_2_0:
+ *l_idc = level_idc_2_0;
+ break;
+ case level_idc_2_1:
+ *l_idc = level_idc_2_1;
+ break;
+ case level_idc_2_2:
+ *l_idc = level_idc_2_2;
+ break;
+ case level_idc_3_0:
+ *l_idc = level_idc_3_0;
+ break;
+ default:
+ *l_idc = level_idc_none;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_codec_h264_get_pay(const void* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp)
+{
+ const uint8_t* pdata = in_data;
+ if(!in_data || !in_size || !out_data || !out_size){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ *out_data = tsk_null;
+ *out_size = 0;
+ *append_scp = tsk_true;
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ switch((pdata[0] & 0x1F)){
+ case undefined_0:
+ case undefined_30:
+ case undefined_31:
+ case stap_a:
+ case stap_b:
+ case mtap16:
+ case mtap24:
+ break;
+ case fu_a:
+ return tdav_codec_h264_get_fua_pay(pdata, in_size, out_data, out_size, append_scp);
+ case fu_b:
+ return -1;
+ default: /* NAL unit (1-23) */
+ return tdav_codec_h264_get_nalunit_pay(pdata, in_size, out_data, out_size);
+ }
+
+ TSK_DEBUG_WARN("%d not supported as valid NAL Unit type", (*pdata & 0x1F));
+ return -1;
+}
+
+
+int tdav_codec_h264_get_fua_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp)
+{
+ if(in_size <=2){
+ TSK_DEBUG_ERROR("Too short");
+ return -1;
+ }
+ /* RFC 3984 - 5.8. Fragmentation Units (FUs)
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | FU indicator | FU header | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ | |
+ | FU payload |
+ | |
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :...OPTIONAL RTP padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The FU indicator octet has the following format:
+
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+
+ The FU header has the following format:
+
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |S|E|R| Type |
+ +---------------+
+ */
+
+ if((in_data[1] & 0x80) == 0x80 /*S*/){
+ /* discard "FU indicator"
+ S: 1 bit
+ When set to one, the Start bit indicates the start of a fragmented
+ NAL unit. When the following FU payload is not the start of a
+ fragmented NAL unit payload, the Start bit is set to zero.
+ */
+ if(in_size> H264_NAL_UNIT_TYPE_HEADER_SIZE){
+ uint8_t hdr;
+ *out_data = (in_data + H264_NAL_UNIT_TYPE_HEADER_SIZE);
+ *out_size = (in_size - H264_NAL_UNIT_TYPE_HEADER_SIZE);
+
+ // F, NRI and Type
+ hdr = (in_data[0] & 0xe0) /* F,NRI from "FU indicator"*/ | (in_data[1] & 0x1f) /* type from "FU header" */;
+ *((uint8_t*)*out_data) = hdr;
+ // Need to append Start Code Prefix
+ *append_scp = tsk_true;
+ }
+ else{
+ TSK_DEBUG_ERROR("Too short");
+ return -1;
+ }
+ }
+ else{
+ /* "FU indicator" and "FU header" */
+ if(in_size> H264_FUA_HEADER_SIZE){
+ *out_data = (in_data + H264_FUA_HEADER_SIZE);
+ *out_size = (in_size - H264_FUA_HEADER_SIZE);
+ *append_scp = tsk_false;
+ }
+ else{
+ TSK_DEBUG_ERROR("Too short");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_codec_h264_get_nalunit_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size)
+{
+
+/* 5.6. Single NAL Unit Packet
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|NRI| type | |
+ +-+-+-+-+-+-+-+-+ |
+ | |
+ | Bytes 2..n of a Single NAL unit |
+ | |
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :...OPTIONAL RTP padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+ *out_data = in_data;
+ *out_size = in_size;
+
+ return 0;
+}
+
+#if TDAV_UNDER_WINDOWS
+# include "tsk_thread.h"
+#endif
+void tdav_codec_h264_rtp_callback(struct tdav_codec_h264_s *self, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+ uint8_t* pdata = (uint8_t*)data;
+
+ if(size>4 && pdata[0] == H264_START_CODE_PREFIX[0] && pdata[1] == H264_START_CODE_PREFIX[1]){
+ if(pdata[2] == H264_START_CODE_PREFIX[3]){
+ pdata += 3, size -= 3;
+ }
+ else if(pdata[2] == H264_START_CODE_PREFIX[2] && pdata[3] == H264_START_CODE_PREFIX[3]){
+ pdata += 4, size -= 4;
+ }
+ }
+
+ //TSK_DEBUG_INFO("==> SCP %2x %2x %2x %2x", pdata[0], pdata[1], pdata[2], pdata[3]);
+
+ if(size < H264_RTP_PAYLOAD_SIZE){
+ /* Can be packet in a Single Nal Unit */
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->callback){
+ TMEDIA_CODEC_VIDEO(self)->callback(TMEDIA_CODEC_VIDEO(self)->callback_data, pdata, size, (3003* (30/TMEDIA_CODEC_VIDEO(self)->fps)), marker);
+ }
+ }
+ else if(size > H264_NAL_UNIT_TYPE_HEADER_SIZE){
+#if TDAV_UNDER_WINDOWS
+ tsk_bool_t burst = ((size/H264_RTP_PAYLOAD_SIZE) > 5);
+ int count = 0;
+#endif
+ /* Should be Fragmented as FUA */
+ uint8_t fua_hdr[H264_FUA_HEADER_SIZE]; /* "FU indicator" and "FU header" - 2bytes */
+ fua_hdr[0] = pdata[0] & 0x60/* F=0 */, fua_hdr[0] |= fu_a;
+ fua_hdr[1] = 0x80/* S=1,E=0,R=0 */, fua_hdr[1] |= pdata[0] & 0x1f; /* type */
+ // discard header
+ pdata += H264_NAL_UNIT_TYPE_HEADER_SIZE;
+ size -= H264_NAL_UNIT_TYPE_HEADER_SIZE;
+
+ while(size){
+ tsk_size_t packet_size = TSK_MIN(H264_RTP_PAYLOAD_SIZE, size);
+
+ if(self->rtp.size < (packet_size + H264_FUA_HEADER_SIZE)){
+ if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (packet_size + H264_FUA_HEADER_SIZE)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return;
+ }
+ self->rtp.size = (packet_size + H264_FUA_HEADER_SIZE);
+ }
+ // set E bit
+ if((size - packet_size) == 0){
+ // Last packet
+ fua_hdr[1] |= 0x40;
+ }
+ // copy FUA header
+ memcpy(self->rtp.ptr, fua_hdr, H264_FUA_HEADER_SIZE);
+ // reset "S" bit
+ fua_hdr[1] &= 0x7F;
+ // copy data
+ memcpy((self->rtp.ptr + H264_FUA_HEADER_SIZE), pdata, packet_size);
+ pdata += packet_size;
+ size -= packet_size;
+
+ // send data
+ if(TMEDIA_CODEC_VIDEO(self)->callback){
+ TMEDIA_CODEC_VIDEO(self)->callback(TMEDIA_CODEC_VIDEO(self)->callback_data, self->rtp.ptr, (packet_size + H264_FUA_HEADER_SIZE), (3003* (30/TMEDIA_CODEC_VIDEO(self)->fps)), (size == 0));
+#if TDAV_UNDER_WINDOWS// FIXME: WinSock problem: Why do we get packet lost (burst case only)?
+ if(burst && (++count % 2 == 0)){
+ tsk_thread_sleep(1); // 1 millisecond
+ }
+#endif
+ }
+ }
+ }
+}
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c b/tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c
new file mode 100644
index 0000000..23271b4
--- /dev/null
+++ b/tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c
@@ -0,0 +1,256 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_ilbc.c
+ * @brief iLBC codec
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/ilbc/tdav_codec_ilbc.h"
+
+#if HAVE_ILBC
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_ILBC_MODE 20
+
+/* ============ iLBC Plugin interface ================= */
+
+#define tdav_codec_ilbc_fmtp_set tsk_null
+
+int tdav_codec_ilbc_open(tmedia_codec_t* self)
+{
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+
+ initEncode(&ilbc->encoder, TDAV_ILBC_MODE);
+ initDecode(&ilbc->decoder, TDAV_ILBC_MODE, tsk_true/* Enhancer */);
+
+ return 0;
+}
+
+int tdav_codec_ilbc_close(tmedia_codec_t* self)
+{
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+
+ //ilbc->encoder = {0};
+ //ilbc->decoder = {0};
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_ilbc_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+ int k;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* convert signal to float */
+ for(k=0; k<ilbc->encoder.blockl; k++){
+ ilbc->encblock[k] = (float)((short*)in_data)[k];
+ }
+
+ /* allocate new buffer if needed */
+ if((int)*out_max_size <ilbc->encoder.no_of_bytes){
+ if(!(*out_data = tsk_realloc(*out_data, ilbc->encoder.no_of_bytes))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = ilbc->encoder.no_of_bytes;
+ }
+
+ /* do the actual encoding */
+ iLBC_encode(*out_data, ilbc->encblock, &ilbc->encoder);
+
+ return ilbc->encoder.no_of_bytes;
+}
+
+tsk_size_t tdav_codec_ilbc_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ int blocks, i, k, block_size;
+ float dtmp;
+ tsk_size_t out_size;
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if((in_size % NO_OF_BYTES_20MS) == 0){
+ /* Using 20ms mode */
+ blocks = (in_size/NO_OF_BYTES_20MS);
+ out_size = (BLOCKL_20MS * blocks) * sizeof(short);
+ block_size = out_size/blocks;
+ if(ilbc->decoder.mode != 20){
+ initDecode(&ilbc->decoder, 20, tsk_true/* Enhancer */);
+ }
+ }
+ else if((in_size % NO_OF_BYTES_30MS) == 0){
+ /* Using 30ms mode */
+ blocks = (in_size/NO_OF_BYTES_30MS);
+ out_size = (BLOCKL_30MS * blocks) * sizeof(short);
+ block_size = out_size/blocks;
+ if(ilbc->decoder.mode != 30){
+ initDecode(&ilbc->decoder, 30, tsk_true/* Enhancer */);
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid iLBC mode");
+ return 0;
+ }
+
+ /* allocate new buffer if needed */
+ if(*out_max_size<out_size){
+ if(!(*out_data = tsk_realloc(*out_data, out_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ for(i = 0; i<blocks; i++){
+ iLBC_decode(ilbc->decblock, &((uint8_t*)in_data)[i*block_size], &ilbc->decoder, 1/* Normal */);
+
+ /* convert to short */
+ for(k=0; k<ilbc->decoder.blockl; k++){
+ dtmp=ilbc->decblock[k];
+
+ if(dtmp<MIN_SAMPLE){
+ dtmp = MIN_SAMPLE;
+ }
+ else if(dtmp>MAX_SAMPLE){
+ dtmp = MAX_SAMPLE;
+ }
+
+ ((short*)*out_data)[(i*block_size) + k] = ((short) dtmp);
+ }
+ }
+
+ return out_size;
+}
+
+char* tdav_codec_ilbc_fmtp_get(const tmedia_codec_t* codec)
+{
+ char* fmtp = tsk_null;
+ tsk_sprintf(&fmtp, "mode=%d", TDAV_ILBC_MODE);
+ return fmtp;
+}
+
+tsk_bool_t tdav_codec_ilbc_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* RFC 3952 - 5. Mapping To SDP Parameters
+
+ The offer contains the preferred mode of the offerer. The answerer
+ may agree to that mode by including the same mode in the answer, or
+ may include a different mode. The resulting mode used by both
+ parties SHALL be the lower of the bandwidth modes in the offer and
+ answer.
+
+ That is, an offer of "mode=20" receiving an answer of "mode=30" will
+ result in "mode=30" being used by both participants. Similarly, an
+ offer of "mode=30" and an answer of "mode=20" will result in
+ "mode=30" being used by both participants.
+
+ This is important when one end point utilizes a bandwidth constrained
+ link (e.g., 28.8k modem link or slower), where only the lower frame
+ size will work.
+ */
+ return tsk_true; // FIXME
+}
+
+
+//
+// iLBC Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_ilbc_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_ilbc_t *ilbc = self;
+ if(ilbc){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_ilbc_dtor(tsk_object_t * self)
+{
+ tdav_codec_ilbc_t *ilbc = self;
+ if(ilbc){
+ /* deinit base */
+ tmedia_codec_audio_deinit(ilbc);
+ /* deinit self */
+ //ilbc->encoder = {0};
+ //ilbc->decoder = {0};
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_ilbc_def_s =
+{
+ sizeof(tdav_codec_ilbc_t),
+ tdav_codec_ilbc_ctor,
+ tdav_codec_ilbc_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_ilbc_plugin_def_s =
+{
+ &tdav_codec_ilbc_def_s,
+
+ tmedia_audio,
+ "iLBC",
+ "iLBC codec",
+ TMEDIA_CODEC_FORMAT_ILBC,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_ilbc_open,
+ tdav_codec_ilbc_close,
+ tdav_codec_ilbc_encode,
+ tdav_codec_ilbc_decode,
+ tdav_codec_ilbc_fmtp_match,
+ tdav_codec_ilbc_fmtp_get,
+ tdav_codec_ilbc_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_ilbc_plugin_def_t = &tdav_codec_ilbc_plugin_def_s;
+
+
+#endif /* HAVE_ILBC */
diff --git a/tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c b/tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c
new file mode 100644
index 0000000..731fe2e
--- /dev/null
+++ b/tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c
@@ -0,0 +1,634 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_mp4ves.c
+ * @brief MP4V-ES codec plugin
+ * RTP payloader/depayloader follows RFC 3016.
+ * ISO-IEC-14496-2: http://www.csus.edu/indiv/p/pangj/aresearch/video_compression/presentation/ISO-IEC-14496-2_2001_MPEG4_Visual.pdf
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Th Dec 2 16:54:58 2010 mdiop
+ */
+#include "tinydav/codecs/mp4ves/tdav_codec_mp4ves.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tnet_endianness.h"
+
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define DEFAULT_PROFILE_LEVEL_ID Simple_Profile_Level_1
+
+#define MP4V_RTP_PAYLOAD_SIZE 900
+
+
+// From ISO-IEC-14496-2
+typedef enum mp4v_codes_e
+{
+ // To initiate a visual session (6.3.2)
+ visual_object_sequence_start_code = 0x000001B0,
+ // To terminate a visual session (6.3.2)
+ visual_object_sequence_end_code = 0x000001B1,
+ // To initiate a visual object (6.3.2)
+ visual_object_start_code = 0x000001B5,
+ // To identify the beginning of user data. The user data continues until receipt of another start code. (6.3.2.1)
+ user_data_start_code = 0x000001B2,
+ // The video_object_layer_start_code is a string of 32 bits. The first 28 bits are
+ // ‘0000 0000 0000 0000 0000 0001 0010‘ in binary and the last 4-bits represent one of the values in the range of
+ // ‘0000’ to ‘1111’ in binary. The video_object_layer_start_code marks a new video object layer. (6.3.3)
+ video_object_layer_start_code = 0x0000012,
+ // To identify the beginning of a GOV header (6.3.4)
+ group_of_vop_start_code = 0x000001B3,
+ // To mark the start of a video object plane (6.3.5 )
+ vop_start_code = 0x000001B6,
+}
+mp4v_start_code_t;
+
+// From ISO-IEC-14496-2 Annex G
+typedef enum mp4v_profiles_e
+{
+ /* Reserved = 0x00000000 */
+ Simple_Profile_Level_1 = 1,
+ Simple_Profile_Level_2 = 2,
+ Simple_Profile_Level_3 = 3,
+ /* Reserved 00000100 ? 00010000 */
+ Simple_Scalable_Profile_Level_1 = 17,
+ Simple_Scalable_Profile_Level_2 = 18,
+ /* Reserved 00010011 ? = 0x00100000 */
+ Core_Profile_Level_1 = 33,
+ Core_Profile_Level_2 = 34,
+ /* Reserved 00100011 ? = 0x00110001 */
+ Main_Profile_Level_2 = 50,
+ Main_Profile_Level_3 = 51,
+ Main_Profile_Level_4 = 52,
+ /* Reserved 00110101 ? = 0x01000001 */
+ N_bit_Profile_Level_2 = 66,
+ /* Reserved 01000011 ? = 0x01010000 */
+ Scalable_Texture_Profile_Level_1 = 81,
+ /* Reserved 01010010 ? 01100000 */
+ Simple_Face_Animation_Profile_Level_1 = 97,
+ Simple_Face_Animation_Profile_Level_2 = 98,
+ Simple_FBA_Profile_Level_1 = 99,
+ Simple_FBA_Profile_Level_2 = 100,
+ /* Reserved 01100101 ? 01110000 */
+ Basic_Animated_Texture_Profile_Level_1 = 113,
+ Basic_Animated_Texture_Profile_Level_2 = 114,
+ /* Reserved 01110011 ? 10000000 */
+ Hybrid_Profile_Level_1 = 129,
+ Hybrid_Profile_Level_2 = 130,
+ /* Reserved 10000011 ? 10010000 */
+ Advanced_Real_Time_Simple_Profile_Level_1 = 145,
+ Advanced_Real_Time_Simple_Profile_Level_2 = 146,
+ Advanced_Real_Time_Simple_Profile_Level_3 = 147,
+ Advanced_Real_Time_Simple_Profile_Level_4 = 148,
+ /* Reserved 10010101 ? 10100000 */
+}
+mp4v_profiles_t;
+
+static void tdav_codec_mp4ves_encap(tdav_codec_mp4ves_t* mp4v, const uint8_t* pdata, tsk_size_t size);
+static void tdav_codec_mp4ves_rtp_callback(tdav_codec_mp4ves_t *mp4v, const void *data, tsk_size_t size, tsk_bool_t marker);
+
+/* ============ MP4V-ES Plugin interface functions ================= */
+#define tdav_codec_mp4ves_fmtp_set tsk_null /* MUST be removed from all codecs */
+
+int tdav_codec_mp4ves_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+ float bitRate = 64000.f;
+
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+
+ if(!mp4v){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ //
+ // Encoder
+ //
+ if(!(mp4v->encoder.codec = avcodec_find_encoder(CODEC_ID_MPEG4))){
+ TSK_DEBUG_ERROR("Failed to find mp4v encoder");
+ return -2;
+ }
+ mp4v->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(mp4v->encoder.context);
+
+ mp4v->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ mp4v->encoder.context->time_base.num = 1;
+ mp4v->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(mp4v)->fps;
+ mp4v->encoder.context->width = TMEDIA_CODEC_VIDEO(mp4v)->width;
+ mp4v->encoder.context->height = TMEDIA_CODEC_VIDEO(mp4v)->height;
+ mp4v->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ bitRate = 64000.f;
+ break;
+ case tmedia_bl_medium:
+ bitRate = 128000.f;
+ break;
+ case tmedia_bl_hight:
+ bitRate = 250000.f;
+ break;
+ }
+
+ mp4v->encoder.context->thread_count = 1;
+ mp4v->encoder.context->rtp_payload_size = MP4V_RTP_PAYLOAD_SIZE;
+ mp4v->encoder.context->opaque = tsk_null;
+ mp4v->encoder.context->bit_rate = (int) (bitRate * 0.80f);
+ mp4v->encoder.context->bit_rate_tolerance = (int) (bitRate * 0.20f);
+ mp4v->encoder.context->profile = mp4v->profile>>4;
+ mp4v->encoder.context->level = mp4v->profile & 0x0F;
+ mp4v->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(mp4v)->fps*2; // each 2 seconds
+ mp4v->encoder.context->max_b_frames = 0;
+ mp4v->encoder.context->b_frame_strategy = 1;
+ mp4v->encoder.context->flags |= CODEC_FLAG_AC_PRED;
+
+ // Picture (YUV 420)
+ if(!(mp4v->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create MP4V-ES encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(mp4v->encoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, mp4v->encoder.context->width, mp4v->encoder.context->height);
+ if(!(mp4v->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate MP4V-ES encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(mp4v->encoder.context, mp4v->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open MP4V-ES encoder");
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ if(!(mp4v->decoder.codec = avcodec_find_decoder(CODEC_ID_MPEG4))){
+ TSK_DEBUG_ERROR("Failed to find MP4V-ES decoder");
+ }
+ mp4v->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(mp4v->decoder.context);
+
+ mp4v->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ mp4v->decoder.context->width = TMEDIA_CODEC_VIDEO(mp4v)->width;
+ mp4v->decoder.context->height = TMEDIA_CODEC_VIDEO(mp4v)->height;
+
+ // Picture (YUV 420)
+ if(!(mp4v->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(mp4v->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, mp4v->decoder.context->width, mp4v->decoder.context->height);
+ if(!(mp4v->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ if(!(mp4v->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(mp4v->decoder.context, mp4v->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open MP4V-ES decoder");
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_mp4ves_close(tmedia_codec_t* self)
+{
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+
+ if(!mp4v){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ //
+ // Encoder
+ //
+ if(mp4v->encoder.context){
+ avcodec_close(mp4v->encoder.context);
+ av_free(mp4v->encoder.context);
+ mp4v->encoder.context = tsk_null;
+ }
+ if(mp4v->encoder.picture){
+ av_free(mp4v->encoder.picture);
+ }
+ if(mp4v->encoder.buffer){
+ TSK_FREE(mp4v->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(mp4v->decoder.context){
+ avcodec_close(mp4v->decoder.context);
+ if(mp4v->decoder.context->extradata){
+ TSK_FREE(mp4v->decoder.context->extradata);
+ mp4v->decoder.context->extradata_size = 0;
+ }
+ av_free(mp4v->decoder.context);
+ mp4v->decoder.context = tsk_null;
+ }
+ if(mp4v->decoder.picture){
+ av_free(mp4v->decoder.picture);
+ mp4v->decoder.picture = tsk_null;
+ }
+ if(mp4v->decoder.accumulator){
+ TSK_FREE(mp4v->decoder.accumulator);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_mp4ves_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)mp4v->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, mp4v->encoder.context->width, mp4v->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+ /* Flip */
+#if FLIP_ENCODED_PICT
+ tdav_converter_video_flip(mp4v->encoder.picture, mp4v->encoder.context->height);
+#endif
+
+ mp4v->encoder.picture->pts = AV_NOPTS_VALUE;
+ ret = avcodec_encode_video(mp4v->encoder.context, mp4v->encoder.buffer, size, mp4v->encoder.picture);
+ if(ret > 0){
+ tdav_codec_mp4ves_encap(mp4v, mp4v->encoder.buffer, (tsk_size_t)ret);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_mp4ves_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ if(!self || !in_data || !in_size || !out_data || !mp4v->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // get expected size
+ xsize = avpicture_get_size(mp4v->decoder.context->pix_fmt, mp4v->decoder.context->width, mp4v->decoder.context->height);
+
+ /* Packet lost? */
+ if(mp4v->decoder.last_seq != (rtp_hdr->seq_num - 1) && mp4v->decoder.last_seq){
+ if(mp4v->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ mp4v->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((mp4v->decoder.accumulator_pos + in_size) <= xsize){
+ memcpy(&((uint8_t*)mp4v->decoder.accumulator)[mp4v->decoder.accumulator_pos], in_data, in_size);
+ mp4v->decoder.accumulator_pos += in_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ mp4v->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size <xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ mp4v->decoder.accumulator_pos = 0;
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ av_init_packet(&packet);
+ packet.size = mp4v->decoder.accumulator_pos;
+ packet.data = mp4v->decoder.accumulator;
+ ret = avcodec_decode_video2(mp4v->decoder.context, mp4v->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+ /* flip */
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(mp4v->decoder.picture, mp4v->decoder.context->height);
+#endif
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)mp4v->decoder.picture, mp4v->decoder.context->pix_fmt, mp4v->decoder.context->width, mp4v->decoder.context->height,
+ *out_data, retsize);
+ }
+ /* in all cases: reset accumulator */
+ mp4v->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_mp4ves_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tsk_params_L_t* params = tsk_null;
+ tdav_codec_mp4ves_t *mp4v = (tdav_codec_mp4ves_t *)codec;
+
+ if(!mp4v){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ /* e.g. profile-level-id=1; xx=yy */
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ int val_int;
+ if((val_int = tsk_params_get_param_value_as_int(params, "profile-level-id")) != -1){
+ TSK_DEBUG_INFO("Proposed profile-level-id=%d", val_int);
+ mp4v->profile = val_int; // FIXME: Take the remote profile-level-id even if the bandwidth level doesn't match
+ }
+ }
+
+ TSK_OBJECT_SAFE_FREE(params);
+
+ return tsk_true;
+}
+
+char* tdav_codec_mp4ves_fmtp_get(const tmedia_codec_t* self)
+{
+ tdav_codec_mp4ves_t *mp4v = (tdav_codec_mp4ves_t *)self;
+ char* fmtp = tsk_null;
+
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ mp4v->profile = Simple_Profile_Level_1;
+ break;
+ case tmedia_bl_medium:
+ mp4v->profile = Simple_Profile_Level_2;
+ break;
+ case tmedia_bl_hight:
+ mp4v->profile = Simple_Profile_Level_3;
+ break;
+ }
+
+ tsk_sprintf(&fmtp, "profile-level-id=%d", mp4v->profile);
+ return fmtp;
+}
+
+
+/* ============ Internal functions ================= */
+static void tdav_codec_mp4ves_encap(tdav_codec_mp4ves_t* mp4v, const uint8_t* pdata, tsk_size_t size)
+{
+ uint32_t scode; // start code
+
+ if(size <= 4/*32bits: start code size*/){
+ TSK_DEBUG_ERROR("Too short");
+ return;
+ }
+ // first 32bits
+ scode = tnet_htonl_2(pdata);
+
+/* RFC 3016 - 3.3 Examples of packetized MPEG-4 Visual bitstream
+
+ VS= Visual Object Sequence
+ VO= Visual Object
+ VOL= Visual Object Layer
+ VOP= Visual Object Plane
+ GOV= Group of Visual Object Plane
+ VP= Video Plane
+
+ +------+------+------+------+
+(a) | RTP | VS | VO | VOL |
+ |header|header|header|header|
+ +------+------+------+------+
+
+ +------+------+------+------+------------+
+(b) | RTP | VS | VO | VOL |Video Packet|
+ |header|header|header|header| |
+ +------+------+------+------+------------+
+
+ +------+-----+------------------+
+(c) | RTP | GOV |Video Object Plane|
+ |header| | |
+ +------+-----+------------------+
+
+ +------+------+------------+ +------+------+------------+
+(d) | RTP | VOP |Video Packet| | RTP | VP |Video Packet|
+ |header|header| (1) | |header|header| (2) |
+ +------+------+------------+ +------+------+------------+
+
+ +------+------+------------+------+------------+------+------------+
+(e) | RTP | VP |Video Packet| VP |Video Packet| VP |Video Packet|
+ |header|header| (1) |header| (2) |header| (3) |
+ +------+------+------------+------+------------+------+------------+
+
+ +------+------+------------+ +------+------------+
+(f) | RTP | VOP |VOP fragment| | RTP |VOP fragment|
+ |header|header| (1) | |header| (2) | ___
+ +------+------+------------+ +------+------------+
+
+ Figure 2 - Examples of RTP packetized MPEG-4 Visual bitstream
+*/
+
+/* RFC 3016 - 3.2 Fragmentation of MPEG-4 Visual bitstream
+
+ A fragmented MPEG-4 Visual bitstream is mapped directly onto the RTP
+ payload without any addition of extra header fields or any removal of
+ Visual syntax elements. The Combined Configuration/Elementary
+ streams mode is used.
+
+ In the following, header means one of the following:
+
+ - Configuration information (Visual Object Sequence Header, Visual
+ Object Header and Video Object Layer Header)
+ - visual_object_sequence_end_code
+ - The header of the entry point function for an elementary stream
+ (Group_of_VideoObjectPlane() or the header of VideoObjectPlane(),
+ video_plane_with_short_header(), MeshObject() or FaceObject())
+ - The video packet header (video_packet_header() excluding
+ next_resync_marker())
+ - The header of gob_layer()
+ See 6.2.1 "Start codes" of ISO/IEC 14496-2 [2][9][4] for the
+ definition of the configuration information and the entry point
+ functions.
+*/
+
+ switch(scode){
+ case visual_object_sequence_start_code:
+ case visual_object_start_code:
+ case user_data_start_code:
+ case video_object_layer_start_code:
+ case group_of_vop_start_code:
+ case vop_start_code:
+ {
+ register uint32_t i, last_index = 0;
+ int startcode = 0xffffffff;
+
+ if(scode == visual_object_sequence_start_code && size >=5){
+ uint8_t profile_and_level_indication = pdata[4]; /* IEC 14496-2: 6.3.2 Visual Object Sequence and Visual Object */
+ TSK_DEBUG_INFO("profile_and_level_indication=%d", profile_and_level_indication);
+ }
+
+ if(size < MP4V_RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+
+ for(i = 4; i<(size - 4); i++){
+ startcode = (startcode <<8) | pdata[i];
+ switch(startcode){
+ case visual_object_sequence_start_code:
+ case group_of_vop_start_code:
+ case vop_start_code:
+ tdav_codec_mp4ves_rtp_callback(mp4v, pdata + last_index, (i - last_index), (last_index == size));
+ last_index = i;
+ }
+ }
+last:
+ if(last_index < size){
+ tdav_codec_mp4ves_rtp_callback(mp4v, pdata + last_index, (size - last_index), tsk_true);
+ }
+ break;
+ }
+ default:
+ TSK_DEBUG_ERROR("%x is an invalide start code", scode);
+ break;
+ }
+}
+
+static void tdav_codec_mp4ves_rtp_callback(tdav_codec_mp4ves_t *mp4v, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(mp4v)->callback){
+ TMEDIA_CODEC_VIDEO(mp4v)->callback(TMEDIA_CODEC_VIDEO(mp4v)->callback_data, data, size, (3003* (30/TMEDIA_CODEC_VIDEO(mp4v)->fps)), marker);
+ }
+}
+
+
+/* ============ MP4V-ES Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_mp4ves_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_mp4ves_t *mp4v = self;
+ if(mp4v){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ mp4v->profile = DEFAULT_PROFILE_LEVEL_ID;
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_mp4ves_dtor(tsk_object_t * self)
+{
+ tdav_codec_mp4ves_t *mp4v = self;
+ if(mp4v){
+ /* deinit base */
+ tmedia_codec_video_deinit(self); // will close the codec if opened
+ /* deinit self */
+ TSK_FREE(mp4v->rtp.ptr);
+ mp4v->rtp.size = 0;
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_mp4ves_def_s =
+{
+ sizeof(tdav_codec_mp4ves_t),
+ tdav_codec_mp4ves_ctor,
+ tdav_codec_mp4ves_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_mp4ves_plugin_def_s =
+{
+ &tdav_codec_mp4ves_def_s,
+
+ tmedia_video,
+ "MP4V-ES",
+ "MP4V-ES Codec",
+ TMEDIA_CODEC_FORMAT_MP4V_ES,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_mp4ves_open,
+ tdav_codec_mp4ves_close,
+ tdav_codec_mp4ves_encode,
+ tdav_codec_mp4ves_decode,
+ tdav_codec_mp4ves_fmtp_match,
+ tdav_codec_mp4ves_fmtp_get,
+ tdav_codec_mp4ves_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_mp4ves_plugin_def_t = &tdav_codec_mp4ves_plugin_def_s;
+
+
+#endif /* HAVE_FFMPEG */
+
diff --git a/tinyDAV/src/codecs/msrp/tdav_codec_msrp.c b/tinyDAV/src/codecs/msrp/tdav_codec_msrp.c
new file mode 100644
index 0000000..c845ec3
--- /dev/null
+++ b/tinyDAV/src/codecs/msrp/tdav_codec_msrp.c
@@ -0,0 +1,110 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_msrp.c
+ * @brief The Message Session Relay Protocol (MSRP) fake codec.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/msrp/tdav_codec_msrp.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ MSRP Plugin interface ================= */
+#define tdav_codec_msrp_open tsk_null
+#define tdav_codec_msrp_close tsk_null
+#define tdav_codec_msrp_fmtp_get tsk_null
+#define tdav_codec_msrp_fmtp_get tsk_null
+#define tdav_codec_msrp_fmtp_set tsk_null
+#define tdav_codec_msrp_encode tsk_null
+#define tdav_codec_msrp_decode tsk_null
+
+tsk_bool_t tdav_codec_msrp_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+//
+// MSRP Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_msrp_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_msrp_t *msrp = self;
+ if(msrp){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_msrp_dtor(tsk_object_t * self)
+{
+ tdav_codec_msrp_t *msrp = self;
+ if(msrp){
+ /* deinit base */
+ tmedia_codec_msrp_deinit(msrp);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_msrp_def_s =
+{
+ sizeof(tdav_codec_msrp_t),
+ tdav_codec_msrp_ctor,
+ tdav_codec_msrp_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_msrp_plugin_def_s =
+{
+ &tdav_codec_msrp_def_s,
+
+ tmedia_msrp,
+ "message",
+ "MSRP fake codec",
+ TMEDIA_CODEC_FORMAT_MSRP,
+ tsk_false,
+ 0, // rate
+
+ /* audio */
+ {0},
+
+ /* video */
+ {0},
+
+ tdav_codec_msrp_open,
+ tdav_codec_msrp_close,
+ tdav_codec_msrp_encode,
+ tdav_codec_msrp_decode,
+ tdav_codec_msrp_fmtp_match,
+ tdav_codec_msrp_fmtp_get,
+ tdav_codec_msrp_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_msrp_plugin_def_t = &tdav_codec_msrp_plugin_def_s;
diff --git a/tinyDAV/src/codecs/speex/tdav_codec_speex.c b/tinyDAV/src/codecs/speex/tdav_codec_speex.c
new file mode 100644
index 0000000..93565aa
--- /dev/null
+++ b/tinyDAV/src/codecs/speex/tdav_codec_speex.c
@@ -0,0 +1,282 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_speex.c
+ * @brief Speex codecs
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/speex/tdav_codec_speex.h"
+
+#if HAVE_LIB_SPEEX
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ Common ================= */
+int tdav_codec_speex_init(tdav_codec_speex_t* self, tdav_codec_speex_type_t type);
+int tdav_codec_speex_deinit(tdav_codec_speex_t* self);
+
+/* ============ iLBC Plugin interface ================= */
+
+#define tdav_codec_speex_fmtp_set tsk_null
+
+int tdav_codec_speex_open(tmedia_codec_t* self)
+{
+ static int quality = 6;
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+ tsk_size_t size = 0;
+
+ switch(speex->type){
+ case tdav_codec_speex_type_nb:
+ speex->encoder.state = speex_encoder_init(&speex_nb_mode);
+ speex->decoder.state = speex_decoder_init(&speex_nb_mode);
+
+ speex_decoder_ctl(speex->decoder.state, SPEEX_GET_FRAME_SIZE, &speex->decoder.size);
+ speex->decoder.size = (speex->decoder.size ? speex->decoder.size : 160) * sizeof(spx_int16_t);
+ if(!(speex->decoder.buffer = tsk_calloc(speex->decoder.size, 1))){
+ speex->decoder.size = 0;
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return -3;
+ }
+
+ speex_encoder_ctl(speex->encoder.state, SPEEX_SET_QUALITY, &quality);
+ speex_encoder_ctl(speex->encoder.state, SPEEX_GET_FRAME_SIZE, &speex->encoder.size);
+ if(!speex->encoder.size){
+ speex->encoder.size = 20;
+ }
+ break;
+ default:
+ TSK_DEBUG_ERROR("Not implemented");
+ return -2;
+ }
+
+ speex_bits_init(&speex->encoder.bits);
+ speex_bits_init(&speex->decoder.bits);
+ speex_bits_reset(&speex->encoder.bits);
+ speex_bits_reset(&speex->decoder.bits);
+
+ return 0;
+}
+
+int tdav_codec_speex_close(tmedia_codec_t* self)
+{
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+
+
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_speex_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+ tsk_size_t outsize = 0;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ speex_bits_reset(&speex->encoder.bits);
+ speex_encode_int(speex->encoder.state, (spx_int16_t*)in_data, &speex->encoder.bits);
+
+ if(*out_max_size <speex->encoder.size){
+ if((*out_data = tsk_realloc(*out_data, speex->encoder.size))){
+ *out_max_size = speex->encoder.size;
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+
+ outsize = speex_bits_write(&speex->encoder.bits, *out_data, speex->encoder.size);
+
+ return outsize;
+}
+
+tsk_size_t tdav_codec_speex_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ int ret;
+ tsk_size_t out_size = 0;
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* initializes the bit-stream */
+ speex_bits_read_from(&speex->decoder.bits, (char*)in_data, in_size);
+
+ do{
+ /* performs decode() */
+ if((ret = speex_decode_int(speex->decoder.state, &speex->decoder.bits, speex->decoder.buffer))){
+ TSK_DEBUG_ERROR("Failed to decode the buffer. retcode=%d", ret);
+ break;
+ }
+
+ if(*out_max_size <(out_size + speex->decoder.size)){
+ if((*out_data = tsk_realloc(*out_data, (out_size + speex->decoder.size)))){
+ *out_max_size = (out_size + speex->decoder.size);
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+
+ /* copy output buffer */
+ memcpy(&((uint8_t*)*out_data)[out_size], speex->decoder.buffer, speex->decoder.size);
+ out_size += speex->decoder.size;
+ }
+ while(speex_bits_remaining(&speex->decoder.bits) >= 5);
+
+
+ return out_size;
+}
+
+char* tdav_codec_speex_fmtp_get(const tmedia_codec_t* codec)
+{
+ return tsk_null;
+}
+
+tsk_bool_t tdav_codec_speex_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ return tsk_true;
+}
+
+
+//
+// Speex Narrow Band Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_speex_nb_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_speex_t *speex = self;
+ if(speex){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_speex_init(speex, tdav_codec_speex_type_nb);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_speex_nb_dtor(tsk_object_t * self)
+{
+ tdav_codec_speex_t *speex = self;
+ if(speex){
+ /* deinit base */
+ tmedia_codec_audio_deinit(speex);
+ /* deinit self */
+ tdav_codec_speex_deinit(speex);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_speex_nb_def_s =
+{
+ sizeof(tdav_codec_speex_t),
+ tdav_codec_speex_nb_ctor,
+ tdav_codec_speex_nb_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_speex_nb_plugin_def_s =
+{
+ &tdav_codec_speex_nb_def_s,
+
+ tmedia_audio,
+ "SPEEX",
+ "Speex Narrow Band Codec",
+ TMEDIA_CODEC_FORMAT_SPEEX_NB,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_speex_open,
+ tdav_codec_speex_close,
+ tdav_codec_speex_encode,
+ tdav_codec_speex_decode,
+ tdav_codec_speex_fmtp_match,
+ tdav_codec_speex_fmtp_get,
+ tdav_codec_speex_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_speex_nb_plugin_def_t = &tdav_codec_speex_nb_plugin_def_s;
+
+
+//
+// Common functions
+//
+int tdav_codec_speex_init(tdav_codec_speex_t* self, tdav_codec_speex_type_t type)
+{
+ if(self){
+ self->type = type;
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+}
+
+int tdav_codec_speex_deinit(tdav_codec_speex_t* self)
+{
+ if(self){
+ if(self->decoder.state){
+ speex_decoder_destroy(self->decoder.state);
+ self->decoder.state = tsk_null;
+ }
+ speex_bits_destroy(&self->decoder.bits);
+ if(self->decoder.buffer){
+ TSK_FREE(self->decoder.buffer);
+ self->decoder.size = 0;
+ }
+
+ if(self->encoder.state){
+ speex_encoder_destroy(self->encoder.state);
+ self->encoder.state = tsk_null;
+ }
+ speex_bits_destroy(&self->encoder.bits);
+ self->encoder.size = 0;
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+}
+
+#endif /* HAVE_LIB_SPEEX */
diff --git a/tinyDAV/src/codecs/theora/tdav_codec_theora.c b/tinyDAV/src/codecs/theora/tdav_codec_theora.c
new file mode 100644
index 0000000..67fb3e6
--- /dev/null
+++ b/tinyDAV/src/codecs/theora/tdav_codec_theora.c
@@ -0,0 +1,707 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_theora.c
+ * @brief Theora codec plugin
+ * RTP payloader/depayloader follows draft-barbato-avt-rtp-theora-01.
+ * For more information about Theora, http://www.theora.org/doc/Theora.pdf.
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/codecs/theora/tdav_codec_theora.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_time.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define THEORA_RTP_PAYLOAD_SIZE 900
+#define THEORA_PAYLOAD_HEADER_SIZE 4 /* 2.2. Payload Header */
+#define THEORA_PAYLOAD_LENGTH_SIZE 2 /* 2.2. Payload Header */
+#define THEORA_IDENT_HEADER_SIZE 42 /* 6.2 Identification Header Decode */
+#define THEORA_CONF_SEND_COUNT 10 /* at 250ms, 500ms, 1000ms, .... */
+
+/* 2.2. Payload Header filed 'F'*/
+typedef enum theora_frag_type_e{
+ Not_Fragmented = 0,
+ Start_Fragment = 1,
+ Continuation_Fragment = 2,
+ End_Fragment = 3,
+}
+theora_frag_type_t;
+
+/* 2.2. Payload Header field 'TDT'*/
+typedef enum theora_datatype_e{
+ Raw_Theora_payload = 0,
+ Theora_Packed_Configuration_payload = 1,
+ Legacy_Theora_Comment_payload = 2,
+ Reserved = 3,
+}
+theora_datatype_t;
+
+static int tdav_codec_theora_send(tdav_codec_theora_t* self, const uint8_t* data, tsk_size_t size, theora_datatype_t tdt);
+static void tdav_codec_theora_rtp_callback(tdav_codec_theora_t *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+
+static void tdav_codec_theora_encap(tdav_codec_theora_t* theora, const uint8_t* pdata, tsk_size_t size);
+
+/* ============ Theora Plugin interface functions ================= */
+
+#define tdav_codec_theora_fmtp_set tsk_null
+
+int tdav_codec_theora_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+ float bitRate = 64000.f;
+
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+
+ if(!theora){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ //
+ // Encoder
+ //
+ if(!(theora->encoder.codec = avcodec_find_encoder(CODEC_ID_THEORA))){
+ TSK_DEBUG_ERROR("Failed to find Theora encoder");
+ return -2;
+ }
+ theora->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(theora->encoder.context);
+
+ theora->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ theora->encoder.context->time_base.num = 1;
+ theora->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(theora)->fps;
+ theora->encoder.context->width = TMEDIA_CODEC_VIDEO(theora)->width;
+ theora->encoder.context->height = TMEDIA_CODEC_VIDEO(theora)->height;
+
+ //theora->encoder.context->mb_qmin = theora->encoder.context->qmin = 4;
+ //theora->encoder.context->mb_qmax = theora->encoder.context->qmax = 4;
+ theora->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ bitRate = 64000.f;
+ break;
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ bitRate = 128000.f;
+ break;
+ }
+
+ theora->encoder.context->thread_count = 1;
+ theora->encoder.context->rtp_payload_size = THEORA_RTP_PAYLOAD_SIZE;
+ theora->encoder.context->opaque = tsk_null;
+ theora->encoder.context->bit_rate = (int) (bitRate * 0.80f);
+ theora->encoder.context->bit_rate_tolerance = (int) (bitRate * 0.20f);
+ theora->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(theora)->fps*2; // each 2 seconds
+
+ // Picture (YUV 420)
+ if(!(theora->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(theora->encoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, theora->encoder.context->width, theora->encoder.context->height);
+ if(!(theora->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(theora->encoder.context, theora->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open Theora encoder");
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ if(!(theora->decoder.codec = avcodec_find_decoder(CODEC_ID_THEORA))){
+ TSK_DEBUG_ERROR("Failed to find Theora decoder");
+ return -2;
+ }
+ theora->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(theora->decoder.context);
+
+ theora->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ theora->decoder.context->width = TMEDIA_CODEC_VIDEO(theora)->width;
+ theora->decoder.context->height = TMEDIA_CODEC_VIDEO(theora)->height;
+
+ // Picture (YUV 420)
+ if(!(theora->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(theora->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, theora->decoder.context->width, theora->decoder.context->height);
+ if(!(theora->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ if(!(theora->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ //if((ret = avcodec_open(theora->decoder.context, theora->decoder.codec)) < 0){
+ // TSK_DEBUG_ERROR("Failed to open Theora decoder");
+ // return ret;
+ //}
+
+ return 0;
+}
+
+int tdav_codec_theora_close(tmedia_codec_t* self)
+{
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+
+ if(!theora){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ //
+ // Encoder
+ //
+ if(theora->encoder.context){
+ avcodec_close(theora->encoder.context);
+ av_free(theora->encoder.context);
+ theora->encoder.context = tsk_null;
+ }
+ if(theora->encoder.picture){
+ av_free(theora->encoder.picture);
+ }
+ if(theora->encoder.buffer){
+ TSK_FREE(theora->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(theora->decoder.context){
+ avcodec_close(theora->decoder.context);
+ if(theora->decoder.context->extradata){
+ TSK_FREE(theora->decoder.context->extradata);
+ theora->decoder.context->extradata_size = 0;
+ }
+ av_free(theora->decoder.context);
+ theora->decoder.context = tsk_null;
+ }
+ if(theora->decoder.picture){
+ av_free(theora->decoder.picture);
+ theora->decoder.picture = tsk_null;
+ }
+ if(theora->decoder.accumulator){
+ TSK_FREE(theora->decoder.accumulator);
+ }
+
+ return 0;
+}
+
+//#include "tsk_time.h"
+tsk_size_t tdav_codec_theora_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)theora->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, theora->encoder.context->width, theora->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+ /* Flip */
+#if FLIP_ENCODED_PICT
+ tdav_converter_video_flip(theora->encoder.picture, theora->encoder.context->height);
+#endif
+
+ // Encode data
+ //theora->encoder.picture->pts = tsk_time_epoch();
+ theora->encoder.picture->pts = AV_NOPTS_VALUE;
+ //theora->encoder.picture->pict_type = FF_I_TYPE;
+ ret = avcodec_encode_video(theora->encoder.context, theora->encoder.buffer, size, theora->encoder.picture);
+ if(ret > 0){
+ tdav_codec_theora_encap(theora, theora->encoder.buffer, (tsk_size_t)ret);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_theora_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ const uint8_t* pdata = in_data;
+ int pkts;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ //tsk_size_t hdr_size;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || (in_size<(THEORA_PAYLOAD_HEADER_SIZE + THEORA_PAYLOAD_LENGTH_SIZE)) || !out_data || !theora->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Packet lost? */
+ if(theora->decoder.last_seq != (rtp_hdr->seq_num - 1) && theora->decoder.last_seq){
+ if(theora->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ theora->decoder.last_seq = rtp_hdr->seq_num;
+
+ xsize = avpicture_get_size(theora->decoder.context->pix_fmt, theora->decoder.context->width, theora->decoder.context->height);
+
+ /* 2.2. Payload Header
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Configuration Ident | F |TDT|# pkts.|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ /* 2.3. Payload Data
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Payload Length | Theora Data ..
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ pkts = (pdata[3] & 0x0F);
+ pay_ptr = (pdata + THEORA_PAYLOAD_HEADER_SIZE);
+
+ do{ /* pkts=0 for fragmented packets */
+
+ pay_size = pay_ptr[0], pay_size<<=8, pay_size |= pay_ptr[1]; /* Big Endian read */
+ pay_ptr += THEORA_PAYLOAD_LENGTH_SIZE;
+ /* check size validity */
+ if((pay_ptr + pay_size)>(pdata + in_size)){
+ TSK_DEBUG_ERROR("Too short");
+ break;
+ }
+
+ switch((pdata[3]>>4) & 0x03){
+ case Raw_Theora_payload:
+ { /* ====== Theora data (2.2. Payload Header, 2.3. Payload Data) ====== */
+ /* append buffer */
+ if((int)(theora->decoder.accumulator_pos + pay_size) <= xsize){
+ memcpy(&((uint8_t*)theora->decoder.accumulator)[theora->decoder.accumulator_pos], pay_ptr, pay_size);
+ theora->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ theora->decoder.accumulator_pos = 0;
+ break;
+ }
+ /* only take care if last packet (What about the RTP marker?) */
+ if(((pdata[3]>>6) == Not_Fragmented || (pdata[3]>>6) == End_Fragment /*|| rtp_hdr->marker*/) && theora->decoder.opened){
+ AVPacket packet;
+ /* Perform decoding */
+ av_init_packet(&packet);
+ packet.size = theora->decoder.accumulator_pos;
+ packet.data = theora->decoder.accumulator;
+ ret = avcodec_decode_video2(theora->decoder.context, theora->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(theora->decoder.picture, theora->decoder.context->height);
+#endif
+ /* allocate buffer */
+ if(*out_max_size <xsize){
+ if((*out_data = tsk_realloc(*out_data, xsize))){
+ *out_max_size = xsize;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)theora->decoder.picture, theora->decoder.context->pix_fmt, theora->decoder.context->width, theora->decoder.context->height,
+ *out_data, retsize);
+ }
+ /* in all cases: reset accumulator */
+ theora->decoder.accumulator_pos = 0;
+ }
+ break;
+ }
+ case Theora_Packed_Configuration_payload:
+ {/* ====== Configuration packet (3.1.1. Packed Configuration) ====== */
+ static uint8_t __theora_comment_hdr[] = {0x81, 0x74, 0x68, 0x65, 0x6F, 0x72, 0x61,
+ 0x00, 0x00, 0x00, 0x08, /* 4-byte length */
+ 'd', 'o', 'u', 'b', 'a', 'n', 'g', 'o', /* UTF-8 encoded string */
+ };
+
+ /* http://www.theora.org/doc/Theora.pdf - Chapter 6
+ A Theora bitstream begins with three header packets. The header packets
+ are, in order, the identifcation header, the comment header, and the setup
+ header. All are required for decode compliance. An end-of-packet condition
+ encountered while decoding the identification or setup header packets renders
+ the stream undecodable. An end-of-packet condition encountered while decode
+ the comment header is a non-fatal error condition, and MAY be ignored by a
+ decoder.
+
+ Decode continues according to HEADERTYPE. The identification header
+ is type 0x80, the comment header is type 0x81, and the setup header is type
+ 0x82.
+ */
+ /*TSK_DEBUG_INFO("Theora_Packed_Configuration_payload");*/
+
+ if(!theora->decoder.opened /*|| (conf_ident changed)*/){
+ if(!theora->decoder.conf_pkt){
+ theora->decoder.conf_pkt = tsk_buffer_create(pay_ptr, pay_size);
+ }
+ else{
+ tsk_buffer_append(theora->decoder.conf_pkt, pay_ptr, pay_size);
+ }
+
+ if((pdata[3]>>6) == Not_Fragmented || (pdata[3]>>6) == End_Fragment || rtp_hdr->marker){
+ if(theora->decoder.conf_pkt->size > THEORA_IDENT_HEADER_SIZE){
+ const uint8_t* conf_ptr = theora->decoder.conf_pkt->data;
+ int setup_size = theora->decoder.conf_pkt->size - THEORA_IDENT_HEADER_SIZE;
+ int extradata_size = (2 + THEORA_IDENT_HEADER_SIZE) + (2 + setup_size) + (2 + sizeof(__theora_comment_hdr));
+ if(conf_ptr[0] == 0x80 && conf_ptr[THEORA_IDENT_HEADER_SIZE] == 0x82){ /* Do not check for 't'h'e'o'r'a' */
+ /* save configration identification */
+ memcpy(theora->decoder.conf_ident, &pdata[0], sizeof(theora->decoder.conf_ident));
+ if(theora->decoder.context->extradata){
+ TSK_FREE(theora->decoder.context->extradata);
+ }
+ if((theora->decoder.context->extradata = tsk_calloc(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE, 1))){
+ int index = 0;
+ /* Because of endianess pb. do not use uint16_t or uint32_t */
+ theora->decoder.context->extradata[index++] = 0x00;
+ theora->decoder.context->extradata[index++] = THEORA_IDENT_HEADER_SIZE;
+ memcpy(&theora->decoder.context->extradata[index], &conf_ptr[0], THEORA_IDENT_HEADER_SIZE);
+ index += THEORA_IDENT_HEADER_SIZE;
+
+ theora->decoder.context->extradata[index++] = (setup_size >>8) & 0xFF;
+ theora->decoder.context->extradata[index++] = (setup_size & 0xFF);
+ memcpy(&theora->decoder.context->extradata[index], &conf_ptr[THEORA_IDENT_HEADER_SIZE], setup_size);
+ index+=setup_size;
+
+ theora->decoder.context->extradata[index++] = 0x00;
+ theora->decoder.context->extradata[index++] = sizeof(__theora_comment_hdr);/* <0xFF */
+ memcpy(&theora->decoder.context->extradata[index], __theora_comment_hdr, sizeof(__theora_comment_hdr));
+
+ theora->decoder.context->extradata_size = extradata_size;
+
+ if((ret = avcodec_open(theora->decoder.context, theora->decoder.codec)) == 0){
+ theora->decoder.opened = tsk_true;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to open theora decoder %d", ret);
+ TSK_FREE(theora->decoder.context->extradata);
+ theora->decoder.context->extradata_size = 0;
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid configuration packet");
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Too short");
+ }
+ tsk_buffer_cleanup(theora->decoder.conf_pkt);
+ }
+ }
+ break;
+ }
+ case Legacy_Theora_Comment_payload:
+ /*TSK_DEBUG_INFO("Legacy_Theora_Comment_payload");*/
+ break;
+ case Reserved:
+ /*TSK_DEBUG_INFO("Reserved");*/
+ break;
+ }
+ }
+ while(--pkts>0);
+
+
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_theora_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tsk_bool_t ret = tsk_true; // accept decoding any size
+ tsk_params_L_t* params = tsk_null;
+ tmedia_codec_video_t* theora = (tmedia_codec_video_t*)codec;
+
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ int width = tsk_params_get_param_value_as_int(params, "width");
+ int height = tsk_params_get_param_value_as_int(params, "height");
+
+ // Set Encoding size
+ switch(codec->bl){
+ case tmedia_bl_low:
+ default:
+ if(width<=176 && height<=144){
+ theora->width = width, theora->height = height;
+ }
+ else{
+ theora->width = 176, theora->height = 144;
+ }
+ break;
+
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ if(width<=352 && height<=288){
+ theora->width = width, theora->height = height;
+ }
+ else{
+ theora->width = 352, theora->height = 288;
+ }
+ break;
+ }
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+
+ return ret;
+}
+
+char* tdav_codec_theora_fmtp_get(const tmedia_codec_t* self)
+{
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ return tsk_strdup("sampling=YCbCr-4:2:0; width=176; height=144");
+ break;
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ return tsk_strdup("sampling=YCbCr-4:2:0; width=352; height=288");
+ break;
+ }
+}
+
+
+
+/* constructor */
+static tsk_object_t* tdav_codec_theora_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_theora_t *theora = self;
+ if(theora){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ //TSK_OBJECT_SAFE_FREE(theora->conf_data);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_theora_dtor(tsk_object_t * self)
+{
+ tdav_codec_theora_t *theora = self;
+ if(theora){
+ /* deinit base */
+ tmedia_codec_video_deinit(self);
+ /* deinit self */
+ TSK_OBJECT_SAFE_FREE(theora->decoder.conf_pkt);
+ TSK_FREE(theora->rtp.ptr);
+ theora->rtp.size = 0;
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_theora_def_s =
+{
+ sizeof(tdav_codec_theora_t),
+ tdav_codec_theora_ctor,
+ tdav_codec_theora_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_theora_plugin_def_s =
+{
+ &tdav_codec_theora_def_s,
+
+ tmedia_video,
+ "theora",
+ "Theora Codec",
+ TMEDIA_CODEC_FORMAT_THEORA,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_theora_open,
+ tdav_codec_theora_close,
+ tdav_codec_theora_encode,
+ tdav_codec_theora_decode,
+ tdav_codec_theora_fmtp_match,
+ tdav_codec_theora_fmtp_get,
+ tdav_codec_theora_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_theora_plugin_def_t = &tdav_codec_theora_plugin_def_s;
+
+
+
+static void tdav_codec_theora_encap(tdav_codec_theora_t* theora, const uint8_t* pdata, tsk_size_t size)
+{
+ if((theora->encoder.conf_count < THEORA_CONF_SEND_COUNT) && theora->encoder.context && theora->encoder.context->extradata){
+ if((theora->encoder.conf_last + (250 *theora->encoder.conf_count)) < tsk_time_epoch()){
+ int hdr_size, i, exd_size = theora->encoder.context->extradata_size, conf_pkt_size = 0;
+ uint8_t *conf_pkt_ptr = tsk_null, *exd_ptr = theora->encoder.context->extradata;
+ for(i=0; i<3 && exd_size; i++){
+ hdr_size = exd_ptr[0], hdr_size<<=8, hdr_size |= exd_ptr[1];
+ exd_ptr += 2;
+ exd_size -= 2;
+ if(hdr_size > exd_size){
+ TSK_DEBUG_ERROR("Invalid extradata");
+ TSK_FREE(conf_pkt_ptr);
+ conf_pkt_size = 0;
+ }
+
+ if(exd_ptr[0] == 0x80 || exd_ptr[0] == 0x82){ /* Ignore 'comment' which is equal to '0x81' */
+ if((conf_pkt_ptr = tsk_realloc(conf_pkt_ptr, (conf_pkt_size + hdr_size)))){
+ memcpy((conf_pkt_ptr + conf_pkt_size), exd_ptr, hdr_size);
+ conf_pkt_size += hdr_size;
+ }
+ }
+ exd_size -= hdr_size;
+ exd_ptr += hdr_size;
+ }
+
+ /* Send the conf pack */
+ if(conf_pkt_ptr && conf_pkt_size){
+ /*TSK_DEBUG_INFO("Sending Configuration Packet");*/
+ tdav_codec_theora_send(theora, conf_pkt_ptr, conf_pkt_size, Theora_Packed_Configuration_payload);
+ TSK_FREE(conf_pkt_ptr);
+ }
+
+ theora->encoder.conf_last = tsk_time_epoch();
+ theora->encoder.conf_count++;
+ }
+ }
+
+ /* Send Theora Raw data */
+ tdav_codec_theora_send(theora, pdata, size, Raw_Theora_payload);
+}
+
+int tdav_codec_theora_send(tdav_codec_theora_t* self, const uint8_t* data, tsk_size_t size, theora_datatype_t tdt)
+{
+ /* 2.2. Payload Header
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Configuration Ident | F |TDT|# pkts.|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ uint8_t pay_hdr[THEORA_PAYLOAD_HEADER_SIZE/*4*/ + THEORA_PAYLOAD_LENGTH_SIZE/*2*/] = {0x01, 0x19, 0x83, 0x00, 0x00, 0x00};
+ //uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size;
+ tsk_bool_t frag, first = tsk_true;
+
+ pay_hdr[3] = (tdt & 0xFF) <<4;
+
+ /* whether the packet will be fragmented or not */
+ frag = (size > THEORA_RTP_PAYLOAD_SIZE);
+
+ while(size){
+ pay_size = TSK_MIN(THEORA_RTP_PAYLOAD_SIZE, size);
+ pay_hdr[4] = pay_size>>8, pay_hdr[5] = pay_size & 0xFF;
+
+ if(frag){
+ if(first){
+ first = tsk_false;
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (Start_Fragment <<6);
+ }
+ else{ /* could not be 'first' and 'last' */
+ if(size<=THEORA_RTP_PAYLOAD_SIZE){
+ /* Last frag */
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (End_Fragment <<6);
+ }
+ else{
+ /* Continuation frag */
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (Continuation_Fragment <<6);
+ }
+ }
+ }
+ else{
+ pay_hdr[3] |= 0x01; /* 'pkts' */
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (Not_Fragmented <<6);
+ }
+
+ if(self->rtp.size < (pay_size + sizeof(pay_hdr))){
+ if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (pay_size + sizeof(pay_hdr))))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return -2;
+ }
+ self->rtp.size = (pay_size + sizeof(pay_hdr));
+ }
+
+ memcpy(self->rtp.ptr, pay_hdr, sizeof(pay_hdr));
+ memcpy((self->rtp.ptr + sizeof(pay_hdr)), data, pay_size);
+ data += pay_size;
+ size -= pay_size;
+
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->callback){
+ TMEDIA_CODEC_VIDEO(self)->callback(TMEDIA_CODEC_VIDEO(self)->callback_data, self->rtp.ptr, (pay_size + sizeof(pay_hdr)), (3003* (30/TMEDIA_CODEC_VIDEO(self)->fps)), (size == 0));
+ }
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
diff --git a/tinyDAV/src/msrp/tdav_consumer_msrp.c b/tinyDAV/src/msrp/tdav_consumer_msrp.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyDAV/src/msrp/tdav_consumer_msrp.c
diff --git a/tinyDAV/src/msrp/tdav_producer_msrp.c b/tinyDAV/src/msrp/tdav_producer_msrp.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyDAV/src/msrp/tdav_producer_msrp.c
diff --git a/tinyDAV/src/msrp/tdav_session_msrp.c b/tinyDAV/src/msrp/tdav_session_msrp.c
new file mode 100644
index 0000000..8ca4daa
--- /dev/null
+++ b/tinyDAV/src/msrp/tdav_session_msrp.c
@@ -0,0 +1,946 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_session_msrp.c
+ * @brief The Message Session Relay Protocol (MSRP) session.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/msrp/tdav_session_msrp.h"
+
+#include <string.h> /* strtok() */
+
+#define TDAV_MSRP_CONNECT_TIMEOUT 2000
+
+static void send_pending_file(tdav_session_msrp_t *session);
+static void send_bodiless(tdav_session_msrp_t *msrp);
+
+/*
+ * http://tools.ietf.org/html/draft-ietf-simple-msrp-acm-09
+ * http://tools.ietf.org/html/draft-ietf-simple-msrp-sessmatch-06
+ * http://www.openmobilealliance.org/technical/release_program/docs/SIMPLE_IM/V1_0-20100322-C/OMA-TS-SIMPLE_IM-V1_0-20100322-C.pdf
+*/
+
+int tdav_msrp_event_proxy_cb(tmsrp_event_t* _event/*!Not the owner of the object*/)
+{
+ tdav_session_msrp_t* msrp;
+ int ret = 0;
+
+ if(!_event || !_event->callback_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ msrp = tsk_object_ref((void*)_event->callback_data);
+ if(TMEDIA_SESSION_MSRP(msrp)->callback.func){
+ _event->callback_data = TMEDIA_SESSION_MSRP(msrp)->callback.data; // steal callback data
+ ret = TMEDIA_SESSION_MSRP(msrp)->callback.func(_event); // call callback function()
+ }
+ tsk_object_unref(msrp);
+
+ return ret;
+}
+
+int tdav_transport_layer_stream_cb(const tnet_transport_event_t* e)
+{
+ const tdav_session_msrp_t *session = e->callback_data;
+ tmsrp_receiver_t* receiver;
+ int ret = -1;
+
+#define TMSRP_ALERT_USER(type) \
+ { \
+ tdav_session_msrp_t *msrp = tsk_object_ref((void*)session); \
+ tmsrp_event_t* _event = tmsrp_event_create(msrp, tsk_false, type, tsk_null); \
+ tdav_msrp_event_proxy_cb(_event); \
+ TSK_OBJECT_SAFE_FREE(_event); \
+ tsk_object_unref(msrp); \
+ }
+
+ switch(e->type){
+ case event_data: {
+ break;
+ }
+ case event_accepted:
+ if(!session->connectedFD){
+ tdav_session_msrp_t *msrp = tsk_object_ref((void*)session);
+ /* We are passive ==> update connection info */
+ msrp->connectedFD = e->local_fd;
+ tmsrp_sender_set_fd(msrp->sender, msrp->connectedFD);
+ tmsrp_receiver_set_fd(msrp->receiver, msrp->connectedFD);
+ msrp->fresh_conn = tsk_true;
+ /* Send Bodiless request */
+ if(msrp->send_bodiless){
+ send_bodiless(msrp);
+ msrp->send_bodiless = tsk_false;
+ }
+ /* Alert user */
+ TMSRP_ALERT_USER(tmsrp_event_type_connected);
+
+ tsk_object_unref(msrp);
+ }
+ break;
+ case event_closed:
+ if(e->local_fd == session->connectedFD){
+ TSK_DEBUG_INFO("MSRP Socket closed");
+ TMSRP_ALERT_USER(tmsrp_event_type_disconnected);
+ }
+ return 0;
+
+ case event_connected:
+ {
+ tdav_session_msrp_t *msrp = tsk_object_ref((void*)session);
+ if(e->local_fd == msrp->connectedFD){
+ msrp->fresh_conn = tsk_true;
+ /* Send Bodiless request */
+ if(msrp->send_bodiless){
+ send_bodiless(msrp);
+ msrp->send_bodiless = tsk_false;
+ }
+ /* Alert user */
+ TMSRP_ALERT_USER(tmsrp_event_type_connected);
+ }
+ tsk_object_unref(msrp);
+ }
+ break;
+ default:{
+ return 0;
+ }
+ }
+
+ if(e->data && (receiver = tsk_object_ref((void*)session->receiver))){
+ ret = tmsrp_receiver_recv(receiver, e->data, e->size);
+ tsk_object_unref(receiver);
+ }
+
+ if(session->fresh_conn){
+ tdav_session_msrp_t *msrp = tsk_object_ref((void*)session);
+ /* send pending file - and data?*/
+ if(session->offerer){
+ send_pending_file(msrp);
+ }
+ msrp->fresh_conn = tsk_false;
+ tsk_object_unref(msrp);
+ }
+
+ return ret;
+}
+
+static void send_pending_file(tdav_session_msrp_t *msrp){
+ if(msrp && msrp->file.path && !msrp->file.sent){
+ msrp->file.sent = tsk_true;
+ tsmrp_sender_send_file(msrp->sender, msrp->file.path);
+ }
+}
+
+static void send_bodiless(tdav_session_msrp_t *msrp){
+ tmsrp_request_t* BODILESS;
+ if(msrp->config->To_Path && msrp->config->From_Path){
+ if((BODILESS = tmsrp_create_bodiless(msrp->config->To_Path->uri, msrp->config->From_Path->uri))){
+ char* str;
+ if((str = tmsrp_message_tostring(BODILESS))){
+ if(!tnet_sockfd_send(msrp->connectedFD, str, tsk_strlen(str), 0)){
+ TSK_DEBUG_WARN("Failed to send bodiless request.");
+ }
+ TSK_FREE(str);
+ }
+
+ TSK_OBJECT_SAFE_FREE(BODILESS);
+ }
+ }
+}
+
+static tdav_msrp_setup_t setup_from_string(const char* setup)
+{
+ tdav_msrp_setup_t ret = msrp_setup_active;
+
+ if(setup){
+ if(tsk_strequals(setup, "holdconn")){
+ ret = msrp_setup_holdconn;
+ }
+ else if(tsk_strequals(setup, "passive")){
+ ret = msrp_setup_passive;
+ }
+ else if(tsk_strequals(setup, "actpass")){
+ ret = msrp_setup_actpass;
+ }
+ else{
+ ret = msrp_setup_active;
+ }
+ }
+ return ret;
+}
+
+static const char* setup_to_string(tdav_msrp_setup_t setup)
+{
+ switch(setup){
+ case msrp_setup_active:
+ return "active";
+ case msrp_setup_passive:
+ return "passive";
+ case msrp_setup_actpass:
+ return "actpass";
+ case msrp_setup_holdconn:
+ return "holdconn";
+ }
+ return "active";
+}
+
+static int init_neg_types(tdav_session_msrp_t* msrp, const tsdp_header_M_t* m)
+{
+ const tsdp_header_A_t* A;
+
+ if((A = tsdp_header_M_findA(m, "accept-types"))){
+ char* atype = strtok((char*)A->value, " ");
+ const char* default_atype = atype;
+ while(atype){
+ if(tsk_striequals(atype, "message/CPIM")){
+ tsk_strupdate(&msrp->neg_accept_type, atype);
+ if((A = tsdp_header_M_findA(m, "accept-wrapped-types"))){
+ char* awtype = strtok((char*)A->value, " ");
+ tsk_strupdate(&msrp->neg_accept_w_type, awtype); // first
+ }
+ break;
+ }
+ atype = strtok(tsk_null, " ");
+ }
+
+ if(!msrp->neg_accept_type){
+ tsk_strupdate(&msrp->neg_accept_type, default_atype);
+ }
+
+ return 0;
+ }
+ return -1;
+}
+
+static int populate_lo(tdav_session_msrp_t* self, tsk_bool_t initial)
+{
+ if(!self || !TMEDIA_SESSION(self)->M.lo){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(initial){
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("setup", setup_to_string(self->setup)),
+
+ tsk_null
+ );
+
+ if(self->accept_types || self->neg_accept_type){
+ /* a=accept-types:message/CPIM application/octet-stream */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("accept-types", self->accept_types ? self->accept_types : self->neg_accept_type),
+ tsk_null);
+ }
+ if(self->accept_w_types || self->neg_accept_w_type){
+ /* a=accept-wrapped-types:application/octet-stream */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("accept-wrapped-types", self->accept_w_types ? self->accept_w_types : self->neg_accept_w_type),
+ tsk_null);
+ }
+
+ /*=== File Transfer ===*/
+ if(self->file.path){
+ /* Compute default 'file-selector' */
+ if(!self->file.selector && !TMEDIA_SESSION(self)->M.ro){
+ int index = tsk_strLastIndexOf(self->file.path, tsk_strlen(self->file.path), "\\");
+ if(index == -1){
+ index = tsk_strLastIndexOf(self->file.path, tsk_strlen(self->file.path), "/");
+ }
+ index++;
+ tsk_sprintf(&self->file.selector, "name:\"%s\" type:application/octet-stream", (self->file.path + index));
+ }
+ /* Compute default 'file-transfer-id' */
+ if(!self->file.transfer_id && !TMEDIA_SESSION(self)->M.ro){
+ tsk_istr_t rand_string;
+ tsk_strrandom(&rand_string);
+ self->file.transfer_id = tsk_strdup(rand_string);
+ }
+
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("sendonly", tsk_null),
+ tsk_null);
+ }
+ else{
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("sendrecv", tsk_null),
+ tsk_null);
+ }
+
+ if(self->file.selector){
+ /* a=file-selector:name:"test.pcap" type:application/octet-stream size:11376 hash:sha-1:8D:55:24:2B:F4:F9:9B:A2:54:A3:5B:91:00:15:9E:A3:D4:48:D7:DF */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("file-selector", self->file.selector),
+ tsk_null);
+ }
+ if(self->file.transfer_id){
+ /* a=file-transfer-id:vscxggbwkfnqduxwfnfozvsrtkjprepg */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("file-transfer-id", self->file.transfer_id),
+ tsk_null);
+ }
+ if(self->file.disposition){
+ /* a=file-disposition:attachment */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("file-disposition", self->file.disposition),
+ tsk_null);
+ }
+ if(self->file.date){
+ /* a=file-date:creation:2010-02-13T17:50:31.763Z */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("file-date", self->file.date),
+ tsk_null);
+ }
+ if(self->file.icon){
+ /* a=file-icon:cid:test@doubango.org */
+ tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ TSDP_HEADER_A_VA_ARGS("file-icon", self->file.icon),
+ tsk_null);
+ }
+ }
+
+ return 0;
+}
+
+static tsk_bool_t match_offer(const tdav_session_msrp_t* self, const tsdp_header_M_t* M)
+{
+ return tsk_true;
+}
+
+/* ============ Plugin interface ================= */
+
+int tdav_session_msrp_set(tmedia_session_t* self, const tmedia_param_t* param)
+{
+ int ret = 0;
+ tdav_session_msrp_t* msrp;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TSK_DEBUG_INFO("tdav_session_msrp_set");
+
+ msrp = (tdav_session_msrp_t*)self;
+
+ if(param->value_type == tmedia_pvt_pchar){
+ if(tsk_striequals(param->key, "remote-ip")){
+ // only if no ip associated to the "m=" line
+ if(param->value && !msrp->remote_ip){
+ msrp->remote_ip = tsk_strdup(param->value);
+ }
+ }
+ else if(tsk_striequals(param->key, "local-ip")){
+ tsk_strupdate(&msrp->local_ip, param->value);
+ }
+ else if(tsk_striequals(param->key, "local-ipver")){
+ msrp->useIPv6 = tsk_striequals(param->value, "ipv6");
+ }
+ else if(tsk_striequals(param->key, "accept-types")){
+ tsk_strupdate(&msrp->accept_types, param->value);
+ }
+ else if(tsk_striequals(param->key, "accept-wrapped-types")){
+ tsk_strupdate(&msrp->accept_w_types, param->value);
+ }
+
+ /* Configuration */
+ else if(tsk_striequals(param->key, "Failure-Report")){
+ msrp->config->Failure_Report = tsk_striequals(param->value, "yes");
+ }
+ else if(tsk_striequals(param->key, "Success-Report")){
+ msrp->config->Success_Report = tsk_striequals(param->value, "yes");
+ }
+
+ /* File Transfer */
+ else if(tsk_striequals(param->key, "file-path") && !tsk_strnullORempty((const char*)param->value)){
+ tsk_strupdate(&msrp->file.path, param->value);
+ }
+ else if(tsk_striequals(param->key, "file-selector")){
+ tsk_strupdate(&msrp->file.selector, param->value);
+ }
+ else if(tsk_striequals(param->key, "file-disposition")){
+ tsk_strupdate(&msrp->file.disposition, param->value);
+ }
+ else if(tsk_striequals(param->key, "file-date")){
+ tsk_strupdate(&msrp->file.date, param->value);
+ }
+ else if(tsk_striequals(param->key, "file-icon")){
+ tsk_strupdate(&msrp->file.icon, param->value);
+ }
+ else if(tsk_striequals(param->key, "file-transfer-id")){
+ tsk_strupdate(&msrp->file.transfer_id, param->value);
+ }
+ }
+ else if(param->value_type == tmedia_pvt_pobject){
+ if(tsk_striequals(param->key, "natt-ctx")){
+ TSK_OBJECT_SAFE_FREE(msrp->natt_ctx);
+ msrp->natt_ctx = tsk_object_ref(param->value);
+ }
+ }
+ else if(param->value_type == tmedia_pvt_int64 || param->value_type == tmedia_pvt_int32){
+ if(tsk_striequals(param->key, "chunck-duration")){
+ msrp->chunck_duration = TSK_TO_UINT32((uint8_t*)param->value);
+ if(msrp->sender){
+ msrp->sender->chunck_duration = msrp->chunck_duration;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int tdav_session_msrp_prepare(tmedia_session_t* self)
+{
+ tdav_session_msrp_t* msrp;
+ tnet_socket_type_t socket_type = tnet_socket_type_tcp_ipv4;
+ int ret = 0;
+
+#if 1
+ tnet_port_t local_port = TNET_SOCKET_PORT_ANY;
+#else
+ tnet_port_t local_port = 2000;
+#endif
+
+ TSK_DEBUG_INFO("tdav_session_msrp_prepare");
+
+ msrp = (tdav_session_msrp_t*)self;
+
+ /* set local port */
+ if(!msrp->transport){
+ if(msrp->useIPv6){
+ TNET_SOCKET_TYPE_SET_IPV6Only(socket_type);
+ }
+
+ if(!msrp->local_ip){
+ tnet_host_t local;
+ tnet_gethostname(&local);
+ msrp->transport = tnet_transport_create(local, local_port, socket_type, "MSRP/MSRPS transport");
+ }
+ else{
+ msrp->transport = tnet_transport_create(msrp->local_ip, local_port, socket_type, "MSRP/MSRPS transport");
+ }
+ /* Set NAT context */
+ if(msrp->natt_ctx){
+ tnet_transport_set_natt_ctx(msrp->transport, msrp->natt_ctx);
+ }
+ }
+
+ /* sender will be prepared in tdav_session_msrp_start() */
+ /* receiver will be prepared in tdav_session_msrp_start() */
+
+ return ret;
+}
+
+int tdav_session_msrp_start(tmedia_session_t* self)
+{
+ tdav_session_msrp_t* msrp;
+ int ret;
+
+ TSK_DEBUG_INFO("tdav_session_msrp_start");
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ msrp = (tdav_session_msrp_t*)self;
+
+ /* start the transport */
+ if((ret = tnet_transport_start(msrp->transport))){
+ goto bail;
+ }
+
+ switch(msrp->setup){
+ case msrp_setup_active:
+ case msrp_setup_actpass:
+ {
+ //
+ // ACTIVE
+ //
+ TSK_DEBUG_INFO("connectto(%s:%d)", msrp->remote_ip, msrp->remote_port);
+ if((msrp->connectedFD = tnet_transport_connectto_2(msrp->transport, msrp->remote_ip, msrp->remote_port)) == TNET_INVALID_FD){
+ TSK_DEBUG_ERROR("Failed to connect to the remote party");
+ ret = -2;
+ goto bail;
+ }
+ else{
+ //TSK_DEBUG_INFO("Msrp connected FD=%d", msrp->connectedFD);
+ //if((ret = tnet_sockfd_waitUntilWritable(msrp->connectedFD, TDAV_MSRP_CONNECT_TIMEOUT)) && msrp->offerer){
+ // TSK_DEBUG_ERROR("%d milliseconds elapsed and the socket is still not connected to (%s:%d).", TDAV_MSRP_CONNECT_TIMEOUT, msrp->remote_ip, msrp->remote_port);
+ // goto bail;
+ //}
+ /* draft-denis-simple-msrp-comedia-02 - 4.2.3. Setting up the connection
+ Once the TCP session is established, and if the answerer was the
+ active connection endpoint, it MUST send an MSRP request. In
+ particular, if it has no pending data to send, it MUST send an empty
+ MSRP SEND request. That is necessary for the other endpoint to
+ authenticate this TCP session.
+
+ ...RFC 4975 - 7.1
+ */
+ msrp->send_bodiless = tsk_true;
+ }
+ break;
+ }
+ default:
+ {
+ //
+ // PASSIVE
+ //
+ break;
+ }
+ }
+
+ // create and start the receiver
+ if(!msrp->receiver){
+ if((msrp->receiver = tmsrp_receiver_create(msrp->config, msrp->connectedFD))){
+ tnet_transport_set_callback(msrp->transport, TNET_TRANSPORT_CB_F(tdav_transport_layer_stream_cb), msrp);
+ if((ret = tmsrp_receiver_start(msrp->receiver, msrp, tdav_msrp_event_proxy_cb))){
+ TSK_DEBUG_ERROR("Failed to start the MSRP receiver");
+ goto bail;
+ }
+ }
+ }
+
+ // create and start the sender
+ if(!msrp->sender){
+ if((msrp->sender = tmsrp_sender_create(msrp->config, msrp->connectedFD))){
+ msrp->sender->chunck_duration = msrp->chunck_duration;
+ if((ret = tmsrp_sender_start(msrp->sender))){
+ TSK_DEBUG_ERROR("Failed to start the MSRP sender");
+ goto bail;
+ }
+ }
+ }
+
+bail:
+ return ret;
+}
+
+int tdav_session_msrp_pause(tmedia_session_t* self)
+{
+ TSK_DEBUG_ERROR("Not Implemented");
+ return -1;
+}
+
+int tdav_session_msrp_stop(tmedia_session_t* self)
+{
+ tdav_session_msrp_t* msrp;
+ int ret;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ msrp = (tdav_session_msrp_t*)self;
+
+ if(msrp->sender){
+ if((ret = tmsrp_sender_stop(msrp->sender))){
+ TSK_DEBUG_ERROR("Failed to stop the MSRP sender");
+ }
+ }
+ if(msrp->receiver){
+ if((ret = tmsrp_receiver_stop(msrp->receiver))){
+ TSK_DEBUG_ERROR("Failed to stop the MSRP receiver");
+ }
+ }
+
+ if(msrp->transport){
+ if((ret = tnet_transport_shutdown(msrp->transport))){
+ TSK_DEBUG_ERROR("Failed to stop the MSRP transport");
+ }
+ }
+
+ return 0;
+}
+
+const tsdp_header_M_t* tdav_session_msrp_get_lo(tmedia_session_t* self)
+{
+ tdav_session_msrp_t* msrp;
+ tsk_bool_t changed = tsk_false;
+
+ const char* proto = "TCP/MSRP";
+ const char* sheme = "msrp";
+
+ TSK_DEBUG_INFO("tdav_session_msrp_get_lo");
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ msrp = (tdav_session_msrp_t*)self;
+
+ if(!msrp->transport){
+ TSK_DEBUG_ERROR("Not prepared");
+ return tsk_null;
+ }
+
+ if(/*TNET_SOCKET_TYPE_IS_TLS(type)*/ tsk_false){
+ proto = "TCP/TLS/MSRP";
+ sheme = "msrps";
+ }
+
+ if(self->ro_changed && self->M.lo){
+ /* Codecs */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "fmtp");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "rtpmap");
+ tsk_list_clear_items(self->M.lo->FMTs);
+
+ /* QoS */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "curr");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "des");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "conf");
+ }
+
+ changed = (self->ro_changed || !self->M.lo);
+
+ if(!self->M.lo){
+ tsk_istr_t id;
+ char* path = tsk_null;
+ tnet_ip_t ip = "0.0.0.0";
+ tnet_port_t port = 0;
+
+ tsk_strrandom(&id);
+ tnet_transport_get_public_ip_n_port(msrp->transport, msrp->transport->master->fd, &ip, &port);
+ tsk_sprintf(&path, "%s://%s:%u/%s;tcp", sheme, ip, port, id); //tcp is ok even if tls is used.
+
+ if((self->M.lo = tsdp_header_M_create(self->plugin->media, port, proto))){
+ tmsrp_uri_t* uri;
+
+ tsdp_header_M_add_headers(self->M.lo,
+ TSDP_FMT_VA_ARGS("*"),
+ TSDP_HEADER_C_VA_ARGS("IN", msrp->useIPv6 ? "IP6" : "IP4", ip),
+ TSDP_HEADER_A_VA_ARGS("path", path),
+ tsk_null);
+
+ if((uri = tmsrp_uri_parse(path, tsk_strlen(path)))){
+ TSK_OBJECT_SAFE_FREE(msrp->config->From_Path);
+ msrp->config->From_Path = tmsrp_header_From_Path_create(uri);
+ TSK_OBJECT_SAFE_FREE(uri);
+ }
+ }
+ TSK_FREE(path);
+
+ if(self->M.ro){ /* We are probably about to send 2xx INVITE(sdp) */
+ /* [OMA-TS-SIMPLE_IM-V1_0-20100322-C] - 5.8.1 Negotiate direction of the MSRP connection setup
+ Offer Answer
+ ________________
+ active passive / holdconn
+ passive active / holdconn
+ actpass active / passive / holdconn
+ holdconn holdconn
+ */
+ const tsdp_header_A_t* A;
+ if((A = tsdp_header_M_findA(self->M.ro, "setup"))){
+ tdav_msrp_setup_t setup = setup_from_string(A->value);
+ switch(setup){
+ case msrp_setup_passive:
+ msrp->setup = msrp_setup_active;
+ break;
+ case msrp_setup_actpass:
+ case msrp_setup_active:
+ msrp->setup = msrp_setup_passive;
+ break;
+ }
+ tsdp_header_M_add_headers(self->M.lo,
+ TSDP_HEADER_A_VA_ARGS("connection", "new"),
+ tsk_null
+ );
+ }
+ msrp->offerer = tsk_false;
+ }
+ else{ /* We are probably about to send initial INVITE */
+ tsdp_header_M_add_headers(self->M.lo,
+ TSDP_HEADER_A_VA_ARGS("connection", "new"),
+ tsk_null
+ );
+ msrp->offerer = tsk_true;
+ }
+
+ /* Other SDP fields */
+ populate_lo(msrp, tsk_true);
+ } // !lo
+
+
+ return self->M.lo;
+}
+
+int tdav_session_msrp_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ tdav_session_msrp_t* msrp;
+ const tsdp_header_A_t* A;
+ tsk_bool_t answer;
+
+ TSK_DEBUG_INFO("tdav_session_msrp_set_ro");
+
+ if(!self || !m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ msrp = (tdav_session_msrp_t*)self;
+
+ // answer or initial offer?
+ answer = (self->M.lo != tsk_null);
+
+ /* update remote offer */
+ TSK_OBJECT_SAFE_FREE(self->M.ro);
+ self->M.ro = tsk_object_ref((void*)m);
+
+ if(self->M.lo){
+ if((match_offer(msrp, m))){
+
+ }
+ else{
+ TSK_DEBUG_ERROR("MSRP offer doesn't match");
+ return -1;
+ }
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ }
+ }
+
+ /* To-Path */
+ if((A = tsdp_header_M_findA(m, "path"))){
+ tmsrp_uri_t* uri;
+ if((uri = tmsrp_uri_parse(A->value, tsk_strlen(A->value)))){
+ TSK_OBJECT_SAFE_FREE(msrp->config->To_Path);
+ msrp->config->To_Path = tmsrp_header_To_Path_create(uri);
+ TSK_OBJECT_SAFE_FREE(uri);
+ }
+ }
+
+ // OMA-TS-SIMPLE_IM-V1_0-20080903-C - 5.8.1 Negotiate direction of the MSRP connection setup
+ if((A = tsdp_header_M_findA(m, "setup"))){
+ tdav_msrp_setup_t setup = setup_from_string(A->value);
+ switch(setup){
+ case msrp_setup_actpass:
+ case msrp_setup_passive:
+ msrp->setup = msrp_setup_active;
+ break;
+ case msrp_setup_active:
+ msrp->setup = msrp_setup_passive;
+ break;
+ }
+ }
+
+ /* Neg parameters */
+ init_neg_types(msrp, m);
+
+
+ /* [OMA-TS-SIMPLE_IM-V1_0-20100322-C] - 5.8.2 Support of Application Level Gateway */
+
+ /* get connection associated to this media line
+ * If the connnection is global, then the manager will call tdav_session_audio_set() */
+ if(m->C && m->C->addr && !msrp->remote_ip){
+ tsk_strupdate(&msrp->remote_ip, m->C->addr);
+ msrp->useIPv6 = tsk_striequals(m->C->addrtype, "IP6");
+ }
+ /* set remote port */
+ msrp->remote_port = m->port;
+
+ return 0;
+}
+
+/* ============ Public functions ================= */
+int tdav_session_msrp_send_file(tmedia_session_msrp_t* self, const char* path, va_list *app)
+{
+ tdav_session_msrp_t* msrp;
+ int ret;
+
+ if(!path || !(msrp = (tdav_session_msrp_t*)self) || !msrp->sender){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ //if(1 || !msrp->file.selector){
+ // /* File transfer without selector line
+ // - a=file-selector:name:"test.pcap" type:application/octet-stream size:20312
+ // */
+ // FILE* file = fopen(path, "rb");
+ // const char* fname = path + tsk_strlen(path);
+ // while(fname && (fname> path) && *fname != '\\' && *fname != '/'){
+ // fname--;
+ // }
+
+ // if(file){
+ // tsk_size_t size = 0;
+ // fseek(file, 0L, SEEK_END);
+ // size = ftell(file);
+ // fclose(file);
+
+ // tsk_sprintf(&msrp->file.selector, "name:\"%s\" type:application/octet-stream size:%u",
+ // fname, size);
+ // tsdp_header_M_add_headers(TMEDIA_SESSION(self)->M.lo,
+ // TSDP_HEADER_A_VA_ARGS("file-selector", msrp->file.selector),
+ // tsk_null);
+ // }
+ //}
+
+ ret = tsmrp_sender_send_file(msrp->sender, path);
+
+ return ret;
+}
+
+int tdav_session_msrp_send_message(tmedia_session_msrp_t* self, const void* data, tsk_size_t size, const tmedia_params_L_t *params)
+{
+ const tdav_session_msrp_t* msrp;
+ const tmedia_param_t* param;
+ int ret;
+ const tsk_list_item_t* item;
+ const char* content_type = tsk_null;
+ const char* w_content_type = tsk_null;
+
+ if(!data || !size || !(msrp = (tdav_session_msrp_t*)self) || !msrp->sender){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_list_foreach(item, params){
+ if((param = TMEDIA_PARAM(item->data))){
+ if((param->media_type & tmedia_msrp) == param->media_type
+ && param->plugin_type == tmedia_ppt_session
+ && param->value_type == tmedia_pvt_pchar){
+
+ if(tsk_striequals(param->key, "content-type")){
+ content_type = (const char*)param->value;
+ }
+ else if(tsk_striequals(param->key, "w-content-type")){
+ w_content_type = (const char*)param->value;
+ }
+ }
+ }
+ }
+
+ if(content_type || w_content_type){ // user-defined content-types
+ ret = tsmrp_sender_send_data(msrp->sender, data, size, content_type, w_content_type);
+ }
+ else{ // neg. content-types
+ ret = tsmrp_sender_send_data(msrp->sender, data, size,
+ msrp->neg_accept_type, msrp->neg_accept_w_type
+ );
+ }
+
+ return ret;
+}
+
+
+
+
+
+//=================================================================================================
+// Session MSRp Plugin object definition
+//
+/* constructor */
+static tsk_object_t* tdav_session_msrp_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_session_msrp_t *session = self;
+ if(session){
+ /* init base: called by tmedia_session_create() */
+ /* init self */
+ TMEDIA_SESSION_MSRP(session)->send_file = tdav_session_msrp_send_file;
+ TMEDIA_SESSION_MSRP(session)->send_message = tdav_session_msrp_send_message;
+
+ session->config = tmsrp_config_create();
+ session->setup = msrp_setup_actpass; /* draft-denis-simple-msrp-comedia-02 - 4.1.1. Sending the offer */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_session_msrp_dtor(tsk_object_t * self)
+{
+ tdav_session_msrp_t *session = self;
+ if(session){
+
+ /* deinit self (rtp manager should be destroyed after the producer) */
+ TSK_OBJECT_SAFE_FREE(session->config);
+
+ TSK_OBJECT_SAFE_FREE(session->receiver);
+ TSK_OBJECT_SAFE_FREE(session->sender);
+ TSK_OBJECT_SAFE_FREE(session->transport);
+
+ TSK_FREE(session->remote_ip);
+ TSK_FREE(session->local_ip);
+
+ TSK_FREE(session->neg_accept_type);
+ TSK_FREE(session->neg_accept_w_type);
+ TSK_FREE(session->accept_types);
+ TSK_FREE(session->accept_w_types);
+
+ /* File */
+ TSK_FREE(session->file.path);
+ TSK_FREE(session->file.selector);
+ TSK_FREE(session->file.disposition);
+ TSK_FREE(session->file.date);
+ TSK_FREE(session->file.icon);
+ TSK_FREE(session->file.transfer_id);
+
+ /* NAT Traversal context */
+ TSK_OBJECT_SAFE_FREE(session->natt_ctx);
+
+ /* deinit base */
+ tmedia_session_deinit(self);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_session_msrp_def_s =
+{
+ sizeof(tdav_session_msrp_t),
+ tdav_session_msrp_ctor,
+ tdav_session_msrp_dtor,
+ tmedia_session_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tdav_session_msrp_plugin_def_s =
+{
+ &tdav_session_msrp_def_s,
+
+ tmedia_msrp,
+ "message",
+
+ tdav_session_msrp_set,
+ tdav_session_msrp_prepare,
+ tdav_session_msrp_start,
+ tdav_session_msrp_pause,
+ tdav_session_msrp_stop,
+
+ /* Audio part */
+ { tsk_null },
+
+ tdav_session_msrp_get_lo,
+ tdav_session_msrp_set_ro
+};
+const tmedia_session_plugin_def_t *tdav_session_msrp_plugin_def_t = &tdav_session_msrp_plugin_def_s;
+
diff --git a/tinyDAV/src/tdav.c b/tinyDAV/src/tdav.c
new file mode 100644
index 0000000..3d81580
--- /dev/null
+++ b/tinyDAV/src/tdav.c
@@ -0,0 +1,419 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav.h
+ * @brief tinyDAV.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/tdav.h"
+
+// Media Contents, ...
+#include "tinymedia.h"
+
+// Sessions
+#include "tinymedia/tmedia_session_ghost.h"
+#include "tinydav/audio/tdav_session_audio.h"
+#include "tinydav/video/tdav_session_video.h"
+#include "tinydav/msrp/tdav_session_msrp.h"
+
+// Codecs
+#include "tinydav/codecs/dtmf/tdav_codec_dtmf.h"
+#include "tinydav/codecs/msrp/tdav_codec_msrp.h"
+#include "tinydav/codecs/amr/tdav_codec_amr.h"
+#include "tinydav/codecs/bv/tdav_codec_bv16.h"
+#include "tinydav/codecs/g711/tdav_codec_g711.h"
+#include "tinydav/codecs/gsm/tdav_codec_gsm.h"
+#include "tinydav/codecs/ilbc/tdav_codec_ilbc.h"
+#include "tinydav/codecs/g729/tdav_codec_g729.h"
+#include "tinydav/codecs/speex/tdav_codec_speex.h"
+#include "tinydav/codecs/h261/tdav_codec_h261.h"
+#include "tinydav/codecs/h263/tdav_codec_h263.h"
+#include "tinydav/codecs/h264/tdav_codec_h264.h"
+#include "tinydav/codecs/theora/tdav_codec_theora.h"
+#include "tinydav/codecs/mp4ves/tdav_codec_mp4ves.h"
+
+// Consumers
+#include "tinydav/audio/waveapi/tdav_consumer_waveapi.h"
+#include "tinydav/audio/directsound/tdav_consumer_dsound.h"
+#include "tinydav/audio/coreaudio/tdav_consumer_coreaudio.h"
+#if HAVE_TINYDSHOW // DirectShow
+# include "tinydshow/plugin/DSConsumer.h"
+#endif
+
+// Producers
+#include "tinydav/audio/waveapi/tdav_producer_waveapi.h"
+#include "tinydav/audio/directsound/tdav_producer_dsound.h"
+#include "tinydav/audio/coreaudio/tdav_producer_coreaudio.h"
+#if HAVE_TINYDSHOW // DirectShow
+# include "tinydshow/plugin/DSProducer.h"
+#endif
+
+// Audio Denoise (AGC, Noise Suppression, VAD and AEC)
+#if HAVE_SPEEX_DSP
+#include "tinydav/audio/tdav_speex_denoise.h"
+#endif
+
+#if HAVE_FFMPEG
+# include <libavcodec/avcodec.h>
+#endif
+
+int tdav_init()
+{
+ /* === Initialize ffmpeg === */
+#if HAVE_FFMPEG
+ avcodec_init();
+#endif
+
+ /* === Register media contents === */
+ tmedia_content_plugin_register("text/html", tmedia_content_dummy_plugin_def_t);
+ tmedia_content_plugin_register("text/plain", tmedia_content_dummy_plugin_def_t);
+ tmedia_content_plugin_register("application/octet-stream", tmedia_content_dummy_plugin_def_t);
+ tmedia_content_plugin_register("message/CPIM", tmedia_content_cpim_plugin_def_t);
+ /*tmedia_content_plugin_register("message/sipfrag", tmedia_content_sipfrag_plugin_def_t);
+ tmedia_content_plugin_register("multipart/digest", tmedia_content_multipart_plugin_def_t);
+ tmedia_content_plugin_register("multipart/mixed", tmedia_content_multipart_plugin_def_t);
+ tmedia_content_plugin_register("multipart/related", tmedia_content_multipart_plugin_def_t);
+ tmedia_content_plugin_register("multipart/alternative", tmedia_content_multipart_plugin_def_t);
+ tmedia_content_plugin_register("multipart/encrypted", tmedia_content_multipart_plugin_def_t);
+ tmedia_content_plugin_register("multipart/parallel", tmedia_content_multipart_plugin_def_t);
+ tmedia_content_plugin_register("multipart/signed", tmedia_content_multipart_plugin_def_t); */
+
+ /* === Register sessions === */
+ tmedia_session_plugin_register(tmedia_session_ghost_plugin_def_t);
+ tmedia_session_plugin_register(tdav_session_audio_plugin_def_t);
+ tmedia_session_plugin_register(tdav_session_video_plugin_def_t);
+ tmedia_session_plugin_register(tdav_session_msrp_plugin_def_t);
+
+ /* === Register codecs === */
+#if HAVE_FFMPEG
+ avcodec_register_all();
+#endif
+
+ tmedia_codec_plugin_register(tdav_codec_msrp_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_g711a_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_g711u_plugin_def_t);
+#if HAVE_OPENCORE_AMR
+ tmedia_codec_plugin_register(tdav_codec_amrnb_oa_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_amrnb_be_plugin_def_t);
+#endif
+#if HAVE_BV16
+ tmedia_codec_plugin_register(tdav_codec_bv16_plugin_def_t);
+#endif
+#if HAVE_LIBGSM
+ tmedia_codec_plugin_register(tdav_codec_gsm_plugin_def_t);
+#endif
+#if HAVE_ILBC
+ tmedia_codec_plugin_register(tdav_codec_ilbc_plugin_def_t);
+#endif
+#if HAVE_LIB_SPEEX
+ tmedia_codec_plugin_register(tdav_codec_speex_nb_plugin_def_t);
+#endif
+#if HAVE_G729
+ tmedia_codec_plugin_register(tdav_codec_g729ab_plugin_def_t);
+#endif
+ // last: dtmf
+ tmedia_codec_plugin_register(tdav_codec_dtmf_plugin_def_t);
+
+#if HAVE_FFMPEG
+ tmedia_codec_plugin_register(tdav_codec_mp4ves_plugin_def_t);
+# if !defined(HAVE_H264) || HAVE_H264
+ tmedia_codec_plugin_register(tdav_codec_h264_bp10_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_h264_bp20_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_h264_bp30_plugin_def_t);
+#endif
+ tmedia_codec_plugin_register(tdav_codec_h263p_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_h263pp_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_theora_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_h263_plugin_def_t);
+ tmedia_codec_plugin_register(tdav_codec_h261_plugin_def_t);
+#endif
+
+
+ /* === Register consumers === */
+#if HAVE_DSOUND_H
+ tmedia_consumer_plugin_register(tdav_consumer_dsound_plugin_def_t);
+#elif HAVE_WAVE_API
+ tmedia_consumer_plugin_register(tdav_consumer_waveapi_plugin_def_t);
+#endif
+#if HAVE_TINYDSHOW // DirectShow
+ tmedia_consumer_plugin_register(tdshow_consumer_plugin_def_t);
+#endif
+
+#if HAVE_COREAUDIO
+ tmedia_consumer_plugin_register(tdav_consumer_coreaudio_plugin_def_t);
+#endif
+
+#if HAVE_OSS_H
+ tmedia_consumer_plugin_register(tmedia_consumer_oss_plugin_def_t);
+#endif
+
+ /* === Register producers === */
+#if HAVE_DSOUND_H // DirectSound
+ tmedia_producer_plugin_register(tdav_producer_dsound_plugin_def_t);
+#elif HAVE_WAVE_API // WaveAPI
+ tmedia_producer_plugin_register(tdav_producer_waveapi_plugin_def_t);
+#endif
+#if HAVE_TINYDSHOW // DirectShow
+ tmedia_producer_plugin_register(tdshow_producer_plugin_def_t);
+#endif
+
+#if HAVE_COREAUDIO
+ tmedia_producer_plugin_register(tdav_producer_coreaudio_plugin_def_t);
+#endif
+
+ /* === Register Audio Denoise (AGC, VAD, Noise Suppression and AEC) === */
+#if HAVE_SPEEX_DSP
+ tmedia_denoise_plugin_register(tdav_speex_denoise_plugin_def_t);
+#endif
+
+ return 0;
+}
+
+typedef struct tdav_codec_decl_s{
+ tdav_codec_id_t id;
+ const tmedia_codec_plugin_def_t** plugin;
+} tdav_codec_decl_t;
+
+void tdav_set_codecs(tdav_codec_id_t codecs)
+{
+ int i;
+ static const tdav_codec_decl_t __codecs[] = {
+#if HAVE_OPENCORE_AMR
+ { tdav_codec_id_amr_nb_oa, &tdav_codec_amrnb_oa_plugin_def_t },
+ { tdav_codec_id_amr_nb_be, &tdav_codec_amrnb_be_plugin_def_t },
+#endif
+#if HAVE_BV16
+ { tdav_codec_id_bv16, &tdav_codec_bv16_plugin_def_t },
+#endif
+#if HAVE_LIBGSM
+ { tdav_codec_id_gsm, &tdav_codec_gsm_plugin_def_t },
+#endif
+ { tdav_codec_id_pcma, &tdav_codec_g711a_plugin_def_t },
+ { tdav_codec_id_pcmu, &tdav_codec_g711u_plugin_def_t },
+#if HAVE_ILBC
+ { tdav_codec_id_ilbc, &tdav_codec_ilbc_plugin_def_t },
+#endif
+#if HAVE_LIB_SPEEX
+ { tdav_codec_id_speex_nb, &tdav_codec_speex_nb_plugin_def_t },
+#endif
+#if HAVE_G729
+ { tdav_codec_id_g729ab, &tdav_codec_g729ab_plugin_def_t },
+#endif
+
+#if HAVE_FFMPEG
+
+ { tdav_codec_id_mp4ves_es, &tdav_codec_mp4ves_plugin_def_t },
+# if !defined(HAVE_H264) || HAVE_H264
+ { tdav_codec_id_h264_bp10, &tdav_codec_h264_bp10_plugin_def_t },
+ { tdav_codec_id_h264_bp20, &tdav_codec_h264_bp20_plugin_def_t },
+ { tdav_codec_id_h264_bp30, &tdav_codec_h264_bp30_plugin_def_t },
+#endif
+ { tdav_codec_id_h263p, &tdav_codec_h263p_plugin_def_t },
+ { tdav_codec_id_h263pp, &tdav_codec_h263pp_plugin_def_t },
+ { tdav_codec_id_theora, &tdav_codec_theora_plugin_def_t },
+ { tdav_codec_id_h263, &tdav_codec_h263_plugin_def_t },
+ { tdav_codec_id_h261, &tdav_codec_h261_plugin_def_t },
+#endif
+ };
+
+ for(i=0; i<sizeof(__codecs)/sizeof(tdav_codec_decl_t); i++){
+ if((codecs & __codecs[i].id)){
+ tmedia_codec_plugin_register(*__codecs[i].plugin);
+ }
+ else{
+ tmedia_codec_plugin_unregister(*__codecs[i].plugin);
+ }
+ }
+}
+
+tsk_bool_t tdav_codec_is_supported(tdav_codec_id_t codec)
+{
+ switch(codec){
+
+ case tdav_codec_id_amr_nb_oa:
+ case tdav_codec_id_amr_nb_be:
+#if HAVE_OPENCORE_AMR
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_gsm:
+#if HAVE_LIBGSM
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_pcma:
+ case tdav_codec_id_pcmu:
+ return tsk_true;
+
+ case tdav_codec_id_ilbc:
+#if HAVE_ILBC
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_speex_nb:
+#if HAVE_LIB_SPEEX
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_bv16:
+#if HAVE_BV16
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_g729ab:
+#if HAVE_G729
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_h261:
+ case tdav_codec_id_h263:
+ case tdav_codec_id_h263p:
+ case tdav_codec_id_h263pp:
+ case tdav_codec_id_theora:
+ case tdav_codec_id_mp4ves_es:
+#if HAVE_FFMPEG
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_h264_bp10:
+ case tdav_codec_id_h264_bp20:
+ case tdav_codec_id_h264_bp30:
+#if !defined(HAVE_H264) || HAVE_H264
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+
+ case tdav_codec_id_amr_wb_oa:
+ case tdav_codec_id_amr_wb_be:
+ case tdav_codec_id_speex_wb:
+ case tdav_codec_id_speex_uwb:
+ case tdav_codec_id_bv32:
+ case tdav_codec_id_evrc:
+ default:
+ return tsk_false;
+ }
+}
+
+int tdav_deinit()
+{
+ /* === UnRegister media contents === */
+ tmedia_content_plugin_unregister_all();
+
+ /* === UnRegister sessions === */
+ tmedia_session_plugin_unregister(tmedia_session_ghost_plugin_def_t);
+ tmedia_session_plugin_unregister(tdav_session_audio_plugin_def_t);
+ tmedia_session_plugin_unregister(tdav_session_video_plugin_def_t);
+ tmedia_session_plugin_unregister(tdav_session_msrp_plugin_def_t);
+
+ /* === UnRegister codecs === */
+ tmedia_codec_plugin_unregister(tdav_codec_dtmf_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_msrp_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_g711a_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_g711u_plugin_def_t);
+#if HAVE_OPENCORE_AMR
+ tmedia_codec_plugin_unregister(tdav_codec_amrnb_oa_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_amrnb_be_plugin_def_t);
+#endif
+#if HAVE_BV16
+ tmedia_codec_plugin_unregister(tdav_codec_bv16_plugin_def_t);
+#endif
+#if HAVE_LIBGSM
+ tmedia_codec_plugin_unregister(tdav_codec_gsm_plugin_def_t);
+#endif
+#if HAVE_ILBC
+ tmedia_codec_plugin_unregister(tdav_codec_ilbc_plugin_def_t);
+#endif
+#if HAVE_LIB_SPEEX
+ tmedia_codec_plugin_unregister(tdav_codec_speex_nb_plugin_def_t);
+#endif
+#if HAVE_G729
+ tmedia_codec_plugin_unregister(tdav_codec_g729ab_plugin_def_t);
+#endif
+#if HAVE_FFMPEG
+ tmedia_codec_plugin_unregister(tdav_codec_mp4ves_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_h261_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_h263_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_h263p_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_h263pp_plugin_def_t);
+# if !defined(HAVE_H264) || HAVE_H264
+ tmedia_codec_plugin_unregister(tdav_codec_h264_bp10_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_h264_bp20_plugin_def_t);
+ tmedia_codec_plugin_unregister(tdav_codec_h264_bp30_plugin_def_t);
+#endif
+ tmedia_codec_plugin_unregister(tdav_codec_theora_plugin_def_t);
+
+#endif
+
+ /* === unRegister consumers === */
+#if HAVE_DSOUND_H
+ tmedia_consumer_plugin_unregister(tdav_consumer_dsound_plugin_def_t);
+#elif HAVE_WAVE_API
+ tmedia_consumer_plugin_unregister(tdav_consumer_waveapi_plugin_def_t);
+#endif
+#if HAVE_TINYDSHOW // DirectShow
+ tmedia_consumer_plugin_unregister(tdshow_consumer_plugin_def_t);
+#endif
+#if HAVE_COREAUDIO // CoreAudio
+ tmedia_consumer_plugin_unregister(tdav_consumer_coreaudio_plugin_def_t);
+#endif
+
+ /* === UnRegister producers === */
+#if HAVE_DSOUND_H // DirectSound
+ tmedia_producer_plugin_unregister(tdav_producer_dsound_plugin_def_t);
+#elif HAVE_WAVE_API // WaveAPI
+ tmedia_producer_plugin_unregister(tdav_producer_waveapi_plugin_def_t);
+#endif
+#if HAVE_TINYDSHOW // DirectShow
+ tmedia_producer_plugin_unregister(tdshow_producer_plugin_def_t);
+#endif
+#if HAVE_COREAUDIO // CoreAudio
+ tmedia_producer_plugin_unregister(tdav_producer_coreaudio_plugin_def_t);
+#endif
+
+#if HAVE_OSS_H
+ tmedia_consumer_plugin_unregister(tmedia_consumer_oss_plugin_def_t);
+#endif
+
+ /* === UnRegister Audio Denoise (AGC, VAD, Noise Suppression and AEC) === */
+#if HAVE_SPEEX_DSP
+ tmedia_denoise_plugin_unregister(tdav_speex_denoise_plugin_def_t);
+#endif
+
+ return 0;
+} \ No newline at end of file
diff --git a/tinyDAV/src/tdav_win32.c b/tinyDAV/src/tdav_win32.c
new file mode 100644
index 0000000..858a3c7
--- /dev/null
+++ b/tinyDAV/src/tdav_win32.c
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav_win32.c
+ * @brief tinyDAV WIN32 helper functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/tdav_win32.h"
+
+#if TDAV_UNDER_WINDOWS
+
+#include "tsk_debug.h"
+
+void tdav_win32_print_error(const char* func, HRESULT hr)
+{
+ CHAR* message = tsk_null;
+#ifdef _WIN32_WCE
+ FormatMessage
+#else
+ FormatMessageA
+#endif
+ (
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ tsk_null,
+ hr,
+ 0,
+ message,
+ 0,
+ tsk_null);
+
+ TSK_DEBUG_ERROR("%s():", message);
+
+ LocalFree(message);
+}
+
+
+#endif /* TDAV_UNDER_WINDOWS */ \ No newline at end of file
diff --git a/tinyDAV/src/video/tdav_converter_video.c b/tinyDAV/src/video/tdav_converter_video.c
new file mode 100644
index 0000000..60bf44b
--- /dev/null
+++ b/tinyDAV/src/video/tdav_converter_video.c
@@ -0,0 +1,285 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav_converter_video.c
+* @brief Video converter.
+*
+* @author Mamadou Diop <diopmamadou(at)doubango.org>
+* @author Alex Vishnev (Added support for rotation)
+*
+* @date Created: Sat Nov 8 16:54:58 2009 mdiop
+*/
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+// use macro for performance reasons keep (called (15x3) times per seconds)
+#define rotate90(srcw, srch, srcdata, dstdata) \
+{ \
+ register int i,j; \
+ register int newx = 0; \
+ for (i = 0; i < (int)srcw; i ++ ){ \
+ for( j = srch-1; j >=0; j -- ){ \
+ dstdata[newx++] = srcdata[j * srcw + i]; \
+ } \
+ } \
+}
+
+
+
+tdav_converter_video_t* tdav_converter_video_create(tsk_size_t srcWidth, tsk_size_t srcHeight, tsk_size_t dstWidth, tsk_size_t dstHeight, tmedia_chroma_t chroma, tsk_bool_t toYUV420)
+{
+#if HAVE_FFMPEG || HAVE_SWSSCALE
+ tdav_converter_video_t* converter;
+ enum PixelFormat pixfmt;
+
+ TSK_DEBUG_INFO("Creating new Video Converter src=(%dx%d) dst=(%dx%d)", srcWidth, srcHeight, dstWidth, dstHeight);
+
+ switch(chroma){
+ case tmedia_rgb24:
+ pixfmt = PIX_FMT_RGB24;
+ break;
+ case tmedia_bgr24:
+ pixfmt = PIX_FMT_BGR24;
+ break;
+ case tmedia_rgb32:
+ pixfmt = PIX_FMT_RGB32;
+ break;
+ case tmedia_rgb565le:
+ pixfmt = PIX_FMT_RGB565LE;
+ break;
+ case tmedia_rgb565be:
+ pixfmt = PIX_FMT_RGB565BE;
+ break;
+ case tmedia_nv21:
+ pixfmt = PIX_FMT_NV21;
+ break;
+ case tmedia_nv12:
+ pixfmt = PIX_FMT_NV12;
+ break;
+ case tmedia_yuv422p:
+ pixfmt = PIX_FMT_YUV422P;
+ break;
+ case tmedia_uyvy422:
+ pixfmt = PIX_FMT_UYVY422;
+ break;
+ case tmedia_yuv420p:
+ pixfmt = PIX_FMT_YUV420P;
+ break;
+ default:
+ TSK_DEBUG_ERROR("Invalid chroma");
+ return tsk_null;
+ }
+
+ if(!(converter = tsk_object_new(tdav_converter_video_def_t))){
+ TSK_DEBUG_ERROR("Failed to create Video Converter object");
+ return tsk_null;
+ }
+
+ // Set values
+ converter->toYUV420 = toYUV420;
+ converter->pixfmt = pixfmt;
+ converter->srcWidth = srcWidth ? srcWidth : dstWidth;
+ converter->srcHeight = srcHeight ? srcHeight : dstHeight;
+ converter->dstWidth = dstWidth ? dstWidth : srcWidth;
+ converter->dstHeight = dstHeight ? dstHeight : srcHeight;
+
+ return converter;
+#else
+ return tsk_null;
+#endif
+}
+
+tsk_size_t tdav_converter_video_convert(tdav_converter_video_t* self, const void* buffer, void** output, tsk_size_t* output_max_size)
+{
+#if HAVE_FFMPEG || HAVE_SWSSCALE
+ int ret, size;
+ enum PixelFormat srcFormat, dstFormat;
+
+ if(!self || !buffer || !output){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Formats */
+ srcFormat = self->toYUV420 ? self->pixfmt : PIX_FMT_YUV420P;
+ dstFormat = self->toYUV420 ? PIX_FMT_YUV420P : self->pixfmt;
+
+
+ /* Pictures */
+ if(!self->srcFrame){
+ if(!(self->srcFrame = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create picture");
+ return 0;
+ }
+ }
+ if(!self->dstFrame){
+ if(!(self->dstFrame = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create picture");
+ return 0;
+ }
+ }
+
+ size = avpicture_get_size(dstFormat, self->dstWidth, self->dstHeight);
+ if((int)*output_max_size <size){
+ if(!(*output = tsk_realloc(*output, size))){
+ *output_max_size = 0;
+ TSK_DEBUG_ERROR("Failed to allocate buffer");
+ return 0;
+ }
+ *output_max_size = size;
+ }
+
+ /* Wrap the source buffer */
+ ret = avpicture_fill((AVPicture *)self->srcFrame, (uint8_t*)buffer, srcFormat, self->srcWidth, self->srcHeight);
+ /* Wrap the destination buffer */
+ ret = avpicture_fill((AVPicture *)self->dstFrame, (uint8_t*)*output, dstFormat, self->dstWidth, self->dstHeight);
+
+ /* === performs conversion === */
+ /* Context */
+ if(!self->context){
+ self->context = sws_getContext(
+ self->srcWidth, self->srcHeight, srcFormat,
+ self->dstWidth, self->dstHeight, dstFormat,
+ SWS_FAST_BILINEAR, NULL, NULL, NULL);
+
+ if(!self->context){
+ TSK_DEBUG_ERROR("Failed to create context");
+ return 0;
+ }
+ }
+ // chroma conversion
+ ret = sws_scale(self->context, (const uint8_t* const*)self->srcFrame->data, self->srcFrame->linesize, 0, self->srcHeight,
+ self->dstFrame->data, self->dstFrame->linesize);
+ if(ret < 0){
+ TSK_FREE(*output);
+ return 0;
+ }
+
+ // Rotation
+ if(self->rotation && (PIX_FMT_YUV420P == dstFormat) && self->rotation==90/*FIXME: For now only 90° rotation is supported */){
+ // because we rotated 90 width = original height, height = original width
+ int w = self->dstHeight;
+ int h = self->dstWidth;
+
+ // allocation rotation frame if not already done
+ if(!(self->rot.frame) && !(self->rot.frame = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("failed to allocate rotation frame");
+ TSK_FREE(*output);
+ return(0);
+ }
+
+ // allocate rotation temporary buffer
+ if(!self->rot.buffer){
+ int buff_size = avpicture_get_size(dstFormat, w, h);
+ if (!(self->rot.buffer = (uint8_t *)av_malloc(buff_size))){
+ TSK_DEBUG_ERROR("failed to allocate new buffer for the frame");
+ TSK_FREE(*output);
+ return(0);
+ }
+ }
+
+ //wrap
+ avpicture_fill((AVPicture *)self->rot.frame, self->rot.buffer, dstFormat, w, h);
+ // rotate
+ rotate90(self->dstWidth, self->dstHeight, self->dstFrame->data[0], self->rot.frame->data[0]);
+ rotate90(self->dstWidth/2, self->dstHeight/2, self->dstFrame->data[1], self->rot.frame->data[1]);
+ rotate90(self->dstWidth/2, self->dstHeight/2, self->dstFrame->data[2], self->rot.frame->data[2]);
+
+ /* Context */
+ if(!self->rot.context){
+ if(!(self->rot.context = sws_getContext(w, h, dstFormat, h, w, dstFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL))){
+ TSK_DEBUG_ERROR("Failed to create context");
+ TSK_FREE(*output);
+ return 0;
+ }
+ }
+
+ // Copy frame
+ if((ret = sws_scale(self->rot.context, (const uint8_t* const*)self->rot.frame->data, self->rot.frame->linesize,
+ 0, h, self->dstFrame->data, self->dstFrame->linesize)) <0)
+ {
+ TSK_DEBUG_ERROR("Failed to copy frame");
+ TSK_FREE(*output);
+ return 0;
+ }
+ }//end of rotation
+
+ return size;
+#else
+ return 0;
+#endif
+}
+
+
+
+//=================================================================================================
+// Video Converter object definition
+//
+static tsk_object_t* tdav_converter_video_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_converter_video_t *converter = self;
+ if(converter){
+
+ }
+ return self;
+}
+
+static tsk_object_t* tdav_converter_video_dtor(tsk_object_t * self)
+{
+ tdav_converter_video_t *converter = self;
+ if(converter){
+#if HAVE_FFMPEG || HAVE_SWSSCALE
+ if(converter->context){
+ sws_freeContext(converter->context);
+ }
+ if(converter->srcFrame){
+ av_free(converter->srcFrame);
+ }
+ if(converter->dstFrame){
+ av_free(converter->dstFrame);
+ }
+
+ // Rotation
+ if(converter->rot.context){
+ sws_freeContext(converter->rot.context);
+ }
+ if(converter->rot.frame){
+ av_free(converter->rot.frame);
+ }
+ if(converter->rot.buffer){
+ TSK_FREE(converter->rot.buffer);
+ }
+#endif
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tdav_converter_video_def_s =
+{
+ sizeof(tdav_converter_video_t),
+ tdav_converter_video_ctor,
+ tdav_converter_video_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tdav_converter_video_def_t = &tdav_converter_video_def_s;
diff --git a/tinyDAV/src/video/tdav_runnable_video.c b/tinyDAV/src/video/tdav_runnable_video.c
new file mode 100644
index 0000000..ebb6896
--- /dev/null
+++ b/tinyDAV/src/video/tdav_runnable_video.c
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+/**@file tdav_runnable_video.c
+ * @brief Video runnable used by codecs.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/video/tdav_runnable_video.h"
+
+#include "tsk_buffer.h"
+#include "tsk_debug.h"
+
+tdav_runnable_video_t* tdav_runnable_video_create(tsk_runnable_func_run run_f, const void* userdata)
+{
+ tdav_runnable_video_t* runnable;
+
+ if((runnable = tsk_object_new(tdav_runnable_video_def_t))){
+ TSK_RUNNABLE(runnable)->run = run_f;
+ runnable->userdata = userdata;
+ }
+ return runnable;
+}
+
+int tdav_runnable_video_start(tdav_runnable_video_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return tsk_runnable_start(TSK_RUNNABLE(self), tsk_buffer_def_t);
+}
+
+int tdav_runnable_video_stop(tdav_runnable_video_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return tsk_runnable_stop(TSK_RUNNABLE(self));
+}
+
+
+//=================================================================================================
+// Video Runable object definition
+//
+static tsk_object_t* tdav_runnable_video_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_runnable_video_t *runnable = self;
+ if(runnable){
+
+ }
+ return self;
+}
+
+static tsk_object_t* tdav_runnable_video_dtor(tsk_object_t * self)
+{
+ tdav_runnable_video_t *runnable = self;
+ if(runnable){
+ tsk_runnable_stop(TSK_RUNNABLE(runnable));
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tdav_runnable_video_def_s =
+{
+ sizeof(tdav_runnable_video_t),
+ tdav_runnable_video_ctor,
+ tdav_runnable_video_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tdav_runnable_video_def_t = &tdav_runnable_video_def_s;
diff --git a/tinyDAV/src/video/tdav_session_video.c b/tinyDAV/src/video/tdav_session_video.c
new file mode 100644
index 0000000..c276fe8
--- /dev/null
+++ b/tinyDAV/src/video/tdav_session_video.c
@@ -0,0 +1,675 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file tdav_session_video.c
+ * @brief Video Session plugin.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinydav/video/tdav_session_video.h"
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinymedia/tmedia_consumer.h"
+#include "tinymedia/tmedia_producer.h"
+
+#include "tinyrtp/trtp_manager.h"
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+// RTP/RTCP callback (From the network to the consumer)
+static int tdav_session_video_rtp_cb(const void* callback_data, const struct trtp_rtp_packet_s* packet)
+{
+ tdav_session_video_t* session = (tdav_session_video_t*)callback_data;
+ int ret = 0;
+
+ if(!session || !packet){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(session->consumer){
+ tmedia_codec_t* codec;
+ tsk_istr_t format;
+ tsk_size_t out_size;
+
+ // Find the codec to use to decode the RTP payload
+ tsk_itoa(packet->header->payload_type, &format);
+ if(!(codec = tmedia_codec_find_by_format(TMEDIA_SESSION(session)->neg_codecs, format)) || !codec->plugin || !codec->plugin->decode){
+ TSK_DEBUG_ERROR("%s is not a valid payload for this session", format);
+ ret = -2;
+ goto bail;
+ }
+ // Open codec if not already done
+ if(!TMEDIA_CODEC(codec)->opened){
+ tsk_safeobj_lock(session);
+ if((ret = tmedia_codec_open(codec))){
+ tsk_safeobj_unlock(session);
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", codec->plugin->desc);
+ goto bail;
+ }
+ tsk_safeobj_unlock(session);
+ }
+ // Decode data
+ out_size = codec->plugin->decode(codec, packet->payload.data, packet->payload.size, &session->decoder.buffer, &session->decoder.buffer_size, packet->header);
+ // check
+ if(!out_size || !session->decoder.buffer){
+ goto bail;
+ }
+
+ // Convert decoded data to the consumer chroma and size
+#define CONSUMER_INSIZE_CHANGED ((session->consumer->video.in.width * session->consumer->video.in.height * 3)/2 != out_size)// we have good reasons not to use 1.5f
+#define CONSUMER_DISPLAY_NEED_RESIZE (session->consumer->video.in.width != session->consumer->video.display.width || session->consumer->video.in.height != session->consumer->video.display.height)
+#define CONSUMER_DISPLAYSIZE_CHANGED (session->conv.consumerLastWidth != session->consumer->video.display.width || session->conv.consumerLastHeight != session->consumer->video.display.height)
+#define CONSUMER_DISPLAY_NEED_CHROMACHANGE (session->consumer->video.display.chroma != tmedia_yuv420p)
+
+ if((CONSUMER_DISPLAY_NEED_CHROMACHANGE || CONSUMER_DISPLAYSIZE_CHANGED || CONSUMER_DISPLAY_NEED_RESIZE || CONSUMER_INSIZE_CHANGED)){
+ tsk_size_t _output_size;
+
+ // Create video converter if not already done
+ if(!session->conv.fromYUV420 || CONSUMER_DISPLAYSIZE_CHANGED || CONSUMER_INSIZE_CHANGED){
+ const tmedia_video_size_t* video_size = tmedia_get_video_size(tmedia_yuv420p, out_size);
+ TSK_OBJECT_SAFE_FREE(session->conv.fromYUV420);
+ // update in (set by the codec)
+ session->consumer->video.in.width = video_size->width;
+ session->consumer->video.in.height = video_size->height;
+
+ // important: do not override the display size (used by the end-user) unless requested
+ if(session->consumer->video.display.auto_resize){
+ session->consumer->video.display.width = session->consumer->video.in.width;
+ session->consumer->video.display.height = session->consumer->video.in.height;
+ }
+ // set xdisplay with latest valid sizes (set by the user)
+ session->conv.consumerLastWidth = session->consumer->video.display.width;
+ session->conv.consumerLastHeight = session->consumer->video.display.height;
+ if(!(session->conv.fromYUV420 = tdav_converter_video_create(video_size->width, video_size->height, session->conv.consumerLastWidth, session->conv.consumerLastHeight,
+ session->consumer->video.display.chroma, tsk_false))){
+ TSK_DEBUG_ERROR("Failed to create video converter");
+ ret = -3;
+ goto bail;
+ }
+ }
+ // convert data to the consumer's chroma
+ _output_size = tdav_converter_video_convert(session->conv.fromYUV420, session->decoder.buffer, &session->decoder.conv_buffer, &session->decoder.conv_buffer_size);
+ if(!_output_size || !session->decoder.conv_buffer){
+ TSK_DEBUG_ERROR("Failed to convert YUV420 buffer to consumer's chroma");
+ ret = -4;
+ goto bail;
+ }
+
+ tmedia_consumer_consume(session->consumer, &session->decoder.conv_buffer, _output_size, packet->header);
+ if(!session->decoder.conv_buffer){
+ /* taken by the consumer */
+ session->decoder.conv_buffer_size = 0;
+ }
+
+ }
+ else{
+ tmedia_consumer_consume(session->consumer, &session->decoder.buffer, out_size, packet->header);
+ if(!session->decoder.buffer){
+ /* taken by the consumer */
+ session->decoder.buffer_size = 0;
+ }
+ }
+
+bail:
+ TSK_OBJECT_SAFE_FREE(codec);
+ }
+ return ret;
+}
+
+// Codec callback (From codec/producer to the network) to send() data "as is"
+static int tdav_session_video_raw_cb(const void* callback_data, const void* buffer, tsk_size_t size, uint32_t duration, tsk_bool_t marker)
+{
+ tdav_session_video_t* session = (tdav_session_video_t*)callback_data;
+
+ if(session->rtp_manager && session->rtp_manager->started){
+ return trtp_manager_send_rtp(session->rtp_manager, buffer, size, duration, marker, marker);
+ }
+
+ return 0;
+}
+
+// Producer callback (From the producer to the network) => encode data before send()
+static int tdav_session_video_producer_enc_cb(const void* callback_data, const void* buffer, tsk_size_t size)
+{
+ tdav_session_video_t* session = (tdav_session_video_t*)callback_data;
+ tsk_size_t yuv420p_size = 0;
+ int ret = 0;
+
+ if(session && session->rtp_manager){
+ /* encode */
+ tsk_size_t out_size = 0;
+ tmedia_codec_t* codec = tsk_null;
+
+ // Use first codec to encode data
+ if((codec = tsk_object_ref(TSK_LIST_FIRST_DATA(TMEDIA_SESSION(session)->neg_codecs)))){
+ if(!codec->plugin || !codec->plugin->encode){
+ TSK_OBJECT_SAFE_FREE(codec);
+ TSK_DEBUG_ERROR("Invalid codec");
+ return -2;
+ }
+ // open the codec if not already done
+ if(!TMEDIA_CODEC(codec)->opened){
+ tsk_safeobj_lock(session);
+ if((ret = tmedia_codec_open(codec))){
+ tsk_safeobj_unlock(session);
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", codec->plugin->desc);
+ TSK_OBJECT_SAFE_FREE(codec);
+ return ret;
+ }
+ tsk_safeobj_unlock(session);
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to find a valid codec");
+ return -4;
+ }
+
+
+#define PRODUCER_SIZE_CHANGED (session->conv.producerWidth != session->producer->video.width) || (session->conv.producerHeight != session->producer->video.height) \
+|| (session->conv.xProducerSize != size)
+ // Video codecs only accept YUV420P buffers ==> do conversion if needed or producer doesn't have the right size
+ if((session->producer->video.chroma != tmedia_yuv420p) || PRODUCER_SIZE_CHANGED){
+ // Create video converter if not already done or producer size has changed
+ if(!session->conv.toYUV420 || PRODUCER_SIZE_CHANGED){
+ TSK_OBJECT_SAFE_FREE(session->conv.toYUV420);
+ session->conv.producerWidth = session->producer->video.width;
+ session->conv.producerHeight = session->producer->video.height;
+ session->conv.xProducerSize = size;
+
+ if(!(session->conv.toYUV420 = tdav_converter_video_create(session->producer->video.width, session->producer->video.height, TMEDIA_CODEC_VIDEO(codec)->width, TMEDIA_CODEC_VIDEO(codec)->height,
+ session->producer->video.chroma, tsk_true))){
+ TSK_DEBUG_ERROR("Failed to create video converter");
+ ret = -5;
+ goto bail;
+ }
+ }
+ // update one-shot parameters
+ tdav_converter_video_init(session->conv.toYUV420, session->producer->video.rotation);
+ // convert data to yuv420p
+ yuv420p_size = tdav_converter_video_convert(session->conv.toYUV420, buffer, &session->encoder.conv_buffer, &session->encoder.conv_buffer_size);
+ if(!yuv420p_size || !session->encoder.conv_buffer){
+ TSK_DEBUG_ERROR("Failed to convert XXX buffer to YUV42P");
+ ret = -6;
+ goto bail;
+ }
+ }
+
+ // Encode data
+ if(session->encoder.conv_buffer && yuv420p_size){
+ /* producer doesn't support yuv42p */
+ out_size = codec->plugin->encode(codec, session->encoder.conv_buffer, yuv420p_size, &session->encoder.buffer, &session->encoder.buffer_size);
+ }
+ else{
+ /* producer supports yuv42p */
+ out_size = codec->plugin->encode(codec, buffer, size, &session->encoder.buffer, &session->encoder.buffer_size);
+ }
+
+ if(out_size){
+ /* Never called, see tdav_session_video_raw_cb() */
+ trtp_manager_send_rtp(session->rtp_manager, session->encoder.buffer, out_size, 6006, tsk_true, tsk_true);
+ }
+bail:
+ TSK_OBJECT_SAFE_FREE(codec);
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/* ============ Plugin interface ================= */
+
+int tmedia_session_video_set(tmedia_session_t* self, const tmedia_param_t* param)
+{
+ int ret = 0;
+ tdav_session_video_t* video;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ video = (tdav_session_video_t*)self;
+
+ if(param->plugin_type == tmedia_ppt_consumer){
+ if(!video->consumer){
+ TSK_DEBUG_ERROR("No consumer associated to this session");
+ return -1;
+ }
+ ret = tmedia_consumer_set(video->consumer, param);
+ }
+ else if(param->plugin_type == tmedia_ppt_producer){
+ if(!video->producer){
+ TSK_DEBUG_ERROR("No producer associated to this session");
+ return -1;
+ }
+ ret = tmedia_producer_set(video->producer, param);
+ }
+ else{
+ if(param->value_type == tmedia_pvt_pchar){
+ if(tsk_striequals(param->key, "remote-ip")){
+ /* only if no ip associated to the "m=" line */
+ if(param->value && !video->remote_ip){
+ video->remote_ip = tsk_strdup(param->value);
+ }
+ }
+ else if(tsk_striequals(param->key, "local-ip")){
+ tsk_strupdate(&video->local_ip, param->value);
+ }
+ else if(tsk_striequals(param->key, "local-ipver")){
+ video->useIPv6 = tsk_striequals(param->value, "ipv6");
+ }
+ }
+ else if(param->value_type == tmedia_pvt_int32){
+ if(tsk_striequals(param->key, "bandwidth-level")){
+ tsk_list_item_t* item;
+ self->bl = (tmedia_bandwidth_level_t) TSK_TO_UINT32((uint8_t*)param->value);
+ self->codecs = tsk_object_ref(self->codecs);
+ tsk_list_foreach(item, self->codecs){
+ ((tmedia_codec_t*)item->data)->bl = self->bl;
+ }
+ tsk_object_unref(self->codecs);
+ }
+ }
+ else if(param->value_type == tmedia_pvt_pobject){
+ if(tsk_striequals(param->key, "natt-ctx")){
+ TSK_OBJECT_SAFE_FREE(video->natt_ctx);
+ video->natt_ctx = tsk_object_ref(param->value);
+ }
+ }
+ }
+
+ return ret;
+}
+
+int tdav_session_video_prepare(tmedia_session_t* self)
+{
+ tdav_session_video_t* video;
+ int ret = 0;
+
+ video = (tdav_session_video_t*)self;
+
+ /* set local port */
+ if(!video->rtp_manager){
+ if((video->rtp_manager = trtp_manager_create(video->rtcp_enabled, video->local_ip, video->useIPv6))){
+
+ ret = trtp_manager_set_rtp_callback(video->rtp_manager, tdav_session_video_rtp_cb, video);
+ ret = trtp_manager_prepare(video->rtp_manager);
+ if(video->natt_ctx){
+ ret = trtp_manager_set_natt_ctx(video->rtp_manager, video->natt_ctx);
+ }
+ }
+ }
+
+ /* Consumer will be prepared in tdav_session_video_start() */
+ /* Producer will be prepared in tdav_session_video_start() */
+
+ return ret;
+}
+
+int tdav_session_video_start(tmedia_session_t* self)
+{
+ tdav_session_video_t* video;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ video = (tdav_session_video_t*)self;
+
+ if(TSK_LIST_IS_EMPTY(self->neg_codecs)){
+ TSK_DEBUG_ERROR("No codec matched");
+ return -2;
+ }
+
+ if(video->rtp_manager){
+ int ret;
+ const tmedia_codec_t* codec = (const tmedia_codec_t*)TSK_LIST_FIRST_DATA(self->neg_codecs);
+ /* RTP/RTCP manager: use latest information. */
+ ret = trtp_manager_set_rtp_remote(video->rtp_manager, video->remote_ip, video->remote_port);
+ trtp_manager_set_payload_type(video->rtp_manager, codec->neg_format ? atoi(codec->neg_format) : atoi(codec->format));
+ ret = trtp_manager_start(video->rtp_manager);
+
+ /* Consumer */
+ if(video->consumer){
+ tmedia_consumer_prepare(video->consumer, codec);
+ tmedia_consumer_start(video->consumer);
+ }
+ /* Producer */
+ if(video->producer){
+ tmedia_producer_prepare(video->producer, codec);
+ tmedia_producer_start(video->producer);
+ }
+
+ /* for test */
+ //trtp_manager_send_rtp(video->rtp_manager, "test", 4, tsk_true);
+ return ret;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid RTP/RTCP manager or neg_codecs");
+ return -3;
+ }
+}
+
+int tdav_session_video_stop(tmedia_session_t* self)
+{
+ tdav_session_video_t* video;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ video = (tdav_session_video_t*)self;
+
+ /* RTP/RTCP manager */
+ if(video->rtp_manager){
+ trtp_manager_stop(video->rtp_manager);
+ }
+
+ /* Consumer */
+ if(video->consumer){
+ tmedia_consumer_stop(video->consumer);
+ }
+ /* Producer */
+ if(video->producer){
+ tmedia_producer_stop(video->producer);
+ }
+
+ return 0;
+}
+
+int tdav_session_video_pause(tmedia_session_t* self)
+{
+ tdav_session_video_t* video;
+
+ video = (tdav_session_video_t*)self;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* Consumer */
+ if(video->consumer){
+ tmedia_consumer_pause(video->consumer);
+ }
+ /* Producer */
+ if(video->producer){
+ tmedia_producer_pause(video->producer);
+ }
+
+ return 0;
+}
+
+const tsdp_header_M_t* tdav_session_video_get_lo(tmedia_session_t* self)
+{
+ tdav_session_video_t* video;
+ tsk_bool_t changed = tsk_false;
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ video = (tdav_session_video_t*)self;
+
+ if(!video->rtp_manager || !video->rtp_manager->transport){
+ TSK_DEBUG_ERROR("RTP/RTCP manager in invalid");
+ return tsk_null;
+ }
+
+ if(self->ro_changed && self->M.lo){
+ /* Codecs */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "fmtp");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "rtpmap");
+ tsk_list_clear_items(self->M.lo->FMTs);
+
+ /* QoS */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "curr");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "des");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "conf");
+ }
+
+ changed = (self->ro_changed || !self->M.lo);
+
+ if(!self->M.lo){
+ if((self->M.lo = tsdp_header_M_create(self->plugin->media, video->rtp_manager->rtp.public_port, "RTP/AVP"))){
+ /* If NATT is active, do not rely on the global IP address Connection line */
+ if(video->natt_ctx){
+ tsdp_header_M_add_headers(self->M.lo,
+ TSDP_HEADER_C_VA_ARGS("IN", video->useIPv6 ? "IP6" : "IP4", video->rtp_manager->rtp.public_ip),
+ tsk_null);
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create lo");
+ return tsk_null;
+ }
+ }
+
+ /* from codecs to sdp */
+ if(changed){
+ tmedia_codecs_L_t* neg_codecs = tsk_null;
+
+ if(self->M.ro){
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+ /* update negociated codecs */
+ if((neg_codecs = tmedia_session_match_codec(self, self->M.ro))){
+ self->neg_codecs = neg_codecs;
+ // set video codec callback
+ if(!TSK_LIST_IS_EMPTY(self->neg_codecs)){
+ tmedia_codec_video_set_callback((tmedia_codec_video_t*)TSK_LIST_FIRST_DATA(self->neg_codecs), tdav_session_video_raw_cb, self);
+ }
+ }
+ /* from codecs to sdp */
+ if(self->neg_codecs){
+ tmedia_codec_to_sdp(self->neg_codecs, self->M.lo);
+ }
+ else{
+ self->M.lo->port = 0; /* Keep the RTP transport and reuse it when we receive a reINVITE or UPDATE request */
+ goto DONE;
+ }
+ }
+ else{
+ /* from codecs to sdp */
+ tmedia_codec_to_sdp(self->codecs, self->M.lo);
+ }
+
+ /* Hold/Resume */
+ if(self->M.ro){
+ if(tsdp_header_M_is_held(self->M.ro, tsk_false)){
+ tsdp_header_M_hold(self->M.lo, tsk_false);
+ }
+ else{
+ tsdp_header_M_resume(self->M.lo, tsk_false);
+ }
+ }
+
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ tmedia_qos_tline_to_sdp(self->qos, self->M.lo);
+ }
+DONE:;
+ }
+
+ return self->M.lo;
+}
+
+int tdav_session_video_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ tdav_session_video_t* video;
+ tmedia_codecs_L_t* neg_codecs;
+
+ if(!self || !m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ video = (tdav_session_video_t*)self;
+
+ /* update remote offer */
+ TSK_OBJECT_SAFE_FREE(self->M.ro);
+ self->M.ro = tsk_object_ref((void*)m);
+
+ if(self->M.lo){
+ if((neg_codecs = tmedia_session_match_codec(self, m))){
+ /* update negociated codecs */
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+ self->neg_codecs = neg_codecs;
+ // set codec callback
+ if(!TSK_LIST_IS_EMPTY(self->neg_codecs)){
+ tmedia_codec_video_set_callback((tmedia_codec_video_t*)TSK_LIST_FIRST_DATA(self->neg_codecs), tdav_session_video_raw_cb, self);
+ }
+ }
+ else{
+ return -1;
+ }
+
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ }
+ }
+
+ /* get connection associated to this media line
+ * If the connnection is global, then the manager will call tmedia_session_video_set() */
+ if(m->C && m->C->addr){
+ tsk_strupdate(&video->remote_ip, m->C->addr);
+ video->useIPv6 = tsk_striequals(m->C->addrtype, "IP6");
+ }
+ /* set remote port */
+ video->remote_port = m->port;
+
+
+ return 0;
+}
+
+
+
+
+
+//=================================================================================================
+// Session Video Plugin object definition
+//
+/* constructor */
+static tsk_object_t* tdav_session_video_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_session_video_t *session = self;
+ if(session){
+ /* init base: called by tmedia_session_create() */
+ /* init self */
+ tsk_safeobj_init(session);
+ if(!(session->consumer = tmedia_consumer_create(tdav_session_video_plugin_def_t->type, TMEDIA_SESSION(session)->id))){
+ TSK_DEBUG_ERROR("Failed to create Video consumer");
+ }
+ if((session->producer = tmedia_producer_create(tdav_session_video_plugin_def_t->type, TMEDIA_SESSION(session)->id))){
+ tmedia_producer_set_enc_callback(session->producer, tdav_session_video_producer_enc_cb, self);
+ tmedia_producer_set_raw_callback(session->producer, tdav_session_video_raw_cb, self);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create Video producer");
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_session_video_dtor(tsk_object_t * self)
+{
+ tdav_session_video_t *session = self;
+ if(session){
+
+ // Do it in this order (deinit self first)
+
+ /* deinit self (rtp manager should be destroyed after the producer) */
+ TSK_OBJECT_SAFE_FREE(session->consumer);
+ TSK_OBJECT_SAFE_FREE(session->producer);
+ TSK_OBJECT_SAFE_FREE(session->conv.toYUV420);
+ TSK_OBJECT_SAFE_FREE(session->conv.fromYUV420);
+ TSK_OBJECT_SAFE_FREE(session->rtp_manager);
+ TSK_FREE(session->remote_ip);
+ TSK_FREE(session->local_ip);
+
+ TSK_FREE(session->encoder.buffer);
+ TSK_FREE(session->encoder.conv_buffer);
+ TSK_FREE(session->decoder.buffer);
+ TSK_FREE(session->decoder.conv_buffer);
+
+ /* NAT Traversal context */
+ TSK_OBJECT_SAFE_FREE(session->natt_ctx);
+
+ tsk_safeobj_deinit(session);
+
+ /* deinit base */
+ tmedia_session_deinit(self);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_session_video_def_s =
+{
+ sizeof(tdav_session_video_t),
+ tdav_session_video_ctor,
+ tdav_session_video_dtor,
+ tmedia_session_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tdav_session_video_plugin_def_s =
+{
+ &tdav_session_video_def_s,
+
+ tmedia_video,
+ "video",
+
+ tmedia_session_video_set,
+ tdav_session_video_prepare,
+ tdav_session_video_start,
+ tdav_session_video_pause,
+ tdav_session_video_stop,
+
+ /* Audio part */
+ { tsk_null },
+
+ tdav_session_video_get_lo,
+ tdav_session_video_set_ro
+};
+const tmedia_session_plugin_def_t *tdav_session_video_plugin_def_t = &tdav_session_video_plugin_def_s;
diff --git a/tinyDAV/test/test.c b/tinyDAV/test/test.c
new file mode 100644
index 0000000..4e97f50
--- /dev/null
+++ b/tinyDAV/test/test.c
@@ -0,0 +1,61 @@
+/*
+* Copyright (C) 2009 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+#include <string.h>
+#include <stdlib.h>
+
+#include "tinydav.h"
+
+#include "test_sessions.h"
+
+#define LOOP 0
+
+#define RUN_TEST_ALL 0
+#define RUN_TEST_SESSIONS 1
+
+// Codecs : http://www.itu.int/rec/T-REC-G.191-200509-S/en
+
+#ifdef _WIN32_WCE
+int _tmain(int argc, _TCHAR* argv[])
+#else
+int main()
+#endif
+{
+ tnet_startup();
+ tdav_init();
+
+ do{
+ /* Print copyright information */
+ printf("Doubango Project\nCopyright (C) 2009-2010 Mamadou Diop \n\n");
+
+#if RUN_TEST_SESSIONS || RUN_TEST_ALL
+ test_sessions();
+#endif
+
+ }
+ while(LOOP);
+
+ tdav_deinit();
+ tnet_cleanup();
+
+ return 0;
+}
+
diff --git a/tinyDAV/test/test.vcproj b/tinyDAV/test/test.vcproj
new file mode 100644
index 0000000..0fe7313
--- /dev/null
+++ b/tinyDAV/test/test.vcproj
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="test"
+ ProjectGUID="{91E0D694-5735-46C2-A56C-F159234C8B30}"
+ RootNamespace="test"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\thirdparties\win32\include;..\..\tinyMEDIA\include;..\..\tinySDP\include;..\..\tinyDAV\include;..\..\tinySAK\src;..\..\tinyNET\src;..\..\tinyMSRP\include"
+ PreprocessorDefinitions="DEBUG_LEVEL=DEBUG_LEVEL_INFO;WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ WarnAsError="true"
+ DebugInformationFormat="4"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyDAV.lib $(OutDir)\tinySDP.lib $(OutDir)\tinyMEDIA.lib $(OutDir)\tinyMSRP.lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="source"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\test.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="include"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="tests"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\test_sessions.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/tinyDAV/test/test_sessions.h b/tinyDAV/test/test_sessions.h
new file mode 100644
index 0000000..c94361a
--- /dev/null
+++ b/tinyDAV/test/test_sessions.h
@@ -0,0 +1,217 @@
+/*
+* Copyright (C) 2009 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+#ifndef _TINYDEV_TEST_SESSIONS_H
+#define _TINYDEV_TEST_SESSIONS_H
+
+#include "tinymsrp.h"
+
+#define SDP_RO \
+ "v=0\r\n" \
+ "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n" \
+ "s=\r\n" \
+ "i=A Seminar on the session description protocol\r\n" \
+ "u=http://www.example.com/seminars/sdp.pdf\r\n" \
+ "e=j.doe@example.com (Jane Doe)\r\n" \
+ "p=+1 617 555-6011\r\n" \
+ "c=IN IP4 192.168.0.13\r\n" \
+ "b=X-YZ:128\r\n" \
+ "z=2882844526 -1h 2898848070 0\r\n" \
+ "k=base64:ZWFzdXJlLg==\r\n" \
+ "t=3034423619 3042462419\r\n" \
+ "r=7d 1h 0 25h\r\n" \
+ "r=604800 3600 0 90000\r\n" \
+ "w=my dummy header\r\n" \
+ "m=audio 6061 RTP/AVP 18 0 115 97 96 3 102 103 8\r\n" \
+ "i=Audio line\r\n" \
+ "c=IN IP4 192.168.0.13\r\n" \
+ "k=base64:ZWFzdXJlLgdddddddddddddddddddddd==\r\n" \
+ "a=rtpmap:96 iLBC/8000\r\n" \
+ "a=rtpmap:3 GSM/8000\r\n" \
+ "a=rtpmap:8 PCMA/8000\r\n" \
+ "a=rtpmap:0 PCMU/8000\r\n" \
+ "a=rtpmap:103 AMR/8000\r\n" \
+ "a=rtpmap:102 AMR/8000\r\n" \
+ "a=rtpmap:97 SPEEX/8000\r\n" \
+ "a=rtpmap:115 BV16/8000\r\n" \
+ "a=fmtp:102 octet-align=0; mode-set=0,1,2,3,4,5,6,7; mode-change-period=1; mode-change-capability=2; mode-change-neighbor=0\r\n" \
+ "a=fmtp:103 octet-align=1; mode-set=0,1,2,3,4,5,6,7; mode-change-period=1; mode-change-capability=2; mode-change-neighbor=0\r\n" \
+ "m=video 6061 RTP/AVP 111 121 98 31 126 34 32\r\n" \
+ "i=Video line\r\n" \
+ "b=A-YZ:92\r\n" \
+ "b=B-YZ:256\r\n" \
+ "a=rtpmap:121 MP4V-ES/90000\r\n" \
+ "a=fmtp:121 profile-level-id=1\r\n" \
+ "a=rtpmap:126 theora/90000\r\n" \
+ "a=rtpmap:34 H263/90000\r\n" \
+ "a=fmtp:34 QCIF=1 MaxBR=4520\r\n" \
+ "a=rtpmap:111 H263-1998/90000\r\n" \
+ "a=rtpmap:31 H261/90000\r\n" \
+ "a=rtpmap:32 MPV/90000\r\n" \
+ "a=rtpmap:98 H264/90000\r\n" \
+ "a=fmtp:98 profile-level-id=42e00a; packetization-mode=1; max-br=452; max-mbps=11880\r\n" \
+ "a=recvonly\r\n" \
+ "m=toto 51372 RTP/AVP 31 32\r\n" \
+ "i=Video line\r\n" \
+ "b=A-YZ:92\r\n" \
+ "b=B-YZ:256\r\n" \
+ "a=rtpmap:31 H261/90000\r\n" \
+ "a=rtpmap:32 MPV/90000\r\n" \
+ "a=recvonly\r\n" \
+ "m=message 2000 TCP/MSRP *\r\n" \
+ "c=IN IP4 192.168.0.14\r\n" \
+ "a=path:msrp://192.168.0.12:2000/fdxfircvscx;tcp\r\n" \
+ "a=accept-types:message/CPIM\r\n" \
+ "a=setup:passive\r\n" \
+ "a=file-transfer-id:wcysyycqpevikeffmznimkkasvwsrenz\r\n" \
+ "a=file-selector:name:\"test.zip\" type:application/octet-stream size:11376 hash:sha-1:8D:55:24:2B:F4:F9:9B:A2:54:A3:5B:91:00:15:9E:A3:D4:48:D7:DF\r\n" \
+
+static int test_session_msrp_cb(const tmsrp_event_t* _event);
+
+void test_sessions_client()
+{
+ tmedia_session_mgr_t* mgr;
+ const tsdp_message_t* sdp_lo;
+ tsdp_message_t* sdp_ro;
+ char* temp;
+ tmedia_type_t type = tmedia_video/*tmedia_msrp | tmedia_audio*//*| tmedia_video tmedia_msrp*/;
+
+ mgr = tmedia_session_mgr_create(type,
+ "192.168.0.13", tsk_false, tsk_true/* offerer */);
+
+ /* set MSRP callback */
+ //tmedia_session_mgr_set_msrp_cb(mgr, tsk_null, test_session_msrp_cb);
+
+ /* MSRP File Transfer */
+ /*tmedia_session_mgr_set(mgr,
+ TMEDIA_SESSION_MSRP_SET_STR("file-path", "C:\\avatar.png"),
+ TMEDIA_SESSION_MSRP_SET_STR("accept-types", "message/CPIM application/octet-stream"),
+ TMEDIA_SESSION_MSRP_SET_STR("accept-wrapped-types", "application/octet-stream"),
+ TMEDIA_SESSION_MSRP_SET_STR("accept-wrapped-types", "application/octet-stream"),
+ TMEDIA_SESSION_MSRP_SET_STR("file-selector", "name:\"test.zip\" type:application/octet-stream size:20312 hash:sha-1:27:D0:AE:39:48:77:37:1D:FD:39:7E:2D:78:2F:BC:7B:94:48:29:81"),
+ TMEDIA_SESSION_MSRP_SET_STR("file-disposition", "attachment"),
+ TMEDIA_SESSION_MSRP_SET_STR("file-date", "creation:2010-02-13T17:50:31.763Z"),
+ TMEDIA_SESSION_MSRP_SET_STR("file-icon", "cid:test@doubango.org"),
+
+ TMEDIA_SESSION_SET_NULL());*/
+
+
+ /* get lo */
+ sdp_lo = tmedia_session_mgr_get_lo(mgr);
+ if((temp = tsdp_message_tostring(sdp_lo))){
+ TSK_DEBUG_INFO("sdp_lo=%s", temp);
+ TSK_FREE(temp);
+ }
+
+ /* set ro */
+ if((sdp_ro = tsdp_message_parse(SDP_RO, tsk_strlen(SDP_RO)))){
+ tmedia_session_mgr_set_ro(mgr, sdp_ro);
+ TSK_OBJECT_SAFE_FREE(sdp_ro);
+ }
+
+ /* start() */
+ tmedia_session_mgr_start(mgr);
+
+ /* send file */
+ //tmedia_session_mgr_send_file(mgr, "C:\\avatar.png", TMEDIA_SESSION_SET_NULL());
+
+ //getchar();
+
+ /* for fun, send DTMF */
+ //tmedia_session_mgr_send_dtmf(mgr, 1);
+ //tmedia_session_mgr_send_dtmf(mgr, 10);
+ //tmedia_session_mgr_send_dtmf(mgr, 11);
+
+ getchar();
+
+ /* stop() */
+ //tmedia_session_mgr_stop(mgr);
+
+ //getchar();
+
+ TSK_OBJECT_SAFE_FREE(mgr);
+}
+
+void test_sessions_server()
+{
+ tmedia_session_mgr_t* mgr;
+ const tsdp_message_t* sdp_lo;
+ tsdp_message_t* sdp_ro;
+ char* temp;
+ tmedia_type_t type;
+
+ /* get ro (INVITE) */
+ if((sdp_ro = tsdp_message_parse(SDP_RO, tsk_strlen(SDP_RO)))){
+ //type = tmedia_type_from_sdp(sdp_ro);
+ type = tmedia_video;
+ mgr = tmedia_session_mgr_create(type,
+ "192.168.0.13", tsk_false, tsk_false/* answerer */);
+ tmedia_session_mgr_set_ro(mgr, sdp_ro);
+ TSK_OBJECT_SAFE_FREE(sdp_ro);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to deserialize remote sdp");
+ return;
+ }
+
+ /* get lo (200 OK) */
+ sdp_lo = tmedia_session_mgr_get_lo(mgr);
+ if((temp = tsdp_message_tostring(sdp_lo))){
+ TSK_DEBUG_INFO("sdp_lo=%s", temp);
+ TSK_FREE(temp);
+ }
+
+ /* ACK */
+
+ /* start() */
+ tmedia_session_mgr_start(mgr);
+
+ getchar();
+
+ /* stop() */
+ //tmedia_session_mgr_stop(mgr);
+
+ //getchar();
+
+ TSK_OBJECT_SAFE_FREE(mgr);
+}
+
+
+void test_sessions()
+{
+ test_sessions_client();
+ //test_sessions_server();
+}
+
+
+int test_session_msrp_cb(const tmsrp_event_t* _event)
+{
+ if(TMSRP_MESSAGE_IS_REQUEST(_event->message)){
+ TSK_DEBUG_INFO("Received MSRP request");
+ }
+ else{
+ TSK_DEBUG_INFO("Received MSRP response");
+ }
+
+ return 0;
+}
+
+#endif /* _TINYDEV_TEST_SESSIONS_H */
diff --git a/tinyDAV/tinyDAV.sln b/tinyDAV/tinyDAV.sln
new file mode 100644
index 0000000..1a4703f
--- /dev/null
+++ b/tinyDAV/tinyDAV.sln
@@ -0,0 +1,184 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyDAV", "tinyDAV.vcproj", "{8E2F0B2E-2596-4010-BF4A-2F688975B5C1}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyMEDIA", "..\tinyMEDIA\tinyMEDIA.vcproj", "{52814B0D-7DCA-45B8-9A16-8B147040D619}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinySAK", "..\tinySAK\tinySAK.vcproj", "{6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyNET", "..\tinyNET\tinyNET.vcproj", "{7522A458-92F4-4259-B906-E84C2A65D9F1}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinySDP", "..\tinySDP\tinySDP.vcproj", "{E45DB518-6562-4033-80E8-60030F0B169F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcproj", "{91E0D694-5735-46C2-A56C-F159234C8B30}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyRTP", "..\tinyRTP\tinyRTP.vcproj", "{99B7D02F-8C70-4B45-AF3C-92313C3CEE15}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyDSHOW", "..\tinyDSHOW\tinyDSHOW.vcproj", "{0CCC02F1-4233-424F-AD5E-A021456E6E8D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyMSRP", "..\tinyMSRP\tinyMSRP.vcproj", "{AA46AF16-9678-4054-8E48-98DC21ECEC82}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyHTTP", "..\tinyHTTP\tinyHTTP.vcproj", "{B3E45009-C7C3-4090-837C-2D30C9058443}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ Release|Win32 = Release|Win32
+ Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ Static_Debug|Win32 = Static_Debug|Win32
+ Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ Static_Release|Win32 = Static_Release|Win32
+ Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Debug|Win32.Build.0 = Debug|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Release|Win32.ActiveCfg = Release|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Release|Win32.Build.0 = Release|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Static_Release|Win32.Build.0 = Release|Win32
+ {8E2F0B2E-2596-4010-BF4A-2F688975B5C1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Debug|Win32.ActiveCfg = Debug|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Debug|Win32.Build.0 = Debug|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Release|Win32.ActiveCfg = Release|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Release|Win32.Build.0 = Release|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Static_Release|Win32.Build.0 = Release|Win32
+ {52814B0D-7DCA-45B8-9A16-8B147040D619}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Win32.Build.0 = Debug|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Win32.ActiveCfg = Release|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Win32.Build.0 = Release|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Win32.ActiveCfg = Static_Debug|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Win32.Build.0 = Static_Debug|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Win32.ActiveCfg = Static_Release|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Win32.Build.0 = Static_Release|Win32
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Win32.Build.0 = Debug|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Win32.ActiveCfg = Release|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Win32.Build.0 = Release|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Win32.Build.0 = Release|Win32
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Debug|Win32.Build.0 = Debug|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Release|Win32.ActiveCfg = Release|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Release|Win32.Build.0 = Release|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Static_Release|Win32.Build.0 = Release|Win32
+ {E45DB518-6562-4033-80E8-60030F0B169F}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Debug|Win32.ActiveCfg = Debug|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Debug|Win32.Build.0 = Debug|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Release|Win32.ActiveCfg = Release|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Release|Win32.Build.0 = Release|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Static_Release|Win32.Build.0 = Release|Win32
+ {91E0D694-5735-46C2-A56C-F159234C8B30}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Debug|Win32.ActiveCfg = Debug|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Debug|Win32.Build.0 = Debug|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Release|Win32.ActiveCfg = Release|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Release|Win32.Build.0 = Release|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Release|Win32.Build.0 = Release|Win32
+ {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Debug|Win32.Build.0 = Debug|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Release|Win32.ActiveCfg = Release|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Release|Win32.Build.0 = Release|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Static_Release|Win32.Build.0 = Release|Win32
+ {0CCC02F1-4233-424F-AD5E-A021456E6E8D}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Debug|Win32.ActiveCfg = Debug|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Debug|Win32.Build.0 = Debug|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Release|Win32.ActiveCfg = Release|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Release|Win32.Build.0 = Release|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Static_Release|Win32.Build.0 = Release|Win32
+ {AA46AF16-9678-4054-8E48-98DC21ECEC82}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Debug|Win32.Build.0 = Debug|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Release|Win32.ActiveCfg = Release|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Release|Win32.Build.0 = Release|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Debug|Win32.Build.0 = Debug|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Release|Win32.ActiveCfg = Release|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Release|Win32.Build.0 = Release|Win32
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ {B3E45009-C7C3-4090-837C-2D30C9058443}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/tinyDAV/tinyDAV.vcproj b/tinyDAV/tinyDAV.vcproj
new file mode 100644
index 0000000..5165699
--- /dev/null
+++ b/tinyDAV/tinyDAV.vcproj
@@ -0,0 +1,710 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="tinyDAV"
+ ProjectGUID="{8E2F0B2E-2596-4010-BF4A-2F688975B5C1}"
+ RootNamespace="tinyDAV"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="&quot;$(PSDK_DIR)include&quot;;&quot;$(DXSDK_DIR)include&quot;;..\thirdparties\win32\include;include;..\tinyMSRP\include;..\tinyRTP\include;..\tinyMEDIA\include;..\tinySDP\include;..\tinyNET\src;..\tinyDSHOW\include;..\tinySAK\src;..\thirdparties\win32\include\BroadVoice16\bvcommon;..\thirdparties\win32\include\BroadVoice16\bv16"
+ PreprocessorDefinitions="HAVE_G729=0;HAVE_BV16=0;HAVE_OPENCORE_AMR=1;HAVE_ILBC=0;HAVE_LIBGSM=1;HAVE_TINYDSHOW=1;HAVE_DSOUND_H=1;HAVE_WAVE_API=0;HAVE_FFMPEG=1;HAVE_SPEEX_DSP=1;HAVE_LIB_SPEEX=1;FLIP_ENCODED_PICT=1;FLIP_DECODED_PICT=1;G192BITSTREAM=0;DEBUG_LEVEL=DEBUG_LEVEL_INFO;WIN32;_DEBUG;_WINDOWS;_USRDLL;_WIN32_WINNT=0x0501;TINYDAV_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="Winmm.lib $(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyRTP.lib $(OutDir)\tinyMSRP.lib $(OutDir)\tinySDP.lib $(OutDir)\tinyMEDIA.lib $(OutDir)\tinyDSHOW.lib &quot;..\thirdparties\win32\lib\gsm\libgsm.a&quot; &quot;..\thirdparties\win32\lib\ilbc\libiLBC.a&quot; &quot;..\thirdparties\win32\lib\speex\libspeex.a&quot; &quot;..\thirdparties\win32\lib\speex\libspeexdsp.a&quot; ..\thirdparties\win32\lib\libgcc.a ..\thirdparties\win32\lib\libmingwex.a &quot;..\thirdparties\win32\lib\ffmpeg\libavcodec.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libavutil.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libswscale.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libavcore.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libx264.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libtheora.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libogg.a&quot;"
+ LinkIncremental="2"
+ IgnoreDefaultLibraryNames="MSVCRT"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ EnableIntrinsicFunctions="false"
+ AdditionalIncludeDirectories="&quot;$(PSDK_DIR)include&quot;;&quot;$(DXSDK_DIR)include&quot;;..\thirdparties\win32\include;include;..\tinyMSRP\include;..\tinyRTP\include;..\tinyMEDIA\include;..\tinySDP\include;..\tinyNET\src;..\tinyDSHOW\include;..\tinySAK\src;..\thirdparties\win32\include\BroadVoice16\bvcommon;..\thirdparties\win32\include\BroadVoice16\bv16"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;TINYDAV_EXPORTS;DEBUG_LEVEL=DEBUG_LEVEL_INFOS;HAVE_G729=0;HAVE_BV16=0;HAVE_OPENCORE_AMR=1;HAVE_ILBC=1;HAVE_LIBGSM=1;HAVE_TINYDSHOW=1;HAVE_DSOUND_H=1;HAVE_WAVE_API=0;HAVE_FFMPEG=1;HAVE_SPEEX_DSP=1;HAVE_LIB_SPEEX=1;FLIP_ENCODED_PICT=1;FLIP_DECODED_PICT=1;G192BITSTREAM=0;_WIN32_WINNT=0x0501"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="0"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="Winmm.lib $(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyRTP.lib $(OutDir)\tinyMSRP.lib $(OutDir)\tinySDP.lib $(OutDir)\tinyMEDIA.lib $(OutDir)\tinyDSHOW.lib &quot;..\thirdparties\win32\lib\gsm\libgsm.a&quot; &quot;..\thirdparties\win32\lib\ilbc\libiLBC.a&quot; &quot;..\thirdparties\win32\lib\speex\libspeex.a&quot; &quot;..\thirdparties\win32\lib\speex\libspeexdsp.a&quot; ..\thirdparties\win32\lib\libgcc.a ..\thirdparties\win32\lib\libmingwex.a &quot;..\thirdparties\win32\lib\ffmpeg\libavcodec.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libavutil.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libswscale.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libavcore.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libx264.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libtheora.a&quot; &quot;..\thirdparties\win32\lib\ffmpeg\libogg.a&quot; &quot;..\thirdparties\win32\lib\BroadVoice16\libbv16.a&quot; &quot;..\thirdparties\win32\lib\opencore\libopencore-amrnb.a&quot;"
+ LinkIncremental="1"
+ IgnoreDefaultLibraryNames="MSVCRTD"
+ GenerateDebugInformation="false"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ RandomizedBaseAddress="2"
+ DataExecutionPrevention="0"
+ TargetMachine="0"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="headers(*.h)"
+ >
+ <File
+ RelativePath=".\include\tinydav\tdav.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\tdav_win32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav_config.h"
+ >
+ </File>
+ <Filter
+ Name="codecs"
+ >
+ <Filter
+ Name="amr"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\amr\tdav_codec_amr.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="g711"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\g711\g711.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\codecs\g711\tdav_codec_g711.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="g722"
+ >
+ </Filter>
+ <Filter
+ Name="gsm"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\gsm\tdav_codec_gsm.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="h261"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\h261\tdav_codec_h261.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="h263"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\h263\tdav_codec_h263.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="h264"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\h264\tdav_codec_h264.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\codecs\h264\tdav_codec_h264_rtp.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="ilbc"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\ilbc\tdav_codec_ilbc.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="theora"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\theora\tdav_codec_theora.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="speex"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\speex\tdav_codec_speex.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="silk"
+ >
+ </Filter>
+ <Filter
+ Name="msrp"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\msrp\tdav_codec_msrp.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="dtmf"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\dtmf\tdav_codec_dtmf.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="bv"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\bv\tdav_codec_bv16.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\codecs\bv\tdav_codec_bv32.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="g729"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\g729\tdav_codec_g729.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="mp4ves"
+ >
+ <File
+ RelativePath=".\include\tinydav\codecs\mp4ves\tdav_codec_mp4ves.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="audio"
+ >
+ <File
+ RelativePath=".\include\tinydav\audio\tdav_consumer_audio.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\audio\tdav_jitterbuffer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\audio\tdav_producer_audio.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\audio\tdav_session_audio.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\audio\tdav_speex_denoise.h"
+ >
+ </File>
+ <Filter
+ Name="android"
+ >
+ </Filter>
+ <Filter
+ Name="directsound"
+ >
+ <File
+ RelativePath=".\include\tinydav\audio\directsound\tdav_consumer_dsound.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\audio\directsound\tdav_producer_dsound.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="oss"
+ >
+ </Filter>
+ <Filter
+ Name="waveapi"
+ >
+ <File
+ RelativePath=".\include\tinydav\audio\waveapi\tdav_consumer_waveapi.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\audio\waveapi\tdav_producer_waveapi.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="coreaudio"
+ >
+ <File
+ RelativePath=".\include\tinydav\audio\coreaudio\tdav_consumer_coreaudio.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="video"
+ >
+ <File
+ RelativePath=".\include\tinydav\video\tdav_converter_video.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\video\tdav_runnable_video.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\video\tdav_session_video.h"
+ >
+ </File>
+ <Filter
+ Name="android"
+ >
+ </Filter>
+ <Filter
+ Name="directshow"
+ >
+ </Filter>
+ <Filter
+ Name="v4linux"
+ >
+ </Filter>
+ </Filter>
+ <Filter
+ Name="msrp"
+ >
+ <File
+ RelativePath=".\include\tinydav\msrp\tdav_consumer_msrp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\msrp\tdav_producer_msrp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\tinydav\msrp\tdav_session_msrp.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="source(*.c)"
+ >
+ <File
+ RelativePath=".\src\tdav.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\tdav_win32.c"
+ >
+ </File>
+ <Filter
+ Name="codecs"
+ >
+ <Filter
+ Name="amr"
+ >
+ <File
+ RelativePath=".\src\codecs\amr\tdav_codec_amr.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="g711"
+ >
+ <File
+ RelativePath=".\src\codecs\g711\g711.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\codecs\g711\tdav_codec_g711.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="g722"
+ >
+ </Filter>
+ <Filter
+ Name="gsm"
+ >
+ <File
+ RelativePath=".\src\codecs\gsm\tdav_codec_gsm.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="h261"
+ >
+ <File
+ RelativePath=".\src\codecs\h261\tdav_codec_h261.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="h263"
+ >
+ <File
+ RelativePath=".\src\codecs\h263\tdav_codec_h263.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="h264"
+ >
+ <File
+ RelativePath=".\src\codecs\h264\tdav_codec_h264.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\codecs\h264\tdav_codec_h264_rtp.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="ilbc"
+ >
+ <File
+ RelativePath=".\src\codecs\ilbc\tdav_codec_ilbc.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="silk"
+ >
+ </Filter>
+ <Filter
+ Name="speex"
+ >
+ <File
+ RelativePath=".\src\codecs\speex\tdav_codec_speex.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="theora"
+ >
+ <File
+ RelativePath=".\src\codecs\theora\tdav_codec_theora.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="msrp"
+ >
+ <File
+ RelativePath=".\src\codecs\msrp\tdav_codec_msrp.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="dtmf"
+ >
+ <File
+ RelativePath=".\src\codecs\dtmf\tdav_codec_dtmf.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="bv"
+ >
+ <File
+ RelativePath=".\src\codecs\bv\tdav_codec_bv16.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\codecs\bv\tdav_codec_bv32.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="g729"
+ >
+ <File
+ RelativePath=".\src\codecs\g729\tdav_codec_g729.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="mp4ves"
+ >
+ <File
+ RelativePath=".\src\codecs\mp4ves\tdav_codec_mp4ves.c"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="audio"
+ >
+ <File
+ RelativePath=".\src\audio\tdav_consumer_audio.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\tdav_jitterbuffer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\tdav_producer_audio.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\tdav_session_audio.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\tdav_speex_denoise.c"
+ >
+ </File>
+ <Filter
+ Name="android"
+ >
+ </Filter>
+ <Filter
+ Name="directsound"
+ >
+ <File
+ RelativePath=".\src\audio\directsound\tdav_consumer_dsound.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\directsound\tdav_producer_dsound.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="oss"
+ >
+ </Filter>
+ <Filter
+ Name="waveapi"
+ >
+ <File
+ RelativePath=".\src\audio\waveapi\tdav_consumer_waveapi.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\waveapi\tdav_producer_waveapi.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="coreaudio"
+ >
+ <File
+ RelativePath=".\src\audio\coreaudio\tdav_consumer_coreaudio.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\audio\coreaudio\tdav_producer_coreaudio.c"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="video"
+ >
+ <File
+ RelativePath=".\src\video\tdav_converter_video.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\video\tdav_runnable_video.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\video\tdav_session_video.c"
+ >
+ </File>
+ <Filter
+ Name="android"
+ >
+ </Filter>
+ <Filter
+ Name="directshow"
+ >
+ </Filter>
+ <Filter
+ Name="v4linux"
+ >
+ </Filter>
+ </Filter>
+ <Filter
+ Name="msrp"
+ >
+ <File
+ RelativePath=".\src\msrp\tdav_consumer_msrp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\msrp\tdav_producer_msrp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\msrp\tdav_session_msrp.c"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
OpenPOWER on IntegriCloud