2012-12-03 03:11:21 +00:00
/*
* Copyright ( C ) 2010 - 2011 Mamadou Diop .
*
* Contact : Mamadou Diop < diopmamadou ( at ) doubango [ dot ] 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 tmedia_codec.c
* @ brief Base codec object .
*
* @ author Mamadou Diop < diopmamadou ( at ) doubango [ dot ] org >
*
*/
# include "tinymedia/tmedia_codec.h"
# include "tinymedia/tmedia_defaults.h"
# include "tinysdp/headers/tsdp_header_M.h"
# include "tsk_string.h"
# include "tsk_memory.h"
# include "tsk_debug.h"
2013-06-03 08:39:53 +00:00
# include <limits.h> /* INT_MAX */
2012-12-03 03:11:21 +00:00
/**@defgroup tmedia_codec_group Codecs
*/
/* pointer to all registered codecs */
const tmedia_codec_plugin_def_t * __tmedia_codec_plugins [ TMED_CODEC_MAX_PLUGINS ] = { 0 } ;
/*== Predicate function to find a codec object by format */
static int __pred_find_codec_by_format ( const tsk_list_item_t * item , const void * format )
{
if ( item & & item - > data ) {
return tsk_strcmp ( ( ( tmedia_codec_t * ) item - > data ) - > format , format ) ;
}
return - 1 ;
}
/*== Predicate function to find a codec object by negociated format */
static int __pred_find_codec_by_neg_format ( const tsk_list_item_t * item , const void * format )
{
if ( item & & item - > data ) {
return tsk_strcmp ( ( ( tmedia_codec_t * ) item - > data ) - > neg_format , format ) ;
}
return - 1 ;
}
/**@ingroup tmedia_codec_group
* Initialize a Codec
* @ param self The codec to initialize . Could be any type of codec ( e . g . @ ref tmedia_codec_audio_t or @ ref tmedia_codec_video_t ) .
* @ param type
* @ param name the name of the codec . e . g . " G.711u " or " G.711a " etc used in the sdp .
* @ param desc full description .
* @ param format the format . e . g . " 0 " for G .711 . u or " 8 " for G .711 a or " * " for MSRP .
* @ retval Zero if succeed and non - zero error code otherwise .
*/
int tmedia_codec_init ( tmedia_codec_t * self , tmedia_type_t type , const char * name , const char * desc , const char * format )
{
if ( ! self | | tsk_strnullORempty ( name ) ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
self - > type = type ;
tsk_strupdate ( & self - > name , name ) ;
tsk_strupdate ( & self - > desc , desc ) ;
tsk_strupdate ( & self - > format , format ) ;
2013-06-03 08:39:53 +00:00
if ( ! self - > bandwidth_max_upload ) self - > bandwidth_max_upload = ( type = = tmedia_video ? tmedia_defaults_get_bandwidth_video_upload_max ( ) : INT_MAX ) ; // INT_MAX or <=0 means undefined
if ( ! self - > bandwidth_max_download ) self - > bandwidth_max_download = ( type = = tmedia_video ? tmedia_defaults_get_bandwidth_video_download_max ( ) : INT_MAX ) ; // INT_MAX or <=0 means undefined
2013-05-07 04:55:21 +00:00
if ( ! self - > in . rate ) self - > in . rate = self - > plugin - > rate ;
if ( ! self - > out . rate ) self - > out . rate = self - > plugin - > rate ;
if ( type & tmedia_audio ) {
tmedia_codec_audio_t * audio = TMEDIA_CODEC_AUDIO ( self ) ;
2013-06-10 05:47:01 +00:00
if ( ! audio - > in . ptime ) audio - > in . ptime = ( self - > plugin - > audio . ptime ? self - > plugin - > audio . ptime : tmedia_defaults_get_audio_ptime ( ) ) ;
if ( ! audio - > out . ptime ) audio - > out . ptime = ( self - > plugin - > audio . ptime ? self - > plugin - > audio . ptime : tmedia_defaults_get_audio_ptime ( ) ) ;
2013-05-07 04:55:21 +00:00
if ( ! audio - > in . channels ) audio - > in . channels = self - > plugin - > audio . channels ;
if ( ! audio - > out . channels ) audio - > out . channels = self - > plugin - > audio . channels ;
if ( ! audio - > in . timestamp_multiplier ) audio - > in . timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier ( self - > id , self - > in . rate ) ;
if ( ! audio - > out . timestamp_multiplier ) audio - > out . timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier ( self - > id , self - > out . rate ) ;
}
2012-12-03 03:11:21 +00:00
// Video flipping: For backward compatibility we have to initialize the default values
// according to the CFLAGS: 'FLIP_ENCODED_PICT' and 'FLIP_DECODED_PICT'. At any time you
// can update thse values (e.g. when the device switch from landscape to portrait) using video_session->set();
2013-05-07 04:55:21 +00:00
else if ( type & tmedia_video ) {
2012-12-03 03:11:21 +00:00
tmedia_codec_video_t * video = TMEDIA_CODEC_VIDEO ( self ) ;
# if FLIP_ENCODED_PICT
video - > out . flip = tsk_true ;
# endif
# if FLIP_DECODED_PICT
video - > in . flip = tsk_true ;
# endif
2013-06-10 15:09:40 +00:00
if ( ! video - > in . fps ) video - > in . fps = self - > plugin - > video . fps ? self - > plugin - > video . fps : tmedia_defaults_get_video_fps ( ) ;
if ( ! video - > out . fps ) video - > out . fps = self - > plugin - > video . fps ? self - > plugin - > video . fps : tmedia_defaults_get_video_fps ( ) ;
2012-12-03 03:11:21 +00:00
if ( video - > in . chroma = = tmedia_chroma_none ) video - > in . chroma = tmedia_chroma_yuv420p ;
if ( video - > out . chroma = = tmedia_chroma_none ) video - > out . chroma = tmedia_chroma_yuv420p ;
if ( 0 ) { // @deprecated
if ( ! video - > in . width ) video - > in . width = video - > out . width = self - > plugin - > video . width ;
if ( ! video - > in . height ) video - > in . height = video - > out . height = self - > plugin - > video . height ;
}
else {
int ret ;
unsigned width , height ;
video - > pref_size = tmedia_defaults_get_pref_video_size ( ) ;
if ( ( ret = tmedia_video_get_size ( video - > pref_size , & width , & height ) ) ! = 0 ) {
width = self - > plugin - > video . width ;
height = self - > plugin - > video . height ;
}
if ( ! video - > in . width ) video - > in . width = video - > out . width = width ;
if ( ! video - > in . height ) video - > in . height = video - > out . height = height ;
}
}
return 0 ;
}
int tmedia_codec_set ( tmedia_codec_t * self , const struct tmedia_param_s * param )
{
if ( self & & self - > plugin & & self - > plugin - > set & & param ) {
return self - > plugin - > set ( self , param ) ;
}
return 0 ;
}
/**@ingroup tmedia_codec_group
* Prepares a codec by opening it .
* @ param self The codec to open .
* @ retval Zero if succeed and non - zero error code otherwise .
* @ sa @ ref tmedia_codec_close ( )
*/
int tmedia_codec_open ( tmedia_codec_t * self )
{
if ( ! self | | ! self - > plugin ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( self - > opened ) {
TSK_DEBUG_WARN ( " Codec already opened " ) ;
return 0 ;
}
if ( self - > plugin - > open ) {
int ret ;
if ( ( ret = self - > plugin - > open ( self ) ) ) {
TSK_DEBUG_ERROR ( " Failed to open [%s] codec " , self - > plugin - > desc ) ;
return ret ;
}
else {
self - > opened = tsk_true ;
return 0 ;
}
}
else {
self - > opened = tsk_true ;
return 0 ;
}
}
/**@ingroup tmedia_codec_group
* UnPrepares a codec by closing it .
* @ param self The codec to close .
* @ retval Zero if succeed and non - zero error code otherwise .
* @ sa @ ref tmedia_codec_open ( )
*/
int tmedia_codec_close ( tmedia_codec_t * self )
{
if ( ! self | | ! self - > plugin ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( ! self - > opened ) {
return 0 ;
}
if ( self - > plugin - > close ) {
int ret ;
if ( ( ret = self - > plugin - > close ( self ) ) ) {
TSK_DEBUG_ERROR ( " Failed to close [%s] codec " , self - > plugin - > desc ) ;
return ret ;
}
else {
self - > opened = tsk_false ;
return 0 ;
}
}
else {
self - > opened = tsk_false ;
return 0 ;
}
}
/**@ingroup tmedia_codec_group
* Generic function to compare two codecs .
* @ param codec1 The first codec to compare .
* @ param codec2 The second codec to compare .
* @ retval Returns an integral value indicating the relationship between the two codecs :
* < 0 : @ a codec1 less than @ a codec2 . < br >
* 0 : @ a codec1 identical to @ a codec2 . < br >
* > 0 : @ a codec1 greater than @ a codec2 . < br >
*/
int tmedia_codec_cmp ( const tsk_object_t * codec1 , const tsk_object_t * codec2 )
{
const tmedia_codec_t * _c1 = codec1 ;
const tmedia_codec_t * _c2 = codec2 ;
if ( ( _c1 & & _c2 ) & & ( _c1 - > type = = _c2 - > type ) ) {
/* Do not compare names. For example, H264 base profile 1.0 will have the
* same name than H264 base profile 3.0 . */
return tsk_stricmp ( _c1 - > format , _c2 - > format ) ;
}
else {
return - 1 ;
}
}
/**@ingroup tmedia_codec_group
* Registers a codec plugin .
* @ param plugin the definition of the plugin .
* @ retval Zero if succeed and non - zero error code otherwise .
* @ sa @ ref tmedia_codec_create ( )
*/
int tmedia_codec_plugin_register ( const tmedia_codec_plugin_def_t * plugin )
{
tsk_size_t i ;
if ( ! plugin | | tsk_strnullORempty ( plugin - > name ) | | tsk_strnullORempty ( plugin - > format ) ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
/* add or replace the plugin */
for ( i = 0 ; i < TMED_CODEC_MAX_PLUGINS ; i + + ) {
if ( ! __tmedia_codec_plugins [ i ] | | ( __tmedia_codec_plugins [ i ] = = plugin ) ) {
__tmedia_codec_plugins [ i ] = plugin ;
2013-07-13 22:28:12 +00:00
TSK_DEBUG_INFO ( " Register codec: %s, %s " , plugin - > name , plugin - > desc ) ;
2012-12-03 03:11:21 +00:00
return 0 ;
}
2013-08-05 04:42:49 +00:00
if ( __tmedia_codec_plugins [ i ] - > codec_id = = plugin - > codec_id & & plugin - > codec_id ! = tmedia_codec_id_none ) { // 'tmedia_codec_id_none' is used for fake codecs
2013-07-28 22:39:50 +00:00
TSK_DEBUG_INFO ( " Codec Registration: '%s' ignored because '%s' already registered " , plugin - > desc , __tmedia_codec_plugins [ i ] - > desc ) ;
return - 3 ;
}
2012-12-03 03:11:21 +00:00
}
TSK_DEBUG_ERROR ( " There are already %d plugins. " , TMED_CODEC_MAX_PLUGINS ) ;
return - 2 ;
}
int tmedia_codec_plugin_register_2 ( const tmedia_codec_plugin_def_t * plugin , int prio )
{
tsk_size_t count = 0 ;
tsk_bool_t already_registered = tsk_false ;
const tmedia_codec_plugin_def_t * tmp ;
if ( ! plugin | | tsk_strnullORempty ( plugin - > name ) | | tsk_strnullORempty ( plugin - > format ) | | ( prio + 1 ) > = TMED_CODEC_MAX_PLUGINS ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
// count codecs and found if already registered
while ( __tmedia_codec_plugins [ count ] ) {
if ( __tmedia_codec_plugins [ count ] = = plugin ) {
already_registered = tsk_true ;
}
+ + count ;
}
if ( count > = TMED_CODEC_MAX_PLUGINS ) {
TSK_DEBUG_ERROR ( " No room " ) ;
return - 1 ;
}
// unregister and compact
if ( already_registered ) {
2013-07-13 22:28:12 +00:00
if ( tmedia_codec_plugin_unregister ( plugin ) = = 0 ) {
- - count ;
}
2012-12-03 03:11:21 +00:00
}
2013-07-13 22:28:12 +00:00
// put current plugin at prio and old (which was at prio) at the end
tmp = __tmedia_codec_plugins [ prio ] ;
2012-12-03 03:11:21 +00:00
__tmedia_codec_plugins [ count ] = tmp ; // put old codec add prio to the end of the list
2013-07-13 22:28:12 +00:00
__tmedia_codec_plugins [ prio ] = plugin ;
2012-12-03 03:11:21 +00:00
return 0 ;
}
/**@ingroup tmedia_codec_group
* Checks whether a codec plugin is registered or not .
* @ param plugin the definition of the plugin to check for availability .
* @ retval 1 ( tsk_true ) if registered and 0 ( tsk_false ) otherwise .
2013-07-13 22:28:12 +00:00
* @ sa @ ref tmedia_codec_plugin_is_registered_2 ( ) @ ref tmedia_codec_plugin_register ( ) and @ ref tmedia_codec_plugin_unregister ( )
2012-12-03 03:11:21 +00:00
*/
tsk_bool_t tmedia_codec_plugin_is_registered ( const tmedia_codec_plugin_def_t * plugin )
{
if ( plugin ) {
tsk_size_t i ;
2013-07-13 22:28:12 +00:00
for ( i = 0 ; i < TMED_CODEC_MAX_PLUGINS & & __tmedia_codec_plugins [ i ] ; i + + ) {
2012-12-03 03:11:21 +00:00
if ( __tmedia_codec_plugins [ i ] = = plugin ) {
return tsk_true ;
}
}
}
return tsk_false ;
}
2013-07-13 22:28:12 +00:00
/**@ingroup tmedia_codec_group
* Checks whether a codec is registered or not .
* @ param codec_id The code id to check .
* @ return 1 @ ref tsk_true if registered and tsk_false otherwise .
* @ sa @ ref tmedia_codec_plugin_is_registered ( ) @ ref tmedia_codec_plugin_register ( ) and @ ref tmedia_codec_plugin_unregister ( )
*/
tsk_bool_t tmedia_codec_plugin_is_registered_2 ( tmedia_codec_id_t codec_id )
{
2013-07-28 22:39:50 +00:00
return ( tmedia_codec_plugin_registered_get_const ( codec_id ) ! = tsk_null ) ;
2013-07-13 22:28:12 +00:00
}
/**@ingroup tmedia_codec_group
* Gets the list of all registered plugins .
* @ param plugins List of the registered plugins .
* @ param count Number of plugins in the list .
* @ return 0 if succeed and non - zero error code otherwise .
*/
2014-12-20 22:57:24 +00:00
int tmedia_codec_plugin_registered_get_all ( const struct tmedia_codec_plugin_def_s * ( * * plugins ) [ TMED_CODEC_MAX_PLUGINS ] , tsk_size_t * count )
2013-07-13 22:28:12 +00:00
{
if ( ! plugins | | ! count ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
2014-12-20 22:57:24 +00:00
* plugins = & __tmedia_codec_plugins ;
2013-07-13 22:28:12 +00:00
* count = sizeof ( __tmedia_codec_plugins ) / sizeof ( __tmedia_codec_plugins [ 0 ] ) ;
return 0 ;
}
2013-07-28 22:39:50 +00:00
/**@ingroup tmedia_codec_group
*/
const struct tmedia_codec_plugin_def_s * tmedia_codec_plugin_registered_get_const ( tmedia_codec_id_t codec_id )
{
tsk_size_t i ;
for ( i = 0 ; i < TMED_CODEC_MAX_PLUGINS & & __tmedia_codec_plugins [ i ] ; i + + ) {
if ( __tmedia_codec_plugins [ i ] - > codec_id = = codec_id ) {
return __tmedia_codec_plugins [ i ] ;
}
}
return tsk_null ;
}
2012-12-03 03:11:21 +00:00
/**@ingroup tmedia_codec_group
* UnRegisters a codec plugin .
* @ param plugin the definition of the plugin .
2013-07-13 22:28:12 +00:00
* @ retval 0 if succeed and non - zero error code otherwise .
2012-12-03 03:11:21 +00:00
*/
int tmedia_codec_plugin_unregister ( const tmedia_codec_plugin_def_t * plugin )
{
tsk_size_t i ;
tsk_bool_t found = tsk_false ;
if ( ! plugin ) {
TSK_DEBUG_ERROR ( " Invalid Parameter " ) ;
return - 1 ;
}
/* find the plugin to unregister */
for ( i = 0 ; i < TMED_CODEC_MAX_PLUGINS & & __tmedia_codec_plugins [ i ] ; i + + ) {
if ( __tmedia_codec_plugins [ i ] = = plugin ) {
2013-07-13 22:28:12 +00:00
TSK_DEBUG_INFO ( " UnRegister codec: %s, %s " , plugin - > name , plugin - > desc ) ;
2012-12-03 03:11:21 +00:00
__tmedia_codec_plugins [ i ] = tsk_null ;
found = tsk_true ;
break ;
}
}
/* compact */
if ( found ) {
for ( ; i < ( TMED_CODEC_MAX_PLUGINS - 1 ) ; i + + ) {
if ( __tmedia_codec_plugins [ i + 1 ] ) {
__tmedia_codec_plugins [ i ] = __tmedia_codec_plugins [ i + 1 ] ;
}
else {
break ;
}
}
__tmedia_codec_plugins [ i ] = tsk_null ;
}
return ( found ? 0 : - 2 ) ;
}
2013-07-13 22:28:12 +00:00
/**@ingroup tmedia_codec_group
* Unregister all codecs
* @ retval 0 if succeed and non - zero error code otherwise .
*/
int tmedia_codec_plugin_unregister_all ( )
{
memset ( ( void * ) __tmedia_codec_plugins , 0 , sizeof ( __tmedia_codec_plugins ) ) ;
return 0 ;
}
2012-12-03 03:11:21 +00:00
/**@ingroup tmedia_codec_group
* Creates a new codec using an already registered plugin .
* @ param format The format of the codec to create ( e . g . " 0 " for PCMU or " 8 " for PCMA or " * " for MSRP )
* @ sa @ ref tmedia_codec_plugin_register ( )
*/
tmedia_codec_t * tmedia_codec_create ( const char * format )
{
tmedia_codec_t * codec = tsk_null ;
const tmedia_codec_plugin_def_t * plugin ;
tsk_size_t i = 0 ;
while ( ( i < TMED_CODEC_MAX_PLUGINS ) & & ( plugin = __tmedia_codec_plugins [ i + + ] ) ) {
if ( plugin - > objdef & & tsk_striequals ( plugin - > format , format ) ) {
if ( ( codec = tsk_object_new ( plugin - > objdef ) ) ) {
/* initialize the newly created codec */
codec - > id = plugin - > codec_id ;
codec - > dyn = plugin - > dyn ;
codec - > plugin = plugin ;
codec - > bl = tmedia_bl_medium ;
switch ( plugin - > type ) {
case tmedia_audio :
{ /* Audio codec */
tmedia_codec_audio_t * audio = TMEDIA_CODEC_AUDIO ( codec ) ;
tmedia_codec_audio_init ( TMEDIA_CODEC ( audio ) , plugin - > name , plugin - > desc , plugin - > format ) ;
break ;
}
case tmedia_video :
{ /* Video codec */
tmedia_codec_video_t * video = TMEDIA_CODEC_VIDEO ( codec ) ;
tmedia_codec_video_init ( TMEDIA_CODEC ( video ) , plugin - > name , plugin - > desc , plugin - > format ) ;
break ;
}
case tmedia_msrp :
{ /* Msrp codec */
tmedia_codec_msrp_init ( codec , plugin - > name , plugin - > desc ) ;
break ;
}
default :
{ /* Any other codec */
tmedia_codec_init ( codec , plugin - > type , plugin - > name , plugin - > desc , plugin - > format ) ;
break ;
}
}
break ;
}
}
}
return codec ;
}
/**@ingroup tmedia_codec_group
* Gets the rtpmap attribute associated to this code .
* @ param self the codec for which to get the rtpmap attribute . Should be created using @ ref tmedia_codec_create ( ) .
* @ retval rtpmap string ( e . g . " AMR-WB/16000/2 " or " H261/90000 " ) if succeed and Null otherwise . It ' s up to the caller to free the
* returned string .
*/
char * tmedia_codec_get_rtpmap ( const tmedia_codec_t * self )
{
char * rtpmap = tsk_null ;
if ( ! self | | ! self - > plugin ) {
TSK_DEBUG_ERROR ( " invalid parameter " ) ;
return tsk_null ;
}
if ( self - > type & tmedia_video ) {
/* const tmedia_codec_video_t* videoCodec = (const tmedia_codec_video_t*)self; */
tsk_sprintf ( & rtpmap , " %s %s " , self - > neg_format ? self - > neg_format : self - > format , self - > name ) ;
if ( self - > plugin - > rate ) {
tsk_strcat_2 ( & rtpmap , " /%d " , self - > plugin - > rate ) ;
}
}
else if ( self - > type & tmedia_audio ) {
/* const tmedia_codec_audio_t* audioCodec = (const tmedia_codec_audio_t*)self; */
// special case for G.722 which has fake rate
if ( tsk_strequals ( self - > plugin - > format , TMEDIA_CODEC_FORMAT_G722 ) ) {
tsk_sprintf ( & rtpmap , " %s %s/8000/%d " , self - > neg_format ? self - > neg_format : self - > format , self - > name , self - > plugin - > audio . channels ) ;
}
else {
tsk_sprintf ( & rtpmap , " %s %s " , self - > neg_format ? self - > neg_format : self - > format , self - > name ) ;
if ( self - > plugin - > rate ) {
tsk_strcat_2 ( & rtpmap , " /%d " , self - > plugin - > rate ) ;
}
if ( self - > plugin - > audio . channels > 0 ) {
tsk_strcat_2 ( & rtpmap , " /%d " , self - > plugin - > audio . channels ) ;
}
}
}
else if ( self - > type & tmedia_t140 ) {
tsk_sprintf ( & rtpmap , " %s %s " , self - > neg_format ? self - > neg_format : self - > format , self - > name ) ;
if ( self - > plugin - > rate ) {
tsk_strcat_2 ( & rtpmap , " /%d " , self - > plugin - > rate ) ;
}
}
else {
}
return rtpmap ;
}
/**@ingroup tmedia_codec_group
* Indicates whether the codec can handle this sdp attribute .
* @ param self the codec to match aginst to .
* @ param att_name the name of the sdp attribute to match e . g . ' fmtp ' or ' imageattr '
* @ retval @ a tsk_true if the codec can handle this fmtp and @ a tsk_false otherwise
*/
tsk_bool_t tmedia_codec_sdp_att_match ( const tmedia_codec_t * self , const char * att_name , const char * att_value )
{
/* checks */
if ( ! self | | ! self - > plugin | | ! self - > plugin - > sdp_att_match | | ! att_name ) {
TSK_DEBUG_ERROR ( " invalid parameter " ) ;
return tsk_false ;
}
/* if attribute value is null or empty -> always match */
if ( tsk_strnullORempty ( att_value ) ) {
return tsk_true ;
}
else {
return self - > plugin - > sdp_att_match ( self , att_name , att_value ) ;
}
}
/**@ingroup tmedia_codec_group
* Gets the codec ' s sdp attribute value
* @ att_name the name of the attribute to get e . g . ' fmtp ' or ' imageattr '
* @ param self the codec for which to get the fmtp attribute . Should be created using @ ref tmedia_codec_create ( ) .
* @ retval sdp attribute attribute string ( e . g . " mode-set=0,2,5,7; mode-change-period=2; mode-change-neighbor=1 " ) . It ' s up to the caller to free the
* returned string .
*/
char * tmedia_codec_sdp_att_get ( const tmedia_codec_t * self , const char * att_name )
{
if ( ! self | | ! self - > plugin | | ! att_name ) {
TSK_DEBUG_ERROR ( " invalid parameter " ) ;
return tsk_null ;
}
if ( self - > plugin - > sdp_att_get ) { /* some codecs, like G711, won't produce fmtp */
return self - > plugin - > sdp_att_get ( self , att_name ) ;
}
return tsk_null ;
}
/**@ingroup tmedia_codec_group
* Remove all codecs except the specified ones .
* @ param codecs the list of codecs from which to remove codecs .
* @ param codecs2keep the codecs which shall not be removed .
* @ retval zero if succeed ( or nothing to do ) and non - zero error code otherwise .
*/
int tmedia_codec_removeAll_exceptThese ( tmedia_codecs_L_t * codecs , const tmedia_codecs_L_t * codecs2keep )
{
tsk_list_item_t * item ;
if ( ! codecs | | ! codecs2keep ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
again :
tsk_list_foreach ( item , codecs ) {
if ( ! tsk_list_find_item_by_pred ( codecs2keep , __pred_find_codec_by_format , ( ( tmedia_codec_t * ) item - > data ) - > format ) ) {
tsk_list_remove_item ( codecs , item ) ;
goto again ;
}
}
return 0 ;
}
/**@ingroup tmedia_codec_group
* Serialize a list of codecs to sdp ( m = line ) message . < br >
* Will add : fmt , rtpmap and fmtp .
* @ param codecs The list of codecs to convert
* @ param m The destination
* @ retval Zero if succeed and non - zero error code otherwise
*/
int tmedia_codec_to_sdp ( const tmedia_codecs_L_t * codecs , tsdp_header_M_t * m )
{
const tsk_list_item_t * item ;
const tmedia_codec_t * codec ;
char * fmtp , * rtpmap , * imageattr ;
tsk_bool_t is_audio , is_video , is_text ;
int ret ;
if ( ! m ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
is_audio = tsk_striequals ( m - > media , " audio " ) ;
is_video = tsk_striequals ( m - > media , " video " ) ;
is_text = tsk_striequals ( m - > media , " text " ) ;
tsk_list_foreach ( item , codecs ) {
const char * neg_format ;
codec = item - > data ;
/* add fmt */
neg_format = codec - > neg_format ? codec - > neg_format : codec - > format ;
if ( ( ret = tsdp_header_M_add_fmt ( m , neg_format ) ) ) {
TSK_DEBUG_ERROR ( " Failed to add format " ) ;
return ret ;
}
if ( is_audio | | is_video | | is_text ) {
char * temp = tsk_null ;
/* add rtpmap attributes */
if ( ( rtpmap = tmedia_codec_get_rtpmap ( codec ) ) ) {
tsdp_header_M_add_headers ( m ,
TSDP_HEADER_A_VA_ARGS ( " rtpmap " , rtpmap ) ,
tsk_null ) ;
TSK_FREE ( rtpmap ) ;
}
/* add 'imageattr' attributes */
if ( ( imageattr = tmedia_codec_sdp_att_get ( codec , " imageattr " ) ) ) {
tsk_sprintf ( & temp , " %s %s " , neg_format , imageattr ) ;
tsdp_header_M_add_headers ( m ,
TSDP_HEADER_A_VA_ARGS ( " imageattr " , temp ) ,
tsk_null ) ;
TSK_FREE ( temp ) ;
TSK_FREE ( imageattr ) ;
}
/* add fmtp attributes */
if ( ( fmtp = tmedia_codec_sdp_att_get ( codec , " fmtp " ) ) ) {
if ( is_video & & tmedia_defaults_get_screen_x ( ) > 0 & & tmedia_defaults_get_screen_y ( ) > 0 ) {
tsk_sprintf ( & temp , " %s %s;sx=%d;sy=%d " , neg_format , fmtp , tmedia_defaults_get_screen_x ( ) , tmedia_defaults_get_screen_y ( ) ) ; //doubango clients
}
else {
tsk_sprintf ( & temp , " %s %s " , neg_format , fmtp ) ;
}
tsdp_header_M_add_headers ( m ,
TSDP_HEADER_A_VA_ARGS ( " fmtp " , temp ) ,
tsk_null ) ;
TSK_FREE ( temp ) ;
TSK_FREE ( fmtp ) ;
}
/* special case for T.140 + red */
if ( is_text & & tsk_striequals ( codec - > format , TMEDIA_CODEC_FORMAT_RED ) ) {
const tmedia_codec_t * codec_t140 = tsk_list_find_object_by_pred ( codecs , __pred_find_codec_by_format , TMEDIA_CODEC_FORMAT_T140 ) ;
if ( codec_t140 ) {
const char * neg_format_t140 = codec_t140 - > neg_format ? codec_t140 - > neg_format : codec_t140 - > format ;
tsk_sprintf ( & temp , " %s %s/%s/%s/%s " , neg_format , neg_format_t140 , neg_format_t140 , neg_format_t140 , neg_format_t140 ) ;
tsdp_header_M_add_headers ( m ,
TSDP_HEADER_A_VA_ARGS ( " fmtp " , temp ) ,
tsk_null ) ;
TSK_FREE ( temp ) ;
}
}
}
}
return 0 ;
}
/**@ingroup tmedia_codec_group
* Finds a codec by format . If the codec has a dyn . payload type , then this function will also compare negociate formats .
* @ param codecs List of codecs from which to retrieve the matching codec .
* @ param format the format of the codec to find .
* @ retval Zero if succeed and non - zero error code otherwise .
*/
tmedia_codec_t * tmedia_codec_find_by_format ( tmedia_codecs_L_t * codecs , const char * format )
{
const tmedia_codec_t * codec = tsk_null ;
if ( ! codecs | | ! format ) {
TSK_DEBUG_ERROR ( " Inalid parameter " ) ;
return tsk_null ;
}
if ( ( codec = tsk_list_find_object_by_pred ( codecs , __pred_find_codec_by_format , format ) ) | |
( codec = tsk_list_find_object_by_pred ( codecs , __pred_find_codec_by_neg_format , format ) ) ) {
return tsk_object_ref ( ( void * ) codec ) ;
}
else {
return tsk_null ;
}
}
/**@ingroup tmedia_codec_group
*/
int tmedia_codec_parse_fmtp ( const char * fmtp , unsigned * maxbr , unsigned * fps , unsigned * width , unsigned * height )
{
2014-04-28 21:16:53 +00:00
char * copy , * pch , * saveptr ;
2012-12-03 03:11:21 +00:00
tsk_bool_t found = tsk_false ;
if ( tsk_strnullORempty ( fmtp ) ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
copy = tsk_strdup ( fmtp ) ;
2014-04-28 21:16:53 +00:00
pch = tsk_strtok_r ( copy , " ; / " , & saveptr ) ;
2012-12-03 03:11:21 +00:00
while ( pch ) {
unsigned div = 0 ;
if ( sscanf ( pch , " QCIF=%u " , & div ) = = 1 & & div ) {
* fps = 30 / div ;
* width = 176 ;
* height = 144 ;
found = tsk_true ;
}
else if ( sscanf ( pch , " CIF=%u " , & div ) = = 1 & & div ) {
* fps = 30 / div ;
* width = 352 ;
* height = 288 ;
found = tsk_true ;
}
else if ( sscanf ( pch , " SQCIF=%u " , & div ) = = 1 & & div ) {
* fps = 30 / div ;
* width = 128 ;
* height = 96 ;
found = tsk_true ;
}
else if ( sscanf ( pch , " QVGA=%u " , & div ) = = 1 & & div ) {
* fps = 30 / div ;
* width = 320 ;
* height = 240 ;
found = tsk_true ;
}
// to be continued
if ( found ) {
//found = tsk_false;
2014-04-28 21:16:53 +00:00
pch = tsk_strtok_r ( tsk_null , " ; " , & saveptr ) ;
2012-12-03 03:11:21 +00:00
while ( pch ) {
if ( sscanf ( pch , " MaxBR=%u " , maxbr ) = = 1 ) {
//found = tsk_true;
break ;
}
2014-04-28 21:16:53 +00:00
pch = tsk_strtok_r ( tsk_null , " ; / " , & saveptr ) ;
2012-12-03 03:11:21 +00:00
}
}
if ( found ) {
break ;
}
2014-04-28 21:16:53 +00:00
pch = tsk_strtok_r ( tsk_null , " ; / " , & saveptr ) ;
2012-12-03 03:11:21 +00:00
}
TSK_FREE ( copy ) ;
return found ? 0 : - 2 ;
}
/**@ingroup tmedia_codec_group
* DeInitialize a Codec .
* @ param self The codec to deinitialize . Could be any type of codec ( e . g . @ ref tmedia_codec_audio_t or @ ref tmedia_codec_video_t ) .
* @ retval Zero if succeed and non - zero error code otherwise .
*/
int tmedia_codec_deinit ( tmedia_codec_t * self )
{
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( self - > opened ) {
tmedia_codec_close ( self ) ;
}
TSK_FREE ( self - > name ) ;
TSK_FREE ( self - > desc ) ;
TSK_FREE ( self - > format ) ;
TSK_FREE ( self - > neg_format ) ;
return 0 ;
}
int tmedia_codec_video_set_enc_callback ( tmedia_codec_video_t * self , tmedia_codec_video_enc_cb_f callback , const void * callback_data )
{
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
self - > out . callback = callback ;
self - > out . result . usr_data = callback_data ;
return 0 ;
}
int tmedia_codec_video_set_dec_callback ( tmedia_codec_video_t * self , tmedia_codec_video_dec_cb_f callback , const void * callback_data )
{
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
self - > in . callback = callback ;
self - > in . result . usr_data = callback_data ;
return 0 ;
2013-05-07 04:55:21 +00:00
}
2013-05-15 21:43:14 +00:00
float tmedia_codec_audio_get_timestamp_multiplier ( tmedia_codec_id_t id , uint32_t sample_rate )
2013-05-07 04:55:21 +00:00
{
switch ( id ) {
case tmedia_codec_id_opus :
{
// draft-spittka-payload-rtp-opus-03 - 4.1. RTP Header Usage
switch ( sample_rate ) {
2013-05-15 21:43:14 +00:00
case 8000 : return 6.f ;
case 12000 : return 4.f ;
case 16000 : return 3.f ;
case 24000 : return 2.f ;
default : case 48000 : return 1.f ;
2013-05-07 04:55:21 +00:00
}
break ;
}
2013-05-15 21:43:14 +00:00
case tmedia_codec_id_g722 :
{
/* http://www.ietf.org/rfc/rfc3551.txt
Even though the actual sampling rate for G .722 audio is 16 , 000 Hz ,
the RTP clock rate for the G722 payload format is 8 , 000 Hz because
that value was erroneously assigned in RFC 1890 and must remain
unchanged for backward compatibility . The octet rate or sample - pair
rate is 8 , 000 Hz .
*/
return .5f ;
}
2013-05-07 04:55:21 +00:00
default :
{
return 1 ;
}
}
2011-03-25 09:38:07 +00:00
}