291 lines
9.6 KiB
Diff
291 lines
9.6 KiB
Diff
From 07ea26d7ce6bde1350ceb15380f4908da454f004 Mon Sep 17 00:00:00 2001
|
|
From: Vasyl Gello <vasek.gello@gmail.com>
|
|
Date: Sat, 15 Oct 2022 16:17:48 +0000
|
|
Subject: [PATCH] ffmpeg5: Get extradata with extract_extradata BSF
|
|
|
|
Fixes the transport stream playback failures described in
|
|
https://bugs.debian.org/1016925
|
|
|
|
@Rogo95 made an excellent technical analysis of the root cause
|
|
and reported that to the bug thread.
|
|
|
|
Later on, James Almer (@jamrial) suggested the solution to use
|
|
extract_extradata bitstream filter to replace the removed split()
|
|
function.
|
|
|
|
Finally, I adapted the following code snippet:
|
|
https://gist.github.com/moonpfe/f6795d51294d91ee0f82f62ff6985db0
|
|
to Kodi and tested it by playing the affected files in TS format.
|
|
|
|
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
|
|
---
|
|
src/stream/FFmpegStream.cpp | 222 ++++++++++++++++++++++++++++++------
|
|
src/stream/FFmpegStream.h | 2 +
|
|
2 files changed, 192 insertions(+), 32 deletions(-)
|
|
|
|
diff --git a/src/stream/FFmpegStream.cpp b/src/stream/FFmpegStream.cpp
|
|
index f2140a17..95d1da83 100644
|
|
--- a/src/stream/FFmpegStream.cpp
|
|
+++ b/src/stream/FFmpegStream.cpp
|
|
@@ -29,6 +29,7 @@
|
|
#endif
|
|
|
|
extern "C" {
|
|
+#include <libavcodec/bsf.h>
|
|
#include <libavutil/dict.h>
|
|
#include <libavutil/opt.h>
|
|
}
|
|
@@ -1586,6 +1587,168 @@ 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)
|
|
+{
|
|
+ int extradata_size = 0;
|
|
+
|
|
+ if (!pkt || !p_extradata)
|
|
+ return 0;
|
|
+
|
|
+ *p_extradata = nullptr;
|
|
+
|
|
+#if LIBAVFORMAT_BUILD >= AV_VERSION_INT(59, 0, 100)
|
|
+ /* extract_extradata bitstream filter is implemented only
|
|
+ * for certain codecs, as noted in discussion of PR#21248
|
|
+ */
|
|
+
|
|
+ AVCodecID codecId = codecCtx->codec_id;
|
|
+
|
|
+ // clang-format off
|
|
+ if (
|
|
+ codecId != AV_CODEC_ID_MPEG1VIDEO &&
|
|
+ codecId != AV_CODEC_ID_MPEG2VIDEO &&
|
|
+ codecId != AV_CODEC_ID_H264 &&
|
|
+ codecId != AV_CODEC_ID_HEVC &&
|
|
+ codecId != AV_CODEC_ID_MPEG4 &&
|
|
+ codecId != AV_CODEC_ID_VC1 &&
|
|
+ codecId != AV_CODEC_ID_AV1 &&
|
|
+ codecId != AV_CODEC_ID_AVS2 &&
|
|
+ codecId != AV_CODEC_ID_AVS3 &&
|
|
+ codecId != AV_CODEC_ID_CAVS
|
|
+ )
|
|
+ // clang-format on
|
|
+ return 0;
|
|
+
|
|
+ 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");
|
|
+ if (!f)
|
|
+ return 0;
|
|
+
|
|
+ bsf = nullptr;
|
|
+ ret = av_bsf_alloc(f, &bsf);
|
|
+ if (ret < 0)
|
|
+ return 0;
|
|
+
|
|
+ bsf->par_in->codec_id = codecCtx->codec_id;
|
|
+
|
|
+ ret = av_bsf_init(bsf);
|
|
+ if (ret < 0)
|
|
+ {
|
|
+ av_bsf_free(&bsf);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dst_pkt = av_packet_alloc();
|
|
+ pkt_ref = dst_pkt;
|
|
+
|
|
+ ret = av_packet_ref(pkt_ref, pkt);
|
|
+ if (ret < 0)
|
|
+ {
|
|
+ av_bsf_free(&bsf);
|
|
+ av_packet_free(&dst_pkt);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = av_bsf_send_packet(bsf, pkt_ref);
|
|
+ if (ret < 0)
|
|
+ {
|
|
+ av_packet_unref(pkt_ref);
|
|
+ av_bsf_free(&bsf);
|
|
+ av_packet_free(&dst_pkt);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+ while (ret >= 0)
|
|
+ {
|
|
+ ret = av_bsf_receive_packet(bsf, pkt_ref);
|
|
+ if (ret < 0)
|
|
+ {
|
|
+ if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
|
+ break;
|
|
+
|
|
+ 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)
|
|
+ {
|
|
+ *p_extradata = (uint8_t*)av_malloc(ret_extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
|
|
+ if (!*p_extradata)
|
|
+ {
|
|
+ Log(LOGLEVEL_ERROR,
|
|
+ "%s - failed to allocate %zu bytes for extradata",
|
|
+ __FUNCTION__,
|
|
+ ret_extradata_size);
|
|
+
|
|
+ av_packet_unref(pkt_ref);
|
|
+ av_bsf_free(&bsf);
|
|
+ av_packet_free(&dst_pkt);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+
|
|
+ av_packet_unref(pkt_ref);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ av_packet_unref(pkt_ref);
|
|
+ }
|
|
+
|
|
+ av_bsf_free(&bsf);
|
|
+ av_packet_free(&dst_pkt);
|
|
+#else
|
|
+ if (codecCtx && parserCtx && parserCtx->parser && parserCtx->parser->split)
|
|
+ extradata_size = parserCtx->parser->split(codecCtx, pkt->data, pkt->size);
|
|
+
|
|
+ if (extradata_size <= 0 || extradata_size >= FF_MAX_EXTRADATA_SIZE)
|
|
+ {
|
|
+ Log(LOGLEVEL_DEBUG, "%s - fetched extradata of weird size %zd",
|
|
+ __FUNCTION__, extradata_size);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ *p_extradata = (uint8_t*)av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
|
|
+ if (!*p_extradata)
|
|
+ {
|
|
+ Log(LOGLEVEL_ERROR,
|
|
+ "%s - failed to allocate %zd bytes for extradata",
|
|
+ __FUNCTION__,
|
|
+ extradata_size);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ Log(LOGLEVEL_DEBUG,
|
|
+ "%s - fetching extradata, extradata_size(%zd)",
|
|
+ __FUNCTION__,
|
|
+ extradata_size);
|
|
+
|
|
+ memcpy(*p_extradata, pkt->data, extradata_size);
|
|
+ memset(*p_extradata + extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
|
|
+#endif
|
|
+
|
|
+ return extradata_size;
|
|
+}
|
|
+
|
|
void FFmpegStream::ParsePacket(AVPacket* pkt)
|
|
{
|
|
AVStream* st = m_pFormatContext->streams[pkt->stream_index];
|
|
@@ -1617,43 +1780,38 @@ void FFmpegStream::ParsePacket(AVPacket* pkt)
|
|
|
|
if (parser->second->m_parserCtx &&
|
|
parser->second->m_parserCtx->parser &&
|
|
- parser->second->m_parserCtx->parser->split &&
|
|
!st->codecpar->extradata)
|
|
{
|
|
- int i = parser->second->m_parserCtx->parser->split(parser->second->m_codecCtx, pkt->data, pkt->size);
|
|
- if (i > 0 && i < FF_MAX_EXTRADATA_SIZE)
|
|
+ int i = GetPacketExtradata(pkt,
|
|
+ parser->second->m_parserCtx,
|
|
+ parser->second->m_codecCtx,
|
|
+ &st->codecpar->extradata);
|
|
+ if (i > 0)
|
|
{
|
|
- st->codecpar->extradata = (uint8_t*)av_malloc(i + AV_INPUT_BUFFER_PADDING_SIZE);
|
|
- if (st->codecpar->extradata)
|
|
- {
|
|
- Log(LOGLEVEL_DEBUG, "CDVDDemuxFFmpeg::ParsePacket() fetching extradata, extradata_size(%d)", i);
|
|
- st->codecpar->extradata_size = i;
|
|
- memcpy(st->codecpar->extradata, pkt->data, i);
|
|
- memset(st->codecpar->extradata + i, 0, AV_INPUT_BUFFER_PADDING_SIZE);
|
|
+ st->codecpar->extradata_size = i;
|
|
|
|
- if (parser->second->m_parserCtx->parser->parser_parse)
|
|
+ if (parser->second->m_parserCtx->parser->parser_parse)
|
|
+ {
|
|
+ parser->second->m_codecCtx->extradata = st->codecpar->extradata;
|
|
+ parser->second->m_codecCtx->extradata_size = st->codecpar->extradata_size;
|
|
+ const uint8_t* outbufptr;
|
|
+ int bufSize;
|
|
+ parser->second->m_parserCtx->flags |= PARSER_FLAG_COMPLETE_FRAMES;
|
|
+ parser->second->m_parserCtx->parser->parser_parse(parser->second->m_parserCtx,
|
|
+ parser->second->m_codecCtx,
|
|
+ &outbufptr, &bufSize,
|
|
+ pkt->data, pkt->size);
|
|
+ parser->second->m_codecCtx->extradata = nullptr;
|
|
+ parser->second->m_codecCtx->extradata_size = 0;
|
|
+
|
|
+ if (parser->second->m_parserCtx->width != 0)
|
|
{
|
|
- parser->second->m_codecCtx->extradata = st->codecpar->extradata;
|
|
- parser->second->m_codecCtx->extradata_size = st->codecpar->extradata_size;
|
|
- const uint8_t* outbufptr;
|
|
- int bufSize;
|
|
- parser->second->m_parserCtx->flags |= PARSER_FLAG_COMPLETE_FRAMES;
|
|
- parser->second->m_parserCtx->parser->parser_parse(parser->second->m_parserCtx,
|
|
- parser->second->m_codecCtx,
|
|
- &outbufptr, &bufSize,
|
|
- pkt->data, pkt->size);
|
|
- parser->second->m_codecCtx->extradata = nullptr;
|
|
- parser->second->m_codecCtx->extradata_size = 0;
|
|
-
|
|
- if (parser->second->m_parserCtx->width != 0)
|
|
- {
|
|
- st->codecpar->width = parser->second->m_parserCtx->width;
|
|
- st->codecpar->height = parser->second->m_parserCtx->height;
|
|
- }
|
|
- else
|
|
- {
|
|
- Log(LOGLEVEL_ERROR, "CDVDDemuxFFmpeg::ParsePacket() invalid width/height");
|
|
- }
|
|
+ st->codecpar->width = parser->second->m_parserCtx->width;
|
|
+ st->codecpar->height = parser->second->m_parserCtx->height;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ Log(LOGLEVEL_ERROR, "CDVDDemuxFFmpeg::ParsePacket() invalid width/height");
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/stream/FFmpegStream.h b/src/stream/FFmpegStream.h
|
|
index 356905dd..f0634d0f 100644
|
|
--- a/src/stream/FFmpegStream.h
|
|
+++ b/src/stream/FFmpegStream.h
|
|
@@ -109,6 +109,8 @@ 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);
|
|
+
|
|
int64_t m_demuxerId;
|
|
mutable std::recursive_mutex m_mutex;
|
|
double m_currentPts; // used for stream length estimation
|