예제 #1
0
/**@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.711a 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);
	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
	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);
		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());
		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);
	}
	// 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();
	else if(type & tmedia_video){
		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
		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();
		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;
}
예제 #2
0
static tsk_bool_t tdav_codec_opus_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
{
    tdav_codec_opus_t* opus = (tdav_codec_opus_t*)codec;

    if(!opus) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tsk_false;
    }

    TSK_DEBUG_INFO("[OPUS] Trying to match [%s:%s]", att_name, att_value);

    if(tsk_striequals(att_name, "fmtp")) {
        int val_int;
        tsk_params_L_t* params;
        /* e.g. FIXME */
        if((params = tsk_params_fromstring(att_value, ";", tsk_true))) {
            tsk_bool_t ret = tsk_false;
            /* === maxplaybackrate ===*/
            if((val_int = tsk_params_get_param_value_as_int(params, "maxplaybackrate")) != -1) {
                if(!_tdav_codec_opus_rate_is_valid(val_int)) {
                    TSK_DEBUG_ERROR("[OPUS] %d not valid as maxplaybackrate value", val_int);
                    goto done;
                }
                TMEDIA_CODEC(opus)->out.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->out.rate, val_int);
                TMEDIA_CODEC_AUDIO(opus)->out.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->out.rate);
            }
            /* === sprop-maxcapturerate ===*/
            if((val_int = tsk_params_get_param_value_as_int(params, "sprop-maxcapturerate")) != -1) {
                if(!_tdav_codec_opus_rate_is_valid(val_int)) {
                    TSK_DEBUG_ERROR("[OPUS] %d not valid as sprop-maxcapturerate value", val_int);
                    goto done;
                }
                TMEDIA_CODEC(opus)->in.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->in.rate, val_int);
                TMEDIA_CODEC_AUDIO(opus)->in.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->in.rate);
            }
            ret = tsk_true;
done:
            TSK_OBJECT_SAFE_FREE(params);
            return ret;
        }
    }

    return tsk_true;
}
예제 #3
0
/* constructor */
static tsk_object_t* tdav_codec_opus_ctor(tsk_object_t * self, va_list * app)
{
    tdav_codec_opus_t *opus = self;
    if(opus) {
        /* init base: called by tmedia_codec_create() */
        /* init self */
        TMEDIA_CODEC(opus)->in.rate = tmedia_defaults_get_opus_maxplaybackrate();
        TMEDIA_CODEC(opus)->out.rate = tmedia_defaults_get_opus_maxcapturerate();
        TMEDIA_CODEC_AUDIO(opus)->in.channels = 1;
        TMEDIA_CODEC_AUDIO(opus)->out.channels = 1;
#if TDAV_OPUS_FEC_ENABLED
        opus->decoder.fec_enabled = tsk_true;
#endif
#if TDAV_OPUS_DTX_ENABLED
        opus->decoder.dtx_enabled = tsk_true;
#endif
    }
    return self;
}
예제 #4
0
static char* tdav_codec_opus_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
{
    tdav_codec_opus_t* opus = (tdav_codec_opus_t*)codec;

    if(!opus) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tsk_null;
    }

    if(tsk_striequals(att_name, "fmtp")) {
        char* fmtp = tsk_null;
        tsk_sprintf(&fmtp, "maxplaybackrate=%d; sprop-maxcapturerate=%d; stereo=%d; sprop-stereo=%d; useinbandfec=%d; usedtx=%d",
                    TMEDIA_CODEC(opus)->in.rate,
                    TMEDIA_CODEC(opus)->out.rate,
                    (TMEDIA_CODEC_AUDIO(opus)->in.channels == 2) ? 1 : 0,
                    (TMEDIA_CODEC_AUDIO(opus)->out.channels == 2) ? 1 : 0,
                    opus->decoder.fec_enabled ? 1 : 0,
                    opus->decoder.dtx_enabled ? 1 : 0
                   );
        return fmtp;
    }

    return tsk_null;
}
예제 #5
0
/**@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;
}
예제 #6
0
static int tdav_codec_opus_open(tmedia_codec_t* self)
{
    tdav_codec_opus_t* opus = (tdav_codec_opus_t*)self;
    int opus_err;

    if(!opus) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }

    // Initialize the decoder
    if(!opus->decoder.inst) {
        TSK_DEBUG_INFO("[OPUS] Open decoder: rate=%d, channels=%d", (int)self->in.rate, (int)TMEDIA_CODEC_AUDIO(self)->in.channels);
        if(!(opus->decoder.inst = opus_decoder_create((opus_int32)self->in.rate, (int)TMEDIA_CODEC_AUDIO(self)->in.channels, &opus_err)) || opus_err != OPUS_OK) {
            TSK_DEBUG_ERROR("Failed to create Opus decoder(rate=%d, channels=%d) instance with error code=%d.", (int)self->in.rate, (int)TMEDIA_CODEC_AUDIO(self)->in.channels, opus_err);
            return -2;
        }
    }
    opus->decoder.last_seq  = 0;

    // Initialize the encoder
    if(!opus->encoder.inst) {
        TSK_DEBUG_INFO("[OPUS] Open encoder: rate=%d, channels=%d", (int)self->out.rate, (int)TMEDIA_CODEC_AUDIO(self)->out.channels);
        if(!(opus->encoder.inst = opus_encoder_create((opus_int32)self->out.rate, (int)TMEDIA_CODEC_AUDIO(self)->out.channels, OPUS_APPLICATION_VOIP, &opus_err)) || opus_err != OPUS_OK) {
            TSK_DEBUG_ERROR("Failed to create Opus decoder(rate=%d, channels=%d) instance with error code=%d.", (int)self->out.rate, (int)TMEDIA_CODEC_AUDIO(self)->out.channels, opus_err);
            return -2;
        }
    }
#if TDAV_UNDER_MOBILE /* iOS, Android and WP8 */
    opus_encoder_ctl(opus->encoder.inst, OPUS_SET_COMPLEXITY(3));
#endif
    opus_encoder_ctl(opus->encoder.inst, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));

    return 0;
}
// RTP/RTCP callback (From the network to the consumer)
static int tdav_session_audio_rtp_cb(const void* callback_data, const struct trtp_rtp_packet_s* packet)
{
	tdav_session_audio_t* audio = (tdav_session_audio_t*)callback_data;
	tdav_session_av_t* base = (tdav_session_av_t*)callback_data;

	if(!audio || !packet || !packet->header){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	if(audio->is_started && base->consumer && base->consumer->is_started){
		tsk_size_t out_size = 0;

		// Find the codec to use to decode the RTP payload
		if(!audio->decoder.codec || audio->decoder.payload_type != packet->header->payload_type){
			tsk_istr_t format;
			TSK_OBJECT_SAFE_FREE(audio->decoder.codec);
			tsk_itoa(packet->header->payload_type, &format);
			if(!(audio->decoder.codec = tmedia_codec_find_by_format(TMEDIA_SESSION(audio)->neg_codecs, format)) || !audio->decoder.codec->plugin || !audio->decoder.codec->plugin->decode){
				TSK_DEBUG_ERROR("%s is not a valid payload for this session", format);
				return -2;
			}
			audio->decoder.payload_type = packet->header->payload_type;
		}
		
		// Open codec if not already done
		if(!TMEDIA_CODEC(audio->decoder.codec)->opened){
			int ret;
			tsk_safeobj_lock(base);
			if((ret = tmedia_codec_open(audio->decoder.codec))){
				tsk_safeobj_unlock(base);
				TSK_DEBUG_ERROR("Failed to open [%s] codec", audio->decoder.codec->plugin->desc);
				TSK_OBJECT_SAFE_FREE(audio->decoder.codec);
				return ret;
			}
			tsk_safeobj_unlock(base);
		}
		// Decode data
		out_size = audio->decoder.codec->plugin->decode(audio->decoder.codec, packet->payload.data, packet->payload.size, &audio->decoder.buffer, &audio->decoder.buffer_size, packet->header);
		if(out_size){
			void* buffer = audio->decoder.buffer;
			tsk_size_t size = out_size;

			// resample if needed
			if((base->consumer->audio.out.rate && base->consumer->audio.out.rate != audio->decoder.codec->in.rate) || (base->consumer->audio.out.channels && base->consumer->audio.out.channels != TMEDIA_CODEC_AUDIO(audio->decoder.codec)->in.channels)){
				tsk_size_t resampler_result_size = 0;
				int bytesPerSample = (base->consumer->audio.bits_per_sample >> 3);

				if(!audio->decoder.resampler.instance){
					TSK_DEBUG_INFO("Create audio resampler(%s) for consumer: rate=%d->%d, channels=%d->%d, bytesPerSample=%d", 
						audio->decoder.codec->plugin->desc, 
						audio->decoder.codec->in.rate, base->consumer->audio.out.rate,
						TMEDIA_CODEC_AUDIO(audio->decoder.codec)->in.channels, base->consumer->audio.out.channels,
						bytesPerSample);
					audio->decoder.resampler.instance = _tdav_session_audio_resampler_create(
							bytesPerSample, 
							audio->decoder.codec->in.rate, base->consumer->audio.out.rate, 
							base->consumer->audio.ptime, 
							TMEDIA_CODEC_AUDIO(audio->decoder.codec)->in.channels, base->consumer->audio.out.channels, 
							TDAV_AUDIO_RESAMPLER_DEFAULT_QUALITY, 
							&audio->decoder.resampler.buffer, &audio->decoder.resampler.buffer_size
						);
				}
				if(!audio->decoder.resampler.instance){
					TSK_DEBUG_ERROR("No resampler to handle data");
					return -5;
				}
				if(!(resampler_result_size = tmedia_resampler_process(audio->decoder.resampler.instance, buffer, size/bytesPerSample, audio->decoder.resampler.buffer, audio->decoder.resampler.buffer_size/bytesPerSample))){
					TSK_DEBUG_ERROR("Failed to process audio resampler input buffer");
					return -6;
				}
				
				buffer = audio->decoder.resampler.buffer;
				size = audio->decoder.resampler.buffer_size;
			}

			// adjust the gain
			if(base->consumer->audio.gain){
				_tdav_session_audio_apply_gain(buffer, size, base->consumer->audio.bits_per_sample, base->consumer->audio.gain);
			}
			// consume the frame
			tmedia_consumer_consume(base->consumer, buffer, size, packet->header);
		}