int tmedia_codec_video_clamp_out_size_to_range_max(tmedia_codec_video_t *self) { int ret = 0; if (!self) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (tmedia_defaults_get_adapt_video_size_range_enabled()) { tmedia_pref_video_size_t min, max; if ((ret = tmedia_defaults_get_pref_video_size_range(&min, &max)) == 0) { unsigned width, height; // clip(max) if ((ret = tmedia_video_get_size(max, &width, &height)) == 0) { unsigned new_width = TSK_CLAMP(0, self->out.width, width); unsigned new_height = TSK_CLAMP(0, self->out.height, height); TSK_DEBUG_INFO("Pref. video size range defined, video size clipped (%ux%u)->(%ux%u)", width, height, self->out.width, self->out.height); self->out.width = width; self->out.height = height; } // no clip(min) as we cannot increase the size to more than what was negotiated without sending reINVITE } } return ret; }
static int tdav_codec_mp4ves_set(tmedia_codec_t* self, const tmedia_param_t* param) { tdav_codec_mp4ves_t* mp4ves = (tdav_codec_mp4ves_t*)self; if(!self->opened){ TSK_DEBUG_ERROR("Codec not opened"); return -1; } 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: { mp4ves->encoder.force_idr = tsk_true; break; } case tmedia_codec_action_bw_down: { mp4ves->encoder.quality = TSK_CLAMP(1, (mp4ves->encoder.quality + 1), 31); mp4ves->encoder.context->global_quality = FF_QP2LAMBDA * mp4ves->encoder.quality; break; } case tmedia_codec_action_bw_up: { mp4ves->encoder.quality = TSK_CLAMP(1, (mp4ves->encoder.quality - 1), 31); mp4ves->encoder.context->global_quality = FF_QP2LAMBDA * mp4ves->encoder.quality; break; } } } else if(tsk_striequals(param->key, "rotation")){ int rotation = *((int32_t*)param->value); if(mp4ves->encoder.rotation != rotation){ if(self->opened){ int ret; mp4ves->encoder.rotation = rotation; if((ret = tdav_codec_mp4ves_close_encoder(mp4ves))){ return ret; } if((ret = tdav_codec_mp4ves_open_encoder(mp4ves))){ return ret; } } } return 0; } } return -1; }
int tdav_consumer_audio_set(tdav_consumer_audio_t* self, const tmedia_param_t* param) { if(!self){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(param->plugin_type == tmedia_ppt_consumer){ if(param->value_type == tmedia_pvt_int32){ if(tsk_striequals(param->key, "gain")){ int32_t gain = *((int32_t*)param->value); if(gain<TDAV_AUDIO_GAIN_MAX && gain>=0){ TMEDIA_CONSUMER(self)->audio.gain = (uint8_t)gain; TSK_DEBUG_INFO("audio consumer gain=%u", gain); } else{ TSK_DEBUG_ERROR("%u is invalid as gain value", gain); return -2; } } else if(tsk_striequals(param->key, "volume")){ TMEDIA_CONSUMER(self)->audio.volume = TSK_TO_INT32((uint8_t*)param->value); TMEDIA_CONSUMER(self)->audio.volume = TSK_CLAMP(0, TMEDIA_CONSUMER(self)->audio.volume, 100); } } } return 0; }
static int tdav_webrtc_denoise_set(tmedia_denoise_t* _self, const tmedia_param_t* param) { tdav_webrtc_denoise_t *self = (tdav_webrtc_denoise_t *)_self; if(!self || !param){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(param->value_type == tmedia_pvt_int32){ if(tsk_striequals(param->key, "echo-tail")){ int32_t echo_tail = *((int32_t*)param->value); self->echo_tail = TSK_CLAMP(WEBRTC_MIN_ECHO_TAIL, echo_tail, WEBRTC_MAX_ECHO_TAIL); TSK_DEBUG_INFO("set_echo_tail (%d->%d)", echo_tail, self->echo_tail); return 0; } } return -1; }
int tmedia_codec_plugin_register_2(const tmedia_codec_plugin_def_t* plugin, int prio) { tsk_size_t index = 0, max; 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[index]) { if (__tmedia_codec_plugins[index] == plugin) { already_registered = tsk_true; } ++index; } if (index >= TMED_CODEC_MAX_PLUGINS) { TSK_DEBUG_ERROR("No room"); return -1; } // clamp prio (must be done here before unregistering the plugin) max = tmedia_codec_plugin_registered_count(__tmedia_codec_plugins, sizeof(__tmedia_codec_plugins)/sizeof(__tmedia_codec_plugins[0])); prio = TSK_CLAMP(0, prio, (int)(max > 0 ? (max - 1) : 0)); // unregister and compact if (already_registered) { if (tmedia_codec_plugin_unregister(plugin) == 0) { --index; } } // put current plugin at prio and old (which was at prio) at the end tmp = __tmedia_codec_plugins[prio]; __tmedia_codec_plugins[index] = tmp;// put old codec add prio to the end of the list __tmedia_codec_plugins[prio] = plugin; return 0; }
// Congestion quality metrics based int tdav_video_jb_get_qcong(tdav_video_jb_t* self, float* q) { float lm; if (!self || !q) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } lm = (float)self->latency_max; if (lm <= 0.f) { // must never happen...but used as a guard against div(0) *q = 1.f; } else { // when "frames_count" is > "latency_max" q is < 0 but it'll be clipped to 0.f *q = 1.f - (self->frames_count / lm); } // 0.0001f instead of zero which could be interpreted as "no data available" // 0.0001f encoded to 1-byte in RTCP-RR-JCNG will be coded as (0.0001f * 255.f) = zero *q = TSK_CLAMP(0.0001f, *q, 1.f); return 0; }
static int tdav_webrtc_denoise_open(tmedia_denoise_t* self, uint32_t frame_size, uint32_t sampling_rate) { tdav_webrtc_denoise_t *denoiser = (tdav_webrtc_denoise_t *)self; int ret; if(!denoiser){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(denoiser->AEC_inst || #if HAVE_SPEEX_DSP && PREFER_SPEEX_DENOISER denoiser->SpeexDenoiser_proc #else denoiser->NS_inst #endif ){ TSK_DEBUG_ERROR("Denoiser already initialized"); return -2; } denoiser->echo_tail = TSK_CLAMP(WEBRTC_MIN_ECHO_TAIL, TMEDIA_DENOISE(denoiser)->echo_tail, WEBRTC_MAX_ECHO_TAIL); TSK_DEBUG_INFO("echo_tail=%d", denoiser->echo_tail); denoiser->echo_skew = TMEDIA_DENOISE(denoiser)->echo_skew; denoiser->frame_size = frame_size; denoiser->sampling_rate = sampling_rate; // // AEC instance // if((ret = TDAV_WebRtcAec_Create(&denoiser->AEC_inst))){ TSK_DEBUG_ERROR("WebRtcAec_Create failed with error code = %d", ret); return ret; } if((ret = TDAV_WebRtcAec_Init(denoiser->AEC_inst, denoiser->sampling_rate, denoiser->sampling_rate))){ TSK_DEBUG_ERROR("WebRtcAec_Init failed with error code = %d", ret); return ret; } #if WEBRTC_AEC_AGGRESSIVE { AecConfig aecConfig; aecConfig.nlpMode = kAecNlpAggressive; aecConfig.skewMode = kAecTrue; aecConfig.metricsMode = kAecFalse; aecConfig.delay_logging = kAecFalse; if((ret = WebRtcAec_set_config(denoiser->AEC_inst, aecConfig))){ TSK_DEBUG_ERROR("WebRtcAec_set_config failed with error code = %d", ret); } } #endif // // Noise Suppression instance // if(TMEDIA_DENOISE(denoiser)->noise_supp_enabled){ #if HAVE_SPEEX_DSP && PREFER_SPEEX_DENOISER if((denoiser->SpeexDenoiser_proc = speex_preprocess_state_init(denoiser->frame_size, denoiser->sampling_rate))){ int i = 1; speex_preprocess_ctl(denoiser->SpeexDenoiser_proc, SPEEX_PREPROCESS_SET_DENOISE, &i); i = TMEDIA_DENOISE(denoiser)->noise_supp_level; speex_preprocess_ctl(denoiser->SpeexDenoiser_proc, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &i); } #else if((ret = TDAV_WebRtcNs_Create(&denoiser->NS_inst))){ TSK_DEBUG_ERROR("WebRtcNs_Create failed with error code = %d", ret); return ret; } if((ret = TDAV_WebRtcNs_Init(denoiser->NS_inst, denoiser->sampling_rate))){ TSK_DEBUG_ERROR("WebRtcNs_Init failed with error code = %d", ret); return ret; } #endif } // allocate temp buffer for record processing if(!(denoiser->temp_rec_out = tsk_realloc(denoiser->temp_rec_out, denoiser->frame_size * kSizeOfWord16))){ TSK_DEBUG_ERROR("Failed to allocate new buffer"); return -3; } TSK_DEBUG_INFO("WebRTC denoiser opened"); return ret; }
/* ============ Internal functions ================= */ int tdav_codec_mp4ves_open_encoder(tdav_codec_mp4ves_t* self) { int ret, size; int32_t max_bw_kpbs; if(!self->encoder.codec && !(self->encoder.codec = avcodec_find_encoder(CODEC_ID_MPEG4))){ TSK_DEBUG_ERROR("Failed to find mp4v encoder"); return -1; } if(self->encoder.context){ TSK_DEBUG_ERROR("Encoder already opened"); return -1; } self->encoder.context = avcodec_alloc_context(); avcodec_get_context_defaults(self->encoder.context); 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)->in.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.context->mb_decision = FF_MB_DECISION_RD; self->encoder.context->noise_reduction = 250; self->encoder.context->flags |= CODEC_FLAG_QSCALE; self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality; 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->rtp_payload_size = MP4V_RTP_PAYLOAD_SIZE; self->encoder.context->opaque = tsk_null; self->encoder.context->profile = self->profile>>4; self->encoder.context->level = self->profile & 0x0F; self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->in.fps * MP4V_GOP_SIZE_IN_SECONDS); self->encoder.context->max_b_frames = 0; self->encoder.context->b_frame_strategy = 1; self->encoder.context->flags |= CODEC_FLAG_AC_PRED; // Picture (YUV 420) if(!(self->encoder.picture = avcodec_alloc_frame())){ TSK_DEBUG_ERROR("Failed to create MP4V-ES 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 MP4V-ES encoder buffer"); return -2; } // Open encoder if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){ TSK_DEBUG_ERROR("Failed to open MP4V-ES encoder"); return ret; } TSK_DEBUG_INFO("[MP4V-ES] bitrate=%d bps", self->encoder.context->bit_rate); return ret; }
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; }
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; }