[FFmpeg-devel] [PATCH 2/7 v2] avformat/demux: support inserting bitstream filters in demuxing scenarios
James Almer
jamrial at gmail.com
Tue Jan 30 19:32:13 EET 2024
Packets will be passed to the bsf immediately after being generated by a
demuxer, and no further data will be read from the input until all packets
have been returned by the bsf.
Signed-off-by: James Almer <jamrial at gmail.com>
---
libavformat/avformat.c | 47 ++++++++++++
libavformat/demux.c | 162 ++++++++++++++++++++++++++++++-----------
libavformat/internal.h | 13 +++-
libavformat/mov.c | 14 +++-
libavformat/mux.c | 43 -----------
libavformat/mux.h | 11 ---
libavformat/rawenc.c | 1 +
7 files changed, 192 insertions(+), 99 deletions(-)
diff --git a/libavformat/avformat.c b/libavformat/avformat.c
index 882927f7b1..f22cb83c98 100644
--- a/libavformat/avformat.c
+++ b/libavformat/avformat.c
@@ -931,3 +931,50 @@ FF_ENABLE_DEPRECATION_WARNINGS
*pb = NULL;
return ret;
}
+
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
+{
+ int ret;
+ const AVBitStreamFilter *bsf;
+ FFStream *const sti = ffstream(st);
+ AVBSFContext *bsfc;
+
+ av_assert0(!sti->bsfc);
+
+ if (name) {
+ bsf = av_bsf_get_by_name(name);
+ if (!bsf) {
+ av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
+ return AVERROR_BSF_NOT_FOUND;
+ }
+ ret = av_bsf_alloc(bsf, &bsfc);
+ } else
+ ret = av_bsf_get_null_filter(&bsfc);
+ if (ret < 0)
+ return ret;
+
+ bsfc->time_base_in = st->time_base;
+ if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ if (args && bsfc->filter->priv_class) {
+ if ((ret = av_set_options_string(bsfc->priv_data, args, "=", ":")) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+ }
+
+ if ((ret = av_bsf_init(bsfc)) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ sti->bsfc = bsfc;
+
+ av_log(NULL, AV_LOG_VERBOSE,
+ "Automatically inserted bitstream filter '%s'; args='%s'\n",
+ name, args ? args : "");
+ return 1;
+}
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 6f640b92b1..fb9bf9e4ac 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -540,6 +540,109 @@ static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_in
return 1;
}
+static void update_timestamps(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+ FFStream *const sti = ffstream(st);
+
+ if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
+ // correct first time stamps to negative values
+ if (!is_relative(sti->first_dts))
+ sti->first_dts = wrap_timestamp(st, sti->first_dts);
+ if (!is_relative(st->start_time))
+ st->start_time = wrap_timestamp(st, st->start_time);
+ if (!is_relative(sti->cur_dts))
+ sti->cur_dts = wrap_timestamp(st, sti->cur_dts);
+ }
+
+ pkt->dts = wrap_timestamp(st, pkt->dts);
+ pkt->pts = wrap_timestamp(st, pkt->pts);
+
+ force_codec_ids(s, st);
+
+ /* TODO: audio: time filter; video: frame reordering (pts != dts) */
+ if (s->use_wallclock_as_timestamps)
+ pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
+}
+
+static int filter_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+ FFFormatContext *const si = ffformatcontext(s);
+ FFStream *const sti = ffstream(st);
+ const AVPacket *pkt1;
+ int err;
+
+ if (!sti->bsfc) {
+ const PacketListEntry *pktl = si->raw_packet_buffer.head;
+ if (AVPACKET_IS_EMPTY(pkt))
+ return 0;
+
+ update_timestamps(s, st, pkt);
+
+ if (!pktl && sti->request_probe <= 0)
+ return 0;
+
+ err = avpriv_packet_list_put(&si->raw_packet_buffer, pkt, NULL, 0);
+ if (err < 0) {
+ av_packet_unref(pkt);
+ return err;
+ }
+
+ pkt1 = &si->raw_packet_buffer.tail->pkt;
+ si->raw_packet_buffer_size += pkt1->size;
+
+ if (sti->request_probe <= 0)
+ return 0;
+
+ return probe_codec(s, s->streams[pkt1->stream_index], pkt1);
+ }
+
+ err = av_bsf_send_packet(sti->bsfc, pkt);
+ if (err < 0) {
+ av_log(s, AV_LOG_ERROR,
+ "Failed to send packet to filter %s for stream %d\n",
+ sti->bsfc->filter->name, st->index);
+ return err;
+ }
+
+ do {
+ AVStream *out_st;
+ FFStream *out_sti;
+
+ err = av_bsf_receive_packet(sti->bsfc, pkt);
+ if (err < 0) {
+ if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
+ return 0;
+ av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
+ "packet for stream #%d: %s\n", st->index, av_err2str(err));
+ if (!(s->error_recognition & AV_EF_EXPLODE) && err != AVERROR(ENOMEM))
+ continue;
+ return err;
+ }
+ out_st = s->streams[pkt->stream_index];
+ out_sti = ffstream(out_st);
+
+ update_timestamps(s, out_st, pkt);
+
+ err = avpriv_packet_list_put(&si->raw_packet_buffer, pkt, NULL, 0);
+ if (err < 0) {
+ av_packet_unref(pkt);
+ return err;
+ }
+
+ pkt1 = &si->raw_packet_buffer.tail->pkt;
+ si->raw_packet_buffer_size += pkt1->size;
+
+ if (out_sti->request_probe <= 0)
+ continue;
+
+ err = probe_codec(s, out_st, pkt1);
+ if (err < 0)
+ return err;
+ } while (1);
+
+ return 0;
+}
+
int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
FFFormatContext *const si = ffformatcontext(s);
@@ -557,9 +660,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
for (;;) {
PacketListEntry *pktl = si->raw_packet_buffer.head;
- AVStream *st;
- FFStream *sti;
- const AVPacket *pkt1;
if (pktl) {
AVStream *const st = s->streams[pktl->pkt.stream_index];
@@ -582,16 +682,27 @@ FF_ENABLE_DEPRECATION_WARNINGS
We must re-call the demuxer to get the real packet. */
if (err == FFERROR_REDO)
continue;
- if (!pktl || err == AVERROR(EAGAIN))
+ if (err == AVERROR(EAGAIN))
return err;
for (unsigned i = 0; i < s->nb_streams; i++) {
AVStream *const st = s->streams[i];
FFStream *const sti = ffstream(st);
+ int ret;
+
+ // Drain buffered packets in the bsf context on eof
+ if (err == AVERROR_EOF)
+ if ((ret = filter_packet(s, st, pkt)) < 0)
+ return ret;
+ pktl = si->raw_packet_buffer.head;
+ if (!pktl)
+ continue;
if (sti->probe_packets || sti->request_probe > 0)
- if ((err = probe_codec(s, st, NULL)) < 0)
- return err;
+ if ((ret = probe_codec(s, st, NULL)) < 0)
+ return ret;
av_assert0(sti->request_probe <= 0);
}
+ if (!pktl)
+ return err;
continue;
}
@@ -616,42 +727,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
av_assert0(pkt->stream_index < (unsigned)s->nb_streams &&
"Invalid stream index.\n");
- st = s->streams[pkt->stream_index];
- sti = ffstream(st);
-
- if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
- // correct first time stamps to negative values
- if (!is_relative(sti->first_dts))
- sti->first_dts = wrap_timestamp(st, sti->first_dts);
- if (!is_relative(st->start_time))
- st->start_time = wrap_timestamp(st, st->start_time);
- if (!is_relative(sti->cur_dts))
- sti->cur_dts = wrap_timestamp(st, sti->cur_dts);
- }
-
- pkt->dts = wrap_timestamp(st, pkt->dts);
- pkt->pts = wrap_timestamp(st, pkt->pts);
-
- force_codec_ids(s, st);
-
- /* TODO: audio: time filter; video: frame reordering (pts != dts) */
- if (s->use_wallclock_as_timestamps)
- pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
-
- if (!pktl && sti->request_probe <= 0)
- return 0;
-
- err = avpriv_packet_list_put(&si->raw_packet_buffer,
- pkt, NULL, 0);
- if (err < 0) {
- av_packet_unref(pkt);
- return err;
- }
- pkt1 = &si->raw_packet_buffer.tail->pkt;
- si->raw_packet_buffer_size += pkt1->size;
-
- if ((err = probe_codec(s, st, pkt1)) < 0)
+ err = filter_packet(s, s->streams[pkt->stream_index], pkt);
+ if (err < 0)
return err;
+ if (!AVPACKET_IS_EMPTY(pkt))
+ return 0;
}
}
diff --git a/libavformat/internal.h b/libavformat/internal.h
index f93832b3c4..c2738a420f 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -212,7 +212,7 @@ typedef struct FFStream {
/**
* bitstream filter to run on stream
* - encoding: Set by muxer using ff_stream_add_bitstream_filter
- * - decoding: unused
+ * - decoding: Set by demuxer using ff_stream_add_bitstream_filter
*/
struct AVBSFContext *bsfc;
@@ -752,4 +752,15 @@ int ff_match_url_ext(const char *url, const char *extensions);
struct FFOutputFormat;
void avpriv_register_devices(const struct FFOutputFormat * const o[], const AVInputFormat * const i[]);
+/**
+ * Add a bitstream filter to a stream.
+ *
+ * @param st output stream to add a filter to
+ * @param name the name of the filter to add
+ * @param args filter-specific argument string
+ * @return >0 on success;
+ * AVERROR code on failure
+ */
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
+
#endif /* AVFORMAT_INTERNAL_H */
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 4cffd6c7db..3e71252e46 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4913,20 +4913,28 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
static int heif_add_stream(MOVContext *c, HEIFItem *item)
{
MOVStreamContext *sc;
+ AVBufferRef *buf;
AVStream *st;
+ uint8_t *data;
st = avformat_new_stream(c->fc, NULL);
if (!st)
return AVERROR(ENOMEM);
- sc = av_mallocz(sizeof(MOVStreamContext));
- if (!sc)
+ data = av_mallocz(sizeof(MOVStreamContext));
+ if (!data)
return AVERROR(ENOMEM);
+ buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
+ if (!buf) {
+ av_free(data);
+ return AVERROR(ENOMEM);
+ }
item->st = st;
st->id = item->item_id;
- st->priv_data = sc;
+ st->priv_data = buf;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = mov_codec_id(st, item->type);
+ sc = mov_get_stream_context(st);
sc->ffindex = st->index;
c->trak_index = st->index;
st->avg_frame_rate.num = st->avg_frame_rate.den = 1;
diff --git a/libavformat/mux.c b/libavformat/mux.c
index de10d2c008..4bc8627617 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -1344,49 +1344,6 @@ int av_get_output_timestamp(struct AVFormatContext *s, int stream,
return 0;
}
-int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
-{
- int ret;
- const AVBitStreamFilter *bsf;
- FFStream *const sti = ffstream(st);
- AVBSFContext *bsfc;
-
- av_assert0(!sti->bsfc);
-
- if (!(bsf = av_bsf_get_by_name(name))) {
- av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
- return AVERROR_BSF_NOT_FOUND;
- }
-
- if ((ret = av_bsf_alloc(bsf, &bsfc)) < 0)
- return ret;
-
- bsfc->time_base_in = st->time_base;
- if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) {
- av_bsf_free(&bsfc);
- return ret;
- }
-
- if (args && bsfc->filter->priv_class) {
- if ((ret = av_set_options_string(bsfc->priv_data, args, "=", ":")) < 0) {
- av_bsf_free(&bsfc);
- return ret;
- }
- }
-
- if ((ret = av_bsf_init(bsfc)) < 0) {
- av_bsf_free(&bsfc);
- return ret;
- }
-
- sti->bsfc = bsfc;
-
- av_log(NULL, AV_LOG_VERBOSE,
- "Automatically inserted bitstream filter '%s'; args='%s'\n",
- name, args ? args : "");
- return 1;
-}
-
int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt,
AVFormatContext *src, int interleave)
{
diff --git a/libavformat/mux.h b/libavformat/mux.h
index b9ec75641d..ab3e8edd60 100644
--- a/libavformat/mux.h
+++ b/libavformat/mux.h
@@ -171,17 +171,6 @@ const AVPacket *ff_interleaved_peek(AVFormatContext *s, int stream);
int ff_get_muxer_ts_offset(AVFormatContext *s, int stream_index, int64_t *offset);
-/**
- * Add a bitstream filter to a stream.
- *
- * @param st output stream to add a filter to
- * @param name the name of the filter to add
- * @param args filter-specific argument string
- * @return >0 on success;
- * AVERROR code on failure
- */
-int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
-
/**
* Write a packet to another muxer than the one the user originally
* intended. Useful when chaining muxers, where one muxer internally
diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
index f916db13a2..ec31d76d88 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -25,6 +25,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
+#include "internal.h"
#include "rawenc.h"
#include "mux.h"
--
2.43.0
More information about the ffmpeg-devel
mailing list