OSDN Git Service

Adds an hb_deep_log() function for multiple levels of debugging verbosity. Level...
[handbrake-jp/handbrake-jp-git.git] / libhb / stream.c
index 930af30..dd5c709 100755 (executable)
@@ -4,15 +4,16 @@
    Homepage: <http://handbrake.fr/>.
    It may be used under the terms of the GNU General Public License. */
 
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
 #include "hb.h"
 #include "lang.h"
 #include "a52dec/a52.h"
 #include "libavcodec/avcodec.h"
 #include "libavformat/avformat.h"
 
-#include <string.h>
-#include <ctype.h>
-
 #define min(a, b) a < b ? a : b
 
 /*
@@ -136,6 +137,8 @@ struct hb_stream_s
     hb_title_t *title;
 
     AVFormatContext *ffmpeg_ic;
+    AVPacket *ffmpeg_pkt;
+    double ffmpeg_tsconv[MAX_STREAMS];
 
     struct {
         int lang_code;
@@ -463,6 +466,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;
     }
@@ -2132,11 +2142,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
@@ -2156,6 +2166,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",
@@ -2199,8 +2210,30 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
                     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] )
@@ -2279,7 +2312,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 );
@@ -2337,6 +2370,9 @@ static int ffmpeg_codec_param( hb_stream_t *stream, int stream_index )
 // (the original scan stream was closed and no longer exists).
 static void ffmpeg_remap_stream( hb_stream_t *stream, hb_title_t *title )
 {
+    // tell ffmpeg we want a pts on every frame it returns
+    stream->ffmpeg_ic->flags |= AVFMT_FLAG_GENPTS;
+
     // all the video & audio came from the same stream so remapping
     // the video's stream slot takes care of everything.
     int slot = title->video_codec_param & (ffmpeg_sl_size - 1);
@@ -2392,6 +2428,8 @@ static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title )
 
     stream->ffmpeg_ic = ic;
     stream->hb_stream_type = ffmpeg;
+    stream->ffmpeg_pkt = malloc(sizeof(*stream->ffmpeg_pkt));
+    av_init_packet( stream->ffmpeg_pkt );
 
     if ( title )
     {
@@ -2440,6 +2478,11 @@ static void ffmpeg_close( hb_stream_t *d )
         av_close_input_file( ffmpeg_deferred_close );
     }
     ffmpeg_deferred_close = d->ffmpeg_ic;
+    if ( d->ffmpeg_pkt != NULL )
+    {
+        free( d->ffmpeg_pkt );
+        d->ffmpeg_pkt = NULL;
+    }
 }
 
 static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
@@ -2561,27 +2604,58 @@ 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 )
 {
-    AVPacket pkt;
+    int err;
+    if ( ( err = av_read_frame( stream->ffmpeg_ic, stream->ffmpeg_pkt )) < 0 )
+    {
+        // XXX the following conditional is to handle avi files that
+        // use M$ 'packed b-frames' and occasionally have negative
+        // sizes for the null frames these require.
+        if ( err != AVERROR_NOMEM || stream->ffmpeg_pkt->size >= 0 )
+            // eof
+            return 0;
+    }
+    if ( stream->ffmpeg_pkt->size <= 0 )
+    {
+        // M$ "invalid and inefficient" packed b-frames require 'null frames'
+        // following them to preserve the timing (since the packing puts two
+        // or more frames in what looks like one avi frame). The contents and
+        // size of these null frames are ignored by the ff_h263_decode_frame
+        // as long as they're < 20 bytes. We need a positive size so we use
+        // one byte if we're given a zero or negative size. We don't know
+        // if the pkt data points anywhere reasonable so we just stick a
+        // byte of zero in our outbound buf.
+        buf->size = 1;
+        *buf->data = 0;
+    }
+    else
+    {
+        if ( stream->ffmpeg_pkt->size > buf->alloc )
+        {
+            // need to expand buffer
+            hb_buffer_realloc( buf, stream->ffmpeg_pkt->size );
+        }
+        memcpy( buf->data, stream->ffmpeg_pkt->data, stream->ffmpeg_pkt->size );
+        buf->size = stream->ffmpeg_pkt->size;
+    }
+    buf->id = stream->ffmpeg_pkt->stream_index;
 
-    if ( av_read_frame( stream->ffmpeg_ic, &pkt ) < 0 )
+    // 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.
+    double tsconv = stream->ffmpeg_tsconv[stream->ffmpeg_pkt->stream_index];
+    if ( ! tsconv )
     {
-        return 0;
+        AVStream *s = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index];
+        tsconv = 90000. * (double)s->time_base.num / (double)s->time_base.den;
+        stream->ffmpeg_tsconv[stream->ffmpeg_pkt->stream_index] = tsconv;
+    }
+
+    buf->start = av_to_hb_pts( stream->ffmpeg_pkt->pts, tsconv );
+    buf->renderOffset = av_to_hb_pts( stream->ffmpeg_pkt->dts, tsconv );
+    if ( buf->renderOffset >= 0 && buf->start == -1 )
+    {
+        buf->start = buf->renderOffset;
     }
-    if ( pkt.size > buf->alloc )
-    {
-        // need to expand buffer
-        hb_buffer_realloc( buf, pkt.size );
-    }
-    memcpy( buf->data, pkt.data, pkt.size );
-    buf->id = pkt.stream_index;
-    buf->size = pkt.size;
-    int64_t pts = pkt.pts != AV_NOPTS_VALUE? pkt.pts : 
-                         pkt.dts != AV_NOPTS_VALUE? pkt.dts : -1;
-    buf->start = av_to_hb_pts( pts,
-                  av_q2d(stream->ffmpeg_ic->streams[pkt.stream_index]->time_base)*90000. );
-    buf->renderOffset = av_to_hb_pts( pkt.pts,
-                  av_q2d(stream->ffmpeg_ic->streams[pkt.stream_index]->time_base)*90000. );
-    av_free_packet( &pkt );
+    av_free_packet( stream->ffmpeg_pkt );
     return 1;
 }