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

Anssi Hannula anssi.hannula at iki.fi
Sun Apr 12 22:56:26 EEST 2020


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(-)

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)"),
-- 
2.25.2



More information about the ffmpeg-devel mailing list