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
/*
hb_title_t *title;
AVFormatContext *ffmpeg_ic;
+ AVPacket *ffmpeg_pkt;
+ double ffmpeg_tsconv[MAX_STREAMS];
struct {
int lang_code;
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;
}
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
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",
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] )
// 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 );
// (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);
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 )
{
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 )
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;
}