static inline struct video_scale_info *get_video_conversion( struct obs_output *output) { if (output->video_conversion_set) { if (!output->video_conversion.width) output->video_conversion.width = obs_output_get_width(output); if (!output->video_conversion.height) output->video_conversion.height = obs_output_get_height(output); return &output->video_conversion; } else if (has_scaling(output)) { const struct video_output_info *info = video_output_get_info(output->video); output->video_conversion.format = info->format; output->video_conversion.colorspace = VIDEO_CS_DEFAULT; output->video_conversion.range = VIDEO_RANGE_DEFAULT; output->video_conversion.width = output->scaled_width; output->video_conversion.height = output->scaled_height; return &output->video_conversion; } return NULL; }
static inline void output_video_data(struct obs_core_video *video, struct video_data *input_frame, int count) { const struct video_output_info *info; struct video_frame output_frame; bool locked; info = video_output_get_info(video->video); locked = video_output_lock_frame(video->video, &output_frame, count, input_frame->timestamp); if (locked) { if (video->gpu_conversion) { set_gpu_converted_data(video, &output_frame, input_frame, info); } else if (format_is_yuv(info->format)) { convert_frame(&output_frame, input_frame, info); } else { copy_rgbx_frame(&output_frame, input_frame, info); } video_output_unlock_frame(video->video); } }
static inline void deinterlace_get_closest_frames(obs_source_t *s, uint64_t sys_time) { const struct video_output_info *info; uint64_t half_interval; if (!s->async_frames.num) return; info = video_output_get_info(obs->video.video); half_interval = (uint64_t)info->fps_den * 500000000ULL / (uint64_t)info->fps_num; if (first_frame(s) || ready_deinterlace_frames(s, sys_time)) { uint64_t offset; s->prev_async_frame = NULL; s->cur_async_frame = s->async_frames.array[0]; da_erase(s->async_frames, 0); if (s->cur_async_frame->prev_frame) { s->prev_async_frame = s->cur_async_frame; s->cur_async_frame = s->async_frames.array[0]; da_erase(s->async_frames, 0); s->deinterlace_half_duration = (uint32_t) ((s->cur_async_frame->timestamp - s->prev_async_frame->timestamp) / 2); } else { s->deinterlace_half_duration = (uint32_t) ((s->cur_async_frame->timestamp - s->deinterlace_frame_ts) / 2); } if (!s->last_frame_ts) s->last_frame_ts = s->cur_async_frame->timestamp; s->deinterlace_frame_ts = s->cur_async_frame->timestamp; offset = obs->video.video_time - s->deinterlace_frame_ts; if (!s->deinterlace_offset) { s->deinterlace_offset = offset; } else { uint64_t offset_diff = uint64_diff( s->deinterlace_offset, offset); if (offset_diff > half_interval) s->deinterlace_offset = offset; } } }
void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video) { const struct video_output_info *voi; if (!video || !encoder || encoder->info.type != OBS_ENCODER_VIDEO) return; voi = video_output_get_info(video); encoder->media = video; encoder->timebase_num = voi->fps_den; encoder->timebase_den = voi->fps_num; }
static void add_video_encoder_params(struct ffmpeg_muxer *stream, struct dstr *cmd, obs_encoder_t *vencoder) { obs_data_t *settings = obs_encoder_get_settings(vencoder); int bitrate = (int)obs_data_get_int(settings, "bitrate"); video_t *video = obs_get_video(); const struct video_output_info *info = video_output_get_info(video); obs_data_release(settings); dstr_catf(cmd, "%s %d %d %d %d %d ", "h264", bitrate, obs_output_get_width(stream->output), obs_output_get_height(stream->output), (int)info->fps_num, (int)info->fps_den); }
static inline void get_video_info(struct obs_encoder *encoder, struct video_scale_info *info) { const struct video_output_info *voi; voi = video_output_get_info(encoder->media); info->format = voi->format; info->colorspace = voi->colorspace; info->range = voi->range; info->width = obs_encoder_get_width(encoder); info->height = obs_encoder_get_height(encoder); if (encoder->info.get_video_info) encoder->info.get_video_info(encoder->context.data, info); if (info->width != voi->width || info->height != voi->height) obs_encoder_set_scaled_size(encoder, info->width, info->height); }
static bool obs_qsv_encode(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet) { struct obs_qsv *obsqsv = data; if (!frame || !packet || !received_packet) return false; EnterCriticalSection(&g_QsvCs); video_t *video = obs_encoder_video(obsqsv->encoder); const struct video_output_info *voi = video_output_get_info(video); mfxBitstream *pBS = NULL; int ret; mfxU64 qsvPTS = frame->pts * 90000 / voi->fps_num; if (frame) ret = qsv_encoder_encode( obsqsv->context, qsvPTS, frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], &pBS); else ret = qsv_encoder_encode( obsqsv->context, qsvPTS, NULL, NULL, 0, 0, &pBS); if (ret < 0) { warn("encode failed"); return false; } parse_packet(obsqsv, packet, pBS, voi->fps_num, received_packet); LeaveCriticalSection(&g_QsvCs); return true; }
void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video) { const struct video_output_info *voi; if (!obs_encoder_valid(encoder, "obs_encoder_set_video")) return; if (encoder->info.type != OBS_ENCODER_VIDEO) { blog(LOG_WARNING, "obs_encoder_set_video: " "encoder '%s' is not a video encoder", obs_encoder_get_name(encoder)); return; } if (!video) return; voi = video_output_get_info(video); encoder->media = video; encoder->timebase_num = voi->fps_den; encoder->timebase_den = voi->fps_num; }
bool obs_get_video_info(struct obs_video_info *ovi) { struct obs_core_video *video = &obs->video; const struct video_output_info *info; if (!obs || !video->graphics) return false; info = video_output_get_info(video->video); if (!info) return false; memset(ovi, 0, sizeof(struct obs_video_info)); ovi->base_width = video->base_width; ovi->base_height = video->base_height; ovi->output_width = info->width; ovi->output_height = info->height; ovi->output_format = info->format; ovi->fps_num = info->fps_num; ovi->fps_den = info->fps_den; return true; }
static void update_params(struct obs_x264 *obsx264, obs_data_t *settings, char **params) { video_t *video = obs_encoder_video(obsx264->encoder); const struct video_output_info *voi = video_output_get_info(video); struct video_scale_info info; info.format = voi->format; info.colorspace = voi->colorspace; info.range = voi->range; obs_x264_video_info(obsx264, &info); int bitrate = (int)obs_data_get_int(settings, "bitrate"); int buffer_size = (int)obs_data_get_int(settings, "buffer_size"); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); int crf = (int)obs_data_get_int(settings, "crf"); int width = (int)obs_encoder_get_width(obsx264->encoder); int height = (int)obs_encoder_get_height(obsx264->encoder); bool use_bufsize = obs_data_get_bool(settings, "use_bufsize"); bool vfr = obs_data_get_bool(settings, "vfr"); bool cbr = obs_data_get_bool(settings, "cbr"); if (keyint_sec) obsx264->params.i_keyint_max = keyint_sec * voi->fps_num / voi->fps_den; if (!use_bufsize) buffer_size = bitrate; obsx264->params.b_vfr_input = vfr; obsx264->params.rc.i_vbv_max_bitrate = bitrate; obsx264->params.rc.i_vbv_buffer_size = buffer_size; obsx264->params.rc.i_bitrate = bitrate; obsx264->params.i_width = width; obsx264->params.i_height = height; obsx264->params.i_fps_num = voi->fps_num; obsx264->params.i_fps_den = voi->fps_den; obsx264->params.pf_log = log_x264; obsx264->params.p_log_private = obsx264; obsx264->params.i_log_level = X264_LOG_WARNING; obsx264->params.vui.i_transfer = get_x264_cs_val(info.colorspace, x264_transfer_names); obsx264->params.vui.i_colmatrix = get_x264_cs_val(info.colorspace, x264_colmatrix_names); obsx264->params.vui.i_colorprim = get_x264_cs_val(info.colorspace, x264_colorprim_names); obsx264->params.vui.b_fullrange = info.range == VIDEO_RANGE_FULL; /* use the new filler method for CBR to allow real-time adjusting of * the bitrate */ if (cbr) { obsx264->params.rc.f_rf_constant = 0.0f; obsx264->params.rc.i_rc_method = X264_RC_ABR; #if X264_BUILD >= 139 obsx264->params.rc.b_filler = true; #else obsx264->params.i_nal_hrd = X264_NAL_HRD_CBR; #endif } else { obsx264->params.rc.i_rc_method = X264_RC_CRF; obsx264->params.rc.f_rf_constant = (float)crf; } if (info.format == VIDEO_FORMAT_NV12) obsx264->params.i_csp = X264_CSP_NV12; else if (info.format == VIDEO_FORMAT_I420) obsx264->params.i_csp = X264_CSP_I420; else if (info.format == VIDEO_FORMAT_I444) obsx264->params.i_csp = X264_CSP_I444; else obsx264->params.i_csp = X264_CSP_NV12; while (*params) set_param(obsx264, *(params++)); info("settings:\n" "\tbitrate: %d\n" "\tbuffer size: %d\n" "\tcrf: %d%s\n" "\tfps_num: %d\n" "\tfps_den: %d\n" "\twidth: %d\n" "\theight: %d\n" "\tkeyint: %d\n" "\tvfr: %s\n" "\tcbr: %s", obsx264->params.rc.i_vbv_max_bitrate, obsx264->params.rc.i_vbv_buffer_size, (int)obsx264->params.rc.f_rf_constant, cbr ? " (0 when CBR is enabled)" : "", voi->fps_num, voi->fps_den, width, height, obsx264->params.i_keyint_max, vfr ? "on" : "off", cbr ? "on" : "off"); }
static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) { video_t *video = obs_encoder_video(obsqsv->encoder); const struct video_output_info *voi = video_output_get_info(video); const char *target_usage = obs_data_get_string(settings, "target_usage"); const char *profile = obs_data_get_string(settings, "profile"); const char *rate_control = obs_data_get_string(settings, "rate_control"); int async_depth = (int)obs_data_get_int(settings, "async_depth"); int target_bitrate = (int)obs_data_get_int(settings, "bitrate"); int max_bitrate = (int)obs_data_get_int(settings, "max_bitrate"); int accuracy = (int)obs_data_get_int(settings, "accuracy"); int convergence = (int)obs_data_get_int(settings, "convergence"); int qpi = (int)obs_data_get_int(settings, "qpi"); int qpp = (int)obs_data_get_int(settings, "qpp"); int qpb = (int)obs_data_get_int(settings, "qpb"); int icq_quality = (int)obs_data_get_int(settings, "icq_quality"); int la_depth = (int)obs_data_get_int(settings, "la_depth"); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); bool cbr_override = obs_data_get_bool(settings, "cbr"); int bFrames = 7; int width = (int)obs_encoder_get_width(obsqsv->encoder); int height = (int)obs_encoder_get_height(obsqsv->encoder); if (astrcmpi(target_usage, "quality") == 0) obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BEST_QUALITY; else if (astrcmpi(target_usage, "balanced") == 0) obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BALANCED; else if (astrcmpi(target_usage, "speed") == 0) obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BEST_SPEED; if (astrcmpi(profile, "baseline") == 0) obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_BASELINE; else if (astrcmpi(profile, "main") == 0) obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_MAIN; else if (astrcmpi(profile, "high") == 0) obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_HIGH; /* internal convenience parameter, overrides rate control param * XXX: Deprecated */ if (cbr_override) { warn("\"cbr\" setting has been deprecated for all encoders! " "Please set \"rate_control\" to \"CBR\" instead. " "Forcing CBR mode. " "(Note to all: this is why you shouldn't use strings for " "common settings)"); rate_control = "CBR"; } if (astrcmpi(rate_control, "CBR") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_CBR; else if (astrcmpi(rate_control, "VBR") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_VBR; else if (astrcmpi(rate_control, "VCM") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_VCM; else if (astrcmpi(rate_control, "CQP") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_CQP; else if (astrcmpi(rate_control, "AVBR") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_AVBR; else if (astrcmpi(rate_control, "ICQ") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_ICQ; else if (astrcmpi(rate_control, "LA_ICQ") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_LA_ICQ; else if (astrcmpi(rate_control, "LA") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_LA; obsqsv->params.nAsyncDepth = (mfxU16)async_depth; obsqsv->params.nAccuracy = (mfxU16)accuracy; obsqsv->params.nConvergence = (mfxU16)convergence; obsqsv->params.nQPI = (mfxU16)qpi; obsqsv->params.nQPP = (mfxU16)qpp; obsqsv->params.nQPB = (mfxU16)qpb; obsqsv->params.nLADEPTH = (mfxU16)la_depth; obsqsv->params.nTargetBitRate = (mfxU16)target_bitrate; obsqsv->params.nMaxBitRate = (mfxU16)max_bitrate; obsqsv->params.nWidth = (mfxU16)width; obsqsv->params.nHeight = (mfxU16)height; obsqsv->params.nFpsNum = (mfxU16)voi->fps_num; obsqsv->params.nFpsDen = (mfxU16)voi->fps_den; obsqsv->params.nbFrames = (mfxU16)bFrames; obsqsv->params.nKeyIntSec = (mfxU16)keyint_sec; obsqsv->params.nICQQuality = (mfxU16)icq_quality; info("settings:\n\trate_control: %s", rate_control); if (obsqsv->params.nRateControl != MFX_RATECONTROL_LA_ICQ && obsqsv->params.nRateControl != MFX_RATECONTROL_ICQ && obsqsv->params.nRateControl != MFX_RATECONTROL_CQP) blog(LOG_INFO, "\ttarget_bitrate: %d", (int)obsqsv->params.nTargetBitRate); if (obsqsv->params.nRateControl == MFX_RATECONTROL_VBR || obsqsv->params.nRateControl == MFX_RATECONTROL_VCM) blog(LOG_INFO, "\tmax_bitrate: %d", (int)obsqsv->params.nMaxBitRate); if (obsqsv->params.nRateControl == MFX_RATECONTROL_LA_ICQ || obsqsv->params.nRateControl == MFX_RATECONTROL_ICQ) blog(LOG_INFO, "\tICQ Quality: %d", (int)obsqsv->params.nICQQuality); if (obsqsv->params.nRateControl == MFX_RATECONTROL_LA_ICQ || obsqsv->params.nRateControl == MFX_RATECONTROL_LA) blog(LOG_INFO, "\tLookahead Depth:%d", (int)obsqsv->params.nLADEPTH); if (obsqsv->params.nRateControl == MFX_RATECONTROL_CQP) blog(LOG_INFO, "\tqpi: %d\n" "\tqpb: %d\n" "\tqpp: %d", qpi, qpb, qpp); blog(LOG_INFO, "\tfps_num: %d\n" "\tfps_den: %d\n" "\twidth: %d\n" "\theight: %d", voi->fps_num, voi->fps_den, width, height); info("debug info:"); }
static bool vaapi_update(void *data, obs_data_t *settings) { struct vaapi_encoder *enc = data; const char *device = obs_data_get_string(settings, "vaapi_device"); int profile = (int)obs_data_get_int(settings, "profile"); int bf = (int)obs_data_get_int(settings, "bf"); int level = (int)obs_data_get_int(settings, "level"); int bitrate = (int)obs_data_get_int(settings, "bitrate"); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); int qp = (int)obs_data_get_int(settings, "qp"); int quality = (int)obs_data_get_int(settings, "quality"); av_opt_set_int(enc->context->priv_data, "qp", qp, 0); av_opt_set_int(enc->context->priv_data, "quality", quality, 0); video_t * video = obs_encoder_video(enc->encoder); const struct video_output_info *voi = video_output_get_info(video); struct video_scale_info info; info.format = voi->format; info.colorspace = voi->colorspace; info.range = voi->range; vaapi_video_info(enc, &info); enc->context->profile = profile; enc->context->max_b_frames = bf; enc->context->level = level; enc->context->bit_rate = bitrate * 1000; enc->context->width = obs_encoder_get_width(enc->encoder); enc->context->height = obs_encoder_get_height(enc->encoder); enc->context->time_base = (AVRational){voi->fps_den, voi->fps_num}; enc->context->pix_fmt = obs_to_ffmpeg_video_format(info.format); enc->context->colorspace = info.colorspace == VIDEO_CS_709 ? AVCOL_SPC_BT709 : AVCOL_SPC_BT470BG; enc->context->color_range = info.range == VIDEO_RANGE_FULL ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; if (keyint_sec > 0) { enc->context->gop_size = keyint_sec * voi->fps_num / voi->fps_den; } else { enc->context->gop_size = 120; } enc->height = enc->context->height; info("settings:\n" "\tdevice: %s\n" "\tqp: %d\n" "\tquality: %d\n" "\tprofile: %d\n" "\tlevel: %d\n" "\tbitrate: %d\n" "\tkeyint: %d\n" "\twidth: %d\n" "\theight: %d\n" "\tb-frames: %d\n", device, qp, quality, profile, level, bitrate, enc->context->gop_size, enc->context->width, enc->context->height, enc->context->max_b_frames); return vaapi_init_codec(enc, device); }