]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/spdifenc.c
avformat/rawenc: remove singlejpeg muxer
[ffmpeg] / libavformat / spdifenc.c
index 9514ff8e10e3518ef736a90ad675dde9fc9c0614..c3ba3a1a19e38051bdfdd4348de1f5067fd1df3b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * IEC 61937 muxer
  * Copyright (c) 2009 Bartlomiej Wolowiec
- * Copyright (c) 2010 Anssi Hannula
+ * Copyright (c) 2010, 2020 Anssi Hannula
  * Copyright (c) 2010 Carl Eugen Hoyos
  *
  * This file is part of FFmpeg.
@@ -69,13 +69,18 @@ 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)
 
-    uint8_t *hd_buf;                ///< allocated buffer to concatenate hd audio frames
-    int hd_buf_size;                ///< size of the hd audio buffer
-    int hd_buf_count;               ///< number of frames in the hd audio buffer
-    int hd_buf_filled;              ///< amount of bytes in the hd audio buffer
+    uint8_t *hd_buf[2];             ///< allocated buffers to concatenate hd audio frames
+    int hd_buf_size;                ///< size of the hd audio buffer (eac3, dts4)
+    int hd_buf_count;               ///< number of frames in the hd audio buffer (eac3)
+    int hd_buf_filled;              ///< amount of bytes in the hd audio buffer (eac3, truehd)
+    int hd_buf_idx;                 ///< active hd buffer index (truehd)
 
     int dtshd_skip;                 ///< counter used for skipping DTS-HD frames
 
+    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
+
     /* AVOptions: */
     int dtshd_rate;
     int dtshd_fallback;
@@ -117,16 +122,18 @@ static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt)
     IEC61937Context *ctx = s->priv_data;
     static const uint8_t eac3_repeat[4] = {6, 3, 2, 1};
     int repeat = 1;
+    uint8_t *tmp;
 
     int bsid = pkt->data[5] >> 3;
     if (bsid > 10 && (pkt->data[4] & 0xc0) != 0xc0) /* fscod */
         repeat = eac3_repeat[(pkt->data[4] & 0x30) >> 4]; /* numblkscod */
 
-    ctx->hd_buf = av_fast_realloc(ctx->hd_buf, &ctx->hd_buf_size, ctx->hd_buf_filled + pkt->size);
-    if (!ctx->hd_buf)
+    tmp = av_fast_realloc(ctx->hd_buf[0], &ctx->hd_buf_size, ctx->hd_buf_filled + pkt->size);
+    if (!tmp)
         return AVERROR(ENOMEM);
+    ctx->hd_buf[0] = tmp;
 
-    memcpy(&ctx->hd_buf[ctx->hd_buf_filled], pkt->data, pkt->size);
+    memcpy(&ctx->hd_buf[0][ctx->hd_buf_filled], pkt->data, pkt->size);
 
     ctx->hd_buf_filled += pkt->size;
     if (++ctx->hd_buf_count < repeat){
@@ -135,7 +142,7 @@ static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt)
     }
     ctx->data_type   = IEC61937_EAC3;
     ctx->pkt_offset  = 24576;
-    ctx->out_buf     = ctx->hd_buf;
+    ctx->out_buf     = ctx->hd_buf[0];
     ctx->out_bytes   = ctx->hd_buf_filled;
     ctx->length_code = ctx->hd_buf_filled;
 
@@ -228,15 +235,15 @@ static int spdif_header_dts4(AVFormatContext *s, AVPacket *pkt, int core_size,
      * with some receivers, but the exact requirement is unconfirmed. */
     ctx->length_code = FFALIGN(ctx->out_bytes + 0x8, 0x10) - 0x8;
 
-    av_fast_malloc(&ctx->hd_buf, &ctx->hd_buf_size, ctx->out_bytes);
-    if (!ctx->hd_buf)
+    av_fast_malloc(&ctx->hd_buf[0], &ctx->hd_buf_size, ctx->out_bytes);
+    if (!ctx->hd_buf[0])
         return AVERROR(ENOMEM);
 
-    ctx->out_buf = ctx->hd_buf;
+    ctx->out_buf = ctx->hd_buf[0];
 
-    memcpy(ctx->hd_buf, dtshd_start_code, sizeof(dtshd_start_code));
-    AV_WB16(ctx->hd_buf + sizeof(dtshd_start_code), pkt_size);
-    memcpy(ctx->hd_buf + sizeof(dtshd_start_code) + 2, pkt->data, pkt_size);
+    memcpy(ctx->hd_buf[0], dtshd_start_code, sizeof(dtshd_start_code));
+    AV_WB16(ctx->hd_buf[0] + sizeof(dtshd_start_code), pkt_size);
+    memcpy(ctx->hd_buf[0] + sizeof(dtshd_start_code) + 2, pkt->data, pkt_size);
 
     return 0;
 }
@@ -384,57 +391,175 @@ static int spdif_header_aac(AVFormatContext *s, AVPacket *pkt)
 /*
  * It seems Dolby TrueHD frames have to be encapsulated in MAT frames before
  * they can be encapsulated in IEC 61937.
- * Here we encapsulate 24 TrueHD frames in a single MAT frame, padding them
- * to achieve constant rate.
- * The actual format of a MAT frame is unknown, but the below seems to work.
- * However, it seems it is not actually necessary for the 24 TrueHD frames to
- * be in an exact alignment with the MAT frame.
  */
+#define MAT_PKT_OFFSET          61440
 #define MAT_FRAME_SIZE          61424
-#define TRUEHD_FRAME_OFFSET     2560
-#define MAT_MIDDLE_CODE_OFFSET  -4
+
+static const uint8_t mat_start_code[20] = {
+    0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83,
+    0x49, 0x80, 0x77, 0xE0,
+};
+static const uint8_t mat_middle_code[12] = {
+    0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0,
+};
+static const uint8_t mat_end_code[16] = {
+    0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11,
+};
+
+#define MAT_CODE(position, data) { .pos = position, .code = data, .len = sizeof(data) }
+
+static const struct {
+    unsigned int pos;
+    const uint8_t *code;
+    unsigned int len;
+} mat_codes[] = {
+    MAT_CODE(0, mat_start_code),
+    MAT_CODE(30708, mat_middle_code),
+    MAT_CODE(MAT_FRAME_SIZE - sizeof(mat_end_code), mat_end_code),
+};
 
 static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
 {
     IEC61937Context *ctx = s->priv_data;
-    int mat_code_length = 0;
-    static const char mat_end_code[16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 };
-
-    if (!ctx->hd_buf_count) {
-        static const char mat_start_code[20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 };
-        mat_code_length = sizeof(mat_start_code) + BURST_HEADER_SIZE;
-        memcpy(ctx->hd_buf, mat_start_code, sizeof(mat_start_code));
-
-    } else if (ctx->hd_buf_count == 12) {
-        static const char mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 };
-        mat_code_length = sizeof(mat_middle_code) + MAT_MIDDLE_CODE_OFFSET;
-        memcpy(&ctx->hd_buf[12 * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + MAT_MIDDLE_CODE_OFFSET],
-               mat_middle_code, sizeof(mat_middle_code));
+    uint8_t *hd_buf = ctx->hd_buf[ctx->hd_buf_idx];
+    int ratebits;
+    int padding_remaining = 0;
+    uint16_t input_timing;
+    int total_frame_size = pkt->size;
+    const uint8_t *dataptr = pkt->data;
+    int data_remaining = pkt->size;
+    int have_pkt = 0;
+    int next_code_idx;
+
+    if (pkt->size < 10)
+        return AVERROR_INVALIDDATA;
+
+    if (AV_RB24(pkt->data + 4) == 0xf8726f) {
+        /* major sync unit, fetch sample rate */
+        if (pkt->data[7] == 0xba)
+            ratebits = pkt->data[8] >> 4;
+        else if (pkt->data[7] == 0xbb)
+            ratebits = pkt->data[9] >> 4;
+        else
+            return AVERROR_INVALIDDATA;
+
+        ctx->truehd_samples_per_frame = 40 << (ratebits & 3);
+        av_log(s, AV_LOG_TRACE, "TrueHD samples per frame: %d\n",
+               ctx->truehd_samples_per_frame);
     }
 
-    if (pkt->size > TRUEHD_FRAME_OFFSET - mat_code_length) {
-        /* if such frames exist, we'd need some more complex logic to
-         * distribute the TrueHD frames in the MAT frame */
-        avpriv_request_sample(s, "Too large TrueHD frame of %d bytes",
-                              pkt->size);
-        return AVERROR_PATCHWELCOME;
+    if (!ctx->truehd_samples_per_frame)
+        return AVERROR_INVALIDDATA;
+
+    input_timing = AV_RB16(pkt->data + 2);
+    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
+         * is 768kHz = 768000*4 bytes/sec.
+         * The nominal space per frame is therefore
+         * (768000*4 bytes/sec) * (1/1200 sec) = 2560 bytes.
+         * For multiple-of-44.1kHz frames: 1/1102.5 sec, 705.6kHz, 2560 bytes.
+         *
+         * 2560 is divisible by truehd_samples_per_frame.
+         */
+        int delta_bytes = delta_samples * 2560 / ctx->truehd_samples_per_frame;
+
+        /* padding needed before this frame */
+        padding_remaining = delta_bytes - ctx->truehd_prev_size;
+
+        av_log(s, AV_LOG_TRACE, "delta_samples: %"PRIu16", delta_bytes: %d\n",
+               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",
+                                  ctx->truehd_prev_time, input_timing, ctx->truehd_samples_per_frame);
+            padding_remaining = 0;
+        }
     }
 
-    memcpy(&ctx->hd_buf[ctx->hd_buf_count * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + mat_code_length],
-           pkt->data, pkt->size);
-    memset(&ctx->hd_buf[ctx->hd_buf_count * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + mat_code_length + pkt->size],
-           0, TRUEHD_FRAME_OFFSET - pkt->size - mat_code_length);
+    for (next_code_idx = 0; next_code_idx < FF_ARRAY_ELEMS(mat_codes); next_code_idx++)
+        if (ctx->hd_buf_filled <= mat_codes[next_code_idx].pos)
+            break;
+
+    if (next_code_idx >= FF_ARRAY_ELEMS(mat_codes))
+        return AVERROR_BUG;
+
+    while (padding_remaining || data_remaining ||
+           mat_codes[next_code_idx].pos == ctx->hd_buf_filled) {
+
+        if (mat_codes[next_code_idx].pos == ctx->hd_buf_filled) {
+            /* time to insert MAT code */
+            int code_len = mat_codes[next_code_idx].len;
+            int code_len_remaining = code_len;
+            memcpy(hd_buf + mat_codes[next_code_idx].pos,
+                   mat_codes[next_code_idx].code, code_len);
+            ctx->hd_buf_filled += code_len;
+
+            next_code_idx++;
+            if (next_code_idx == FF_ARRAY_ELEMS(mat_codes)) {
+                next_code_idx = 0;
+
+                /* this was the last code, move to the next MAT frame */
+                have_pkt = 1;
+                ctx->out_buf = hd_buf;
+                ctx->hd_buf_idx ^= 1;
+                hd_buf = ctx->hd_buf[ctx->hd_buf_idx];
+                ctx->hd_buf_filled = 0;
+
+                /* inter-frame gap has to be counted as well, add it */
+                code_len_remaining += MAT_PKT_OFFSET - MAT_FRAME_SIZE;
+            }
+
+            if (padding_remaining) {
+                /* consider the MAT code as padding */
+                int counted_as_padding = FFMIN(padding_remaining,
+                                               code_len_remaining);
+                padding_remaining -= counted_as_padding;
+                code_len_remaining -= counted_as_padding;
+            }
+            /* count the remainder of the code as part of frame size */
+            if (code_len_remaining)
+                total_frame_size += code_len_remaining;
+        }
+
+        if (padding_remaining) {
+            int padding_to_insert = FFMIN(mat_codes[next_code_idx].pos - ctx->hd_buf_filled,
+                                          padding_remaining);
+
+            memset(hd_buf + ctx->hd_buf_filled, 0, padding_to_insert);
+            ctx->hd_buf_filled += padding_to_insert;
+            padding_remaining -= padding_to_insert;
+
+            if (padding_remaining)
+                continue; /* time to insert MAT code */
+        }
+
+        if (data_remaining) {
+            int data_to_insert = FFMIN(mat_codes[next_code_idx].pos - ctx->hd_buf_filled,
+                                       data_remaining);
+
+            memcpy(hd_buf + ctx->hd_buf_filled, dataptr, data_to_insert);
+            ctx->hd_buf_filled += data_to_insert;
+            dataptr += data_to_insert;
+            data_remaining -= data_to_insert;
+        }
+    }
+
+    ctx->truehd_prev_size = total_frame_size;
+    ctx->truehd_prev_time = input_timing;
+
+    av_log(s, AV_LOG_TRACE, "TrueHD frame inserted, total size %d, buffer position %d\n",
+           total_frame_size, ctx->hd_buf_filled);
 
-    if (++ctx->hd_buf_count < 24){
+    if (!have_pkt) {
         ctx->pkt_offset = 0;
         return 0;
     }
-    memcpy(&ctx->hd_buf[MAT_FRAME_SIZE - sizeof(mat_end_code)], mat_end_code, sizeof(mat_end_code));
-    ctx->hd_buf_count = 0;
 
     ctx->data_type   = IEC61937_TRUEHD;
-    ctx->pkt_offset  = 61440;
-    ctx->out_buf     = ctx->hd_buf;
+    ctx->pkt_offset  = MAT_PKT_OFFSET;
     ctx->out_bytes   = MAT_FRAME_SIZE;
     ctx->length_code = MAT_FRAME_SIZE;
     return 0;
@@ -465,9 +590,11 @@ static int spdif_write_header(AVFormatContext *s)
     case AV_CODEC_ID_TRUEHD:
     case AV_CODEC_ID_MLP:
         ctx->header_info = spdif_header_truehd;
-        ctx->hd_buf = av_malloc(MAT_FRAME_SIZE);
-        if (!ctx->hd_buf)
-            return AVERROR(ENOMEM);
+        for (int i = 0; i < FF_ARRAY_ELEMS(ctx->hd_buf); i++) {
+            ctx->hd_buf[i] = av_malloc(MAT_FRAME_SIZE);
+            if (!ctx->hd_buf[i])
+                return AVERROR(ENOMEM);
+        }
         break;
     default:
         avpriv_report_missing_feature(s, "Codec %d",
@@ -477,12 +604,12 @@ static int spdif_write_header(AVFormatContext *s)
     return 0;
 }
 
-static int spdif_write_trailer(AVFormatContext *s)
+static void spdif_deinit(AVFormatContext *s)
 {
     IEC61937Context *ctx = s->priv_data;
     av_freep(&ctx->buffer);
-    av_freep(&ctx->hd_buf);
-    return 0;
+    for (int i = 0; i < FF_ARRAY_ELEMS(ctx->hd_buf); i++)
+        av_freep(&ctx->hd_buf[i]);
 }
 
 static av_always_inline void spdif_put_16(IEC61937Context *ctx,
@@ -555,7 +682,7 @@ AVOutputFormat ff_spdif_muxer = {
     .video_codec       = AV_CODEC_ID_NONE,
     .write_header      = spdif_write_header,
     .write_packet      = spdif_write_packet,
-    .write_trailer     = spdif_write_trailer,
+    .deinit            = spdif_deinit,
     .flags             = AVFMT_NOTIMESTAMPS,
     .priv_class        = &spdif_class,
 };