From 0f1833dfae9d3618d316bfff357629d0913a591b Mon Sep 17 00:00:00 2001 From: phunkyfish Date: Mon, 29 Jan 2024 13:06:40 +0000 Subject: [PATCH] FFmpeg: Handle FFmpeg extradata in dedicated class --- src/stream/DemuxStream.cpp | 68 ++++++++++++++++++- src/stream/DemuxStream.h | 33 +++++++-- src/stream/FFmpegStream.cpp | 132 +++++++++++++++++------------------- src/stream/FFmpegStream.h | 2 +- 4 files changed, 159 insertions(+), 76 deletions(-) diff --git a/src/stream/DemuxStream.cpp b/src/stream/DemuxStream.cpp index 6b0a646b..6db31264 100644 --- a/src/stream/DemuxStream.cpp +++ b/src/stream/DemuxStream.cpp @@ -48,7 +48,7 @@ bool DemuxStream::GetInformation(kodi::addon::InputstreamInfo& info) info.SetCodecName(codecName); info.SetCodecProfile(static_cast(profile)); info.SetPhysicalIndex(uniqueId); - info.SetExtraData(ExtraData, ExtraSize); + info.SetExtraData(extraData->GetData(), extraData->GetSize()); info.SetLanguage(language); info.SetCodecFourCC(codec_fourcc); @@ -178,3 +178,69 @@ DemuxParserFFmpeg::~DemuxParserFFmpeg() m_parserCtx = nullptr; } } + +FFmpegExtraData::FFmpegExtraData(size_t size) + : m_data(reinterpret_cast(av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE))), m_size(size) +{ + if (!m_data) + throw std::bad_alloc(); +} + +FFmpegExtraData::FFmpegExtraData(const uint8_t* data, size_t size) : FFmpegExtraData(size) +{ + std::memcpy(m_data, data, size); +} + +FFmpegExtraData::~FFmpegExtraData() +{ + av_free(m_data); +} + +FFmpegExtraData::FFmpegExtraData(const FFmpegExtraData& e) : FFmpegExtraData(e.m_size) +{ + std::memcpy(m_data, e.m_data, m_size); +} + +FFmpegExtraData::FFmpegExtraData(FFmpegExtraData&& other) noexcept : FFmpegExtraData() +{ + std::swap(m_data, other.m_data); + std::swap(m_size, other.m_size); +} + +FFmpegExtraData& FFmpegExtraData::operator=(const FFmpegExtraData& other) +{ + if (this != &other) + { + if (m_size >= other.m_size) // reuse current buffer if large enough + { + std::memcpy(m_data, other.m_data, other.m_size); + m_size = other.m_size; + } + else + { + FFmpegExtraData extraData(other); + *this = std::move(extraData); + } + } + return *this; +} + +FFmpegExtraData& FFmpegExtraData::operator=(FFmpegExtraData&& other) noexcept +{ + if (this != &other) + { + std::swap(m_data, other.m_data); + std::swap(m_size, other.m_size); + } + return *this; +} + +bool FFmpegExtraData::operator==(const FFmpegExtraData& other) const +{ + return m_size == other.m_size && std::memcmp(m_data, other.m_data, m_size) == 0; +} + +bool FFmpegExtraData::operator!=(const FFmpegExtraData& other) const +{ + return !(*this == other); +} diff --git a/src/stream/DemuxStream.h b/src/stream/DemuxStream.h index 2967ff11..c3a1e959 100644 --- a/src/stream/DemuxStream.h +++ b/src/stream/DemuxStream.h @@ -34,6 +34,34 @@ extern "C" namespace ffmpegdirect { + +class FFmpegExtraData +{ +public: + FFmpegExtraData() = default; + explicit FFmpegExtraData(size_t size); + FFmpegExtraData(const uint8_t* data, size_t size); + FFmpegExtraData(const FFmpegExtraData& other); + FFmpegExtraData(FFmpegExtraData&& other) noexcept; + + ~FFmpegExtraData(); + + FFmpegExtraData& operator=(const FFmpegExtraData& other); + FFmpegExtraData& operator=(FFmpegExtraData&& other) noexcept; + + bool operator==(const FFmpegExtraData& other) const; + bool operator!=(const FFmpegExtraData& other) const; + + operator bool() const { return m_data != nullptr && m_size != 0; } + uint8_t* GetData() { return m_data; } + const uint8_t* GetData() const { return m_data; } + size_t GetSize() const { return m_size; } + +private: + uint8_t* m_data{nullptr}; + size_t m_size{}; +}; + class DemuxStream { public: @@ -48,8 +76,6 @@ class DemuxStream type = INPUTSTREAM_TYPE_NONE; iDuration = 0; pPrivate = NULL; - ExtraData = NULL; - ExtraSize = 0; disabled = false; changes = 0; flags = INPUTSTREAM_FLAG_NONE; @@ -71,8 +97,7 @@ class DemuxStream int iDuration; // in mseconds void* pPrivate; // private pointer for the demuxer - uint8_t* ExtraData; // extra data for codec to use - unsigned int ExtraSize; // size of extra data + FFmpegExtraData extraData; INPUTSTREAM_FLAGS flags; std::string language; // RFC 5646 language code (empty string if undefined) diff --git a/src/stream/FFmpegStream.cpp b/src/stream/FFmpegStream.cpp index 33222e00..cdafcea5 100644 --- a/src/stream/FFmpegStream.cpp +++ b/src/stream/FFmpegStream.cpp @@ -1248,7 +1248,8 @@ bool FFmpegStream::IsProgramChange() return true; } } - if (m_pFormatContext->streams[idx]->codecpar->extradata_size != static_cast(stream->ExtraSize)) + if (m_pFormatContext->streams[idx]->codecpar->extradata_size != + static_cast(stream->extraData.GetSize())) return true; } return false; @@ -1582,20 +1583,18 @@ bool FFmpegStream::SeekTime(double time, bool backwards, double* startpts) return false; } -int FFmpegStream::GetPacketExtradata(const AVPacket* pkt, const AVCodecParserContext* parserCtx, AVCodecContext* codecCtx, uint8_t **p_extradata) +FFmpegExtraData FFmpegStream::GetPacketExtradata(const AVPacket* pkt, const AVCodecParameters* codecPar) { - int extradata_size = 0; + constexpr int FF_MAX_EXTRADATA_SIZE = ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE); - if (!pkt || !p_extradata) - return 0; - - *p_extradata = nullptr; + if (!pkt) + return {}; /* extract_extradata bitstream filter is implemented only * for certain codecs, as noted in discussion of PR#21248 */ - AVCodecID codecId = codecCtx->codec_id; + AVCodecID codecId = codecPar->codec_id; // clang-format off if ( @@ -1611,58 +1610,63 @@ int FFmpegStream::GetPacketExtradata(const AVPacket* pkt, const AVCodecParserCon codecId != AV_CODEC_ID_CAVS ) // clang-format on - return 0; + return {}; - AVBSFContext *bsf = nullptr; - AVPacket *dst_pkt = nullptr; - const AVBitStreamFilter *f; - AVPacket *pkt_ref = nullptr; - int ret = 0; - uint8_t *ret_extradata = nullptr; - size_t ret_extradata_size = 0; - - f = av_bsf_get_by_name("extract_extradata"); + const AVBitStreamFilter* f = av_bsf_get_by_name("extract_extradata"); if (!f) - return 0; + return {}; - bsf = nullptr; - ret = av_bsf_alloc(f, &bsf); + AVBSFContext* bsf = nullptr; + int ret = av_bsf_alloc(f, &bsf); if (ret < 0) - return 0; + return {}; - bsf->par_in->codec_id = codecCtx->codec_id; + ret = avcodec_parameters_copy(bsf->par_in, codecPar); + if (ret < 0) + { + av_bsf_free(&bsf); + return {}; + } ret = av_bsf_init(bsf); if (ret < 0) { av_bsf_free(&bsf); - return 0; + return {}; } - dst_pkt = av_packet_alloc(); - pkt_ref = dst_pkt; + AVPacket* dstPkt = av_packet_alloc(); + if (!dstPkt) + { + CLog::LogF(LOGERROR, "failed to allocate packet"); + + av_bsf_free(&bsf); + return {}; + } + AVPacket* pktRef = dstPkt; - ret = av_packet_ref(pkt_ref, pkt); + ret = av_packet_ref(pktRef, pkt); if (ret < 0) { av_bsf_free(&bsf); - av_packet_free(&dst_pkt); - return 0; + av_packet_free(&dstPkt); + return {}; } - ret = av_bsf_send_packet(bsf, pkt_ref); + ret = av_bsf_send_packet(bsf, pktRef); if (ret < 0) { - av_packet_unref(pkt_ref); + av_packet_unref(pktRef); av_bsf_free(&bsf); - av_packet_free(&dst_pkt); - return 0; + av_packet_free(&dstPkt); + return {}; } + FFmpegExtraData extraData; ret = 0; while (ret >= 0) { - ret = av_bsf_receive_packet(bsf, pkt_ref); + ret = av_bsf_receive_packet(bsf, pktRef); if (ret < 0) { if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) @@ -1671,47 +1675,38 @@ int FFmpegStream::GetPacketExtradata(const AVPacket* pkt, const AVCodecParserCon continue; } - ret_extradata = av_packet_get_side_data(pkt_ref, - AV_PKT_DATA_NEW_EXTRADATA, - &ret_extradata_size); - if (ret_extradata && - ret_extradata_size > 0 && - ret_extradata_size < FF_MAX_EXTRADATA_SIZE) + size_t retExtraDataSize = 0; + uint8_t* retExtraData = + av_packet_get_side_data(pktRef, AV_PKT_DATA_NEW_EXTRADATA, &retExtraDataSize); + if (retExtraData && retExtraDataSize > 0 && retExtraDataSize < FF_MAX_EXTRADATA_SIZE) { - *p_extradata = (uint8_t*)av_malloc(ret_extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!*p_extradata) + try { - Log(LOGLEVEL_ERROR, - "%s - failed to allocate %zu bytes for extradata", - __FUNCTION__, - ret_extradata_size); + extraData = FFmpegExtraData(retExtraData, retExtraDataSize); + } + catch (const std::bad_alloc&) + { + CLog::LogF(LOGERROR, "failed to allocate {} bytes for extradata", retExtraDataSize); - av_packet_unref(pkt_ref); + av_packet_unref(pktRef); av_bsf_free(&bsf); - av_packet_free(&dst_pkt); - return 0; + av_packet_free(&dstPkt); + return {}; } - Log(LOGLEVEL_DEBUG, - "%s - fetching extradata, extradata_size(%zu)", - __FUNCTION__, - ret_extradata_size); - - memcpy(*p_extradata, ret_extradata, ret_extradata_size); - memset(*p_extradata + ret_extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - extradata_size = ret_extradata_size; + CLog::LogF(LOGDEBUG, "fetching extradata, extradata_size({})", retExtraDataSize); - av_packet_unref(pkt_ref); + av_packet_unref(pktRef); break; } - av_packet_unref(pkt_ref); + av_packet_unref(pktRef); } av_bsf_free(&bsf); - av_packet_free(&dst_pkt); + av_packet_free(&dstPkt); - return extradata_size; + return extraData; } void FFmpegStream::ParsePacket(AVPacket* pkt) @@ -1747,13 +1742,11 @@ void FFmpegStream::ParsePacket(AVPacket* pkt) parser->second->m_parserCtx->parser && !st->codecpar->extradata) { - int i = GetPacketExtradata(pkt, - parser->second->m_parserCtx, - parser->second->m_codecCtx, - &st->codecpar->extradata); - if (i > 0) + FFmpegExtraData retExtraData = GetPacketExtradata(pkt, st->codecpar); + if (retExtraData) { - st->codecpar->extradata_size = i; + st->codecpar->extradata_size = retExtraData.GetSize(); + st->codecpar->extradata = retExtraData.GetData(); if (parser->second->m_parserCtx->parser->parser_parse) { @@ -2250,9 +2243,8 @@ DemuxStream* FFmpegStream::AddStream(int streamIdx) if (stream->type != INPUTSTREAM_TYPE_NONE && pStream->codecpar->extradata && pStream->codecpar->extradata_size > 0) { - stream->ExtraSize = pStream->codecpar->extradata_size; - stream->ExtraData = new uint8_t[pStream->codecpar->extradata_size]; - memcpy(stream->ExtraData, pStream->codecpar->extradata, pStream->codecpar->extradata_size); + stream->extraData = + FFmpegExtraData(pStream->codecpar->extradata, pStream->codecpar->extradata_size); } stream->uniqueId = pStream->index; diff --git a/src/stream/FFmpegStream.h b/src/stream/FFmpegStream.h index 45a3c93c..fa86e03f 100644 --- a/src/stream/FFmpegStream.h +++ b/src/stream/FFmpegStream.h @@ -108,7 +108,7 @@ class FFmpegStream bool IsPaused() { return m_speed == STREAM_PLAYSPEED_PAUSE; } virtual bool CheckReturnEmptyOnPacketResult(int result); - int GetPacketExtradata(const AVPacket* pkt, const AVCodecParserContext* parserCtx, AVCodecContext* codecCtx, uint8_t **p_extradata); + FFmpegExtraData GetPacketExtradata(const AVPacket* pkt, const AVCodecParameters* codecPar) int64_t m_demuxerId; mutable std::recursive_mutex m_mutex;