[FFmpeg-devel] [RFC/PATCH]TrueHD (and E-AC-3) over HDMI

Carl Eugen Hoyos cehoyos
Sun Aug 1 02:16:52 CEST 2010


Hi!

Anssi Hannula succeeded in bitstreaming TrueHD over HDMI. To test, it is 
necessary to hack ALSA to correctly set HBR mode:
http://thread.gmane.org/gmane.linux.alsa.devel/75317
plus "format |= 0x8000;" in hdmi_setup_stream().

Attached patch is untested (I am away from my receiver atm), but comments 
are welcome, I will test before end of the week.

Will we need a second muxer "hdmi" to refuse muxing E-AC-3 and TrueHD (and 
DTS-HD) into "spdif" or is it the users responsibility to only mux useful 
streams? And how will the user be able to signal to libavformat if he 
wants the complete DTS-HD stream muxed (for hdmi) or only the DTS core 
(for SPDIF)?

Please comment, Carl Eugen
-------------- next part --------------
Index: libavformat/spdif.c
===================================================================
--- libavformat/spdif.c	(Revision 24623)
+++ libavformat/spdif.c	(Arbeitskopie)
@@ -82,6 +82,11 @@
     /// function, which generates codec dependent header information.
     /// Sets data_type and data_offset
     int (*header_info) (AVFormatContext *s, AVPacket *pkt);
+
+    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 old_pkt_size;               ///< size of all frames in the hd audio buffer
 } IEC958Context;
 
 //TODO move to DSP
@@ -103,6 +108,66 @@
         dst[i + 0] = av_bswap16(src[i + 0]);
 }
 
+static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
+{
+    IEC958Context *ctx = s->priv_data;
+    int mat_code_length = 0;
+    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) {
+        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 = 20 + 8;
+        memcpy(ctx->hd_buf, mat_start_code, 20);
+    } else if (ctx->hd_buf_count == 12) {
+        const char mat_middle_code[12] = {0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0};
+        mat_code_length = 12 - 4;
+        memcpy(&ctx->hd_buf[12 * 2560 - 8 - 4], mat_middle_code, 12);
+    }
+
+    if (pkt->size > 2560 - mat_code_length) {
+        av_log(s, AV_LOG_ERROR, "TrueHD packet too big, %d bytes\n", pkt->size);
+        return -1;
+    }
+
+    memcpy(&ctx->hd_buf[ctx->hd_buf_count * 2560 - 8 + mat_code_length], pkt->data, pkt->size);
+    memset(&ctx->hd_buf[ctx->hd_buf_count * 2560 - 8 + mat_code_length + pkt->size], 0, 2560 - pkt->size - mat_code_length);
+    if (++ctx->hd_buf_count < 24){
+        ctx->pkt_offset = 0;
+        return 0;
+    }
+    memcpy(&ctx->hd_buf[61408], mat_end_code, 16);
+    ctx->hd_buf_count = 0;
+
+    ctx->data_type   = IEC958_TRUEHD;
+    ctx->pkt_offset  = 61440;
+    ctx->hd_buf_size = 61424;
+    return 0;
+}
+
+static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt)
+{
+    IEC958Context *ctx = s->priv_data;
+    static const uint8_t eac3_repeat[4] = {6, 3, 2, 1};
+    int repeat = 1;
+    if ((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->old_pkt_size + pkt->size);
+    if (!ctx->hd_buf)
+        return AVERROR(ENOMEM);
+    memcpy(&ctx->hd_buf[ctx->old_pkt_size], pkt->data, pkt->size);
+    if (++ctx->hd_buf_count < repeat){
+        ctx->pkt_offset = 0;
+        ctx->old_pkt_size += pkt->size;
+        return 0;
+    }
+    ctx->hd_buf_count = 0;
+    ctx->old_pkt_size = 0;
+
+    ctx->data_type  = IEC958_EAC3;
+    ctx->pkt_offset = 24576;
+    return 0;
+}
+
 static int spdif_header_ac3(AVFormatContext *s, AVPacket *pkt)
 {
     IEC958Context *ctx = s->priv_data;
@@ -110,6 +175,8 @@
 
     ctx->data_type  = IEC958_AC3 | (bitstream_mode << 8);
     ctx->pkt_offset = AC3_FRAME_SIZE << 2;
+    ctx->hd_buf      = pkt->data;
+    ctx->hd_buf_size = pkt->size;
     return 0;
 }
 
@@ -150,6 +217,8 @@
     }
     ctx->pkt_offset = blocks << 7;
 
+    ctx->hd_buf      = pkt->data;
+    ctx->hd_buf_size = pkt->size;
     return 0;
 }
 
@@ -185,6 +254,8 @@
         ctx->pkt_offset = mpeg_pkt_offset[version & 1][layer];
     }
     // TODO Data type dependant info (normal/karaoke, dynamic range control)
+    ctx->hd_buf      = pkt->data;
+    ctx->hd_buf_size = pkt->size;
     return 0;
 }
 
@@ -219,6 +290,8 @@
         return -1;
     }
     //TODO Data type dependent info (LC profile/SBR)
+    ctx->hd_buf      = pkt->data;
+    ctx->hd_buf_size = pkt->size;
     return 0;
 }
 
@@ -230,6 +303,15 @@
     case CODEC_ID_AC3:
         ctx->header_info = spdif_header_ac3;
         break;
+    case CODEC_ID_EAC3:
+        ctx->header_info = spdif_header_eac3;
+        break;
+    case CODEC_ID_TRUEHD:
+        ctx->header_info = spdif_header_truehd;
+        ctx->hd_buf = av_malloc(61424);
+        if (!ctx->hd_buf)
+            return AVERROR(ENOMEM);
+        break;
     case CODEC_ID_MP1:
     case CODEC_ID_MP2:
     case CODEC_ID_MP3:
@@ -252,6 +334,7 @@
 {
     IEC958Context *ctx = s->priv_data;
     av_freep(&ctx->buffer);
+    av_freep(&ctx->hd_buf);
     return 0;
 }
 
@@ -264,8 +347,10 @@
     ret = ctx->header_info(s, pkt);
     if (ret < 0)
         return -1;
+    if (!ctx->pkt_offset)
+        return 0;
 
-    padding = (ctx->pkt_offset - BURST_HEADER_SIZE - pkt->size) >> 1;
+    padding = (ctx->pkt_offset - BURST_HEADER_SIZE - ctx->hd_buf_size) >> 1;
     if (padding < 0) {
         av_log(s, AV_LOG_ERROR, "bitrate is too high\n");
         return -1;
@@ -277,23 +362,24 @@
     put_le16(s->pb, ctx->pkt_size);  //Pd
 
 #if HAVE_BIGENDIAN
-    put_buffer(s->pb, pkt->data, pkt->size & ~1);
+    put_buffer(s->pb, pkt->data, ctx->hd_buf_size & ~1);
 #else
-    av_fast_malloc(&ctx->buffer, &ctx->buffer_size, pkt->size + FF_INPUT_BUFFER_PADDING_SIZE);
+    av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->hd_buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
     if (!ctx->buffer)
         return AVERROR(ENOMEM);
-    bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)pkt->data, pkt->size >> 1);
-    put_buffer(s->pb, ctx->buffer, pkt->size & ~1);
+    bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->hd_buf, ctx->hd_buf_size >> 1);
+    put_buffer(s->pb, ctx->buffer, ctx->hd_buf_size & ~1);
 #endif
 
     if (pkt->size & 1)
-        put_be16(s->pb, pkt->data[pkt->size - 1]);
+        put_be16(s->pb, ctx->hd_buf[ctx->hd_buf_size - 1]);
 
     for (; padding > 0; padding--)
         put_be16(s->pb, 0);
 
     av_log(s, AV_LOG_DEBUG, "type=%x len=%i pkt_offset=%i\n",
            ctx->data_type, pkt->size, ctx->pkt_offset);
+    ctx->hd_buf_size = 0;
 
     put_flush_packet(s->pb);
     return 0;



More information about the ffmpeg-devel mailing list