Add support for IEC 61937 ("SPDIF") encapsulation for E-AC-3.
authorCarl Eugen Hoyos <cehoyos@rainbow.studorg.tuwien.ac.at>
Wed, 17 Nov 2010 12:22:59 +0000 (12:22 +0000)
committerCarl Eugen Hoyos <cehoyos@rainbow.studorg.tuwien.ac.at>
Wed, 17 Nov 2010 12:22:59 +0000 (12:22 +0000)
Only works via HDMI.

Patch by Anssi Hannula (anssi d hannula a iki d fi), based on some work
by myself.

Originally committed as revision 25760 to svn://svn.ffmpeg.org/ffmpeg/trunk

Changelog
libavformat/spdifenc.c

index b173e1c..295338a 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -55,6 +55,7 @@ version <next>:
 - Win64 support for optimized asm functions
 - MJPEG/AVI1 to JPEG/JFIF bitstream filter
 - ASS subtitle encoder and decoder
+- IEC 61937 encapsulation for E-AC3 (for HDMI passthrough)
 
 
 version 0.6:
index 78817a3..ed02b2e 100644 (file)
 
 typedef struct IEC958Context {
     enum IEC958DataType data_type;  ///< burst info - reference to type of payload of the data-burst
-    int pkt_size;                   ///< length code in bits
+    int length_code;                ///< length code in bits or bytes, depending on data type
     int pkt_offset;                 ///< data burst repetition period in bytes
     uint8_t *buffer;                ///< allocated buffer, used for swap bytes
     int buffer_size;                ///< size of allocated buffer
 
+    uint8_t *out_buf;               ///< pointer to the outgoing data before byte-swapping
+    int out_bytes;                  ///< amount of outgoing bytes
+
+    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
+
     /// function, which generates codec dependent header information.
-    /// Sets data_type and data_offset
+    /// Sets data_type and pkt_offset, and length_code, out_bytes, out_buf if necessary
     int (*header_info) (AVFormatContext *s, AVPacket *pkt);
 } IEC958Context;
 
@@ -69,6 +77,37 @@ static int spdif_header_ac3(AVFormatContext *s, AVPacket *pkt)
     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->hd_buf_filled + pkt->size);
+    if (!ctx->hd_buf)
+        return AVERROR(ENOMEM);
+
+    memcpy(&ctx->hd_buf[ctx->hd_buf_filled], pkt->data, pkt->size);
+
+    ctx->hd_buf_filled += pkt->size;
+    if (++ctx->hd_buf_count < repeat){
+        ctx->pkt_offset = 0;
+        return 0;
+    }
+    ctx->data_type   = IEC958_EAC3;
+    ctx->pkt_offset  = 24576;
+    ctx->out_buf     = ctx->hd_buf;
+    ctx->out_bytes   = ctx->hd_buf_filled;
+    ctx->length_code = ctx->hd_buf_filled;
+
+    ctx->hd_buf_count  = 0;
+    ctx->hd_buf_filled = 0;
+    return 0;
+}
+
 static int spdif_header_dts(AVFormatContext *s, AVPacket *pkt)
 {
     IEC958Context *ctx = s->priv_data;
@@ -180,6 +219,9 @@ static int spdif_write_header(AVFormatContext *s)
     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_MP1:
     case CODEC_ID_MP2:
     case CODEC_ID_MP3:
@@ -202,6 +244,7 @@ static int spdif_write_trailer(AVFormatContext *s)
 {
     IEC958Context *ctx = s->priv_data;
     av_freep(&ctx->buffer);
+    av_freep(&ctx->hd_buf);
     return 0;
 }
 
@@ -210,12 +253,16 @@ static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt)
     IEC958Context *ctx = s->priv_data;
     int ret, padding;
 
-    ctx->pkt_size = FFALIGN(pkt->size, 2) << 3;
+    ctx->out_bytes = pkt->size;
+    ctx->length_code = FFALIGN(pkt->size, 2) << 3;
+
     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->out_bytes) >> 1;
     if (padding < 0) {
         av_log(s, AV_LOG_ERROR, "bitrate is too high\n");
         return -1;
@@ -224,26 +271,26 @@ static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt)
     put_le16(s->pb, SYNCWORD1);      //Pa
     put_le16(s->pb, SYNCWORD2);      //Pb
     put_le16(s->pb, ctx->data_type); //Pc
-    put_le16(s->pb, ctx->pkt_size);  //Pd
+    put_le16(s->pb, ctx->length_code);//Pd
 
 #if HAVE_BIGENDIAN
-    put_buffer(s->pb, pkt->data, pkt->size & ~1);
+    put_buffer(s->pb, ctx->out_buf, ctx->out_bytes & ~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->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE);
     if (!ctx->buffer)
         return AVERROR(ENOMEM);
-    ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)pkt->data, pkt->size >> 1);
-    put_buffer(s->pb, ctx->buffer, pkt->size & ~1);
+    ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1);
+    put_buffer(s->pb, ctx->buffer, ctx->out_bytes & ~1);
 #endif
 
-    if (pkt->size & 1)
-        put_be16(s->pb, pkt->data[pkt->size - 1]);
+    if (ctx->out_bytes & 1)
+        put_be16(s->pb, ctx->out_buf[ctx->out_bytes - 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->data_type, ctx->out_bytes, ctx->pkt_offset);
 
     put_flush_packet(s->pb);
     return 0;