diff mbox series

[FFmpeg-devel] avformat/spdifenc: handle long TrueHD input_timing gaps

Message ID 20200412195626.9090-1-anssi.hannula@iki.fi
State New
Headers show
Series [FFmpeg-devel] avformat/spdifenc: handle long TrueHD input_timing gaps | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Anssi Hannula April 12, 2020, 7:56 p.m. UTC
Some TrueHD streams contain frames that have very long gaps in
input_timing fields, while output_timing remains constant-rate. These
are likely due to encoding discontinuities of some sort as the TrueHD
substream terminator marker is observed before the gap.

Such frames trigger a sanity check in the current code - however, such
gaps are valid.

The gaps require us to insert many IEC 61937 bursts worth of MAT padding
into the output. To facilitate that, add a mechanism in
spdif_write_packet() to allow writing out multiple bursts per AVPacket,
and use that in spdif_header_truehd() to write out any full bursts that
do not yet contain any actual audio data from the current AVPacket.

Modify the sanity check to allow up to 50 MAT frames full of padding.

Fixes: incredibles2-truehd-bitstreaming.thd
---

I'll apply this soon unless there are comments.


 libavformat/spdifenc.c | 48 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

Comments

Carl Eugen Hoyos April 13, 2020, 5:16 p.m. UTC | #1
Am So., 12. Apr. 2020 um 21:56 Uhr schrieb Anssi Hannula <anssi.hannula@iki.fi>:
>
> Some TrueHD streams contain frames that have very long gaps in
> input_timing fields, while output_timing remains constant-rate. These
> are likely due to encoding discontinuities of some sort as the TrueHD
> substream terminator marker is observed before the gap.
>
> Such frames trigger a sanity check in the current code - however, such
> gaps are valid.
>
> The gaps require us to insert many IEC 61937 bursts worth of MAT padding
> into the output. To facilitate that, add a mechanism in
> spdif_write_packet() to allow writing out multiple bursts per AVPacket,
> and use that in spdif_header_truehd() to write out any full bursts that
> do not yet contain any actual audio data from the current AVPacket.
>
> Modify the sanity check to allow up to 50 MAT frames full of padding.
>
> Fixes: incredibles2-truehd-bitstreaming.thd

Works fine with my receiver, maybe Hendrik wants to comment.

Carl Eugen
diff mbox series

Patch

diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c
index 0288872fd3..7041ecf2c8 100644
--- a/libavformat/spdifenc.c
+++ b/libavformat/spdifenc.c
@@ -68,6 +68,7 @@  typedef struct IEC61937Context {
 
     int use_preamble;               ///< preamble enabled (disabled for exactly pre-padded DTS)
     int extra_bswap;                ///< extra bswap for payload (for LE DTS => standard BE DTS)
+    int more_bursts_needed;         ///< more bursts needed for the same AVPacket
 
     uint8_t *hd_buf[2];             ///< allocated buffers to concatenate hd audio frames
     int hd_buf_size;                ///< size of the hd audio buffer (eac3, dts4)
@@ -80,6 +81,7 @@  typedef struct IEC61937Context {
     uint16_t truehd_prev_time;      ///< input_timing from the last frame
     int truehd_prev_size;           ///< previous frame size in bytes, including any MAT codes
     int truehd_samples_per_frame;   ///< samples per frame for padding calculation
+    int truehd_padding_remaining;   ///< amount of padding still needed before data
 
     /* AVOptions: */
     int dtshd_rate;
@@ -450,7 +452,12 @@  static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
         return AVERROR_INVALIDDATA;
 
     input_timing = AV_RB16(pkt->data + 2);
-    if (ctx->truehd_prev_size) {
+
+    if (ctx->truehd_padding_remaining) {
+        /* padding was calculated on previous call and some still remains */
+        padding_remaining = ctx->truehd_padding_remaining;
+
+    } else if (ctx->truehd_prev_size) {
         uint16_t delta_samples = input_timing - ctx->truehd_prev_time;
         /*
          * One multiple-of-48kHz frame is 1/1200 sec and the IEC 61937 rate
@@ -470,8 +477,8 @@  static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
                delta_samples, delta_bytes);
 
         /* sanity check */
-        if (padding_remaining < 0 || padding_remaining >= MAT_FRAME_SIZE / 2) {
-            avpriv_request_sample(s, "Unusual frame timing: %"PRIu16" => %"PRIu16", %d samples/frame",
+        if (padding_remaining < 0 || padding_remaining >= MAT_PKT_OFFSET * 50) {
+            avpriv_request_sample(s, "Unusual frame timing (%"PRIu16" => %"PRIu16", %d samples/frame)",
                                   ctx->truehd_prev_time, input_timing, ctx->truehd_samples_per_frame);
             padding_remaining = 0;
         }
@@ -520,6 +527,15 @@  static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
             /* count the remainder of the code as part of frame size */
             if (code_len_remaining)
                 total_frame_size += code_len_remaining;
+
+            if (have_pkt && padding_remaining) {
+                /*
+                 * We already have a full burst but padding still remains,
+                 * write out the current burst and ask us to be called again
+                 * via ctx->more_bursts_needed to avoid filling our buffers.
+                 */
+                break;
+            }
         }
 
         if (padding_remaining) {
@@ -547,9 +563,14 @@  static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
 
     ctx->truehd_prev_size = total_frame_size;
     ctx->truehd_prev_time = input_timing;
+    ctx->truehd_padding_remaining = padding_remaining;
 
-    av_log(s, AV_LOG_TRACE, "TrueHD frame inserted, total size %d, buffer position %d\n",
-           total_frame_size, ctx->hd_buf_filled);
+    if (padding_remaining)
+        av_log(s, AV_LOG_TRACE, "TrueHD frame not yet inserted, %d bytes more padding needed\n",
+               padding_remaining);
+    else
+        av_log(s, AV_LOG_TRACE, "TrueHD frame inserted, total size %d, buffer position %d\n",
+               total_frame_size, ctx->hd_buf_filled);
 
     if (!have_pkt) {
         ctx->pkt_offset = 0;
@@ -560,6 +581,8 @@  static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
     ctx->pkt_offset  = MAT_PKT_OFFSET;
     ctx->out_bytes   = MAT_FRAME_SIZE;
     ctx->length_code = MAT_FRAME_SIZE;
+    if (padding_remaining)
+        ctx->more_bursts_needed = 1;
     return 0;
 }
 
@@ -619,7 +642,7 @@  static av_always_inline void spdif_put_16(IEC61937Context *ctx,
         avio_wl16(pb, val);
 }
 
-static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt)
+static int spdif_write_burst(AVFormatContext *s, AVPacket *pkt)
 {
     IEC61937Context *ctx = s->priv_data;
     int ret, padding;
@@ -629,6 +652,7 @@  static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt)
     ctx->length_code = FFALIGN(pkt->size, 2) << 3;
     ctx->use_preamble = 1;
     ctx->extra_bswap = 0;
+    ctx->more_bursts_needed = 0;
 
     ret = ctx->header_info(s, pkt);
     if (ret < 0)
@@ -671,6 +695,18 @@  static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt)
     return 0;
 }
 
+static int spdif_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    IEC61937Context *ctx = s->priv_data;
+    int ret = 0;
+
+    ctx->more_bursts_needed = 1;
+    while (ctx->more_bursts_needed && ret == 0)
+        ret = spdif_write_burst(s, pkt);
+
+    return ret;
+}
+
 AVOutputFormat ff_spdif_muxer = {
     .name              = "spdif",
     .long_name         = NULL_IF_CONFIG_SMALL("IEC 61937 (used on S/PDIF - IEC958)"),