diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index e281354..beeb809 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -137,6 +137,7 @@ Encoder.FFmpeg.KeyFrames.IntervalType="Interval Type" Encoder.FFmpeg.KeyFrames.IntervalType.Frames="Frames" Encoder.FFmpeg.KeyFrames.IntervalType.Seconds="Seconds" Encoder.FFmpeg.KeyFrames.Interval="Interval" +Encoder.FFmpeg.Framerate="Framerate Override" # Encoder/FFmpeg/AMF Encoder.FFmpeg.AMF.Deprecated="This encoder is deprecated and will be removed soon. Users are urged to migrate to the integrated 'AMD HW H.264 (AVC)' or 'AMD HW H.265 (HEVC)' encoder as soon as possible." diff --git a/source/encoders/encoder-ffmpeg.cpp b/source/encoders/encoder-ffmpeg.cpp index 3202a7b..8ea383b 100644 --- a/source/encoders/encoder-ffmpeg.cpp +++ b/source/encoders/encoder-ffmpeg.cpp @@ -1,5 +1,5 @@ // FFMPEG Video Encoder Integration for OBS Studio -// Copyright (c) 2019 Michael Fabian Dirks +// Copyright (c) 2019-2022 Michael Fabian Dirks // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -74,6 +74,8 @@ extern "C" { #define ST_KEY_FFMPEG_CUSTOMSETTINGS "FFmpeg.CustomSettings" #define ST_I18N_FFMPEG_THREADS ST_I18N_FFMPEG ".Threads" #define ST_KEY_FFMPEG_THREADS "FFmpeg.Threads" +#define ST_I18N_FFMPEG_FRAMERATE ST_I18N_FFMPEG ".Framerate" +#define ST_KEY_FFMPEG_FRAMERATE "FFmpeg.Framerate" #define ST_I18N_FFMPEG_GPU ST_I18N_FFMPEG ".GPU" #define ST_KEY_FFMPEG_GPU "FFmpeg.GPU" @@ -145,6 +147,14 @@ ffmpeg_instance::ffmpeg_instance(obs_data_t* settings, obs_encoder_t* self, bool initialize_sw(settings); } + { // Set up framerate division. + _framerate_divisor = obs_data_get_int(settings, ST_KEY_FFMPEG_FRAMERATE); + + _context->ticks_per_frame = 1; + _context->time_base.num *= _framerate_divisor; + _context->framerate.den *= _framerate_divisor; + } + // Update settings update(settings); @@ -263,8 +273,10 @@ bool ffmpeg_instance::update(obs_data_t* settings) bool is_seconds = (kf_type == 0); if (is_seconds) { - _context->gop_size = static_cast(obs_data_get_double(settings, ST_KEY_KEYFRAMES_INTERVAL_SECONDS) - * (ovi.fps_num / ovi.fps_den)); + double framerate = + static_cast(ovi.fps_num) / (static_cast(ovi.fps_den) * _framerate_divisor); + _context->gop_size = + static_cast(obs_data_get_double(settings, ST_KEY_KEYFRAMES_INTERVAL_SECONDS) * framerate); } else { _context->gop_size = static_cast(obs_data_get_int(settings, ST_KEY_KEYFRAMES_INTERVAL_FRAMES)); } @@ -377,6 +389,10 @@ bool ffmpeg_instance::encode_audio(struct encoder_frame* frame, struct encoder_p bool ffmpeg_instance::encode_video(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet) { + if ((_framerate_divisor > 1) && (frame->pts % _framerate_divisor != 0)) { + return true; + } + std::shared_ptr vframe = pop_free_frame(); // Retrieve an empty frame. // Convert frame. @@ -413,6 +429,11 @@ bool ffmpeg_instance::encode_video(struct encoder_frame* frame, struct encoder_p bool ffmpeg_instance::encode_video(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key, struct encoder_packet* packet, bool* received_packet) { + if ((_framerate_divisor > 1) && (pts % _framerate_divisor != 0)) { + *next_key = lock_key; + return true; + } + #ifdef D_PLATFORM_WINDOWS if (handle == GS_INVALID_HANDLE) { DLOG_ERROR("Received invalid handle."); @@ -1145,6 +1166,25 @@ obs_properties_t* ffmpeg_factory::get_properties2(instance_t* data) auto p = obs_properties_add_int_slider(grp, ST_KEY_FFMPEG_THREADS, D_TRANSLATE(ST_I18N_FFMPEG_THREADS), 0, static_cast(std::thread::hardware_concurrency()) * 2, 1); } + + { // Frame Skipping + obs_video_info ovi; + if (!obs_get_video_info(&ovi)) { + throw std::runtime_error("obs_get_video_info failed unexpectedly."); + } + + auto p = obs_properties_add_list(grp, ST_KEY_FFMPEG_FRAMERATE, D_TRANSLATE(ST_I18N_FFMPEG_FRAMERATE), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + // For now, an arbitrary limit of 1/10th the Framerate should be fine. + std::vector buf{size_t{256}, 0, std::allocator()}; + for (uint32_t divisor = 1; divisor <= 10; divisor++) { + double fps_num = static_cast(ovi.fps_num) / static_cast(divisor); + double fps = fps_num / static_cast(ovi.fps_den); + snprintf(buf.data(), buf.size(), "%8.2f (%" PRIu32 "/%" PRIu32 ")", fps, ovi.fps_num, + ovi.fps_den * divisor); + obs_property_list_add_int(p, buf.data(), divisor); + } + } }; return props; diff --git a/source/encoders/encoder-ffmpeg.hpp b/source/encoders/encoder-ffmpeg.hpp index bd0a5d7..96704cb 100644 --- a/source/encoders/encoder-ffmpeg.hpp +++ b/source/encoders/encoder-ffmpeg.hpp @@ -1,5 +1,5 @@ // FFMPEG Video Encoder Integration for OBS Studio -// Copyright (c) 2019 Michael Fabian Dirks +// Copyright (c) 2019-2022 Michael Fabian Dirks // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -62,6 +62,7 @@ namespace streamfx::encoder::ffmpeg { std::size_t _lag_in_frames; std::size_t _sent_frames; + std::size_t _framerate_divisor; // Extra Data bool _have_first_frame;