diff mbox series

[FFmpeg-devel,17/18] lavf/dv: do not update AVCodecParameters.sample_rate while demuxing

Message ID 20220824084318.333-17-anton@khirnov.net
State Accepted
Commit 1ef4620290bcea29097a79c3f27be3d5cb366687
Headers show
Series [FFmpeg-devel,01/18] tests/fate/mov: add a test for dv audio demuxed through dv demuxer | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 fail Make fate failed
andriy/make_x86 success Make finished
andriy/make_fate_x86 fail Make fate failed

Commit Message

Anton Khirnov Aug. 24, 2022, 8:43 a.m. UTC
Demuxers are not allowed to do this and few callers, if any, will handle
this correctly. Send the AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE side data
instead.
---
 libavformat/dv.c | 49 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 34 insertions(+), 15 deletions(-)

Comments

Andreas Rheinhardt Aug. 24, 2022, 2:20 p.m. UTC | #1
Anton Khirnov:
> Demuxers are not allowed to do this and few callers, if any, will handle
> this correctly. Send the AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE side data
> instead.
> ---
>  libavformat/dv.c | 49 +++++++++++++++++++++++++++++++++---------------
>  1 file changed, 34 insertions(+), 15 deletions(-)
> 
> diff --git a/libavformat/dv.c b/libavformat/dv.c
> index 9c8b0a262c..ffed1a7a90 100644
> --- a/libavformat/dv.c
> +++ b/libavformat/dv.c
> @@ -33,6 +33,7 @@
>  
>  #include <time.h>
>  #include "avformat.h"
> +#include "demux.h"
>  #include "internal.h"
>  #include "libavcodec/dv_profile.h"
>  #include "libavcodec/dv.h"
> @@ -46,7 +47,7 @@
>  #if CONFIG_DV_DEMUXER
>  
>  // Must be kept in sync with AVPacket
> -struct DVPacket {
> +typedef struct DVPacket {
>      int64_t  pts;
>      uint8_t *data;
>      int      size;
> @@ -54,7 +55,10 @@ struct DVPacket {
>      int      flags;
>      int64_t  pos;
>      int64_t  duration;
> -};
> +
> +    int sample_rate;
> +    int last_sample_rate;
> +} DVPacket;
>  
>  struct DVDemuxContext {
>      const AVDVProfile*  sys;    /* Current DV profile. E.g.: 525/60, 625/50 */
> @@ -237,7 +241,7 @@ static int dv_extract_audio(const uint8_t *frame, uint8_t **ppcm,
>  static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
>  {
>      const uint8_t *as_pack;
> -    int freq, stype, smpls, quant, i, ach;
> +    int freq, stype, smpls, quant, i, ach, sr;
>  
>      as_pack = dv_extract_pack(frame, DV_AUDIO_SOURCE);
>      if (!as_pack || !c->sys) {    /* No audio ? */
> @@ -255,6 +259,7 @@ static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
>                 "Unrecognized audio sample rate index (%d)\n", freq);
>          return 0;
>      }
> +    sr = dv_audio_frequency[freq];
>  
>      if (stype > 3) {
>          av_log(c->fctx, AV_LOG_ERROR, "stype %d is invalid\n", stype);
> @@ -280,7 +285,10 @@ static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
>              c->ast[i]->codecpar->codec_id   = AV_CODEC_ID_PCM_S16LE;
>              c->ast[i]->codecpar->ch_layout  = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
>              c->ast[i]->start_time           = 0;
> -            c->ast[i]->codecpar->bit_rate   = 2 * dv_audio_frequency[freq] * 16;
> +            c->ast[i]->codecpar->bit_rate   = 2 * sr * 16;
> +
> +            c->ast[i]->codecpar->sample_rate = sr;
> +            c->audio_pkt[i].last_sample_rate = sr;
>  
>              c->audio_pkt[i].size         = 0;
>              c->audio_pkt[i].data         = c->audio_buf[i];
> @@ -290,7 +298,8 @@ static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
>              c->audio_pkt[i].duration     = 0;
>              c->audio_pkt[i].pos          = -1;
>          }
> -        c->ast[i]->codecpar->sample_rate    = dv_audio_frequency[freq];
> +
> +        c->audio_pkt[i].sample_rate = sr;
>      }
>      c->ach = ach;
>  
> @@ -380,16 +389,26 @@ int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt)
>  
>      for (i = 0; i < c->ach; i++) {
>          if (c->ast[i] && c->audio_pkt[i].size) {
> -            pkt->size         = c->audio_pkt[i].size;
> -            pkt->data         = c->audio_pkt[i].data;
> -            pkt->stream_index = c->audio_pkt[i].stream_index;
> -            pkt->flags        = c->audio_pkt[i].flags;
> -            pkt->pts          = c->audio_pkt[i].pts;
> -            pkt->duration     = c->audio_pkt[i].duration;
> -            pkt->pos          = c->audio_pkt[i].pos;
> -
> -            c->audio_pkt[i].size = 0;
> -            size                 = pkt->size;
> +            DVPacket *dpkt = &c->audio_pkt[i];
> +
> +            pkt->size         = dpkt->size;
> +            pkt->data         = dpkt->data;
> +            pkt->stream_index = dpkt->stream_index;
> +            pkt->flags        = dpkt->flags;
> +            pkt->pts          = dpkt->pts;
> +            pkt->duration     = dpkt->duration;
> +            pkt->pos          = dpkt->pos;
> +
> +            dpkt->size = 0;
> +            size       = pkt->size;
> +
> +            if (dpkt->last_sample_rate != dpkt->sample_rate) {
> +                int ret = ff_add_param_change(pkt, 0, 0, dpkt->sample_rate, 0, 0);
> +                if (ret < 0)
> +                    return ret;
> +                dpkt->last_sample_rate = dpkt->sample_rate;
> +            }
> +
>              break;
>          }
>      }

1. One of the callers which did not handle this correctly is the mov
demuxer: A sample rate update would not propagate to the user, because
it uses a separate AVFormatContext for it. This should be mentioned in
the commit message. (Btw: I don't see anything that guarantees that the
samplerate and timebase of the inner demuxer and the sample rate and
time base reported to the user (presumably taken from mov structures)
coincide. Given that dv_fctx and dv_demux are part of MOVContext, they
would also leak if it would be attempted to allocate them multiple
times. Do you see anything that guarantees that they will not be
allocated multiple times?)
2. In case of ff_add_param_change() failure, users will just interpret
that as "no more packets available", won't they? This might be wrong,
but it is not problematic.
3. Have you tested this with a sample with actual parameter changes?

- Andreas
Anton Khirnov Aug. 31, 2022, 3:11 a.m. UTC | #2
Quoting Andreas Rheinhardt (2022-08-24 16:20:35)
> Anton Khirnov:
> > Demuxers are not allowed to do this and few callers, if any, will handle
> > this correctly. Send the AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE side data
> > instead.
> 
> 1. One of the callers which did not handle this correctly is the mov
> demuxer: A sample rate update would not propagate to the user, because
> it uses a separate AVFormatContext for it. This should be mentioned in
> the commit message. (Btw: I don't see anything that guarantees that the
> samplerate and timebase of the inner demuxer and the sample rate and
> time base reported to the user (presumably taken from mov structures)
> coincide. Given that dv_fctx and dv_demux are part of MOVContext, they
> would also leak if it would be attempted to allocate them multiple
> times. Do you see anything that guarantees that they will not be
> allocated multiple times?)
> 2. In case of ff_add_param_change() failure, users will just interpret
> that as "no more packets available", won't they?

yes, I think so

> This might be wrong, but it is not problematic.

I agree

> 3. Have you tested this with a sample with actual parameter changes?

Yes, I made a sample by concatenating two dv files. The side data is
exported correctly, but the PCM decoder fails because it is not marked
with AV_CODEC_CAP_PARAM_CHANGE. Patches welcome.
diff mbox series

Patch

diff --git a/libavformat/dv.c b/libavformat/dv.c
index 9c8b0a262c..ffed1a7a90 100644
--- a/libavformat/dv.c
+++ b/libavformat/dv.c
@@ -33,6 +33,7 @@ 
 
 #include <time.h>
 #include "avformat.h"
+#include "demux.h"
 #include "internal.h"
 #include "libavcodec/dv_profile.h"
 #include "libavcodec/dv.h"
@@ -46,7 +47,7 @@ 
 #if CONFIG_DV_DEMUXER
 
 // Must be kept in sync with AVPacket
-struct DVPacket {
+typedef struct DVPacket {
     int64_t  pts;
     uint8_t *data;
     int      size;
@@ -54,7 +55,10 @@  struct DVPacket {
     int      flags;
     int64_t  pos;
     int64_t  duration;
-};
+
+    int sample_rate;
+    int last_sample_rate;
+} DVPacket;
 
 struct DVDemuxContext {
     const AVDVProfile*  sys;    /* Current DV profile. E.g.: 525/60, 625/50 */
@@ -237,7 +241,7 @@  static int dv_extract_audio(const uint8_t *frame, uint8_t **ppcm,
 static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
 {
     const uint8_t *as_pack;
-    int freq, stype, smpls, quant, i, ach;
+    int freq, stype, smpls, quant, i, ach, sr;
 
     as_pack = dv_extract_pack(frame, DV_AUDIO_SOURCE);
     if (!as_pack || !c->sys) {    /* No audio ? */
@@ -255,6 +259,7 @@  static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
                "Unrecognized audio sample rate index (%d)\n", freq);
         return 0;
     }
+    sr = dv_audio_frequency[freq];
 
     if (stype > 3) {
         av_log(c->fctx, AV_LOG_ERROR, "stype %d is invalid\n", stype);
@@ -280,7 +285,10 @@  static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
             c->ast[i]->codecpar->codec_id   = AV_CODEC_ID_PCM_S16LE;
             c->ast[i]->codecpar->ch_layout  = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
             c->ast[i]->start_time           = 0;
-            c->ast[i]->codecpar->bit_rate   = 2 * dv_audio_frequency[freq] * 16;
+            c->ast[i]->codecpar->bit_rate   = 2 * sr * 16;
+
+            c->ast[i]->codecpar->sample_rate = sr;
+            c->audio_pkt[i].last_sample_rate = sr;
 
             c->audio_pkt[i].size         = 0;
             c->audio_pkt[i].data         = c->audio_buf[i];
@@ -290,7 +298,8 @@  static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
             c->audio_pkt[i].duration     = 0;
             c->audio_pkt[i].pos          = -1;
         }
-        c->ast[i]->codecpar->sample_rate    = dv_audio_frequency[freq];
+
+        c->audio_pkt[i].sample_rate = sr;
     }
     c->ach = ach;
 
@@ -380,16 +389,26 @@  int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt)
 
     for (i = 0; i < c->ach; i++) {
         if (c->ast[i] && c->audio_pkt[i].size) {
-            pkt->size         = c->audio_pkt[i].size;
-            pkt->data         = c->audio_pkt[i].data;
-            pkt->stream_index = c->audio_pkt[i].stream_index;
-            pkt->flags        = c->audio_pkt[i].flags;
-            pkt->pts          = c->audio_pkt[i].pts;
-            pkt->duration     = c->audio_pkt[i].duration;
-            pkt->pos          = c->audio_pkt[i].pos;
-
-            c->audio_pkt[i].size = 0;
-            size                 = pkt->size;
+            DVPacket *dpkt = &c->audio_pkt[i];
+
+            pkt->size         = dpkt->size;
+            pkt->data         = dpkt->data;
+            pkt->stream_index = dpkt->stream_index;
+            pkt->flags        = dpkt->flags;
+            pkt->pts          = dpkt->pts;
+            pkt->duration     = dpkt->duration;
+            pkt->pos          = dpkt->pos;
+
+            dpkt->size = 0;
+            size       = pkt->size;
+
+            if (dpkt->last_sample_rate != dpkt->sample_rate) {
+                int ret = ff_add_param_change(pkt, 0, 0, dpkt->sample_rate, 0, 0);
+                if (ret < 0)
+                    return ret;
+                dpkt->last_sample_rate = dpkt->sample_rate;
+            }
+
             break;
         }
     }