Skip to content

Commit

Permalink
FFmpeg: Handle FFmpeg extradata in dedicated class
Browse files Browse the repository at this point in the history
  • Loading branch information
phunkyfish committed Jan 29, 2024
1 parent 59557dd commit 0f1833d
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 76 deletions.
68 changes: 67 additions & 1 deletion src/stream/DemuxStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ bool DemuxStream::GetInformation(kodi::addon::InputstreamInfo& info)
info.SetCodecName(codecName);
info.SetCodecProfile(static_cast<STREAMCODEC_PROFILE>(profile));
info.SetPhysicalIndex(uniqueId);
info.SetExtraData(ExtraData, ExtraSize);
info.SetExtraData(extraData->GetData(), extraData->GetSize());
info.SetLanguage(language);
info.SetCodecFourCC(codec_fourcc);

Expand Down Expand Up @@ -178,3 +178,69 @@ DemuxParserFFmpeg::~DemuxParserFFmpeg()
m_parserCtx = nullptr;
}
}

FFmpegExtraData::FFmpegExtraData(size_t size)
: m_data(reinterpret_cast<uint8_t*>(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);
}
33 changes: 29 additions & 4 deletions src/stream/DemuxStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
Expand All @@ -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)
Expand Down
132 changes: 62 additions & 70 deletions src/stream/FFmpegStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,8 @@ bool FFmpegStream::IsProgramChange()
return true;
}
}
if (m_pFormatContext->streams[idx]->codecpar->extradata_size != static_cast<int>(stream->ExtraSize))
if (m_pFormatContext->streams[idx]->codecpar->extradata_size !=
static_cast<int>(stream->extraData.GetSize()))
return true;
}
return false;
Expand Down Expand Up @@ -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 (
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/stream/FFmpegStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 0f1833d

Please sign in to comment.