diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index a7063d85..a55cd647 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -547,17 +547,14 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) } ret = avformat_write_header(ffm->output, &dict); + av_dict_free(&dict); if (ret < 0) { - fprintf(stderr, "Error opening '%s': %s", ffm->params.file, + fprintf(stderr, "Error opening '%s': %s\n", ffm->params.file, av_err2str(ret)); - av_dict_free(&dict); - return ret == -22 ? FFM_UNSUPPORTED : FFM_ERROR; } - av_dict_free(&dict); - return FFM_SUCCESS; } diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c index 9f2660a4..cdf8f8a8 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c @@ -69,10 +69,35 @@ struct vaapi_encoder { bool initialized; }; -static const char *vaapi_getname(void *unused) +/* Identify codecs, and some default values */ +struct type_data { + const int id; + const int profile; + const int level; + const char *name; + const char *rate_control; +}; + +static struct type_data h264_type = { + .id = AV_CODEC_ID_H264, + .profile = FF_PROFILE_H264_CONSTRAINED_BASELINE, + .level = 40, + .rate_control = "CBR", + .name = "FFMPEG VAAPI", +}; + +static struct type_data hevc_type = { + .id = AV_CODEC_ID_HEVC, + .profile = FF_PROFILE_HEVC_MAIN, + .level = 0, + .name = "FFMPEG VAAPI (hevc)", + .rate_control = "CQP", +}; + +static const char *vaapi_getname(void *type_data) { - UNUSED_PARAMETER(unused); - return "FFMPEG VAAPI"; + struct type_data *data = type_data; + return data->name; } static inline bool valid_format(enum video_format format) @@ -170,21 +195,21 @@ typedef struct { bool maxrate; } rc_mode_t; +/* Set "allowed" options per Rate Control */ +static const rc_mode_t RC_MODES[] = { + {.name = "CBR", .qp = false, .bitrate = true, .maxrate = false}, + {.name = "CQP", .qp = true, .bitrate = false, .maxrate = false}, + {.name = "VBR", .qp = false, .bitrate = true, .maxrate = true}, + NULL}; + static const rc_mode_t *get_rc_mode(const char *name) { - /* Set "allowed" options per Rate Control */ - static const rc_mode_t RC_MODES[] = { - {.name = "CBR", .qp = false, .bitrate = true, .maxrate = false}, - {.name = "CQP", .qp = true, .bitrate = false, .maxrate = false}, - {.name = "VBR", .qp = false, .bitrate = true, .maxrate = true}, - NULL}; - const rc_mode_t *rc_mode = RC_MODES; - while (!!rc_mode && strcmp(rc_mode->name, name) != 0) + while (!!rc_mode->name && strcmp(rc_mode->name, name) != 0) rc_mode++; - return rc_mode ? rc_mode : RC_MODES; + return !!rc_mode->name ? rc_mode : RC_MODES; } static bool vaapi_update(void *data, obs_data_t *settings) @@ -262,6 +287,7 @@ static bool vaapi_update(void *data, obs_data_t *settings) info("settings:\n" "\tdevice: %s\n" + "\tcodec: %s\n" "\trate_control: %s\n" "\tprofile: %d\n" "\tlevel: %d\n" @@ -272,9 +298,9 @@ static bool vaapi_update(void *data, obs_data_t *settings) "\twidth: %d\n" "\theight: %d\n" "\tb-frames: %d\n", - device, rate_control, profile, level, qp, bitrate, maxrate, - enc->context->gop_size, enc->context->width, enc->context->height, - enc->context->max_b_frames); + device, enc->vaapi->name, rate_control, profile, level, qp, + bitrate, maxrate, enc->context->gop_size, enc->context->width, + enc->context->height, enc->context->max_b_frames); return vaapi_init_codec(enc, device); } @@ -328,9 +354,12 @@ static void *vaapi_create(obs_data_t *settings, obs_encoder_t *encoder) if (vaapi_codec == AV_CODEC_ID_H264) { enc->vaapi = avcodec_find_encoder_by_name("h264_vaapi"); + enc->first_packet = true; + } + if (vaapi_codec == AV_CODEC_ID_HEVC) { + enc->vaapi = avcodec_find_encoder_by_name("hevc_vaapi"); + enc->first_packet = false; } - - enc->first_packet = true; blog(LOG_INFO, "---------------------------------"); @@ -466,7 +495,10 @@ static bool vaapi_encode(void *data, struct encoder_frame *frame, packet->data = enc->buffer.array; packet->size = enc->buffer.num; packet->type = OBS_ENCODER_VIDEO; - packet->keyframe = obs_avc_keyframe(packet->data, packet->size); + packet->keyframe = + enc->vaapi->id == AV_CODEC_ID_H264 + ? obs_avc_keyframe(packet->data, packet->size) + : av_pkt.flags & AV_PKT_FLAG_KEY; *received_packet = true; } else { *received_packet = false; @@ -487,19 +519,20 @@ static void set_visible(obs_properties_t *ppts, const char *name, bool visible) obs_property_set_visible(p, visible); } -static void vaapi_defaults(obs_data_t *settings) +static void vaapi_defaults(obs_data_t *settings, void *type_data) { + struct type_data *codec = type_data; + obs_data_set_default_string(settings, "vaapi_device", "/dev/dri/renderD128"); - obs_data_set_default_int(settings, "vaapi_codec", AV_CODEC_ID_H264); - obs_data_set_default_int(settings, "profile", - FF_PROFILE_H264_CONSTRAINED_BASELINE); - obs_data_set_default_int(settings, "level", 40); + obs_data_set_default_int(settings, "vaapi_codec", codec->id); + obs_data_set_default_int(settings, "profile", codec->profile); + obs_data_set_default_int(settings, "level", codec->level); obs_data_set_default_int(settings, "bitrate", 2500); obs_data_set_default_int(settings, "keyint_sec", 0); obs_data_set_default_int(settings, "bf", 0); - obs_data_set_default_int(settings, "rendermode", 0); - obs_data_set_default_string(settings, "rate_control", "CBR"); + obs_data_set_default_string(settings, "rate_control", + codec->rate_control); obs_data_set_default_int(settings, "qp", 20); obs_data_set_default_int(settings, "maxrate", 0); } @@ -522,9 +555,13 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, return true; } -static obs_properties_t *vaapi_properties(void *unused) +static obs_properties_t *vaapi_properties(void *unused, void *type_data) { UNUSED_PARAMETER(unused); + struct type_data *codec = type_data; + struct dstr name; + + dstr_init(&name); obs_properties_t *props = obs_properties_create(); obs_property_t *list; @@ -544,41 +581,44 @@ static obs_properties_t *vaapi_properties(void *unused) } } - list = obs_properties_add_list(props, "vaapi_codec", "VAAPI Codec", - OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - - obs_property_list_add_int(list, "H.264 (default)", AV_CODEC_ID_H264); - - list = obs_properties_add_list(props, "profile", "Profile", - OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, "Constrained Baseline (default)", - FF_PROFILE_H264_CONSTRAINED_BASELINE); - obs_property_list_add_int(list, "Main", FF_PROFILE_H264_MAIN); - obs_property_list_add_int(list, "High", FF_PROFILE_H264_HIGH); - - list = obs_properties_add_list(props, "level", "Level", - OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, "Auto", FF_LEVEL_UNKNOWN); - obs_property_list_add_int(list, "3.0", 30); - obs_property_list_add_int(list, "3.1", 31); - obs_property_list_add_int(list, "4.0 (default) (Compatibility mode)", - 40); - obs_property_list_add_int(list, "4.1", 41); - obs_property_list_add_int(list, "4.2", 42); - obs_property_list_add_int(list, "5.0", 50); - obs_property_list_add_int(list, "5.1", 51); - obs_property_list_add_int(list, "5.2", 52); + if (codec->id == AV_CODEC_ID_H264) { + list = obs_properties_add_list(props, "profile", "Profile", + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, "Constrained Baseline (default)", + FF_PROFILE_H264_CONSTRAINED_BASELINE); + obs_property_list_add_int(list, "Main", FF_PROFILE_H264_MAIN); + obs_property_list_add_int(list, "High", FF_PROFILE_H264_HIGH); + + list = obs_properties_add_list(props, "level", "Level", + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, "Auto", FF_LEVEL_UNKNOWN); + obs_property_list_add_int(list, "3.0", 30); + obs_property_list_add_int(list, "3.1", 31); + obs_property_list_add_int(list, "4.0 (default) (Compatibility mode)", + 40); + obs_property_list_add_int(list, "4.1", 41); + obs_property_list_add_int(list, "4.2", 42); + obs_property_list_add_int(list, "5.0", 50); + obs_property_list_add_int(list, "5.1", 51); + obs_property_list_add_int(list, "5.2", 52); + } list = obs_properties_add_list(props, "rate_control", obs_module_text("RateControl"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - obs_property_list_add_string(list, "CBR (default)", "CBR"); - obs_property_list_add_string(list, "CQP", "CQP"); - obs_property_list_add_string(list, "VBR", "VBR"); + + for (const rc_mode_t *rc_mode = RC_MODES; !!rc_mode->name; rc_mode++) { + /* Identify the default choice */ + char *def = strcmp(rc_mode->name, codec->rate_control) == 0 + ? " (default)" + : ""; + + dstr_printf(&name, "%s%s", rc_mode->name, def); + obs_property_list_add_string(list, name.array, rc_mode->name); + } obs_property_set_modified_callback(list, rate_control_modified); @@ -597,6 +637,7 @@ static obs_properties_t *vaapi_properties(void *unused) obs_module_text("KeyframeIntervalSec"), 0, 20, 1); + dstr_free(&name); return props; } @@ -626,11 +667,26 @@ struct obs_encoder_info vaapi_encoder_info = { .create = vaapi_create, .destroy = vaapi_destroy, .encode = vaapi_encode, - .get_defaults = vaapi_defaults, - .get_properties = vaapi_properties, + .get_defaults2 = vaapi_defaults, + .get_properties2 = vaapi_properties, .get_extra_data = vaapi_extra_data, .get_sei_data = vaapi_sei_data, .get_video_info = vaapi_video_info, + .type_data = &h264_type, +}; + +struct obs_encoder_info vaapi_hevc_encoder_info = { + .id = "ffmpeg_vaapi_hevc", + .type = OBS_ENCODER_VIDEO, + .codec = "hevc", + .get_name = vaapi_getname, + .create = vaapi_create, + .destroy = vaapi_destroy, + .encode = vaapi_encode, + .get_defaults2 = vaapi_defaults, + .get_properties2 = vaapi_properties, + .get_video_info = vaapi_video_info, + .type_data = &hevc_type, }; #endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c index 38a7c857..03b270e9 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg.c @@ -34,6 +34,7 @@ extern struct obs_encoder_info nvenc_encoder_info; #ifdef LIBAVUTIL_VAAPI_AVAILABLE extern struct obs_encoder_info vaapi_encoder_info; +extern struct obs_encoder_info vaapi_hevc_encoder_info; #endif #ifndef __APPLE__ @@ -208,9 +209,9 @@ finish: #endif #ifdef LIBAVUTIL_VAAPI_AVAILABLE -static bool vaapi_supported(void) +static bool vaapi_supported(const char *codec_name) { - AVCodec *vaenc = avcodec_find_encoder_by_name("h264_vaapi"); + AVCodec *vaenc = avcodec_find_encoder_by_name(codec_name); return !!vaenc; } #endif @@ -250,10 +251,14 @@ bool obs_module_load(void) obs_register_encoder(&nvenc_encoder_info); } #if !defined(_WIN32) && defined(LIBAVUTIL_VAAPI_AVAILABLE) - if (vaapi_supported()) { + if (vaapi_supported("h264_vaapi")) { blog(LOG_INFO, "FFMPEG VAAPI supported"); obs_register_encoder(&vaapi_encoder_info); } + if (vaapi_supported("hevc_vaapi")) { + blog(LOG_INFO, "FFMPEG HEVC VAAPI supported"); + obs_register_encoder(&vaapi_hevc_encoder_info); + } #endif #endif