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.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2022-08-27 13:15:30 +02:00
parent 0fe5c7e654
commit de703867e6
5 changed files with 19 additions and 34 deletions

View file

@ -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<size_t>(_packet.size), &tmp_packet, &sz_packet,
obs_extract_avc_headers(_packet->data, static_cast<size_t>(_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<size_t>(_packet.size), _extra_data, _sei_data);
hevc::extract_header_sei(_packet->data, static_cast<size_t>(_packet->size), _extra_data, _sei_data);
} else if (_context->extradata != nullptr) {
_extra_data.resize(static_cast<size_t>(_context->extradata_size));
std::memcpy(_extra_data.data(), _context->extradata, static_cast<size_t>(_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<size_t>(_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<size_t>(_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

View file

@ -58,7 +58,7 @@ namespace streamfx::encoder::ffmpeg {
std::shared_ptr<handler::handler> _handler;
::streamfx::ffmpeg::swscale _scaler;
AVPacket _packet;
std::shared_ptr<AVPacket> _packet;
std::shared_ptr<::streamfx::ffmpeg::hwapi::base> _hwapi;
std::shared_ptr<::streamfx::ffmpeg::hwapi::instance> _hwinst;

View file

@ -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<AVPacket> packet, const AVCodec* codec,
AVCodecContext* context){};
};
} // namespace handler
} // namespace streamfx::encoder::ffmpeg

View file

@ -112,17 +112,3 @@ void prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec,
return std::string("<Unknown>");
});
}
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);
}

View file

@ -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