Update media layer.

This commit is contained in:
bossiel 2010-06-09 01:16:07 +00:00
parent 37a0c93c2f
commit dcdb3dd3ac
51 changed files with 22672 additions and 100 deletions

View File

@ -1,7 +1,7 @@
#!/bin/bash
# Build tinyDEMO for Google Android Systems
for project in tinySAK tinyNET tinyIPSec tinySMS tinyHTTP tinySDP tinyMEDIA tinySIP tinyDEMO
for project in tinySAK tinyNET tinyIPSec tinySMS tinyHTTP tinySDP tinyMEDIA tinySIP tinyDAV tinyDEMO
do
echo -e building "$project....\n"
make PROJECT=$project clean

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
APP := lib$(PROJECT).$(EXT)
FFMPEG_CFLAGS := -I../thirdparties/android/include
FFMPEG_LDFLAGS := -L../thirdparties/android/lib -lavutil -lswscale -lavcodec -lgcc
CFLAGS := $(CFLAGS_LIB) $(FFMPEG_CFLAGS) -I../tinySAK/src -I../tinyNET/src -I../tinySDP/include -I../tinyMEDIA/include -I./include
LDFLAGS := $(LDFLAGS_LIB) -lm $(FFMPEG_LDFLAGS) -ltinySAK -ltinyNET -ltinySDP -ltinyMEDIA
all: $(APP)
OBJS = \
src/tdav.o
### audio
OBJS += src/audio/tdav_session_audio.o
### codecs
OBJS += src/codecs/g711/tdav_codec_g711.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)

View File

@ -0,0 +1,78 @@
/*
* 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 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;
} jb;
TSK_DECLARE_SAFEOBJ;
}
tdav_consumer_audio_t;
int tdav_consumer_audio_init(tdav_consumer_audio_t* self);
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))
int tdav_consumer_audio_put(tdav_consumer_audio_t* self, void** data);
int tdav_consumer_audio_get(tdav_consumer_audio_t* self);
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 */

View File

@ -0,0 +1,326 @@
/* 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 */
#define JB_HISTORY_SIZE 500
/* 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_ */

View File

@ -0,0 +1,62 @@
/*
* 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 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
typedef struct tdav_producer_audio_s
{
TMEDIA_DECLARE_PRODUCER;
uint8_t channels;
uint32_t rate;
uint8_t bits_per_sample;
}
tdav_producer_audio_t;
int tdav_producer_audio_init(tdav_producer_audio_t* self);
int tdav_producer_audio_prepare(tdav_producer_audio_t *self);
int tdav_producer_audio_start(tdav_producer_audio_t *self);
int tdav_producer_audio_consume(tdav_producer_audio_t* self, const void* buffer, tsk_size_t size);
int tdav_producer_audio_pause(tdav_producer_audio_t *self);
int tdav_producer_audio_stop(tdav_producer_audio_t *self);
int tdav_producer_audio_deinit(tdav_producer_audio_t* self);
TDAV_END_DECLS
#endif /* TINYDAV_PRODUCER_AUDIO_H */

View File

@ -34,16 +34,35 @@
#include "tinymedia/tmedia_session.h"
TDAV_BEGIN_DECLS
// Forward declaration
struct trtp_manager_s;
struct tdav_consumer_audio_s;
typedef struct tdav_session_audio_s
{
TMEDIA_DECLARE_SESSION_AUDIO;
uint16_t local_port;
tsk_bool_t useIPv6;
char* local_ip;
//uint16_t local_port;
char* remote_ip;
uint16_t remote_port;
tsk_bool_t rtcp_enabled;
struct trtp_manager_s* rtp_manager;
struct tmedia_consumer_s* consumer;
}
tdav_session_audio_t;
TINYDAV_GEXTERN const tmedia_session_plugin_def_t *tdav_session_audio_plugin_def_t;
TDAV_END_DECLS
#endif /* TINYDAV_SESSION_AUDIO_H */

View File

@ -0,0 +1,64 @@
/*
* 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 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"
#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 *tmedia_consumer_waveapi_plugin_def_t;
TDAV_END_DECLS
#endif /* TINYDAV_CONSUMER_WAVEAPI_H */

View File

@ -0,0 +1,44 @@
/*
* 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 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 */

View File

@ -34,6 +34,8 @@
#include "tinymedia/tmedia_codec.h"
TDAV_BEGIN_DECLS
/** G.711u codec */
typedef struct tdav_codec_g711u_s
{
@ -52,4 +54,6 @@ 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 */

View File

@ -31,7 +31,11 @@
#include "tinydav_config.h"
TDAV_BEGIN_DECLS
TINYDAV_API int tdav_init();
TINYDAV_API int tdav_deinit();
TDAV_END_DECLS
#endif /* TINYMEDIA_TDAV_H */

View File

@ -75,6 +75,7 @@
#include <stdlib.h>
#endif
#if HAVE_CONFIG_H
#include "../config.h"
#endif

View File

@ -0,0 +1,167 @@
/*
* 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 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 "tsk_memory.h"
#include "tsk_time.h"
#include "tsk_debug.h"
#define TDAV_BITS_PER_SAMPLE_DEFAULT 16
#define TDAV_CHANNELS_DEFAULT 2
#define TDAV_RATE_DEFAULT 8000
#define TDAV_PTIME_DEFAULT 20
/** 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;
}
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 into the jitter buffer */
int tdav_consumer_audio_put(tdav_consumer_audio_t* self, void** data)
{
static long ts = 0; /* FIXME: should come from the the RTP header */
if(!self || !data || !*data || !self->jb.jbuffer){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(self);
ts += (self->ptime * self->rate)/1000;
jb_put(self->jb.jbuffer, *data, JB_TYPE_VOICE, self->ptime, ts, (long)tsk_time_now(), self->jb.jcodec);
*data = tsk_null;
tsk_safeobj_unlock(self);
return 0;
}
/* get data drom the jitter buffer */
int tdav_consumer_audio_get(tdav_consumer_audio_t* self)
{
void* data = tsk_null;
int jret;
if(!self || !self->jb.jbuffer){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(self);
jret = jb_get(self->jb.jbuffer, (void**)&data, (long)tsk_time_now(), self->ptime);
tsk_safeobj_unlock(self);
switch(jret){
case JB_OK:
break;
case JB_INTERP:
{
jb_reset_all(self->jb.jbuffer);
}
break;
case JB_EMPTY:
case JB_NOFRAME:
case JB_NOJB:
default:
break;
}
TSK_FREE(data);
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_safeobj_deinit(self);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
/*
* 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 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"

View File

@ -29,41 +29,146 @@
*/
#include "tinydav/audio/tdav_session_audio.h"
#include "tinymedia/tmedia_consumer.h"
#include "tinyrtp/trtp_manager.h"
#include "tinyrtp/rtp/trtp_rtp_packet.h"
#include "tsk_memory.h"
#include "tsk_debug.h"
extern const tmedia_codec_t* _tmedia_session_match_codec(tmedia_session_t* self, const tsdp_header_M_t* M, char** format);
static int tdav_session_audio_rtp_cb(const void* callback_data, 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){
return tmedia_consumer_consume(audio->consumer, packet->payload.data, packet->payload.size);
}
return 0;
}
/* ============ Plugin interface ================= */
int tmedia_session_audio_configure(tmedia_session_t* self, const va_list *app)
int tmedia_session_audio_set(tmedia_session_t* self, va_list *app)
{
int ret = 0;
tdav_session_audio_t* audio;
tmedia_session_param_type_t type;
TSK_DEBUG_INFO("tmedia_session_audio_configure");
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
TSK_DEBUG_INFO("tmedia_session_audio_set");
audio = (tdav_session_audio_t*)self;
while((type = va_arg(*app, tmedia_session_param_type_t)) != tmedia_sptype_null){
switch(type){
case tmedia_sptype_remote_ip:
{ /* (const char*) IP_STR */
const char* ip = va_arg(*app, const char *);
/* only if no ip associated to the "m=" line */
if(ip && !audio->remote_ip){
audio->remote_ip = tsk_strdup(ip);
}
break;
}
case tmedia_sptype_local_ip:
{ /* IP_STR, IPv6_BOOL */
tsk_strupdate(&audio->local_ip, va_arg(*app, const char *));
audio->useIPv6 = va_arg(*app, tsk_bool_t);
break;
}
case tmedia_sptype_set_rtcp:
{/* (tsk_bool_t)ENABLED_BOOL */
audio->rtcp_enabled = va_arg(*app, tsk_bool_t);
break;
}
default:
{ /* MUST exit if unknown parameter */
if((ret = tmedia_session_skip_param(type, app))){
TSK_DEBUG_ERROR("Unknown parameter");
goto bail;
}
break;
}
}
}
return 0;
bail:
return ret;
}
int tdav_session_audio_prepare(tmedia_session_t* self)
{
tdav_session_audio_t* audio;
int ret = 0;
TSK_DEBUG_INFO("tdav_session_audio_prepare");
audio = (tdav_session_audio_t*)self;
/* set local port */
audio->local_port = rand() ^ rand();
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);
}
}
return 0;
/* 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;
TSK_DEBUG_INFO("tdav_session_audio_start");
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
audio = (tdav_session_audio_t*)self;
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, self->negociated_format ? atoi(self->negociated_format) : 255);
ret = trtp_manager_start(audio->rtp_manager);
/* Consumer */
if(audio->consumer){
tmedia_consumer_prepare(audio->consumer, self->negociated_codec);
tmedia_consumer_start(audio->consumer);
}
/* for test */
//trtp_manager_send_rtp(audio->rtp_manager, "test", 4, tsk_true);
return ret;
}
else{
TSK_DEBUG_ERROR("Invalid RTP/RTCP manager");
return -2;
}
return 0;
}
@ -73,17 +178,47 @@ int tdav_session_audio_stop(tmedia_session_t* self)
TSK_DEBUG_INFO("tdav_session_audio_stop");
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);
}
/* very important */
audio->local_port = 0;
//audio->local_port = 0;
return 0;
}
int tdav_session_audio_pause(tmedia_session_t* self)
{
tdav_session_audio_t* audio;
TSK_DEBUG_INFO("tdav_session_audio_pause");
audio = (tdav_session_audio_t*)self;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
/* Consumer */
if(audio->consumer){
tmedia_consumer_pause(audio->consumer);
}
return 0;
}
@ -100,6 +235,11 @@ const tsdp_header_M_t* tdav_session_audio_get_lo(tmedia_session_t* self)
audio = (tdav_session_audio_t*)self;
if(!audio->rtp_manager || !audio->rtp_manager->rtp.local_socket){
TSK_DEBUG_ERROR("RTP/RTCP manager in invalid");
return tsk_null;
}
if(self->ro_changed && self->M.lo){
TSK_OBJECT_SAFE_FREE(self->M.lo);
}
@ -107,22 +247,14 @@ const tsdp_header_M_t* tdav_session_audio_get_lo(tmedia_session_t* self)
if(self->M.lo){
return self->M.lo;
}
else if(!(self->M.lo = tsdp_header_M_create(self->plugin->media, audio->local_port, "RTP/AVP"))){
else if(!(self->M.lo = tsdp_header_M_create(self->plugin->media, audio->rtp_manager->rtp.local_socket->port, "RTP/AVP"))){
TSK_DEBUG_ERROR("Failed to create lo");
return tsk_null;
}
/* from codecs to sdp */
if(self->negociated_codec && !TSK_LIST_IS_EMPTY(self->codecs)){
/* filter codecs */
tmedia_codec_removeAll_exceptThis(self->codecs, self->negociated_codec);
/* update format */
if(self->negociated_codec->dyn){
tmedia_codec_to_sdp_2(self->codecs->head->data, self->M.lo, self->negociated_format);
}
else{
tmedia_codec_to_sdp_2(self->codecs->head->data, self->M.lo, tsk_null);
}
/* codec to sdp */
if(self->negociated_codec){
tmedia_codec_to_sdp_2(self->codecs->head->data, self->M.lo, self->negociated_format);
}
else{
tmedia_codec_to_sdp(self->codecs, self->M.lo);
@ -134,19 +266,39 @@ const tsdp_header_M_t* tdav_session_audio_get_lo(tmedia_session_t* self)
int tdav_session_audio_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
{
const tmedia_codec_t* codec;
tdav_session_audio_t* audio;
TSK_DEBUG_INFO("tdav_session_audio_set_ro");
if(!self || !m){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
audio = (tdav_session_audio_t*)self;
if((codec = tmedia_session_match_codec(self, m, &self->negociated_format))){
/* update negociated codec */
TSK_OBJECT_SAFE_FREE(self->negociated_codec);
self->negociated_codec = tsk_object_ref((void*)codec);
/* filter codecs */
tmedia_codec_removeAll_exceptThis(self->codecs, self->negociated_codec);
/* update remote offer */
TSK_OBJECT_SAFE_FREE(self->M.ro);
self->M.ro = tsk_object_ref((void*)m);
/* get connection associated to this media line
* If the connnection is at session-level, then the manager will call tmedia_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;
}
return -1;
}
@ -164,6 +316,9 @@ static tsk_object_t* tdav_session_audio_ctor(tsk_object_t * self, va_list * app)
if(session){
/* init base: called by tmedia_session_create() */
/* init self */
if(!(session->consumer = tmedia_consumer_create(tdav_session_audio_plugin_def_t->type))){
TSK_DEBUG_ERROR("Failed to create Audio consumer");
}
}
return self;
}
@ -175,6 +330,10 @@ static tsk_object_t* tdav_session_audio_dtor(tsk_object_t * self)
/* deinit base */
tmedia_session_deinit(self);
/* deinit self */
TSK_OBJECT_SAFE_FREE(session->rtp_manager);
TSK_OBJECT_SAFE_FREE(session->consumer);
TSK_FREE(session->remote_ip);
TSK_FREE(session->local_ip);
}
return self;
@ -195,11 +354,12 @@ static const tmedia_session_plugin_def_t tdav_session_audio_plugin_def_s =
tmedia_audio,
"audio",
tmedia_session_audio_configure,
tmedia_session_audio_set,
tdav_session_audio_prepare,
tdav_session_audio_start,
tdav_session_audio_stop,
tdav_session_audio_pause,
tdav_session_audio_stop,
tdav_session_audio_get_lo,
tdav_session_audio_set_ro

View File

@ -0,0 +1,409 @@
/*
* 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 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"
#include "tsk_thread.h"
#include "tsk_memory.h"
#include "tsk_debug.h"
#define TDAV_WAVEAPI_CONSUMER_ERROR_BUFF_COUNT 0xFF
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;
tsk_size_t i;
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
//
//
tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer)); /* FIXXXXXXXXXXXME */
for(i = 0; i<lpHdr->dwBufferLength; i++){
lpHdr->lpData[i] = rand();
}
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;
}
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");
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;
TSK_DEBUG_INFO("tdav_consumer_waveapi_prepare");
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;
TSK_DEBUG_INFO("tdav_consumer_waveapi_start");
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], tsk_null, 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, const void* buffer, tsk_size_t size)
{
tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
void* data;
TSK_DEBUG_INFO("tdav_consumer_waveapi_consume");
if(!consumer || !buffer || !size){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
/* buffer is already decoded */
/* FIXME: should own the buffer */
data = tsk_calloc(size, sizeof(uint8_t));
memcpy(data, buffer, size);
return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(consumer), &data);
}
int tdav_consumer_waveapi_pause(tmedia_consumer_t* self)
{
tdav_consumer_waveapi_t* consumer = (tdav_consumer_waveapi_t*)self;
TSK_DEBUG_INFO("tdav_consumer_waveapi_pause");
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;
TSK_DEBUG_INFO("tdav_consumer_waveapi_stop");
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 tmedia_consumer_waveapi_plugin_def_s =
{
&tdav_consumer_waveapi_def_s,
tmedia_audio,
"Microsoft WaveAPI consumer",
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 *tmedia_consumer_waveapi_plugin_def_t = &tmedia_consumer_waveapi_plugin_def_s;

View File

@ -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)));
}

View File

@ -28,30 +28,74 @@
*/
#include "tinydav/tdav.h"
// Sessions
#include "tinymedia/tmedia_session_ghost.h"
#include "tinydav/audio/tdav_session_audio.h"
// Codecs
#include "tinydav/codecs/g711/tdav_codec_g711.h"
// Consumers
#include "tinydav/audio/waveapi/tdav_consumer_waveapi.h"
// Producers
#if 0
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#endif
int tdav_init()
{
#if 0
AVCodecContext * context_encode;
AVCodec* codec;
/* === Initialize ffmpeg === */
avcodec_init();
#endif
/* === Register sessions === */
tmedia_session_plugin_register(tmedia_session_ghost_plugin_def_t);
tmedia_session_plugin_register(tdav_session_audio_plugin_def_t);
/* === Register codecs === */
#if 0
avcodec_register_all();
#endif
tmedia_codec_plugin_register(tdav_codec_g711a_plugin_def_t);
tmedia_codec_plugin_register(tdav_codec_g711u_plugin_def_t);
/* === Register consumers === */
tmedia_consumer_plugin_register(tmedia_consumer_waveapi_plugin_def_t);
//if((context_encode = avcodec_alloc_context())){
// printf("avcodec_alloc_context()");
//}
//if((codec = avcodec_find_encoder(CODEC_ID_H263))){
// printf("avcodec_find_encoder(H.263)");
//}
//if((codec = avcodec_find_encoder(CODEC_ID_H264))){
// printf("avcodec_find_encoder(H.264)");
//}
return 0;
}
int tdav_deinit()
{
/* === UnRegister sessions === */
tmedia_session_plugin_register(tmedia_session_ghost_plugin_def_t);
tmedia_session_plugin_unregister(tdav_session_audio_plugin_def_t);
/* === UnRegister codecs === */
tmedia_codec_plugin_unregister(tdav_codec_g711a_plugin_def_t);
tmedia_codec_plugin_unregister(tdav_codec_g711u_plugin_def_t);
/* === unRegister consumers === */
tmedia_consumer_plugin_unregister(tmedia_consumer_waveapi_plugin_def_t);
return 0;
}

View File

@ -24,11 +24,14 @@
#include "tinydav.h"
#include "test_sessions.h"
#define LOOP 1
#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[])
@ -43,6 +46,10 @@ int main()
/* 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);

View File

@ -42,7 +42,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\thirdparties\win32\include;..\..\tinyMEDIA\include;..\..\tinySDP\include;..\..\tinyDAV\include;..\..\tinySAK\src;..\..\tinyNET\src"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
PreprocessorDefinitions="DEBUG_LEVEL=DEBUG_LEVEL_INFO;WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
@ -63,7 +63,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="$(OutDir)\tinyNET.lib $(OutDir)\tinyDAV.lib"
AdditionalDependencies="$(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyDAV.lib $(OutDir)\tinySDP.lib $(OutDir)\tinyMEDIA.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
@ -190,6 +190,10 @@
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>

View File

@ -0,0 +1,154 @@
/*
* 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
#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.16\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 49170 RTP/AVP 8 0 97 98\r\n" \
"i=Audio line\r\n" \
"c=IN IP4 192.168.0.13\r\n" \
"k=base64:ZWFzdXJlLgdddddddddddddddddddddd==\r\n" \
"a=rtpmap:8 PCMA/8000\r\n" \
"a=rtpmap:0 PCMU/8000\r\n" \
"a=rtpmap:97 iLBC/8000\r\n" \
"a=rtpmap:98 AMR-WB/16000\r\n" \
"a=fmtp:98 octet-align=1\r\n" \
"m=video 51372 RTP/AVP 31 32 98\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=rtpmap:98 H264/90000\r\n" \
"a=fmtp:98 profile-level-id=42A01E\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"
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_audio | tmedia_video | tmedia_msrp | tmedia_t38;
mgr = tmedia_session_mgr_create(type,
"192.168.0.12", tsk_false, tsk_true/* offerer */);
/* 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);
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);
mgr = tmedia_session_mgr_create(type,
"192.168.0.12", 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();
}
#endif /* _TINYDEV_TEST_SESSIONS_H */

View File

@ -41,8 +41,8 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\thirdparties\win32\include;include;..\tinyMEDIA\include;..\tinySDP\include;..\tinySAK\src"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;TINYDAV_EXPORTS"
AdditionalIncludeDirectories="..\thirdparties\win32\include;include;..\tinyRTP\include;..\tinyMEDIA\include;..\tinySDP\include;..\tinyNET\src;..\tinySAK\src"
PreprocessorDefinitions="DEBUG_LEVEL=DEBUG_LEVEL_INFO;WIN32;_DEBUG;_WINDOWS;_USRDLL;TINYDAV_EXPORTS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
@ -64,7 +64,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="$(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyRTP.lib $(OutDir)\tinySDP.lib $(OutDir)\tinyMEDIA.lib"
AdditionalDependencies="Winmm.lib $(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyRTP.lib $(OutDir)\tinySDP.lib $(OutDir)\tinyMEDIA.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="2"
@ -199,6 +199,10 @@
<Filter
Name="g711"
>
<File
RelativePath=".\include\tinydav\codecs\g711\g711.h"
>
</File>
<File
RelativePath=".\include\tinydav\codecs\g711\tdav_codec_g711.h"
>
@ -244,6 +248,18 @@
<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"
>
@ -263,6 +279,14 @@
<Filter
Name="waveapi"
>
<File
RelativePath=".\include\tinydav\audio\waveapi\tdav_consumer_waveapi.h"
>
</File>
<File
RelativePath=".\include\tinydav\audio\waveapi\tdav_producer_wave_api.h"
>
</File>
</Filter>
</Filter>
<Filter
@ -299,6 +323,10 @@
<Filter
Name="g711"
>
<File
RelativePath=".\src\codecs\g711\g711.c"
>
</File>
<File
RelativePath=".\src\codecs\g711\tdav_codec_g711.c"
>
@ -344,6 +372,18 @@
<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"
>
@ -363,6 +403,14 @@
<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>
<Filter

View File

@ -71,10 +71,10 @@
#send INVITE
++a --to sip:bob@$$(domain) --header Supported=100rel >>(inv_audio_sid)
++sleep --sec -1
++ho --sid $$(inv_audio_sid)
++sleep --sec -1
++res --sid $$(inv_audio_sid)
#++sleep --sec -1
#++ho --sid $$(inv_audio_sid)
#++sleep --sec -1
#++res --sid $$(inv_audio_sid)
# Press ENTER

View File

@ -1,7 +1,9 @@
APP := demo
CFLAGS := $(CFLAGS_COMMON) -I../tinySAK/src -I../tinyNET/src -I../tinySMS/include -I../tinyHTTP/include -I../tinySIP/include
LDFLAGS := $(LDFLAGS_COMMON) -Wl,-Bsymbolic,--whole-archive -Wl,--entry=main -lm -ltinySAK -ltinyNET -ltinyHTTP -ltinyIPSec -ltinySMS -ltinySIP
FFMPEG_LDFLAGS := -L../thirdparties/android/lib -lavutil -lswscale -lavcodec -lgcc
CFLAGS := $(CFLAGS_COMMON) -I../tinySAK/src -I../tinyNET/src -I../tinySMS/include -I../tinyHTTP/include -I../tinySDP/include -I../tinyDAV/include -I../tinyMEDIA/include -I../tinySIP/include
LDFLAGS := $(LDFLAGS_COMMON) -Wl,-Bsymbolic,--whole-archive -Wl,--entry=main -lm $(FFMPEG_LDFLAGS) -ltinySAK -ltinyNET -ltinyHTTP -ltinyIPSec -ltinySMS -ltinySDP -ltinyMEDIA -ltinyDAV -ltinySIP
all: $(APP)

View File

@ -149,7 +149,7 @@ typedef struct tmedia_codec_plugin_def_s
const char* format;
//! whether the pay. type is dyn. or not
tsk_bool_t dyn;
int32_t rate;
uint32_t rate;
/* default values could be updated at any time */
struct{

View File

@ -32,6 +32,7 @@
#include "tinymedia_config.h"
#include "tinymedia/tmedia_codec.h"
#include "tmedia_common.h"
/**Max number of plugins (consumer types) we can create */
@ -40,7 +41,7 @@
/** cast any pointer to @ref tmedia_consumer_t* object */
#define TMEDIA_CONSUMER(self) ((tmedia_consumer_t*)(self))
/** Base object for all Producers */
/** Base object for all Consumers */
typedef struct tmedia_consumer_s
{
TSK_DECLARE_OBJECT;
@ -64,7 +65,7 @@ typedef struct tmedia_consumer_plugin_def_s
//! full description (usefull for debugging)
const char* desc;
int (* prepare) (tmedia_consumer_t* );
int (* prepare) (tmedia_consumer_t*, const tmedia_codec_t* );
int (* start) (tmedia_consumer_t* );
int (* consume) (tmedia_consumer_t*, const void* buffer, tsk_size_t size);
int (* pause) (tmedia_consumer_t* );
@ -72,15 +73,18 @@ typedef struct tmedia_consumer_plugin_def_s
}
tmedia_consumer_plugin_def_t;
#define TMEDIA_DECLARE_CONSUMER tmedia_consumer_t __consumer__
TINYMEDIA_API tmedia_consumer_t* tmedia_consumer_create(tmedia_type_t type);
TINYMEDIA_API int tmedia_consumer_init(tmedia_consumer_t* self);
TINYMEDIA_API int tmedia_consumer_prepare(tmedia_consumer_t *self);
TINYMEDIA_API int tmedia_consumer_prepare(tmedia_consumer_t *self, const tmedia_codec_t* codec);
TINYMEDIA_API int tmedia_consumer_start(tmedia_consumer_t *self);
TINYMEDIA_API int tmedia_consumer_consume(tmedia_consumer_t* self, const void* buffer, tsk_size_t size);
TINYMEDIA_API int tmedia_consumer_pause(tmedia_consumer_t *self);
TINYMEDIA_API int tmedia_consumer_stop(tmedia_consumer_t *self);
TINYMEDIA_API int tmedia_consumer_deinit(tmedia_consumer_t* self);
TINYMEDIA_API int tmedia_consumer_plugin_register(const tmedia_consumer_plugin_def_t* plugin);
TINYMEDIA_API int tmedia_consumer_plugin_unregister(const tmedia_consumer_plugin_def_t* plugin);
#endif /* TINYMEDIA_CONSUMER_H */

View File

@ -76,6 +76,7 @@ typedef struct tmedia_producer_plugin_def_s
}
tmedia_producer_plugin_def_t;
#define TMEDIA_DECLARE_PRODUCER tmedia_producer_t __producer__
TINYMEDIA_API tmedia_producer_t* tmedia_producer_create(tmedia_type_t type);
TINYMEDIA_API int tmedia_producer_init(tmedia_producer_t* self);

View File

@ -36,6 +36,8 @@
#include "tsk_object.h"
TMEDIA_BEGIN_DECLS
#define TMEDIA_QOS_TLINE(self) ((tmedia_qos_tline_t*)(self))
/** List of all supported statues*/
@ -185,4 +187,6 @@ TINYMEDIA_API tsk_bool_t tmedia_qos_tline_segmented_canresume(const tmedia_qos_t
TINYMEDIA_GEXTERN const tsk_object_def_t *tmedia_qos_tline_segmented_def_t;
TINYMEDIA_GEXTERN const tsk_object_def_t *tmedia_qos_tline_e2e_def_t;
TMEDIA_END_DECLS
#endif /* TINYMEDIA_QOS_H */

View File

@ -86,7 +86,7 @@ typedef struct tmedia_session_plugin_def_s
//! the media name. e.g. "audio", "video", "message", "image" etc.
const char* media;
int (*configure) (tmedia_session_t* , const va_list *app);
int (*set) (tmedia_session_t* , va_list *app);
int (* prepare) (tmedia_session_t* );
int (* start) (tmedia_session_t* );
int (* pause) (tmedia_session_t* );
@ -105,6 +105,7 @@ TINYMEDIA_API const tmedia_session_plugin_def_t* tmedia_session_plugin_find_by_m
TINYMEDIA_API int tmedia_session_plugin_unregister(const tmedia_session_plugin_def_t* plugin);
TINYMEDIA_API tmedia_session_t* tmedia_session_create(tmedia_type_t type);
TINYMEDIA_API const tmedia_codec_t* tmedia_session_match_codec(tmedia_session_t* self, const tsdp_header_M_t* M, char** format);
TINYMEDIA_API int tmedia_session_skip_param(enum tmedia_session_param_type_e type, va_list *app);
TINYMEDIA_API int tmedia_session_deinit(tmedia_session_t* self);
typedef tsk_list_t tmedia_sessions_L_t; /**< List of @ref tmedia_session_t objects */
#define TMEDIA_DECLARE_SESSION tmedia_session_t __session__
@ -196,9 +197,30 @@ typedef struct tmedia_session_mgr_s
}
tmedia_session_mgr_t;
TINYMEDIA_API tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char* addr, tsk_bool_t ipv6);
typedef enum tmedia_session_param_type_e
{
tmedia_sptype_null = tsk_null,
tmedia_sptype_remote_ip,
tmedia_sptype_local_ip,
tmedia_sptype_set_rtcp,
tmedia_sptype_set_qos,
tmedia_sptype_unset_qos,
tmedia_sptype_set_timers,
tmedia_sptype_unset_timers,
}
tmedia_session_param_type_t;
#define TMEDIA_SESSION_SET_REMOTE_IP(IP_STR) tmedia_sptype_remote_ip, (const char*) IP_STR
#define TMEDIA_SESSION_SET_LOCAL_IP(IP_STR, IPv6_BOOL) tmedia_sptype_local_ip, (const char*) IP_STR, (tsk_bool_t)IPv6_BOOL
#define TMEDIA_SESSION_SET_RTCP(ENABLED_BOOL) tmedia_sptype_set_rtcp, (tsk_bool_t)ENABLED_BOOL
#define TMEDIA_SESSION_SET_NULL() tmedia_sptype_null
TINYMEDIA_API tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char* addr, tsk_bool_t ipv6, tsk_bool_t offerer);
TINYMEDIA_API int tmedia_session_mgr_start(tmedia_session_mgr_t* self);
TINYMEDIA_API int tmedia_session_mgr_configure(tmedia_session_mgr_t* self, tmedia_type_t type, ...);
TINYMEDIA_API int tmedia_session_mgr_set(tmedia_session_mgr_t* self, tmedia_type_t type, ...);
TINYMEDIA_API int tmedia_session_mgr_stop(tmedia_session_mgr_t* self);
TINYMEDIA_API const tsdp_message_t* tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self);
TINYMEDIA_API int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t* sdp);

View File

@ -56,15 +56,16 @@ int tmedia_consumer_init(tmedia_consumer_t* self)
/**@ingroup tmedia_consumer_group
* Alert the consumer to be prepared to start.
* @param self the consumer to prepare
* @param codec Negociated codec
* @retval Zero if succeed and non-zero error code otherwise
*/
int tmedia_consumer_prepare(tmedia_consumer_t *self)
int tmedia_consumer_prepare(tmedia_consumer_t *self, const tmedia_codec_t* codec)
{
if(!self || !self->plugin || !self->plugin->prepare){
if(!self || !self->plugin || !self->plugin->prepare || !codec){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
return self->plugin->prepare(self);
return self->plugin->prepare(self, codec);
}
/**@ingroup tmedia_consumer_group
@ -225,6 +226,7 @@ int tmedia_consumer_plugin_unregister(const tmedia_consumer_plugin_def_t* plugin
break;
}
}
__tmedia_consumer_plugins[i] = tsk_null;
}
return (found ? 0 : -2);
}

View File

@ -232,6 +232,7 @@ int tmedia_producer_plugin_unregister(const tmedia_producer_plugin_def_t* plugin
break;
}
}
__tmedia_producer_plugins[i] = tsk_null;
}
return (found ? 0 : -2);
}

View File

@ -339,13 +339,12 @@ const tmedia_codec_t* tmedia_session_match_codec(tmedia_session_t* self, const t
compare_fmtp:
if((fmtp = tsdp_header_M_get_fmtp(M, fmt->value))){ /* remote have fmtp? */
if(tmedia_codec_match_fmtp(codec, fmtp)){ /* fmtp matches? */
if(codec->dyn){
tsk_strupdate(format, fmt->value);
}
tsk_strupdate(format, fmt->value);
found = tsk_true;
}
}
else{ /* no fmtp -> always match */
tsk_strupdate(format, fmt->value);
found = tsk_true;
}
next:
@ -362,6 +361,34 @@ next:
return tsk_null;
}
/**@ingroup tmedia_session_group
* skip unsupported param
*/
int tmedia_session_skip_param(enum tmedia_session_param_type_e type, va_list *app)
{
switch(type){
case tmedia_sptype_remote_ip:
/* (const char*) IP_STR */
va_arg(*app, const char *);
break;
case tmedia_sptype_local_ip:
/* (const char*) IP_STR, (tsk_bool_t)IPv6_BOOL */
va_arg(*app, const char *);
va_arg(*app, tsk_bool_t);
break;
case tmedia_sptype_set_rtcp:
/* (tsk_bool_t)ENABLED_BOOL */
va_arg(*app, tsk_bool_t);
break;
default:
TSK_DEBUG_ERROR("%d is an unknown parameter", type);
return -1;
}
return 0;
}
/**@ingroup tmedia_session_group
* DeInitializes a media session.
* @param self the media session to deinitialize.
@ -421,10 +448,11 @@ int _tmedia_session_load_codecs(tmedia_session_t* self)
* @param type the type of the session to create. For example, (@ref tmed_sess_type_audio | @ref tmed_sess_type_video).
* @param addr the local ip address or FQDN to use in the sdp message.
* @param ipv6 indicates whether @a addr is IPv6 address or not. Useful when @a addr is a FQDN.
* @param load_sessions Whether the offerer or not.
* will create an audio/video session.
* @retval new @ref tmedia_session_mgr_t object
*/
tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char* addr, tsk_bool_t ipv6)
tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char* addr, tsk_bool_t ipv6, tsk_bool_t offerer)
{
tmedia_session_mgr_t* mgr;
@ -432,12 +460,20 @@ tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char*
TSK_DEBUG_ERROR("Failed to create Media Session manager");
return tsk_null;
}
/* init */
mgr->type = type;
mgr->addr = tsk_strdup(addr);
mgr->ipv6 = ipv6;
/* load sessions (will allow us to generate lo) */
if(offerer){
if(_tmedia_session_mgr_load_sessions(mgr)){
/* Do nothing */
TSK_DEBUG_ERROR("Failed to load sessions");
}
}
return mgr;
}
@ -471,8 +507,8 @@ int tmedia_session_mgr_start(tmedia_session_mgr_t* self)
return -2;
}
if((ret = session->plugin->start(session))){
TSK_DEBUG_ERROR("Failed to start session");
return ret;
TSK_DEBUG_ERROR("Failed to start %s session", session->plugin->media);
continue;
}
}
@ -481,13 +517,13 @@ int tmedia_session_mgr_start(tmedia_session_mgr_t* self)
}
/**@ingroup tmedia_session_group
* Configures one or several sessions.
* sets one or several sessions.
* @param self The session manager
* @param type The type of the sessions to configure
* @param type The type of the sessions to set
* @param ... Any TMEDIA_SESSION_SET_*() macros
* @retval Zero if succeed and non-zero error code otherwise
*/
int tmedia_session_mgr_configure(tmedia_session_mgr_t* self, tmedia_type_t type, ...)
int tmedia_session_mgr_set(tmedia_session_mgr_t* self, tmedia_type_t type, ...)
{
tsk_list_item_t* item;
tmedia_session_t* session;
@ -504,14 +540,14 @@ int tmedia_session_mgr_configure(tmedia_session_mgr_t* self, tmedia_type_t type,
return -2;
}
/* does not support configure() */
if(!session->plugin->configure){
/* does not support set() */
if(!session->plugin->set){
continue;
}
va_start(ap, type);
if(((session->type & type) == session->type) && session->plugin->configure(session, &ap)){
TSK_DEBUG_ERROR("Failed to configue (%s) session", session->plugin->media);
if(((session->type & type) == session->type) && session->plugin->set(session, &ap)){
TSK_DEBUG_ERROR("Failed to set (%s) session", session->plugin->media);
}
va_end(ap);
}
@ -630,6 +666,7 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
{
const tmedia_session_t* ms;
const tsdp_header_M_t* M;
const tsdp_header_C_t* C; /* global "c=" line */
tsk_size_t index = 0;
tsk_bool_t found;
@ -647,10 +684,19 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
if(TSK_LIST_IS_EMPTY(self->sessions)){
if(_tmedia_session_mgr_load_sessions(self)){
TSK_DEBUG_ERROR("Failed to prepare the session manager");
return tsk_null;
return -2;
}
}
/* get global connection line (common to all sessions)
* Each session should override this info if it has a different one in its "m=" line
*/
if((C = (const tsdp_header_C_t*)tsdp_message_get_header(sdp, tsdp_htype_C)) && C->addr){
tmedia_session_mgr_set(self, self->type,
TMEDIA_SESSION_SET_REMOTE_IP(C->addr),
TMEDIA_SESSION_SET_NULL());
}
/* foreach "m=" line in the remote offer create a session*/
while((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index++))){
found = tsk_false;
@ -853,8 +899,7 @@ int tmedia_session_mgr_remove_media(tmedia_session_mgr_t* self, tmedia_type_t ty
}
/* internal functions used to prepare a session manager
* only sessions matching the manager type will be loaded
/** internal function used to load sessions
*/
int _tmedia_session_mgr_load_sessions(tmedia_session_mgr_t* self)
{
@ -871,6 +916,10 @@ int _tmedia_session_mgr_load_sessions(tmedia_session_mgr_t* self)
}
}
}
/* set ldefault values */
tmedia_session_mgr_set(self, self->type,
TMEDIA_SESSION_SET_LOCAL_IP(self->addr, self->ipv6),
TMEDIA_SESSION_SET_NULL());
}
return 0;
}

View File

@ -34,7 +34,7 @@
/* ============ Audio Session ================= */
int tmedia_session_daudio_configure(tmedia_session_t* self, const va_list *app)
int tmedia_session_daudio_set(tmedia_session_t* self, va_list *app)
{
tmedia_session_daudio_t* daudio;
@ -150,11 +150,11 @@ int tmedia_session_daudio_set_ro(tmedia_session_t* self, const tsdp_header_M_t*
/* ============ Video Session ================= */
int tmedia_session_dvideo_configure(tmedia_session_t* self, const va_list *app)
int tmedia_session_dvideo_set(tmedia_session_t* self, va_list *app)
{
tmedia_session_dvideo_t* dvideo;
TSK_DEBUG_INFO("tmedia_session_dvideo_configure");
TSK_DEBUG_INFO("tmedia_session_dvideo_set");
dvideo = (tmedia_session_dvideo_t*)self;
@ -266,11 +266,11 @@ int tmedia_session_dvideo_set_ro(tmedia_session_t* self, const tsdp_header_M_t*
/* ============ Msrp Session ================= */
int tmedia_session_dmsrp_configure(tmedia_session_t* self, const va_list *app)
int tmedia_session_dmsrp_set(tmedia_session_t* self, va_list *app)
{
tmedia_session_dmsrp_t* dmsrp;
TSK_DEBUG_INFO("tmedia_session_dmsrp_configure");
TSK_DEBUG_INFO("tmedia_session_dmsrp_set");
dmsrp = (tmedia_session_dmsrp_t*)self;
@ -359,11 +359,12 @@ static const tmedia_session_plugin_def_t tmedia_session_daudio_plugin_def_s =
tmedia_audio,
"audio",
tmedia_session_daudio_configure,
tmedia_session_daudio_set,
tmedia_session_daudio_prepare,
tmedia_session_daudio_start,
tmedia_session_daudio_stop,
tmedia_session_daudio_pause,
tmedia_session_daudio_stop,
tmedia_session_daudio_get_lo,
tmedia_session_daudio_set_ro
@ -412,11 +413,12 @@ static const tmedia_session_plugin_def_t tmedia_session_dvideo_plugin_def_s =
tmedia_video,
"video",
tmedia_session_dvideo_configure,
tmedia_session_dvideo_set,
tmedia_session_dvideo_prepare,
tmedia_session_dvideo_start,
tmedia_session_dvideo_stop,
tmedia_session_dvideo_pause,
tmedia_session_dvideo_stop,
tmedia_session_dvideo_get_lo,
tmedia_session_dvideo_set_ro
@ -465,11 +467,12 @@ static const tmedia_session_plugin_def_t tmedia_session_dmsrp_plugin_def_s =
tmedia_msrp,
"message",
tmedia_session_dmsrp_configure,
tmedia_session_dmsrp_set,
tmedia_session_dmsrp_prepare,
tmedia_session_dmsrp_start,
tmedia_session_dmsrp_stop,
tmedia_session_dmsrp_pause,
tmedia_session_dmsrp_stop,
tmedia_session_dmsrp_get_lo,
tmedia_session_dmsrp_set_ro

View File

@ -202,6 +202,14 @@
RelativePath=".\include\tinymedia\tmedia_common.h"
>
</File>
<File
RelativePath=".\include\tinymedia\tmedia_consumer.h"
>
</File>
<File
RelativePath=".\include\tinymedia\tmedia_producer.h"
>
</File>
<File
RelativePath=".\include\tinymedia\tmedia_qos.h"
>
@ -240,6 +248,14 @@
RelativePath=".\src\tmedia_common.c"
>
</File>
<File
RelativePath=".\src\tmedia_consumer.c"
>
</File>
<File
RelativePath=".\src\tmedia_producer.c"
>
</File>
<File
RelativePath=".\src\tmedia_qos.c"
>

View File

@ -63,9 +63,7 @@ int tnet_transport_start(tnet_transport_handle_t* handle)
int ret = -1;
if(handle){
tnet_transport_t *transport = handle;
TSK_DEBUG_INFO("tnet_transport_start()");
/* prepare transport */
if((ret = tnet_transport_prepare(transport))){
TSK_DEBUG_ERROR("Failed to prepare transport.");
@ -284,8 +282,6 @@ static void *run(void* self)
TSK_DEBUG_FATAL("Failed to create main thread [%d]", ret);
return tsk_null;
}
TSK_DEBUG_INFO("tnet_transport_run()");
TSK_RUNNABLE_RUN_BEGIN(transport);

View File

@ -31,6 +31,8 @@
#include "tinyrtp_config.h"
TRTP_BEGIN_DECLS
TRTP_END_DECLS
#endif /* TINYMEDIA_RTCP_SESSION_H */

View File

@ -33,6 +33,8 @@
#include "tsk_buffer.h"
TRTP_BEGIN_DECLS
#define TRTP_RTP_HEADER_MIN_SIZE 12
typedef struct trtp_rtp_header_s
@ -73,5 +75,6 @@ TINYRTP_API trtp_rtp_header_t* trtp_rtp_header_deserialize(const void *data, tsk
TINYRTP_GEXTERN const tsk_object_def_t *trtp_rtp_header_def_t;
TRTP_END_DECLS
#endif /* TINYMEDIA_RTP_HEADER_H */

View File

@ -35,6 +35,9 @@
#include "tsk_object.h"
TRTP_BEGIN_DECLS
typedef struct trtp_rtp_packet_s
{
TSK_DECLARE_OBJECT;
@ -62,4 +65,6 @@ TINYRTP_API trtp_rtp_packet_t* trtp_rtp_packet_deserialize(const void *data, tsk
TINYRTP_GEXTERN const tsk_object_def_t *trtp_rtp_packet_def_t;
TRTP_END_DECLS
#endif /* TINYMEDIA_RTP_PACKET_H */

View File

@ -31,6 +31,8 @@
#include "tinyrtp_config.h"
TRTP_BEGIN_DECLS
TRTP_END_DECLS
#endif /* TINYMEDIA_RTP_SESSION_H */

View File

@ -31,6 +31,8 @@
#include "tinyrtp_config.h"
TRTP_BEGIN_DECLS
TRTP_END_DECLS
#endif /* TINYMEDIA_TRTP_H */

View File

@ -33,6 +33,15 @@
#include "tnet_transport.h"
TRTP_BEGIN_DECLS
/* Forward declarations */
struct trtp_rtp_packet_s;
typedef int (*trtp_manager_rtp_cb_f)(const void* callback_data, struct trtp_rtp_packet_s* packet);
typedef int (*trtp_manager_rtcp_cb_f)(const void* callback_data, struct trtp_rtpc_packet_s* packet);
/** RTP/RTCP manager */
typedef struct trtp_manager_s
{
TSK_DECLARE_OBJECT;
@ -42,15 +51,22 @@ typedef struct trtp_manager_s
uint32_t timestamp;
uint32_t ssrc;
uint8_t payload_type;
char* remote_ip;
tnet_port_t remote_port;
tnet_socket_t* local_socket;
const void* callback_data;
trtp_manager_rtp_cb_f callback;
} rtp;
struct{
char* remote_ip;
tnet_port_t remote_port;
tnet_socket_t* local_socket;
const void* callback_data;
trtp_manager_rtcp_cb_f callback;
} rtcp;
char* local_ip;
@ -61,9 +77,12 @@ typedef struct trtp_manager_s
}
trtp_manager_t;
TINYRTP_API trtp_manager_t* trtp_manager_create(tsk_bool_t enable_rtcp, const char* local_ip, tsk_bool_t ipv6, uint8_t payload_type);
TINYRTP_API trtp_manager_t* trtp_manager_create(tsk_bool_t enable_rtcp, const char* local_ip, tsk_bool_t ipv6);
TINYRTP_API int trtp_manager_prepare(trtp_manager_t*self);
TINYRTP_API tsk_bool_t trtp_manager_is_prepared(trtp_manager_t* self);
TINYRTP_API int trtp_manager_set_rtp_callback(trtp_manager_t* self, trtp_manager_rtp_cb_f callback, const void* callback_data);
TINYRTP_API int trtp_manager_set_payload_type(trtp_manager_t* self, uint8_t payload_type);
TINYRTP_API int trtp_manager_set_rtp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port);
TINYRTP_API int trtp_manager_set_rtp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port);
TINYRTP_API int trtp_manager_set_rtcp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port);
TINYRTP_API int trtp_manager_start(trtp_manager_t* self);
@ -72,4 +91,6 @@ TINYRTP_API int trtp_manager_stop(trtp_manager_t* self);
TINYRTP_GEXTERN const tsk_object_def_t *trtp_manager_def_t;
TRTP_END_DECLS
#endif /* TINYMEDIA_MANAGER_H */

View File

@ -34,15 +34,69 @@
#include "tsk_memory.h"
#include "tsk_debug.h"
/* ======================= Transport callback ========================== */
static int trtp_transport_layer_cb(const tnet_transport_event_t* e)
{
int ret = -1;
const trtp_manager_t *manager = e->callback_data;
trtp_rtp_packet_t* packet = tsk_null;
switch(e->type){
case event_data: {
break;
}
case event_closed:
case event_connected:
default:{
return 0;
}
}
//
// RTCP
//
if(manager->rtcp.local_socket && manager->rtcp.local_socket->fd == e->fd){
TSK_DEBUG_INFO("RTCP packet");
}
//
// RTP
//
else if(manager->rtp.local_socket && manager->rtp.local_socket->fd == e->fd){
if(manager->rtp.callback){
if((packet = trtp_rtp_packet_deserialize(e->data, e->size))){
manager->rtp.callback(manager->rtp.callback_data, packet);
TSK_OBJECT_SAFE_FREE(packet);
}
else{
TSK_DEBUG_ERROR("RTP packet === NOK");
goto bail;
}
}
}
//
// UNKNOWN
//
else{
TSK_DEBUG_INFO("XXXX packet");
goto bail;
}
bail:
return ret;
}
/** Create RTP/RTCP manager */
trtp_manager_t* trtp_manager_create(tsk_bool_t enable_rtcp, const char* local_ip, tsk_bool_t ipv6, uint8_t payload_type)
trtp_manager_t* trtp_manager_create(tsk_bool_t enable_rtcp, const char* local_ip, tsk_bool_t ipv6)
{
trtp_manager_t* manager;
if((manager = tsk_object_new(trtp_manager_def_t))){
manager->enable_rtcp = enable_rtcp;
manager->local_ip = tsk_strdup(local_ip);
manager->ipv6 = ipv6;
manager->rtp.payload_type = payload_type;
manager->rtp.payload_type = 127;
}
return manager;
}
@ -66,9 +120,13 @@ int trtp_manager_prepare(trtp_manager_t* self)
/* Creates local rtp and rtcp sockets */
while(retry_count--){
tnet_port_t local_port = TNET_SOCKET_PORT_ANY;
/* random number in the range 1024 to 65535 */
tnet_port_t local_port = ((rand() % 64511) + 1024);
local_port = (local_port % 0x01) ? (local_port + 1) : local_port; /* turn to even number */
if(self->enable_rtcp){
local_port = ((rand() % 64511) + 1024);
local_port = (local_port % 0x01) ? (local_port + 1) : local_port; /* turn to even number */
}
/* beacuse failure will cause errors in the log, print a message to alert that there is
* nothing to worry about */
TSK_DEBUG_INFO("RTP/RTCP manager[Begin]: Trying to bind to random ports");
@ -88,14 +146,18 @@ int trtp_manager_prepare(trtp_manager_t* self)
}
}
TSK_DEBUG_INFO("RTP/RTCP manager[End]: Trying to bind to random ports");
TSK_DEBUG_INFO("RTP/RTCP manager[End]: Trying to bind to random ports (%u)", self->rtp.local_socket->port);
break;
}
/* creates the transport */
if(self->rtp.local_socket && ((self->enable_rtcp && self->rtcp.local_socket) || !self->enable_rtcp)){
self->transport = tnet_transport_create(self->local_ip, TNET_SOCKET_PORT_ANY, socket_type, "RTP/RTCP Manager");
if(!self->transport){
if(self->transport){
/* set callback function */
tnet_transport_set_callback(self->transport, trtp_transport_layer_cb, self);
}
else {
TSK_DEBUG_ERROR("Failed to create RTP/RTCP manager");
return -3;
}
@ -118,6 +180,31 @@ tsk_bool_t trtp_manager_is_prepared(trtp_manager_t* self)
return self->transport == tsk_null ? tsk_false : tsk_true;
}
/** Sets RTP callback */
int trtp_manager_set_rtp_callback(trtp_manager_t* self, trtp_manager_rtp_cb_f callback, const void* callback_data)
{
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
self->rtp.callback = callback;
self->rtp.callback_data = callback_data;
return 0;
}
/** Sets the payload type */
int trtp_manager_set_payload_type(trtp_manager_t* self, uint8_t payload_type)
{
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
self->rtp.payload_type = payload_type;
return 0;
}
/** Sets remote parameters for rtp session */
int trtp_manager_set_rtp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port)
{
@ -188,7 +275,7 @@ int trtp_manager_start(trtp_manager_t* self)
self->rtcp.remote_port = self->rtp.remote_port;
}
}
if((ret = tnet_sockaddr_init(self->rtcp.remote_ip, self->rtcp.remote_port, self->rtcp.local_socket->type, &remote_rtcp_addr))){
if(self->enable_rtcp && (ret = tnet_sockaddr_init(self->rtcp.remote_ip, self->rtcp.remote_port, self->rtcp.local_socket->type, &remote_rtcp_addr))){
TSK_DEBUG_ERROR("Invalid RTCP host:port [%s:%u]", self->rtcp.remote_ip, self->rtcp.remote_port);
return ret;
}
@ -203,12 +290,12 @@ int trtp_manager_start(trtp_manager_t* self)
return ret;
}
/* connect and add RTP socket to the transport */
if((ret = tnet_sockfd_connectto(self->rtcp.local_socket->fd, &remote_rtcp_addr))){
/* connect and add RTCP socket to the transport */
if(self->enable_rtcp && (ret = tnet_sockfd_connectto(self->rtcp.local_socket->fd, &remote_rtcp_addr))){
TSK_DEBUG_ERROR("Failed to connect RTPC socket");
return ret;
}
if((ret = tnet_transport_add_socket(self->transport, self->rtcp.local_socket->fd, self->rtcp.local_socket->type, tsk_false, tsk_true/* only Meaningful for tls*/))){
if(self->enable_rtcp && (ret = tnet_transport_add_socket(self->transport, self->rtcp.local_socket->fd, self->rtcp.local_socket->type, tsk_false, tsk_true/* only Meaningful for tls*/))){
TSK_DEBUG_ERROR("Failed to add RTPC socket");
return ret;
}

View File

@ -27,10 +27,12 @@ void test_manager()
tsk_size_t i;
trtp_manager_t* manager;
if(!(manager = trtp_manager_create(tsk_true, "192.168.0.12", tsk_false, 8))){
if(!(manager = trtp_manager_create(tsk_true, "192.168.0.12", tsk_false))){
goto bail;
}
trtp_manager_set_payload_type(manager, 8);
/* Prepare: this will allow you to generate local port and ip */
if(trtp_manager_prepare(manager)){
goto bail;

View File

@ -28,6 +28,7 @@
* @date Created: Sat Nov 8 16:54:58 2009 mdiop
*/
#include "tsk_thread.h"
#include "tsk_debug.h"
#include "tsk_memory.h"
#if TSK_UNDER_WINDOWS
@ -80,14 +81,25 @@ int tsk_thread_create(void** tid, void *(*start) (void *), void *arg)
*/
int tsk_thread_join(void** tid)
{
#if TSK_UNDER_WINDOWS
return (WaitForSingleObject(*((HANDLE*)tid), INFINITE) == WAIT_FAILED) ? -1 : 0;
#else
int ret;
if(!tid){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
#if TSK_UNDER_WINDOWS
ret = (WaitForSingleObject(*((HANDLE*)tid), INFINITE) == WAIT_FAILED) ? -1 : 0;
if(ret == 0){
CloseHandle(*((HANDLE*)tid));
*tid = tsk_null;
}
#else
if((ret = pthread_join(*((pthread_t*)*tid), 0)) == 0){
tsk_free(tid);
}
return ret;
#endif
return ret;
}

View File

@ -107,7 +107,7 @@ typedef struct tsdp_header_s
}
tsdp_header_t;
#define TSDP_DECLARE_HEADER tsdp_header_t header
#define TSDP_DECLARE_HEADER tsdp_header_t __header__
typedef tsk_list_t tsdp_headers_L_t; /**< List of @ref tsdp_header_t elements. */
/*
================================*/

View File

@ -716,7 +716,12 @@ int tsip_dialog_invite_OnTerminated(tsip_dialog_invite_t *self)
{
TSK_DEBUG_INFO("=== INVITE Dialog terminated ===");
/* Alert the user */
/* stop session manager */
if(self->msession_mgr){
tmedia_session_mgr_stop(self->msession_mgr);
}
/* alert the user */
TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminated, "Dialog terminated");
/* Remove from the dialog layer. */

View File

@ -33,6 +33,8 @@
#include "tinysip/dialogs/tsip_dialog_invite.common.h"
#include "tinysdp/parsers/tsdp_parser_message.h"
#include "tsk_debug.h"
extern int send_INVITE(tsip_dialog_invite_t *self);
@ -49,7 +51,7 @@ int c0000_Started_2_Outgoing_X_oINVITE(va_list *app)
/* This is the first transaction when you try to make an audio/video/msrp call */
if(self->msession_mgr == tsk_null){
self->msession_mgr = tmedia_session_mgr_create((tmedia_audio | tmedia_video | tmedia_msrp | tmedia_t38),
"192.168.0.12", tsk_false);
"192.168.0.12", tsk_false, tsk_true);
}
/* send the request */
@ -67,6 +69,8 @@ int c0000_Started_2_Outgoing_X_oINVITE(va_list *app)
int c0001_Outgoing_2_Connected_X_i2xxINVITE(va_list *app)
{
int ret;
tsdp_message_t* sdp_ro;
tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
const tsip_response_t *r2xxINVITE = va_arg(*app, const tsip_response_t *);
@ -75,12 +79,34 @@ int c0001_Outgoing_2_Connected_X_i2xxINVITE(va_list *app)
return ret;
}
/* set ro and start the session (if not already done) */
if(self->msession_mgr && TSIP_MESSAGE_HAS_CONTENT(r2xxINVITE)){
if((sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(r2xxINVITE), TSIP_MESSAGE_CONTENT_DATA_LENGTH(r2xxINVITE)))){
ret = tmedia_session_mgr_set_ro(self->msession_mgr, sdp_ro);
TSK_OBJECT_SAFE_FREE(sdp_ro);
/* start session manager */
ret = tmedia_session_mgr_start(self->msession_mgr);
}
}
else{
TSK_DEBUG_ERROR("Invalid session manager");
return -1;
}
/* send ack */
if(ret == 0){
ret = send_ACK(self, r2xxINVITE);
}
else{
/* send error */
}
/* alert the user */
TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_invite,
TSIP_RESPONSE_CODE(r2xxINVITE), TSIP_RESPONSE_PHRASE(r2xxINVITE), r2xxINVITE);
/* send ACK */
return send_ACK(self, r2xxINVITE);
return ret;
}
/* Outgoing -> (i300-i699 INVITE) -> Terminated

View File

@ -176,7 +176,7 @@ static int tsip_transport_layer_dgram_cb(const tnet_transport_event_t* e)
{
int ret = -1;
tsk_ragel_state_t state;
tsip_message_t *message = 0;
tsip_message_t *message = tsk_null;
const tsip_transport_t *transport = e->callback_data;
switch(e->type){