Allow the combined TrueHD/AC3 tracks on Blu-Ray discs to be demuxed as two separate...
authorIan Caulfield <ian.caulfield@gmx.at>
Mon, 5 Oct 2009 08:38:39 +0000 (08:38 +0000)
committerCarl Eugen Hoyos <cehoyos@rainbow.studorg.tuwien.ac.at>
Mon, 5 Oct 2009 08:38:39 +0000 (08:38 +0000)
Patch by Ian Caulfield, ian D caulfield A gmail

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

libavformat/mpegts.c

index 92a29a3..76efe30 100644 (file)
@@ -148,11 +148,13 @@ struct PESContext {
     MpegTSContext *ts;
     AVFormatContext *stream;
     AVStream *st;
+    AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */
     enum MpegTSState state;
     /* used to get the format */
     int data_index;
     int total_size;
     int pes_header_size;
+    int extended_stream_id;
     int64_t pts, dts;
     int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */
     uint8_t header[MAX_PES_HEADER_SIZE];
@@ -509,6 +511,7 @@ static const StreamType HDMV_types[] = {
     { 0x80, CODEC_TYPE_AUDIO, CODEC_ID_PCM_BLURAY },
     { 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
     { 0x82, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
+    { 0x83, CODEC_TYPE_AUDIO, CODEC_ID_TRUEHD },
     { 0x90, CODEC_TYPE_SUBTITLE, CODEC_ID_HDMV_PGS_SUBTITLE },
     { 0 },
 };
@@ -568,8 +571,32 @@ static AVStream *new_pes_av_stream(PESContext *pes, uint32_t prog_reg_desc, uint
 
     mpegts_find_stream_type(st, pes->stream_type, ISO_types);
     if (prog_reg_desc == AV_RL32("HDMV") &&
-        st->codec->codec_id == CODEC_ID_NONE)
+        st->codec->codec_id == CODEC_ID_NONE) {
         mpegts_find_stream_type(st, pes->stream_type, HDMV_types);
+        if (pes->stream_type == 0x83) {
+            // HDMV TrueHD streams also contain an AC3 coded version of the
+            // audio track - add a second stream for this
+            AVStream *sub_st;
+            // priv_data cannot be shared between streams
+            PESContext *sub_pes = av_malloc(sizeof(*sub_pes));
+            if (!sub_pes)
+                return NULL;
+            memcpy(sub_pes, pes, sizeof(*sub_pes));
+
+            sub_st = av_new_stream(pes->stream, pes->pid);
+            if (!sub_st) {
+                av_free(sub_pes);
+                return NULL;
+            }
+
+            av_set_pts_info(sub_st, 33, 1, 90000);
+            sub_st->priv_data = sub_pes;
+            sub_st->codec->codec_type = CODEC_TYPE_AUDIO;
+            sub_st->codec->codec_id   = CODEC_ID_AC3;
+            sub_st->need_parsing = AVSTREAM_PARSE_FULL;
+            sub_pes->sub_st = pes->sub_st = sub_st;
+        }
+    }
     if (st->codec->codec_id == CODEC_ID_NONE)
         mpegts_find_stream_type(st, pes->stream_type, MISC_types);
 
@@ -730,6 +757,11 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
                 break;
             }
             p = desc_end;
+
+            if (prog_reg_desc == AV_RL32("HDMV") && stream_type == 0x83 && pes->sub_st) {
+                av_program_add_stream_index(ts->stream, h->id, pes->sub_st->index);
+                pes->sub_st->codec->codec_tag = st->codec->codec_tag;
+            }
         }
         p = desc_list_end;
     }
@@ -873,7 +905,11 @@ static void new_pes_packet(PESContext *pes, AVPacket *pkt)
     pkt->size = pes->data_index;
     memset(pkt->data+pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
 
-    pkt->stream_index = pes->st->index;
+    // Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID
+    if (pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76)
+        pkt->stream_index = pes->sub_st->index;
+    else
+        pkt->stream_index = pes->st->index;
     pkt->pts = pes->pts;
     pkt->dts = pes->dts;
     /* store position of first TS packet of this PES packet */
@@ -1005,7 +1041,7 @@ static int mpegts_push_data(MpegTSFilter *filter,
             buf_size -= len;
             if (pes->data_index == pes->pes_header_size) {
                 const uint8_t *r;
-                unsigned int flags;
+                unsigned int flags, pes_ext, skip;
 
                 flags = pes->header[7];
                 r = pes->header + 9;
@@ -1020,6 +1056,20 @@ static int mpegts_push_data(MpegTSFilter *filter,
                     pes->dts = get_pts(r);
                     r += 5;
                 }
+                pes->extended_stream_id = -1;
+                if (flags & 0x01) { /* PES extension */
+                    pes_ext = *r++;
+                    /* Skip PES private data, program packet sequence counter and P-STD buffer */
+                    skip = (pes_ext >> 4) & 0xb;
+                    skip += skip & 0x9;
+                    r += skip;
+                    if ((pes_ext & 0x41) == 0x01 &&
+                        (r + 2) <= (pes->header + pes->pes_header_size)) {
+                        /* PES extension 2 */
+                        if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0)
+                            pes->extended_stream_id = r[1];
+                    }
+                }
 
                 /* we got the full header. We parse it and get the payload */
                 pes->state = MPEGTS_PAYLOAD;