int tdav_codec_h264_init(tdav_codec_h264_t* self, profile_idc_t profile) { int ret = 0; level_idc_t level; if(!self){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if((ret = tdav_codec_h264_common_init(TDAV_CODEC_H264_COMMON(self)))){ TSK_DEBUG_ERROR("tdav_codec_h264_common_init() faile with error code=%d", ret); return ret; } if((ret = tdav_codec_h264_common_level_from_size(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, &level))){ TSK_DEBUG_ERROR("Failed to find level for size=[%u, %u]", TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height); return ret; } (self)->encoder.max_bw_kpbs = TMEDIA_CODEC(self)->bandwidth_max_upload; TDAV_CODEC_H264_COMMON(self)->pack_mode_local = H264_PACKETIZATION_MODE; TDAV_CODEC_H264_COMMON(self)->profile = profile; TDAV_CODEC_H264_COMMON(self)->level = level; TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS*1000; TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR*1000; #if HAVE_FFMPEG if(!(self->encoder.codec = avcodec_find_encoder(CODEC_ID_H264))){ TSK_DEBUG_ERROR("Failed to find H.264 encoder"); ret = -2; } if(!(self->decoder.codec = avcodec_find_decoder(CODEC_ID_H264))){ TSK_DEBUG_ERROR("Failed to find H.264 decoder"); ret = -3; } #endif #if HAVE_H264_PASSTHROUGH TMEDIA_CODEC(self)->passthrough = tsk_true; self->decoder.passthrough = tsk_true; self->encoder.passthrough = tsk_true; #endif self->encoder.quality = 1; /* allocations MUST be done by open() */ return ret; }
/**@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; }
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; }
/* 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; }
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; }
int tdav_codec_h264_open_decoder(tdav_codec_h264_t* self) { #if HAVE_FFMPEG int ret; if(self->decoder.context){ TSK_DEBUG_ERROR("Decoder already opened"); return -1; } self->decoder.context = avcodec_alloc_context(); avcodec_get_context_defaults(self->decoder.context); self->decoder.context->pix_fmt = PIX_FMT_YUV420P; self->decoder.context->flags2 |= CODEC_FLAG2_FAST; self->decoder.context->width = TMEDIA_CODEC_VIDEO(self)->in.width; self->decoder.context->height = TMEDIA_CODEC_VIDEO(self)->in.height; // Picture (YUV 420) if(!(self->decoder.picture = avcodec_alloc_frame())){ TSK_DEBUG_ERROR("Failed to create decoder picture"); return -2; } avcodec_get_frame_defaults(self->decoder.picture); // Open decoder if((ret = avcodec_open(self->decoder.context, self->decoder.codec)) < 0){ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc); return ret; } self->decoder.last_seq = 0; return ret; #elif HAVE_H264_PASSTHROUGH return 0; #endif TSK_DEBUG_ERROR("Unexpected code called"); return -1; }
static void tdav_codec_mp4ves_rtp_callback(tdav_codec_mp4ves_t *mp4v, const void *data, tsk_size_t size, tsk_bool_t marker) { // Send data over the network if(TMEDIA_CODEC_VIDEO(mp4v)->out.callback){ TMEDIA_CODEC_VIDEO(mp4v)->out.result.buffer.ptr = data; TMEDIA_CODEC_VIDEO(mp4v)->out.result.buffer.size = size; TMEDIA_CODEC_VIDEO(mp4v)->out.result.duration = (uint32_t)((1./(double)TMEDIA_CODEC_VIDEO(mp4v)->out.fps) * TMEDIA_CODEC(mp4v)->plugin->rate); TMEDIA_CODEC_VIDEO(mp4v)->out.result.last_chunck = marker; TMEDIA_CODEC_VIDEO(mp4v)->out.callback(&TMEDIA_CODEC_VIDEO(mp4v)->out.result); } }
int tdav_codec_h264_open_encoder(tdav_codec_h264_t* self) { #if HAVE_FFMPEG int ret; tsk_size_t size; if(self->encoder.context){ TSK_DEBUG_ERROR("Encoder already opened"); return -1; } #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0)) if((self->encoder.context = avcodec_alloc_context3(self->encoder.codec))){ avcodec_get_context_defaults3(self->encoder.context, self->encoder.codec); } #else if((self->encoder.context = avcodec_alloc_context())){ avcodec_get_context_defaults(self->encoder.context); } #endif if(!self->encoder.context){ TSK_DEBUG_ERROR("Failed to allocate context"); return -1; } #if TDAV_UNDER_X86 && LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE); #endif self->encoder.context->pix_fmt = PIX_FMT_YUV420P; self->encoder.context->time_base.num = 1; self->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(self)->out.fps; self->encoder.context->width = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width; self->encoder.context->height = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height; self->encoder.max_bw_kpbs = TSK_CLAMP( 0, tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps), TMEDIA_CODEC(self)->bandwidth_max_upload ); self->encoder.context->bit_rate = (self->encoder.max_bw_kpbs * 1024);// bps self->encoder.context->rc_min_rate = (self->encoder.context->bit_rate >> 3); self->encoder.context->rc_max_rate = self->encoder.context->bit_rate; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->rc_lookahead = 0; #endif self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->partitions = X264_PART_I4X4 | X264_PART_I8X8 | X264_PART_P8X8 | X264_PART_B8X8; #endif self->encoder.context->me_method = ME_UMH; self->encoder.context->me_range = 16; self->encoder.context->qmin = 10; self->encoder.context->qmax = 51; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->mb_qmin = self->encoder.context->qmin; self->encoder.context->mb_qmax = self->encoder.context->qmax; #endif /* METROPOLIS = G2J.COM TelePresence client. Check Issue 378: No video when calling "TANDBERG/4129 (X8.1.1)" */ #if !METROPOLIS && 0 self->encoder.context->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif self->encoder.context->flags |= CODEC_FLAG_LOW_DELAY; if (self->encoder.context->profile == FF_PROFILE_H264_BASELINE) { self->encoder.context->max_b_frames = 0; } switch(TDAV_CODEC_H264_COMMON(self)->profile){ case profile_idc_baseline: default: self->encoder.context->profile = FF_PROFILE_H264_BASELINE; self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level; break; case profile_idc_main: self->encoder.context->profile = FF_PROFILE_H264_MAIN; self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level; break; } /* Comment from libavcodec/libx264.c: * Allow x264 to be instructed through AVCodecContext about the maximum * size of the RTP payload. For example, this enables the production of * payload suitable for the H.264 RTP packetization-mode 0 i.e. single * NAL unit per RTP packet. */ self->encoder.context->rtp_payload_size = H264_RTP_PAYLOAD_SIZE; self->encoder.context->opaque = tsk_null; self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->out.fps * TDAV_H264_GOP_SIZE_IN_SECONDS); #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0)) if((ret = av_opt_set_int(self->encoder.context->priv_data, "slice-max-size", H264_RTP_PAYLOAD_SIZE, 0))){ TSK_DEBUG_ERROR("Failed to set x264 slice-max-size to %d", H264_RTP_PAYLOAD_SIZE); } if((ret = av_opt_set(self->encoder.context->priv_data, "profile", (self->encoder.context->profile == FF_PROFILE_H264_BASELINE ? "baseline" : "main"), 0))){ TSK_DEBUG_ERROR("Failed to set x264 profile"); } if((ret = av_opt_set(self->encoder.context->priv_data, "preset", "veryfast", 0))){ TSK_DEBUG_ERROR("Failed to set x264 preset to veryfast"); } if((ret = av_opt_set_int(self->encoder.context->priv_data, "rc-lookahead", 0, 0)) && (ret = av_opt_set_int(self->encoder.context->priv_data, "rc_lookahead", 0, 0))){ TSK_DEBUG_ERROR("Failed to set x264 rc_lookahead=0"); } if((ret = av_opt_set(self->encoder.context->priv_data, "tune", "animation+zerolatency", 0))){ TSK_DEBUG_ERROR("Failed to set x264 tune to zerolatency"); } #endif // Picture (YUV 420) if(!(self->encoder.picture = avcodec_alloc_frame())){ TSK_DEBUG_ERROR("Failed to create encoder picture"); return -2; } avcodec_get_frame_defaults(self->encoder.picture); size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height); if(!(self->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){ TSK_DEBUG_ERROR("Failed to allocate encoder buffer"); return -2; } // Open encoder if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc); return ret; } self->encoder.frame_count = 0; TSK_DEBUG_INFO("[H.264] bitrate=%d bps", self->encoder.context->bit_rate); return ret; #elif HAVE_H264_PASSTHROUGH self->encoder.frame_count = 0; return 0; #endif TSK_DEBUG_ERROR("Not expected code called"); return -1; }
static int tdav_codec_h264_set(tmedia_codec_t* self, const tmedia_param_t* param) { tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self; if (param->value_type == tmedia_pvt_int32) { if(tsk_striequals(param->key, "action")){ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value); switch(action){ case tmedia_codec_action_encode_idr: { h264->encoder.force_idr = tsk_true; break; } case tmedia_codec_action_bw_down: { h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality + 1), 31); #if HAVE_FFMPEG if (h264->encoder.context) { h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality; } #endif break; } case tmedia_codec_action_bw_up: { h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality - 1), 31); #if HAVE_FFMPEG if (h264->encoder.context) { h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality; } #endif break; } } return 0; } else if(tsk_striequals(param->key, "bw_kbps")){ int32_t max_bw_userdefine = self->bandwidth_max_upload; int32_t max_bw_new = *((int32_t*)param->value); if (max_bw_userdefine > 0) { // do not use more than what the user defined in it's configuration h264->encoder.max_bw_kpbs = TSK_MIN(max_bw_new, max_bw_userdefine); } else { h264->encoder.max_bw_kpbs = max_bw_new; } return 0; } else if(tsk_striequals(param->key, "bypass-encoding")){ h264->encoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false; TSK_DEBUG_INFO("[H.264] bypass-encoding = %d", h264->encoder.passthrough); return 0; } else if(tsk_striequals(param->key, "bypass-decoding")){ h264->decoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false; TSK_DEBUG_INFO("[H.264] bypass-decoding = %d", h264->decoder.passthrough); return 0; } else if(tsk_striequals(param->key, "rotation")){ int32_t rotation = *((int32_t*)param->value); if(h264->encoder.rotation != rotation){ h264->encoder.rotation = rotation; if (self->opened) { int ret; if ((ret = tdav_codec_h264_close_encoder(h264, kResetRotationFalse))) { return ret; } if ((ret = tdav_codec_h264_open_encoder(h264))) { return ret; } #if 0 // Not working if((ret = avcodec_close(h264->encoder.context))){ TSK_DEBUG_ERROR("Failed to close [%s] codec", TMEDIA_CODEC(h264)->plugin->desc); return ret; } h264->encoder.context->width = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.height : TMEDIA_CODEC_VIDEO(h264)->out.width; h264->encoder.context->height = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.width : TMEDIA_CODEC_VIDEO(h264)->out.height; if((ret = avcodec_open(h264->encoder.context, h264->encoder.codec)) < 0){ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc); return ret; } h264->encoder.force_idr = tsk_true; #endif } } return 0; } } return -1; }
// 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); }
int tdav_codec_h264_open_encoder(tdav_codec_h264_t* self) { #if HAVE_FFMPEG int ret; tsk_size_t size; int32_t max_bw_kpbs; if(self->encoder.context){ TSK_DEBUG_ERROR("Encoder already opened"); return -1; } #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0)) if((self->encoder.context = avcodec_alloc_context3(self->encoder.codec))){ avcodec_get_context_defaults3(self->encoder.context, self->encoder.codec); } #else if((self->encoder.context = avcodec_alloc_context())){ avcodec_get_context_defaults(self->encoder.context); } #endif if(!self->encoder.context){ TSK_DEBUG_ERROR("Failed to allocate context"); return -1; } #if TDAV_UNDER_X86 && LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE); #endif self->encoder.context->pix_fmt = PIX_FMT_YUV420P; self->encoder.context->time_base.num = 1; self->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(self)->out.fps; self->encoder.context->width = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width; self->encoder.context->height = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height; max_bw_kpbs = TSK_CLAMP( 0, tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps), self->encoder.max_bw_kpbs ); self->encoder.context->bit_rate = (max_bw_kpbs * 1024);// bps self->encoder.context->rc_min_rate = (self->encoder.context->bit_rate >> 3); self->encoder.context->rc_max_rate = self->encoder.context->bit_rate; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->rc_lookahead = 0; #endif self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality; self->encoder.context->scenechange_threshold = 0; self->encoder.context->me_subpel_quality = 0; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->partitions = X264_PART_I4X4 | X264_PART_I8X8 | X264_PART_P8X8 | X264_PART_B8X8; #endif self->encoder.context->me_method = ME_EPZS; self->encoder.context->trellis = 0; self->encoder.context->me_range = 16; self->encoder.context->qmin = 10; self->encoder.context->qmax = 51; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->mb_qmin = self->encoder.context->qmin; self->encoder.context->mb_qmax = self->encoder.context->qmax; #endif self->encoder.context->qcompress = 0.6f; self->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE; #if LIBAVCODEC_VERSION_MAJOR <= 53 self->encoder.context->flags2 |= CODEC_FLAG2_FASTPSKIP; #else self->encoder.context->flags2 |= CODEC_FLAG2_FAST; #endif self->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER; self->encoder.context->flags |= CODEC_FLAG_GLOBAL_HEADER; self->encoder.context->flags |= CODEC_FLAG_LOW_DELAY; self->encoder.context->max_b_frames = 0; self->encoder.context->b_frame_strategy = 1; self->encoder.context->chromaoffset = 0; switch(TDAV_CODEC_H264_COMMON(self)->profile){ case profile_idc_baseline: default: self->encoder.context->profile = FF_PROFILE_H264_BASELINE; self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level; break; case profile_idc_main: self->encoder.context->profile = FF_PROFILE_H264_MAIN; self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level; break; } self->encoder.context->rtp_payload_size = H264_RTP_PAYLOAD_SIZE; self->encoder.context->opaque = tsk_null; self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->out.fps * TDAV_H264_GOP_SIZE_IN_SECONDS); #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0)) if((ret = av_opt_set_int(self->encoder.context->priv_data, "slice-max-size", H264_RTP_PAYLOAD_SIZE, 0))){ TSK_DEBUG_ERROR("Failed to set x264 slice-max-size to %d", H264_RTP_PAYLOAD_SIZE); } if((ret = av_opt_set(self->encoder.context->priv_data, "profile", (self->encoder.context->profile == FF_PROFILE_H264_BASELINE ? "baseline" : "main"), 0))){ TSK_DEBUG_ERROR("Failed to set x264 profile"); } if((ret = av_opt_set(self->encoder.context->priv_data, "preset", "veryfast", 0))){ TSK_DEBUG_ERROR("Failed to set x264 preset to veryfast"); } if((ret = av_opt_set_int(self->encoder.context->priv_data, "rc-lookahead", 0, 0)) && (ret = av_opt_set_int(self->encoder.context->priv_data, "rc_lookahead", 0, 0))){ TSK_DEBUG_ERROR("Failed to set x264 rc_lookahead=0"); } if((ret = av_opt_set(self->encoder.context->priv_data, "tune", "animation+zerolatency", 0))){ TSK_DEBUG_ERROR("Failed to set x264 tune to zerolatency"); } #endif // Picture (YUV 420) if(!(self->encoder.picture = avcodec_alloc_frame())){ TSK_DEBUG_ERROR("Failed to create encoder picture"); return -2; } avcodec_get_frame_defaults(self->encoder.picture); size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height); if(!(self->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){ TSK_DEBUG_ERROR("Failed to allocate encoder buffer"); return -2; } // Open encoder if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc); return ret; } TSK_DEBUG_INFO("[H.264] bitrate=%d bps", self->encoder.context->bit_rate); return ret; #elif HAVE_H264_PASSTHROUGH return 0; #endif TSK_DEBUG_ERROR("Not expected code called"); return -1; }