OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / libhb / stream.c
index 9768d95..156fe99 100755 (executable)
@@ -30,10 +30,13 @@ typedef struct {
     int codec;          /* HB worker object id of codec */
     int codec_param;    /* param for codec (usually ffmpeg codec id) */
     const char* name;   /* description of type */
+    int extra_hdr;      /* needs a substream header added to PS pack */
 } stream2codec_t;
 
 #define st(id, kind, codec, codec_param, name) \
- [id] = { kind, codec, codec_param, name }
+ [id] = { kind, codec, codec_param, name, 0 }
+#define se(id, kind, codec, codec_param, name) \
+ [id] = { kind, codec, codec_param, name, 1 }
 
 static const stream2codec_t st2codec[256] = {
     st(0x01, V, WORK_DECMPEG2,     0,              "MPEG1"),
@@ -59,18 +62,18 @@ static const stream2codec_t st2codec[256] = {
 
     st(0x1b, V, WORK_DECAVCODECV,  CODEC_ID_H264,  "H.264"),
 
-    st(0x80, U, 0,                 0,              "DigiCipher II Video"),
-    st(0x81, A, HB_ACODEC_AC3,     0,              "AC-3"),
-    st(0x82, A, HB_ACODEC_MPGA,    CODEC_ID_DTS,   "HDMV DTS"),
+    //st(0x80, U, 0,                 0,              "DigiCipher II Video"),
+    se(0x81, A, HB_ACODEC_AC3,     0,              "AC-3"),
+    se(0x82, A, HB_ACODEC_DCA,     0,              "HDMV DTS"),
     st(0x83, A, HB_ACODEC_LPCM,    0,              "LPCM"),
     st(0x84, A, 0,                 0,              "SDDS"),
     st(0x85, U, 0,                 0,              "ATSC Program ID"),
     st(0x86, U, 0,                 0,              "SCTE 35 splice info"),
     st(0x87, A, 0,                 0,              "E-AC-3"),
 
-    st(0x8a, A, HB_ACODEC_DCA,     0,              "DTS"),
+    se(0x8a, A, HB_ACODEC_DCA,     0,              "DTS"),
 
-    st(0x91, A, HB_ACODEC_AC3,     0,              "AC-3"),
+    se(0x91, A, HB_ACODEC_AC3,     0,              "AC-3"),
     st(0x92, U, 0,                 0,              "Subtitle"),
 
     st(0x94, A, 0,                 0,              "SDDS"),
@@ -116,6 +119,8 @@ struct hb_stream_s
     uint8_t *fwrite_buf;        /* PS buffer (set by hb_ts_stream_decode) */
     uint8_t *fwrite_buf_orig;   /* PS buffer start (set by hb_ts_stream_decode) */
 
+    uint8_t need_keyframe;
+
     /*
      * Stuff before this point is dynamic state updated as we read the
      * stream. Stuff after this point is stream description state that
@@ -130,6 +135,7 @@ struct hb_stream_s
 
     uint8_t ts_streamid[kMaxNumberDecodeStreams];
     uint8_t ts_stream_type[kMaxNumberDecodeStreams];
+    uint8_t ts_extra_hdr[kMaxNumberDecodeStreams];
 
     char    *path;
     FILE    *file_handle;
@@ -139,6 +145,7 @@ struct hb_stream_s
     AVFormatContext *ffmpeg_ic;
     AVPacket *ffmpeg_pkt;
     double ffmpeg_tsconv[MAX_STREAMS];
+    uint8_t ffmpeg_video_id;
 
     struct {
         int lang_code;
@@ -366,6 +373,10 @@ static int hb_stream_get_type(hb_stream_t *stream)
             stream->packetsize = psize;
             stream->hb_stream_type = transport;
             hb_ts_stream_init(stream);
+            if ( !stream->ts_number_video_pids || !stream->ts_number_audio_pids )
+            {
+                return 0;
+            }
             return 1;
         }
         if ( hb_stream_check_for_dvd_ps(buf) != 0 )
@@ -466,6 +477,13 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
                 d->ts_buf[i] = malloc( HB_DVD_READ_BUFFER_SIZE );
             }
             hb_stream_seek( d, 0. );
+
+            if ( d->packetsize == 188 )
+            {
+                // Assume that an over-the-air transport stream can lose PCR
+                // packets and try to filter out the timing inconsistencies.
+                title->flaky_clock = 1;
+            }
         }
         return d;
     }
@@ -566,6 +584,7 @@ static void hb_stream_delete_audio_entry(hb_stream_t *stream, int indx)
     {
         stream->ts_audio_pids[indx] = stream->ts_audio_pids[i];
         stream->ts_stream_type[1 + indx] = stream->ts_stream_type[1+i];
+        stream->ts_extra_hdr[1 + indx] = stream->ts_extra_hdr[1+i];
         stream->ts_streamid[1 + indx] = stream->ts_streamid[1 + i];
         ++indx;
     }
@@ -1062,6 +1081,7 @@ int hb_stream_seek( hb_stream_t * src_stream, float f )
         // We need to drop the current decoder output and move
         // forwards to the next transport stream packet.
         hb_ts_stream_reset(src_stream);
+        src_stream->need_keyframe = ( f != 0 );
     }
     else if ( src_stream->hb_stream_type == program )
     {
@@ -1182,6 +1202,7 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
     if ( st2codec[stype].kind == A && st2codec[stype].codec )
     {
         stream->ts_streamid[1 + aud_pid_index] = audio->id;
+        stream->ts_extra_hdr[1 + aud_pid_index] = st2codec[stype].extra_hdr;
         audio->config.in.codec = st2codec[stype].codec;
         audio->config.in.codec_param = st2codec[stype].codec_param;
                set_audio_description( audio,
@@ -1496,6 +1517,10 @@ int decode_program_map(hb_stream_t* stream)
         }
 
 
+        if ( index_of_pid( elementary_PID, stream ) < 0 )
+        {
+            // already have this pid - do nothing
+        }
         if (stream->ts_number_video_pids == 0 && st2codec[stream_type].kind == V )
         {
             stream->ts_video_pids[0] = elementary_PID;
@@ -1556,19 +1581,6 @@ static int build_program_map(const uint8_t *buf, hb_stream_t *stream)
 
     // Get pointer length - only valid in packets with a start flag
     int pointer_len = 0;
-       if (start && stream->pmt_info.reading)
-       {
-               // We just finished a bunch of packets - parse the program map details
-               int decode_ok = 0;
-               if (stream->pmt_info.tablebuf[0] == 0x02)
-                       decode_ok = decode_program_map(stream);
-               free(stream->pmt_info.tablebuf);
-               stream->pmt_info.tablebuf = NULL;
-               stream->pmt_info.tablepos = 0;
-        stream->pmt_info.reading = 0;
-        if (decode_ok)
-                       return decode_ok;
-       }
 
        if (start)
        {
@@ -1594,6 +1606,27 @@ static int build_program_map(const uint8_t *buf, hb_stream_t *stream)
             memcpy(stream->pmt_info.tablebuf + stream->pmt_info.tablepos, buf + 4 + adapt_len + pointer_len, amount_to_copy);
             stream->pmt_info.tablepos += amount_to_copy;
     }
+    if (stream->pmt_info.tablepos > 3)
+    {
+        // We have enough to check the section length
+        int length;
+        length = ((stream->pmt_info.tablebuf[1] << 8) + 
+                  stream->pmt_info.tablebuf[2]) & 0xFFF;
+        if (stream->pmt_info.tablepos > length + 1)
+        {
+            // We just finished a bunch of packets - parse the program map details
+            int decode_ok = 0;
+            if (stream->pmt_info.tablebuf[0] == 0x02)
+                decode_ok = decode_program_map(stream);
+            free(stream->pmt_info.tablebuf);
+            stream->pmt_info.tablebuf = NULL;
+            stream->pmt_info.tablepos = 0;
+            stream->pmt_info.reading = 0;
+            if (decode_ok)
+                return decode_ok;
+        }
+
+    }
 
     return 0;
 }
@@ -1719,6 +1752,7 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
        for (;;)
        {
         const uint8_t *buf = next_packet( stream );
+
         if ( buf == NULL )
         {
                        hb_log("hb_ts_stream_find_pids - end of file");
@@ -1748,7 +1782,8 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
                        // on the first pat entry for which we find a matching program map PID.  The ideal solution would
                        // be to build a title choice popup from the PAT program number details and then select from
                        // their - but right now the API's not capable of that.
-                       if (pid == stream->pat_info[pat_index].program_map_PID)
+            if (stream->pat_info[pat_index].program_number != 0 &&
+                pid == stream->pat_info[pat_index].program_map_PID)
                        {
                          if (build_program_map(buf, stream) > 0)
                                break;
@@ -1758,6 +1793,14 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
                if ((stream->ts_number_video_pids > 0) && (stream->ts_number_audio_pids > 0))
                  break;
        }
+    // XXX - until we figure out how to handle VC1 just bail when we find it so
+    // that ffmpeg will claim the input stream.
+    if ( stream->ts_stream_type[0] == 0xea )
+    {
+        stream->ts_number_video_pids = 0;
+        stream->ts_number_audio_pids = 0;
+        return;
+    }
 
        hb_log("hb_ts_stream_find_pids - found the following PIDS");
        hb_log("    Video PIDS : ");
@@ -1869,7 +1912,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
     // we always ship a PACK header plus all the data in our demux buf.
     // AC3 audio also always needs its substream header.
     len = 14 + stream->ts_pos[curstream];
-    if ( stream->ts_stream_type[curstream] == 0x81)
+    if ( stream->ts_extra_hdr[curstream] )
     {
         len += 4;
     }
@@ -1910,7 +1953,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
         tdat[3] = stream->ts_streamid[curstream];
 
         uint16_t plen = stream->ts_pos[curstream] - 6;
-        if ( stream->ts_stream_type[curstream] == 0x81)
+        if ( stream->ts_extra_hdr[curstream] )
         {
             // We have to add an AC3 header in front of the data. Add its
             // size to the PES packet length.
@@ -1946,7 +1989,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
     {
         // data without a PES start header needs a simple 'continuation'
         // PES header. AC3 audio also needs its substream header.
-        if ( stream->ts_stream_type[curstream] != 0x81)
+        if ( stream->ts_extra_hdr[curstream] == 0 )
         {
             make_pes_header(stream, stream->ts_pos[curstream],
                             stream->ts_streamid[curstream]);
@@ -2035,6 +2078,14 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len )
                     // h.264 IDR picture start
                     return 1;
 
+                if ( stream->packetsize == 192 )
+                {
+                    // m2ts files have idr frames so keep looking for one
+                    continue;
+                }
+
+                // h226 in ts files (ATSC or DVB video) often seem to be
+                // missing IDR frames so look for at least an I
                 if ( nal_type == 0x01 )
                 {
                     // h.264 slice: has to be start MB 0 & type I (2, 4, 7 or 9)
@@ -2135,11 +2186,11 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
             stream->ts_lastpcr = pcr;
         }
 
-               if ( pcr == -1 )
-               {
-            // don't accumulate data until we get a pcr
-                   continue;
-               }
+        // If we don't have a pcr yet, the right thing to do here would
+        // be a 'continue' so we don't process anything until we have a
+        // clock reference. Unfortunately the HD Home Run appears to null
+        // out the pcr field of some streams so we keep going & substitute
+        // the video stream dts for the pcr when there's no pcr.
 
                // Get continuity
         // Continuity only increments for adaption values of 0x3 or 0x01
@@ -2159,6 +2210,7 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
                 continue;
             }
             if ( !start && (stream->ts_streamcont[curstream] != -1) &&
+                 stream->ts_foundfirst[curstream] &&
                  (continuity != ( (stream->ts_streamcont[curstream] + 1) & 0xf ) ) )
                        {
                                ts_err( stream, curstream,  "continuity error: got %d expected %d",
@@ -2194,16 +2246,42 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
             {
                 if ( !stream->ts_foundfirst[0] )
                 {
-                    if ( !isIframe( stream, buf, adapt_len ) )
+                    if ( stream->need_keyframe )
                     {
-                        // didn't find an I frame
-                        continue;
+                        if ( !isIframe( stream, buf, adapt_len ) )
+                        {
+                            // didn't find an I frame
+                            continue;
+                        }
+                        stream->need_keyframe = 0;
                     }
                     stream->ts_foundfirst[0] = 1;
                 }
                 ++stream->frames;
+
+                // if we don't have a pcr yet use the dts from this frame
+                if ( pcr == -1 )
+                {
+                    // PES must begin with an mpeg start code & contain
+                    // a DTS or PTS.
+                    const uint8_t *pes = buf + adapt_len + 4;
+                    if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ||
+                         ( pes[7] >> 6 ) == 0 )
+                    {
+                        continue;
+                    }
+                    // if we have a dts use it otherwise use the pts
+                    pes += (pes[7] & 0x40)? 14 : 9;
+
+                    pcr = ( (uint64_t)(pes[0] & 0xe ) << 29 );
+                    pcr |= ( pes[1] << 22 ) |
+                           ( ( pes[2] >> 1 ) << 15 ) |
+                           ( pes[3] << 7 ) |
+                           ( pes[4] >> 1 );
+                    stream->ts_nextpcr = pcr;
+                }
             }
-                       else if ( ! stream->ts_foundfirst[curstream] )
+            else if ( ! stream->ts_foundfirst[curstream] )
             {
                 // start other streams only after first video frame found.
                 if ( ! stream->ts_foundfirst[0] )
@@ -2267,6 +2345,7 @@ static void hb_ts_stream_reset(hb_stream_t *stream)
 
     stream->frames = 0;
     stream->errors = 0;
+    stream->need_keyframe = 0;
     stream->last_error_frame = -10000;
     stream->last_error_count = 0;
 
@@ -2282,7 +2361,7 @@ static void ffmpeg_add_codec( hb_stream_t *stream, int stream_index )
     // read the first packet.
     AVCodecContext *context = stream->ffmpeg_ic->streams[stream_index]->codec;
     context->workaround_bugs = FF_BUG_AUTODETECT;
-    context->error_resilience = 1;
+    context->error_recognition = 1;
     context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
     AVCodec *codec = avcodec_find_decoder( context->codec_id );
     avcodec_open( context, codec );
@@ -2407,7 +2486,6 @@ static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title )
         // indexed its stream so we need to remap them so they point
         // to this stream.
         ffmpeg_remap_stream( stream, title );
-        ffmpeg_seek( stream, 0. );
         av_log_set_level( AV_LOG_ERROR );
     }
     else
@@ -2484,6 +2562,10 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
         {
             audio->config.in.codec = HB_ACODEC_AC3;
         }
+        else if ( codec->codec_id == CODEC_ID_DTS )
+        {
+            audio->config.in.codec = HB_ACODEC_DCA;
+        }
         else
         {
             audio->config.in.codec = HB_ACODEC_FFMPEG;
@@ -2544,6 +2626,7 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream )
              title->video_codec == 0 )
         {
             title->video_id = i;
+            stream->ffmpeg_video_id = i;
 
             // We have to use the 'internal' avcodec decoder because
             // it needs to share the codec context from this video
@@ -2575,6 +2658,7 @@ static int64_t av_to_hb_pts( int64_t pts, double conv_factor )
 static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf )
 {
     int err;
+  again:
     if ( ( err = av_read_frame( stream->ffmpeg_ic, stream->ffmpeg_pkt )) < 0 )
     {
         // XXX the following conditional is to handle avi files that
@@ -2601,6 +2685,13 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf )
     {
         if ( stream->ffmpeg_pkt->size > buf->alloc )
         {
+            // sometimes we get absurd sizes from ffmpeg
+            if ( stream->ffmpeg_pkt->size >= (1 << 25) )
+            {
+                hb_log( "ffmpeg_read: pkt too big: %d bytes", stream->ffmpeg_pkt->size );
+                av_free_packet( stream->ffmpeg_pkt );
+                return ffmpeg_read( stream, buf );
+            }
             // need to expand buffer
             hb_buffer_realloc( buf, stream->ffmpeg_pkt->size );
         }
@@ -2608,6 +2699,24 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf )
         buf->size = stream->ffmpeg_pkt->size;
     }
     buf->id = stream->ffmpeg_pkt->stream_index;
+    if ( buf->id == stream->ffmpeg_video_id )
+    {
+        if ( stream->need_keyframe &&
+             stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->codec_id == 
+               CODEC_ID_VC1 )
+        {
+            // XXX the VC1 codec doesn't seek to key frames so to get previews
+            // we do it ourselves here. The decoder gets messed up if it
+            // doesn't get a SEQ header first so we consider that to be a key frame.
+            uint8_t *pkt = stream->ffmpeg_pkt->data;
+            if ( pkt[0] || pkt[1] || pkt[2] != 1 || pkt[3] != 0x0f )
+            {
+                goto again;
+            }
+            stream->need_keyframe = 0;
+        }
+        ++stream->frames;
+    }
 
     // if we haven't done it already, compute a conversion factor to go
     // from the ffmpeg timebase for the stream to HB's 90KHz timebase.
@@ -2633,6 +2742,14 @@ static int ffmpeg_seek( hb_stream_t *stream, float frac )
 {
     AVFormatContext *ic = stream->ffmpeg_ic;
     int64_t pos = (double)ic->duration * (double)frac;
-    av_seek_frame( ic, -1, pos, pos? 0 : AVSEEK_FLAG_BACKWARD );
+    if ( pos )
+    {
+        av_seek_frame( ic, -1, pos, 0 );
+        stream->need_keyframe = 1;
+    }
+    else
+    {
+        av_seek_frame( ic, -1, pos, AVSEEK_FLAG_BACKWARD );
+    }
     return 1;
 }