Re: [FFmpeg-devel] [PATCH 5/8] avformat/mux: add proper support for full N:M bitstream filtering

2020-03-31 Thread Marton Balint



On Sat, 28 Mar 2020, Andreas Rheinhardt wrote:


Marton Balint:

Previously only 1:1 bitstream filters were supported, the end of the stream was
not signalled to the bitstream filters and time base changes were ignored.

Signed-off-by: Marton Balint 
---
 libavformat/internal.h |   1 +
 libavformat/mux.c  | 128 ++---
 2 files changed, 91 insertions(+), 38 deletions(-)

diff --git a/libavformat/internal.h b/libavformat/internal.h
index 332477a532..45aeef717a 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -158,6 +158,7 @@ struct AVStreamInternal {
  */
 AVBSFContext **bsfcs;
 int nb_bsfcs;
+int bsfcs_idx;

 /**
  * Whether or not check_bitstream should still be run on each packet
diff --git a/libavformat/mux.c b/libavformat/mux.c
index 8c2d6a8060..3054ab8644 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -840,14 +840,48 @@ static int prepare_input_packet(AVFormatContext *s, 
AVPacket *pkt)
 return 0;
 }

-static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) {
-AVStream *st = s->streams[pkt->stream_index];
-int i, ret;
+static int auto_bsf_receive_packet(AVFormatContext *s, AVStream *st, AVPacket 
*pkt)
+{
+AVStreamInternal *sti = st->internal;
+int ret = AVERROR(EAGAIN);
+int eof = 0;
+
+while (sti->bsfcs_idx) {
+/* get a packet from the previous filter up the chain */
+ret = av_bsf_receive_packet(sti->bsfcs[sti->bsfcs_idx - 1], pkt);
+if (ret == AVERROR(EAGAIN)) {
+sti->bsfcs_idx--;
+continue;
+} else if (ret == AVERROR_EOF) {
+eof = 1;
+} else if (ret < 0)
+break;
+
+/* send it to the next filter down the chain */
+if (sti->bsfcs_idx < sti->nb_bsfcs) {
+ret = av_bsf_send_packet(sti->bsfcs[sti->bsfcs_idx], eof ? NULL : 
pkt);
+av_assert2(ret != AVERROR(EAGAIN));
+if (ret < 0)
+break;
+sti->bsfcs_idx++;
+eof = 0;
+} else if (eof) {
+break;
+} else {
+return 0;
+}
+}


Would it actually be possible to simplify this by using the
av_bsf_list-API (right now code like this exists here and in ffmpeg.c
and probably at even more places)? The problem I see with this is that
one cannot send a packet for filtering to an unfinished bsf list whereas
the current code has this capability (although AFAIK no one uses it; if
I am not mistaken, all bsf chains that are automatically inserted
consist of exactly one bsf).


I am not aware of practical uses of more than one bsf, but it *was* 
supported for a long time.


Yes, the main issue with the list_bsf-API is that it does not currently 
supports adding filters to already initialized chains. I wrapped up some 
new API for it which can be used to do exactly that:


+/**
+ * Join a new bitstream filter to an already operating one.
+ *
+ * @param[in,out] out_bsf *out_bsf contains the existing, already initialized 
bitstream
+ *filter, the new filter will be joined to the output 
of this.
+ *It can be NULL, in which case an empty filter is 
assumed.
+ *Upon successful return *out_bsf will contain the 
new, combined
+ *bitstream filter which will be initialized.
+ * @param[in] bsf the bitstream filter to be joined to the output of 
*out_bsf.
+ *This filter must be uninitialized but it must be 
ready to be
+ *initalized, so input codec parameters and time base 
must be
+ *set if *out_bsf is NULL, otherwise the function will 
set these
+ *automatically based on the output parameters of 
*out_bsf.
+ *Upon successful return the bsf will be initialized.
+ *
+ * @return 0 on success, negative on error.
+ */
+int av_bsf_join(AVBSFContext **out_bsf, AVBSFContext *bsf);
+

So this API basically creates a list BSF transparenly if somebody wants to 
concatenate two BSF-s. It is a bit fraglie because it is using list bfs 
internals directly, instead of calling the init() method of the list bsf, 
but it works.


If people prefer this option, I can continue on this path and simply 
provide patches on top of the existing series, it is quite 
straightforward to do the conversion.




(This actually brings up a question: If check_bitstream returns that
more packets from this stream need to be checked, the current packet is
currently sent to the bsf-list as-is; it would not be sent to any bsf
that get added to the list later. Makes me wonder whether this is
actually a problem.)


I always thought that is how it is supposed to work by design. But since 
there are no use cases for that at the moment, we don't know if that is 
satisfactory...


Regards,
Marton
___
ff

Re: [FFmpeg-devel] [PATCH 5/8] avformat/mux: add proper support for full N:M bitstream filtering

2020-03-31 Thread Andreas Rheinhardt
Marton Balint:
> Previously only 1:1 bitstream filters were supported, the end of the stream 
> was
> not signalled to the bitstream filters and time base changes were ignored.
> 
> Signed-off-by: Marton Balint 
> ---
>  libavformat/internal.h |   1 +
>  libavformat/mux.c  | 128 
> ++---
>  2 files changed, 91 insertions(+), 38 deletions(-)
> 
> diff --git a/libavformat/internal.h b/libavformat/internal.h
> index 332477a532..45aeef717a 100644
> --- a/libavformat/internal.h
> +++ b/libavformat/internal.h
> @@ -158,6 +158,7 @@ struct AVStreamInternal {
>   */
>  AVBSFContext **bsfcs;
>  int nb_bsfcs;
> +int bsfcs_idx;
>  
>  /**
>   * Whether or not check_bitstream should still be run on each packet
> diff --git a/libavformat/mux.c b/libavformat/mux.c
> index 8c2d6a8060..3054ab8644 100644
> --- a/libavformat/mux.c
> +++ b/libavformat/mux.c
> @@ -840,14 +840,48 @@ static int prepare_input_packet(AVFormatContext *s, 
> AVPacket *pkt)
>  return 0;
>  }
>  
> -static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) {
> -AVStream *st = s->streams[pkt->stream_index];
> -int i, ret;
> +static int auto_bsf_receive_packet(AVFormatContext *s, AVStream *st, 
> AVPacket *pkt)
> +{
> +AVStreamInternal *sti = st->internal;
> +int ret = AVERROR(EAGAIN);
> +int eof = 0;
> +
> +while (sti->bsfcs_idx) {
> +/* get a packet from the previous filter up the chain */
> +ret = av_bsf_receive_packet(sti->bsfcs[sti->bsfcs_idx - 1], pkt);
> +if (ret == AVERROR(EAGAIN)) {
> +sti->bsfcs_idx--;
> +continue;
> +} else if (ret == AVERROR_EOF) {
> +eof = 1;
> +} else if (ret < 0)
> +break;
> +
> +/* send it to the next filter down the chain */
> +if (sti->bsfcs_idx < sti->nb_bsfcs) {
> +ret = av_bsf_send_packet(sti->bsfcs[sti->bsfcs_idx], eof ? NULL 
> : pkt);
> +av_assert2(ret != AVERROR(EAGAIN));
> +if (ret < 0)
> +break;
> +sti->bsfcs_idx++;
> +eof = 0;
> +} else if (eof) {
> +break;
> +} else {
> +return 0;
> +}
> +}
> +
> +return ret;
> +}
> +
> +static int need_auto_bsf(AVFormatContext *s, AVStream *st, AVPacket *pkt) {

{ belongs into a line of its own.

> +int ret;
>  
>  if (!(s->flags & AVFMT_FLAG_AUTO_BSF))
> -return 1;
> +return 0;
>  
> -if (s->oformat->check_bitstream) {
> +if (pkt && s->oformat->check_bitstream) {
>  if (!st->internal->bitstream_checked) {
>  if ((ret = s->oformat->check_bitstream(s, pkt)) < 0)
>  return ret;
> @@ -856,31 +890,7 @@ static int do_packet_auto_bsf(AVFormatContext *s, 
> AVPacket *pkt) {
>  }
>  }
>  
> -for (i = 0; i < st->internal->nb_bsfcs; i++) {
> -AVBSFContext *ctx = st->internal->bsfcs[i];
> -// TODO: when any bitstream filter requires flushing at EOF, we'll 
> need to
> -// flush each stream's BSF chain on write_trailer.
> -if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {
> -av_log(ctx, AV_LOG_ERROR,
> -"Failed to send packet to filter %s for stream %d\n",
> -ctx->filter->name, pkt->stream_index);
> -return ret;
> -}
> -// TODO: when any automatically-added bitstream filter is generating 
> multiple
> -// output packets for a single input one, we'll need to call this in 
> a loop
> -// and write each output packet.
> -if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {
> -if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
> -return 0;
> -av_log(ctx, AV_LOG_ERROR,
> -"Failed to receive packet from filter %s for stream 
> %d\n",
> -ctx->filter->name, pkt->stream_index);
> -if (s->error_recognition & AV_EF_EXPLODE)
> -return ret;
> -return 0;
> -}
> -}
> -return 1;
> +return st->internal->nb_bsfcs;
>  }
>  
>  static int interleaved_write_packet(AVFormatContext *s, AVPacket *pkt, int 
> flush);
> @@ -909,19 +919,54 @@ static int write_packet_common(AVFormatContext *s, 
> AVStream *st, AVPacket *pkt,
>  
>  static int write_packets_common(AVFormatContext *s, AVStream *st, AVPacket 
> *pkt, int interleaved)
>  {
> -int ret = do_packet_auto_bsf(s, pkt);
> -if (ret == 0)
> -return 0;
> -else if (ret < 0)
> +AVStreamInternal *sti = st->internal;
> +int ret;
> +
> +ret = need_auto_bsf(s, st, pkt);
> +if (ret < 0)
>  goto fail;
>  
> -ret = write_packet_common(s, st, pkt, interleaved);
> +if (ret) {
> +AVPacket opkt = {0};
> +int consumed_packet = 0;
> +
> +do {
> +ret = auto_bsf_receive_packet(s, st, &opkt);
> +   

Re: [FFmpeg-devel] [PATCH 5/8] avformat/mux: add proper support for full N:M bitstream filtering

2020-03-28 Thread Andreas Rheinhardt
Marton Balint:
> Previously only 1:1 bitstream filters were supported, the end of the stream 
> was
> not signalled to the bitstream filters and time base changes were ignored.
> 
> Signed-off-by: Marton Balint 
> ---
>  libavformat/internal.h |   1 +
>  libavformat/mux.c  | 128 
> ++---
>  2 files changed, 91 insertions(+), 38 deletions(-)
> 
> diff --git a/libavformat/internal.h b/libavformat/internal.h
> index 332477a532..45aeef717a 100644
> --- a/libavformat/internal.h
> +++ b/libavformat/internal.h
> @@ -158,6 +158,7 @@ struct AVStreamInternal {
>   */
>  AVBSFContext **bsfcs;
>  int nb_bsfcs;
> +int bsfcs_idx;
>  
>  /**
>   * Whether or not check_bitstream should still be run on each packet
> diff --git a/libavformat/mux.c b/libavformat/mux.c
> index 8c2d6a8060..3054ab8644 100644
> --- a/libavformat/mux.c
> +++ b/libavformat/mux.c
> @@ -840,14 +840,48 @@ static int prepare_input_packet(AVFormatContext *s, 
> AVPacket *pkt)
>  return 0;
>  }
>  
> -static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) {
> -AVStream *st = s->streams[pkt->stream_index];
> -int i, ret;
> +static int auto_bsf_receive_packet(AVFormatContext *s, AVStream *st, 
> AVPacket *pkt)
> +{
> +AVStreamInternal *sti = st->internal;
> +int ret = AVERROR(EAGAIN);
> +int eof = 0;
> +
> +while (sti->bsfcs_idx) {
> +/* get a packet from the previous filter up the chain */
> +ret = av_bsf_receive_packet(sti->bsfcs[sti->bsfcs_idx - 1], pkt);
> +if (ret == AVERROR(EAGAIN)) {
> +sti->bsfcs_idx--;
> +continue;
> +} else if (ret == AVERROR_EOF) {
> +eof = 1;
> +} else if (ret < 0)
> +break;
> +
> +/* send it to the next filter down the chain */
> +if (sti->bsfcs_idx < sti->nb_bsfcs) {
> +ret = av_bsf_send_packet(sti->bsfcs[sti->bsfcs_idx], eof ? NULL 
> : pkt);
> +av_assert2(ret != AVERROR(EAGAIN));
> +if (ret < 0)
> +break;
> +sti->bsfcs_idx++;
> +eof = 0;
> +} else if (eof) {
> +break;
> +} else {
> +return 0;
> +}
> +}

Would it actually be possible to simplify this by using the
av_bsf_list-API (right now code like this exists here and in ffmpeg.c
and probably at even more places)? The problem I see with this is that
one cannot send a packet for filtering to an unfinished bsf list whereas
the current code has this capability (although AFAIK no one uses it; if
I am not mistaken, all bsf chains that are automatically inserted
consist of exactly one bsf).

(This actually brings up a question: If check_bitstream returns that
more packets from this stream need to be checked, the current packet is
currently sent to the bsf-list as-is; it would not be sent to any bsf
that get added to the list later. Makes me wonder whether this is
actually a problem.)

- Andreas
___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".

[FFmpeg-devel] [PATCH 5/8] avformat/mux: add proper support for full N:M bitstream filtering

2020-03-28 Thread Marton Balint
Previously only 1:1 bitstream filters were supported, the end of the stream was
not signalled to the bitstream filters and time base changes were ignored.

Signed-off-by: Marton Balint 
---
 libavformat/internal.h |   1 +
 libavformat/mux.c  | 128 ++---
 2 files changed, 91 insertions(+), 38 deletions(-)

diff --git a/libavformat/internal.h b/libavformat/internal.h
index 332477a532..45aeef717a 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -158,6 +158,7 @@ struct AVStreamInternal {
  */
 AVBSFContext **bsfcs;
 int nb_bsfcs;
+int bsfcs_idx;
 
 /**
  * Whether or not check_bitstream should still be run on each packet
diff --git a/libavformat/mux.c b/libavformat/mux.c
index 8c2d6a8060..3054ab8644 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -840,14 +840,48 @@ static int prepare_input_packet(AVFormatContext *s, 
AVPacket *pkt)
 return 0;
 }
 
-static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) {
-AVStream *st = s->streams[pkt->stream_index];
-int i, ret;
+static int auto_bsf_receive_packet(AVFormatContext *s, AVStream *st, AVPacket 
*pkt)
+{
+AVStreamInternal *sti = st->internal;
+int ret = AVERROR(EAGAIN);
+int eof = 0;
+
+while (sti->bsfcs_idx) {
+/* get a packet from the previous filter up the chain */
+ret = av_bsf_receive_packet(sti->bsfcs[sti->bsfcs_idx - 1], pkt);
+if (ret == AVERROR(EAGAIN)) {
+sti->bsfcs_idx--;
+continue;
+} else if (ret == AVERROR_EOF) {
+eof = 1;
+} else if (ret < 0)
+break;
+
+/* send it to the next filter down the chain */
+if (sti->bsfcs_idx < sti->nb_bsfcs) {
+ret = av_bsf_send_packet(sti->bsfcs[sti->bsfcs_idx], eof ? NULL : 
pkt);
+av_assert2(ret != AVERROR(EAGAIN));
+if (ret < 0)
+break;
+sti->bsfcs_idx++;
+eof = 0;
+} else if (eof) {
+break;
+} else {
+return 0;
+}
+}
+
+return ret;
+}
+
+static int need_auto_bsf(AVFormatContext *s, AVStream *st, AVPacket *pkt) {
+int ret;
 
 if (!(s->flags & AVFMT_FLAG_AUTO_BSF))
-return 1;
+return 0;
 
-if (s->oformat->check_bitstream) {
+if (pkt && s->oformat->check_bitstream) {
 if (!st->internal->bitstream_checked) {
 if ((ret = s->oformat->check_bitstream(s, pkt)) < 0)
 return ret;
@@ -856,31 +890,7 @@ static int do_packet_auto_bsf(AVFormatContext *s, AVPacket 
*pkt) {
 }
 }
 
-for (i = 0; i < st->internal->nb_bsfcs; i++) {
-AVBSFContext *ctx = st->internal->bsfcs[i];
-// TODO: when any bitstream filter requires flushing at EOF, we'll 
need to
-// flush each stream's BSF chain on write_trailer.
-if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {
-av_log(ctx, AV_LOG_ERROR,
-"Failed to send packet to filter %s for stream %d\n",
-ctx->filter->name, pkt->stream_index);
-return ret;
-}
-// TODO: when any automatically-added bitstream filter is generating 
multiple
-// output packets for a single input one, we'll need to call this in a 
loop
-// and write each output packet.
-if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {
-if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
-return 0;
-av_log(ctx, AV_LOG_ERROR,
-"Failed to receive packet from filter %s for stream %d\n",
-ctx->filter->name, pkt->stream_index);
-if (s->error_recognition & AV_EF_EXPLODE)
-return ret;
-return 0;
-}
-}
-return 1;
+return st->internal->nb_bsfcs;
 }
 
 static int interleaved_write_packet(AVFormatContext *s, AVPacket *pkt, int 
flush);
@@ -909,19 +919,54 @@ static int write_packet_common(AVFormatContext *s, 
AVStream *st, AVPacket *pkt,
 
 static int write_packets_common(AVFormatContext *s, AVStream *st, AVPacket 
*pkt, int interleaved)
 {
-int ret = do_packet_auto_bsf(s, pkt);
-if (ret == 0)
-return 0;
-else if (ret < 0)
+AVStreamInternal *sti = st->internal;
+int ret;
+
+ret = need_auto_bsf(s, st, pkt);
+if (ret < 0)
 goto fail;
 
-ret = write_packet_common(s, st, pkt, interleaved);
+if (ret) {
+AVPacket opkt = {0};
+int consumed_packet = 0;
+
+do {
+ret = auto_bsf_receive_packet(s, st, &opkt);
+if (ret < 0) {
+if (ret == AVERROR(EAGAIN) && !consumed_packet) {
+av_assert2(sti->bsfcs_idx == 0);
+ret = av_bsf_send_packet(sti->bsfcs[0], pkt);
+av_assert2(ret != AVERROR(EAGAIN));
+if (ret >= 0) {
+consumed_packet = 1;
+