[FFmpeg-devel] [PATCH 11/13] avformat/demux: support inserting bitstream filters in demuxing scenarios
Gyan Doshi
ffmpeg at gyani.pro
Tue Nov 28 06:00:04 EET 2023
On 2023-11-28 12:13 am, James Almer wrote:
> 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.
Do you plan to add a lib/cli option for user-specified insertions?
Will be useful for something like dts2pts in some cases.
Regards,
Gyan
>
> Signed-off-by: James Almer <jamrial at gmail.com>
> ---
> libavformat/avformat.c | 47 ++++++++++++
> libavformat/demux.c | 162 ++++++++++++++++++++++++++++++-----------
> libavformat/internal.h | 13 +++-
> libavformat/mux.c | 43 -----------
> libavformat/mux.h | 11 ---
> libavformat/rawenc.c | 1 +
> 6 files changed, 181 insertions(+), 96 deletions(-)
>
> diff --git a/libavformat/avformat.c b/libavformat/avformat.c
> index a02ec965dd..a41c0b391c 100644
> --- a/libavformat/avformat.c
> +++ b/libavformat/avformat.c
> @@ -1033,3 +1033,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 c6181683ef..0a5d512697 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;
>
> @@ -757,4 +757,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/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"
>
More information about the ffmpeg-devel
mailing list