From de703867e638ef556e734c2bc85b796caaf1cd53 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sat, 27 Aug 2022 13:15:30 +0200 Subject: [PATCH] encoder/ffmpeg: Drop support for broken FFmpeg versions This removes the Matroska fix for ProRes content, and upgrades from av_init_packet to av_packet_alloc. --- source/encoders/encoder-ffmpeg.cpp | 32 +++++++++---------- source/encoders/encoder-ffmpeg.hpp | 2 +- source/encoders/handlers/handler.hpp | 3 +- .../encoders/handlers/prores_aw_handler.cpp | 14 -------- .../encoders/handlers/prores_aw_handler.hpp | 2 -- 5 files changed, 19 insertions(+), 34 deletions(-) diff --git a/source/encoders/encoder-ffmpeg.cpp b/source/encoders/encoder-ffmpeg.cpp index 503cc3ff..78ab4705 100644 --- a/source/encoders/encoder-ffmpeg.cpp +++ b/source/encoders/encoder-ffmpeg.cpp @@ -130,8 +130,8 @@ ffmpeg_instance::ffmpeg_instance(obs_data_t* settings, obs_encoder_t* self, bool } // Allocate a small packet for later use. - av_init_packet(&_packet); - av_new_packet(&_packet, 8 * 1024 * 1024); // 8 MB precached Packet size. + _packet = {av_packet_alloc(), [](AVPacket* ptr) { av_packet_free(&ptr); }}; + av_new_packet(_packet.get(), 8 * 1024 * 1024); // 8 MiB is usually enough for compressed data. // Initialize if (is_hw) { @@ -158,7 +158,7 @@ ffmpeg_instance::~ffmpeg_instance() // Flush encoders that require it. if ((_codec->capabilities & AV_CODEC_CAP_DELAY) != 0) { avcodec_send_frame(_context, nullptr); - while (avcodec_receive_packet(_context, &_packet) >= 0) { + while (avcodec_receive_packet(_context, _packet.get()) >= 0) { avcodec_send_frame(_context, nullptr); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -169,7 +169,7 @@ ffmpeg_instance::~ffmpeg_instance() avcodec_free_context(&_context); } - av_packet_unref(&_packet); + av_packet_unref(_packet.get()); _scaler.finalize(); } @@ -608,11 +608,11 @@ int ffmpeg_instance::receive_packet(bool* received_packet, struct encoder_packet { int res = 0; - av_packet_unref(&_packet); + av_packet_unref(_packet.get()); { auto gctx = streamfx::obs::gs::context(); - res = avcodec_receive_packet(_context, &_packet); + res = avcodec_receive_packet(_context, _packet.get()); } if (res != 0) { return res; @@ -625,7 +625,7 @@ int ffmpeg_instance::receive_packet(bool* received_packet, struct encoder_packet uint8_t* tmp_sei; std::size_t sz_packet, sz_header, sz_sei; - obs_extract_avc_headers(_packet.data, static_cast(_packet.size), &tmp_packet, &sz_packet, + obs_extract_avc_headers(_packet->data, static_cast(_packet->size), &tmp_packet, &sz_packet, &tmp_header, &sz_header, &tmp_sei, &sz_sei); if (sz_header) { @@ -646,7 +646,7 @@ int ffmpeg_instance::receive_packet(bool* received_packet, struct encoder_packet bfree(tmp_header); bfree(tmp_sei); } else if (_codec->id == AV_CODEC_ID_HEVC) { - hevc::extract_header_sei(_packet.data, static_cast(_packet.size), _extra_data, _sei_data); + hevc::extract_header_sei(_packet->data, static_cast(_packet->size), _extra_data, _sei_data); } else if (_context->extradata != nullptr) { _extra_data.resize(static_cast(_context->extradata_size)); std::memcpy(_extra_data.data(), _context->extradata, static_cast(_context->extradata_size)); @@ -660,25 +660,25 @@ int ffmpeg_instance::receive_packet(bool* received_packet, struct encoder_packet // Build packet for use in OBS. packet->type = OBS_ENCODER_VIDEO; - packet->pts = _packet.pts; - packet->dts = _packet.dts; - packet->data = _packet.data; - packet->size = static_cast(_packet.size); - packet->keyframe = !!(_packet.flags & AV_PKT_FLAG_KEY); + packet->pts = _packet->pts; + packet->dts = _packet->dts; + packet->data = _packet->data; + packet->size = static_cast(_packet->size); + packet->keyframe = !!(_packet->flags & AV_PKT_FLAG_KEY); *received_packet = true; // Figure out priority and drop_priority. // In theory, this is done by OBS, but its not doing a great job. packet->priority = packet->keyframe ? 3 : 2; packet->drop_priority = 3; - for (size_t idx = 0, edx = _packet.side_data_elems; idx < edx; idx++) { - auto& side_data = _packet.side_data[idx]; + for (size_t idx = 0, edx = _packet->side_data_elems; idx < edx; idx++) { + auto& side_data = _packet->side_data[idx]; if (side_data.type == AV_PKT_DATA_QUALITY_STATS) { // Decisions based on picture type, if present. switch (side_data.data[sizeof(uint32_t)]) { case AV_PICTURE_TYPE_I: // I-Frame case AV_PICTURE_TYPE_SI: // Switching I-Frame - if (_packet.flags & AV_PKT_FLAG_KEY) { + if (_packet->flags & AV_PKT_FLAG_KEY) { // Recovery only via IDR-Frame. packet->priority = 3; // OBS_NAL_PRIORITY_HIGHEST packet->drop_priority = 2; // OBS_NAL_PRIORITY_HIGH diff --git a/source/encoders/encoder-ffmpeg.hpp b/source/encoders/encoder-ffmpeg.hpp index 33182df1..81440201 100644 --- a/source/encoders/encoder-ffmpeg.hpp +++ b/source/encoders/encoder-ffmpeg.hpp @@ -58,7 +58,7 @@ namespace streamfx::encoder::ffmpeg { std::shared_ptr _handler; ::streamfx::ffmpeg::swscale _scaler; - AVPacket _packet; + std::shared_ptr _packet; std::shared_ptr<::streamfx::ffmpeg::hwapi::base> _hwapi; std::shared_ptr<::streamfx::ffmpeg::hwapi::instance> _hwinst; diff --git a/source/encoders/handlers/handler.hpp b/source/encoders/handlers/handler.hpp index b3bc772f..383ed95c 100644 --- a/source/encoders/handlers/handler.hpp +++ b/source/encoders/handlers/handler.hpp @@ -81,7 +81,8 @@ namespace streamfx::encoder::ffmpeg { virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context){}; - virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context){}; + virtual void process_avpacket(std::shared_ptr packet, const AVCodec* codec, + AVCodecContext* context){}; }; } // namespace handler } // namespace streamfx::encoder::ffmpeg diff --git a/source/encoders/handlers/prores_aw_handler.cpp b/source/encoders/handlers/prores_aw_handler.cpp index e63681db..5f117ec5 100644 --- a/source/encoders/handlers/prores_aw_handler.cpp +++ b/source/encoders/handlers/prores_aw_handler.cpp @@ -112,17 +112,3 @@ void prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, return std::string(""); }); } - -void prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*) -{ - //FFmpeg Bug: - // When ProRes content is stored in Matroska, FFmpeg strips the size - // from the atom. Later when the ProRes content is demuxed from Matroska, - // FFmpeg creates an atom with the incorrect size, as the ATOM size - // should be content + atom, but FFmpeg set it to only be content. This - // difference leads to decoders to be off by 8 bytes. - //Fix (until FFmpeg stops being broken): - // Pad the packet with 8 bytes of 0x00. - - av_grow_packet(&packet, 8); -} diff --git a/source/encoders/handlers/prores_aw_handler.hpp b/source/encoders/handlers/prores_aw_handler.hpp index a58630ba..35f55778 100644 --- a/source/encoders/handlers/prores_aw_handler.hpp +++ b/source/encoders/handlers/prores_aw_handler.hpp @@ -56,7 +56,5 @@ namespace streamfx::encoder::ffmpeg::handler { public /*instance*/: void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) override; - - void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context) override; }; } // namespace streamfx::encoder::ffmpeg::handler