summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2011-07-27 15:09:49 +0000
committerjstebbins <[email protected]>2011-07-27 15:09:49 +0000
commit7a47aa6da23391fa2f4f0bc46eb4f814e4590661 (patch)
treec464eed037523e1fee104e78b68c4a75e71e478d
parente71ac4bb36c9cec69ed4cb45a48e179d662fa828 (diff)
libhb: fix or simplify several hacks involved with Libav support
For files that are demuxed by Libav, we must share the format context with the decoder iso that it can obtain the codec context for each stream. The code that did this was very convoluted and difficult to understand. It is simplified by simply passing the context in hb_title_t. Reader was closing stream files before the decoder was finished with the context. This created the need to delay the actual close and cache the context. Changed reader so it behaves more like the rest of handbrake's work objects which lets us explicitly close after the decoders are finished. Libav does some probing of the file when av_find_stream_info is called. This probing leaves the format context in a bad state for some files and causes subsequent reads or seeks to misbehave. So open 2 contexts in ffmpeg_open. One is used only for probing, and the other only for reading. decavcodec.c had 2 separate decoders for files demuxed by hb and files demuxed by Libav. They have been combined and simplified. Previously, it was not possible to decode one source audio track multiple times in order to fan it out to multiple output tracks if the file is demuxed by Libav. We were using the codec context from the format context. Since there is only one of these for each stream, we could only do one decode for each stream. Use avcodec_copy_context to make copies of the codec context and allow multiple decodes. This allows removal of a lot of special case code for Libav streams that was necessary to duplicate the output of the decoder. Patch Libav's mkv demux to fix a seek problem. This has been pushed upstreams, so the next time we update Libav, we must remove this patch. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4141 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--contrib/ffmpeg/A05-mkv-seek.patch20
-rw-r--r--libhb/batch.c9
-rw-r--r--libhb/common.c6
-rw-r--r--libhb/common.h12
-rw-r--r--libhb/decavcodec.c879
-rw-r--r--libhb/decmpeg2.c1
-rw-r--r--libhb/hb.c10
-rw-r--r--libhb/internal.h14
-rw-r--r--libhb/reader.c157
-rw-r--r--libhb/scan.c133
-rw-r--r--libhb/stream.c345
-rw-r--r--libhb/work.c59
12 files changed, 612 insertions, 1033 deletions
diff --git a/contrib/ffmpeg/A05-mkv-seek.patch b/contrib/ffmpeg/A05-mkv-seek.patch
new file mode 100644
index 000000000..7ac47014a
--- /dev/null
+++ b/contrib/ffmpeg/A05-mkv-seek.patch
@@ -0,0 +1,20 @@
+diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
+index 57a8f62..60f6c69 100644
+--- a/libavformat/matroskadec.c
++++ b/libavformat/matroskadec.c
+@@ -1903,6 +1903,7 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
+
+ if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
+ avio_seek(s->pb, st->index_entries[st->nb_index_entries-1].pos, SEEK_SET);
++ matroska->current_id = 0;
+ while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
+ matroska_clear_queue(matroska);
+ if (matroska_parse_cluster(matroska) < 0)
+@@ -1931,6 +1932,7 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
+ }
+
+ avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET);
++ matroska->current_id = 0;
+ matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
+ matroska->skip_to_timecode = st->index_entries[index].timestamp;
+ matroska->done = 0;
diff --git a/libhb/batch.c b/libhb/batch.c
index e96520a67..9e2f11a06 100644
--- a/libhb/batch.c
+++ b/libhb/batch.c
@@ -95,11 +95,16 @@ hb_title_t * hb_batch_title_scan( hb_batch_t * d, int t )
if ( filename == NULL )
return NULL;
- stream = hb_stream_open( filename, 0 );
+ hb_log( "batch: scanning %s", filename );
+ title = hb_title_init( filename, 0 );
+ stream = hb_stream_open( filename, title, 1 );
if ( stream == NULL )
+ {
+ hb_title_close( &title );
return NULL;
+ }
- title = hb_stream_title_scan( stream );
+ title = hb_stream_title_scan( stream, title );
hb_stream_close( &stream );
if ( title != NULL )
{
diff --git a/libhb/common.c b/libhb/common.c
index 3497a24f4..5cd2867df 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -1058,6 +1058,8 @@ hb_title_t * hb_title_init( char * path, int index )
t->video_id = 0xE0;
t->video_codec = WORK_DECMPEG2;
t->angle_count = 1;
+ t->pixel_aspect_width = 1;
+ t->pixel_aspect_height = 1;
return t;
}
@@ -1077,10 +1079,6 @@ void hb_title_close( hb_title_t ** _t )
while( ( audio = hb_list_item( t->list_audio, 0 ) ) )
{
- if ( audio->priv.ff_audio_list != NULL )
- {
- hb_list_close( &audio->priv.ff_audio_list );
- }
hb_list_rem( t->list_audio, audio );
free( audio );
}
diff --git a/libhb/common.h b/libhb/common.h
index 41463c135..48a4812bb 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -299,8 +299,6 @@ struct hb_job_s
hb_fifo_t * fifo_render; /* Raw pictures, scaled */
hb_fifo_t * fifo_mpeg4; /* MPEG-4 video ES */
- hb_thread_t * reader;
-
hb_list_t * list_work;
hb_esconfig_t config;
@@ -324,7 +322,6 @@ struct hb_job_s
#define HB_ACODEC_FFMPEG 0x00020000
#define HB_ACODEC_DCA_HD 0x00040000
#define HB_ACODEC_FF_MASK 0x00060000
-#define HB_ACODEC_FF_I_FLAG 0x80000000
#define HB_ACODEC_PASS_FLAG 0x40000000
#define HB_ACODEC_PASS_MASK (HB_ACODEC_DCA_HD | HB_ACODEC_AC3 | HB_ACODEC_DCA)
#define HB_ACODEC_AC3_PASS (HB_ACODEC_AC3 | HB_ACODEC_PASS_FLAG)
@@ -465,8 +462,6 @@ struct hb_audio_s
hb_esconfig_t config;
hb_mux_data_t * mux_data;
hb_fifo_t * scan_cache;
-
- hb_list_t * ff_audio_list;
} priv;
};
#endif
@@ -599,6 +594,7 @@ struct hb_title_s
uint64_t block_end;
uint64_t block_count;
int angle_count;
+ void *opaque_priv;
/* Visual-friendly duration */
int hours;
@@ -714,7 +710,6 @@ typedef struct hb_work_info_s
int height;
int pixel_aspect_width;
int pixel_aspect_height;
- double aspect;
};
struct { // info only valid for audio decoders
int channel_layout;
@@ -785,10 +780,8 @@ extern hb_work_object_t hb_encx264;
extern hb_work_object_t hb_enctheora;
extern hb_work_object_t hb_deca52;
extern hb_work_object_t hb_decdca;
-extern hb_work_object_t hb_decavcodec;
+extern hb_work_object_t hb_decavcodeca;
extern hb_work_object_t hb_decavcodecv;
-extern hb_work_object_t hb_decavcodecvi;
-extern hb_work_object_t hb_decavcodecai;
extern hb_work_object_t hb_declpcm;
extern hb_work_object_t hb_encfaac;
extern hb_work_object_t hb_enclame;
@@ -797,6 +790,7 @@ extern hb_work_object_t hb_muxer;
extern hb_work_object_t hb_encca_aac;
extern hb_work_object_t hb_encca_haac;
extern hb_work_object_t hb_encavcodeca;
+extern hb_work_object_t hb_reader;
#define FILTER_OK 0
#define FILTER_DELAY 1
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index b2f5af8c5..e52021655 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -8,51 +8,27 @@
(libavcodec & small parts of libavformat). It contains four Handbrake
"work objects":
- decavcodec connects HB to an ffmpeg audio decoder
+ decavcodeca connects HB to an ffmpeg audio decoder
decavcodecv connects HB to an ffmpeg video decoder
(Two different routines are needed because the ffmpeg library
has different decoder calling conventions for audio & video.
- The audio decoder should have had its name changed to "decavcodeca"
- but I got lazy.) These work objects are self-contained & follow all
+ These work objects are self-contained & follow all
of HB's conventions for a decoder module. They can be used like
any other HB decoder (deca52, decmpeg2, etc.).
- decavcodecai "internal" (incestuous?) version of decavcodec
- decavcodecvi "internal" (incestuous?) version of decavcodecv
-
- These routine are functionally equivalent to the routines above but
- can only be used by the ffmpeg-based stream reader in libhb/stream.c.
- The reason they exist is because the ffmpeg library leaves some of
- the information needed by the decoder in the AVStream (the data
- structure used by the stream reader) and we need to retrieve it
- to successfully decode frames. But in HB the reader and decoder
- modules are in completely separate threads and nothing goes between
- them but hb_buffers containing frames to be decoded. I.e., there's
- no easy way for the ffmpeg stream reader to pass a pointer to its
- AVStream over to the ffmpeg video or audio decoder. So the *i work
- objects use a private back door to the stream reader to get access
- to the AVStream (routines hb_ffmpeg_avstream and hb_ffmpeg_context)
- and the codec_param passed to these work objects is the key to this
- back door (it's basically an index that allows the correct AVStream
- to be retrieved).
-
- The normal & *i objects share a lot of code (the basic frame decoding
- and bitstream info code is factored out into subroutines that can be
- called by either) but the top level routines of the *i objects
- (decavcodecviWork, decavcodecviInfo, etc.) are different because:
- 1) they *have* to use the AVCodecContext that's contained in the
- reader's AVStream rather than just allocating & using their own,
- 2) the Info routines have access to stuff kept in the AVStream in addition
- to stuff kept in the AVCodecContext. This shouldn't be necessary but
- crucial information like video frame rate that should be in the
- AVCodecContext is either missing or wrong in the version of ffmpeg
- we're currently using.
-
- A consequence of the above is that the non-i work objects *can't* use
- information from the AVStream because there isn't one - they get their
- data from either the dvd reader or the mpeg reader, not the ffmpeg stream
- reader. That means that they have to make up for deficiencies in the
+ These decoders handle 2 kinds of input. Streams that are demuxed
+ by HandBrake and streams that are demuxed by libavformat. In the
+ case of streams that are demuxed by HandBrake, there is an extra
+ parse step required that happens in decodeVideo and decavcodecaWork.
+ In the case of streams that are demuxed by libavformat, there is context
+ information that we need from the libavformat. This information is
+ propagated from hb_stream_open to these decoders through title->opaque_priv.
+
+ A consequence of the above is that the streams that are demuxed by HandBrake
+ *can't* use information from the AVStream because there isn't one - they
+ get their data from either the dvd reader or the mpeg reader, not the ffmpeg
+ stream reader. That means that they have to make up for deficiencies in the
AVCodecContext info by using stuff kept in the HB "title" struct. It
also means that ffmpeg codecs that randomly scatter state needed by
the decoder across both the AVCodecContext & the AVStream (e.g., the
@@ -64,23 +40,23 @@
#include "downmix.h"
#include "libavcodec/audioconvert.h"
+static void compute_frame_duration( hb_work_private_t *pv );
static void flushDelayQueue( hb_work_private_t *pv );
-static int decavcodecInit( hb_work_object_t *, hb_job_t * );
-static int decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+static int decavcodecaInit( hb_work_object_t *, hb_job_t * );
+static int decavcodecaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
static void decavcodecClose( hb_work_object_t * );
-static void decavcodeciClose( hb_work_object_t * );
-static int decavcodecInfo( hb_work_object_t *, hb_work_info_t * );
-static int decavcodecBSInfo( hb_work_object_t *, const hb_buffer_t *, hb_work_info_t * );
+static int decavcodecaInfo( hb_work_object_t *, hb_work_info_t * );
+static int decavcodecaBSInfo( hb_work_object_t *, const hb_buffer_t *, hb_work_info_t * );
-hb_work_object_t hb_decavcodec =
+hb_work_object_t hb_decavcodeca =
{
WORK_DECAVCODEC,
"Audio decoder (libavcodec)",
- decavcodecInit,
- decavcodecWork,
+ decavcodecaInit,
+ decavcodecaWork,
decavcodecClose,
- decavcodecInfo,
- decavcodecBSInfo
+ decavcodecaInfo,
+ decavcodecaBSInfo
};
#define HEAP_SIZE 8
@@ -95,11 +71,14 @@ typedef struct {
struct hb_work_private_s
{
hb_job_t *job;
+ hb_title_t *title;
AVCodecContext *context;
AVCodecParserContext *parser;
+ int video_codec_opened;
hb_list_t *list;
- hb_list_t *ff_audio_list;
double duration; // frame duration (for video)
+ double field_duration; // field duration (for video)
+ int frame_duration_set; // Indicates valid timing was found in stream
double pts_next; // next pts we expect to generate
int64_t chap_time; // time of next chap mark (if new_chap != 0)
int new_chap; // output chapter mark pending
@@ -166,34 +145,36 @@ static void heap_push( pts_heap_t *heap, int64_t v )
// stick the new value on the bottom of the heap then bubble it
// up to its correct spot.
- int child = heap->nheap;
- while (child > 1) {
- int parent = child >> 1;
- if (heap->h[parent] <= v)
- break;
- // move parent down
- int64_t hp = heap->h[parent];
- heap->h[child] = hp;
- child = parent;
- }
- heap->h[child] = v;
+ int child = heap->nheap;
+ while (child > 1) {
+ int parent = child >> 1;
+ if (heap->h[parent] <= v)
+ break;
+ // move parent down
+ int64_t hp = heap->h[parent];
+ heap->h[child] = hp;
+ child = parent;
+ }
+ heap->h[child] = v;
}
-
/***********************************************************************
* hb_work_decavcodec_init
***********************************************************************
*
**********************************************************************/
-static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
+static int decavcodecaInit( hb_work_object_t * w, hb_job_t * job )
{
AVCodec * codec;
- int i;
hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
w->private_data = pv;
pv->job = job;
+ if ( job )
+ pv->title = job->title;
+ else
+ pv->title = w->title;
pv->list = hb_list_init();
int codec_id = w->codec_param;
@@ -202,11 +183,25 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
codec_id = CODEC_ID_MP2;
codec = avcodec_find_decoder( codec_id );
- pv->parser = av_parser_init( codec_id );
+ if ( pv->title->opaque_priv )
+ {
+ AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv;
+ pv->context = avcodec_alloc_context();
+ avcodec_copy_context( pv->context, ic->streams[w->audio->id]->codec);
+ hb_ff_set_sample_fmt( pv->context, codec );
+ }
+ else
+ {
+ pv->parser = av_parser_init( codec_id );
- pv->context = avcodec_alloc_context3(codec);
- hb_ff_set_sample_fmt( pv->context, codec );
- hb_avcodec_open( pv->context, codec, 0 );
+ pv->context = avcodec_alloc_context3(codec);
+ hb_ff_set_sample_fmt( pv->context, codec );
+ }
+ if ( hb_avcodec_open( pv->context, codec, 0 ) )
+ {
+ hb_log( "decavcodecaInit: avcodec_open failed" );
+ return 1;
+ }
if ( w->audio != NULL )
{
@@ -217,26 +212,6 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
w->audio->config.out.mixdown);
hb_downmix_set_chan_map( pv->downmix, &hb_smpte_chan_map, &hb_smpte_chan_map );
}
-
- pv->ff_audio_list = hb_list_init();
- for ( i = 0; i < hb_list_count( w->audio->priv.ff_audio_list ); i++ )
- {
- hb_work_private_t * ff_pv = calloc( 1, sizeof( hb_work_private_t ) );
- hb_list_add( pv->ff_audio_list, ff_pv );
-
- hb_audio_t *audio = hb_list_item( w->audio->priv.ff_audio_list, i );
-
- ff_pv->list = hb_list_init();
- ff_pv->job = job;
-
- if ( hb_need_downmix( audio->config.in.channel_layout,
- audio->config.out.mixdown) )
- {
- ff_pv->downmix = hb_downmix_init(audio->config.in.channel_layout,
- audio->config.out.mixdown);
- hb_downmix_set_chan_map( ff_pv->downmix, &hb_smpte_chan_map, &hb_smpte_chan_map );
- }
- }
}
return 0;
@@ -247,7 +222,7 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
***********************************************************************
*
**********************************************************************/
-static void closePrivData( hb_work_private_t ** ppv, int free_av_context )
+static void closePrivData( hb_work_private_t ** ppv )
{
hb_work_private_t * pv = *ppv;
@@ -273,7 +248,7 @@ static void closePrivData( hb_work_private_t ** ppv, int free_av_context )
{
hb_avcodec_close( pv->context );
}
- if ( free_av_context )
+ if ( pv->context )
{
av_free( pv->context );
}
@@ -301,118 +276,17 @@ static void decavcodecClose( hb_work_object_t * w )
if ( pv )
{
- if ( pv->ff_audio_list != NULL )
- {
- hb_work_private_t * ff_pv;
- while ( ( ff_pv = hb_list_item( pv->list, 0 ) ) != NULL )
- {
- hb_list_rem( pv->ff_audio_list, ff_pv );
- closePrivData( &ff_pv, 0 );
- }
- }
- closePrivData( &pv, 1 );
- w->private_data = NULL;
- }
-}
-
-static void decavcodeciClose( hb_work_object_t * w )
-{
- hb_work_private_t * pv = w->private_data;
-
- if ( pv )
- {
- if ( pv->ff_audio_list != NULL )
- {
- hb_work_private_t * ff_pv;
- while ( ( ff_pv = hb_list_item( pv->list, 0 ) ) != NULL )
- {
- hb_list_rem( pv->ff_audio_list, ff_pv );
- closePrivData( &ff_pv, 0 );
- }
- }
- closePrivData( &pv, 0 );
+ closePrivData( &pv );
w->private_data = NULL;
}
}
-static void writeAudioEof( hb_work_object_t * w )
-{
- hb_work_private_t * pv = w->private_data;
- hb_audio_t * audio = w->audio;
- int i;
- hb_buffer_t * buf;
-
- for ( i = 0; i < hb_list_count( audio->priv.ff_audio_list ); i++ )
- {
- hb_audio_t *ff_audio = hb_list_item( audio->priv.ff_audio_list, i );
- hb_work_private_t *ff_pv = hb_list_item( pv->ff_audio_list, i );
- if ( ff_pv )
- {
- buf = hb_buffer_init( 0 );
- if ( buf )
- {
- while ( !*w->done )
- {
- if ( hb_fifo_full_wait( ff_audio->priv.fifo_raw ) )
- {
- hb_fifo_push( ff_audio->priv.fifo_raw, buf );
- buf = NULL;
- break;
- }
- }
- if ( buf )
- {
- // w->done == true while waiting
- hb_buffer_close( &buf );
- break;
- }
- }
- }
- }
-}
-
-static void writeAudioFifos( hb_work_object_t * w )
-{
- hb_work_private_t * pv = w->private_data;
- hb_audio_t * audio = w->audio;
- int i;
- hb_buffer_t * buf;
-
- for ( i = 0; i < hb_list_count( audio->priv.ff_audio_list ); i++ )
- {
- hb_audio_t *ff_audio = hb_list_item( audio->priv.ff_audio_list, i );
- hb_work_private_t *ff_pv = hb_list_item( pv->ff_audio_list, i );
- if ( ff_pv )
- {
- buf = link_buf_list( ff_pv );
- if ( buf )
- {
- while ( !*w->done )
- {
- if ( hb_fifo_full_wait( ff_audio->priv.fifo_raw ) )
- {
- hb_fifo_push( ff_audio->priv.fifo_raw, buf );
- buf = NULL;
- break;
- }
- }
- if ( buf )
- {
- // w->done == true while waiting
- hb_buffer_close( &buf );
- break;
- }
- }
- }
- }
-}
-
/***********************************************************************
* Work
***********************************************************************
*
**********************************************************************/
-static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int decavcodecaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -423,7 +297,6 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
/* EOF on input stream - send it downstream & say that we're done */
*buf_out = in;
*buf_in = NULL;
- writeAudioEof( w );
return HB_WORK_DONE;
}
@@ -438,27 +311,26 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
int pos, len;
for ( pos = 0; pos < in->size; pos += len )
{
- uint8_t *parser_output_buffer;
- int parser_output_buffer_len;
+ uint8_t *pout;
+ int pout_len;
int64_t cur;
cur = in->start;
if ( pv->parser != NULL )
{
- len = av_parser_parse2( pv->parser, pv->context,
- &parser_output_buffer, &parser_output_buffer_len,
- in->data + pos, in->size - pos, cur, cur, 0 );
+ len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len,
+ in->data + pos, in->size - pos, cur, cur, 0 );
cur = pv->parser->pts;
if ( cur == AV_NOPTS_VALUE )
cur = -1;
}
else
{
- parser_output_buffer = in->data;
- len = parser_output_buffer_len = in->size;
+ pout = in->data;
+ len = pout_len = in->size;
}
- if (parser_output_buffer_len)
+ if (pout)
{
// set the duration on every frame since the stream format can
// change (it shouldn't but there's no way to guarantee it).
@@ -470,17 +342,16 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
if ( pv->context->sample_rate && pv->context->channels )
{
pv->duration = 90000. /
- (double)( pv->context->sample_rate * pv->context->channels );
+ (double)( pv->context->sample_rate * pv->context->channels );
}
- decodeAudio( w->audio, pv, parser_output_buffer, parser_output_buffer_len, cur );
+ decodeAudio( w->audio, pv, pout, pout_len, cur );
}
}
- writeAudioFifos( w );
*buf_out = link_buf_list( pv );
return HB_WORK_OK;
}
-static int decavcodecInfo( hb_work_object_t *w, hb_work_info_t *info )
+static int decavcodecaInfo( hb_work_object_t *w, hb_work_info_t *info )
{
hb_work_private_t *pv = w->private_data;
@@ -499,7 +370,7 @@ static int decavcodecInfo( hb_work_object_t *w, hb_work_info_t *info )
return 0;
}
-static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
+static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
hb_work_info_t *info )
{
hb_work_private_t *pv = w->private_data;
@@ -509,7 +380,7 @@ static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
if ( pv && pv->context )
{
- return decavcodecInfo( w, info );
+ return decavcodecaInfo( w, info );
}
// XXX
// We should parse the bitstream to find its parameters but for right
@@ -528,7 +399,10 @@ static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
AVCodecParserContext *parser = av_parser_init( codec->id );
AVCodecContext *context = avcodec_alloc_context3(codec);
hb_ff_set_sample_fmt( context, codec );
- hb_avcodec_open( context, codec, 0 );
+ if ( hb_avcodec_open( context, codec, 0 ) )
+ {
+ return -1;
+ }
uint8_t *buffer = av_malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE );
int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
unsigned char *pbuffer;
@@ -541,7 +415,7 @@ static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
{
int len;
- if (parser != NULL )
+ if ( parser != NULL )
{
len = av_parser_parse2( parser, context, &pbuffer,
&pbuffer_size, buf->data + pos,
@@ -839,20 +713,13 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequ
// point frame.pts should hold the frame's pts from the original data
// stream or AV_NOPTS_VALUE if it didn't have one. in the latter case
// we generate the next pts in sequence for it.
+ if ( !pv->frame_duration_set )
+ compute_frame_duration( pv );
+
double frame_dur = pv->duration;
- if ( frame_dur <= 0 )
- {
- frame_dur = 90000. * (double)pv->context->time_base.num /
- (double)pv->context->time_base.den;
- pv->duration = frame_dur;
- }
- if ( pv->context->ticks_per_frame > 1 )
- {
- frame_dur *= 2;
- }
if ( frame.repeat_pict )
{
- frame_dur += frame.repeat_pict * pv->duration;
+ frame_dur += frame.repeat_pict * pv->field_duration;
}
// If there was no pts for this frame, assume constant frame rate
@@ -963,9 +830,10 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequ
return got_picture;
}
-
-static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size, int sequence, int64_t pts, int64_t dts )
+static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int sequence, int64_t pts, int64_t dts )
{
+ hb_work_private_t *pv = w->private_data;
+
/*
* The following loop is a do..while because we need to handle both
* data & the flush at the end (signaled by size=0). At the end there's
@@ -975,14 +843,27 @@ static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size, int seq
int pos = 0;
do {
uint8_t *pout;
- int pout_len;
- int len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len,
+ int pout_len, len;
+ int64_t parser_pts, parser_dts;
+ if ( pv->parser )
+ {
+ len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len,
data + pos, size - pos, pts, dts, 0 );
+ parser_pts = pv->parser->pts;
+ parser_dts = pv->parser->dts;
+ }
+ else
+ {
+ pout = data;
+ len = pout_len = size;
+ parser_pts = pts;
+ parser_dts = dts;
+ }
pos += len;
if ( pout_len > 0 )
{
- decodeFrame( pv, pout, pout_len, sequence, pv->parser->pts, pv->parser->dts );
+ decodeFrame( pv, pout, pout_len, sequence, parser_pts, parser_dts );
}
} while ( pos < size );
@@ -1020,24 +901,65 @@ static hb_buffer_t *link_buf_list( hb_work_private_t *pv )
return head;
}
+static void init_video_avcodec_context( hb_work_private_t *pv )
+{
+ /* we have to wrap ffmpeg's get_buffer to be able to set the pts (?!) */
+ pv->context->opaque = pv;
+ pv->context->get_buffer = get_frame_buf;
+ pv->context->reget_buffer = reget_frame_buf;
+}
static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
{
hb_work_private_t *pv = calloc( 1, sizeof( hb_work_private_t ) );
+
w->private_data = pv;
pv->job = job;
+ if ( job )
+ pv->title = job->title;
+ else
+ pv->title = w->title;
pv->list = hb_list_init();
- int codec_id = w->codec_param;
- pv->parser = av_parser_init( codec_id );
- pv->context = avcodec_alloc_context2( AVMEDIA_TYPE_VIDEO );
-
- /* we have to wrap ffmpeg's get_buffer to be able to set the pts (?!) */
- pv->context->opaque = pv;
- pv->context->get_buffer = get_frame_buf;
- pv->context->reget_buffer = reget_frame_buf;
+ if ( pv->title->opaque_priv )
+ {
+ AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv;
+ AVCodec *codec = avcodec_find_decoder( w->codec_param );
+ if ( codec == NULL )
+ {
+ hb_log( "decavcodecvInit: failed to find codec for id (%d)", w->codec_param );
+ return 1;
+ }
+ pv->context = avcodec_alloc_context();
+ avcodec_copy_context( pv->context, ic->streams[pv->title->video_id]->codec);
+ pv->context->workaround_bugs = FF_BUG_AUTODETECT;
+ pv->context->error_recognition = 1;
+ pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
+ if ( hb_avcodec_open( pv->context, codec, pv->job ? HB_FFMPEG_THREADS_AUTO : 0 ) )
+ {
+ hb_log( "decavcodecvInit: avcodec_open failed" );
+ return 1;
+ }
+ pv->video_codec_opened = 1;
+ // avi, mkv and possibly mp4 containers can contain the M$ VFW packed
+ // b-frames abortion that messes up frame ordering and timestamps.
+ // XXX ffmpeg knows which streams are broken but doesn't expose the
+ // info externally. We should patch ffmpeg to add a flag to the
+ // codec context for this but until then we mark all ffmpeg streams
+ // as suspicious.
+ pv->brokenByMicrosoft = 1;
+ }
+ else
+ {
+ pv->parser = av_parser_init( w->codec_param );
+ pv->context = avcodec_alloc_context2( AVMEDIA_TYPE_VIDEO );
+ pv->context->workaround_bugs = FF_BUG_AUTODETECT;
+ pv->context->error_recognition = 1;
+ pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
+ init_video_avcodec_context( pv );
+ }
return 0;
}
@@ -1140,13 +1062,14 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
int64_t dts = pts;
*buf_in = NULL;
+ *buf_out = NULL;
/* if we got an empty buffer signaling end-of-stream send it downstream */
if ( in->size == 0 )
{
if ( pv->context->codec != NULL )
{
- decodeVideo( pv, in->data, in->size, in->sequence, pts, dts );
+ decodeVideo( w, in->data, in->size, in->sequence, pts, dts );
}
hb_list_add( pv->list, in );
*buf_out = link_buf_list( pv );
@@ -1155,11 +1078,17 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
// if this is the first frame open the codec (we have to wait for the
// first frame because of M$ VC1 braindamage).
- if ( pv->context->extradata == NULL )
+ if ( !pv->video_codec_opened )
{
AVCodec *codec = avcodec_find_decoder( w->codec_param );
+ if ( codec == NULL )
+ {
+ hb_log( "decavcodecvWork: failed to find codec for id (%d)", w->codec_param );
+ *buf_out = hb_buffer_init( 0 );;
+ return HB_WORK_DONE;
+ }
avcodec_get_context_defaults3( pv->context, codec );
- hb_ff_set_sample_fmt( pv->context, codec );
+ init_video_avcodec_context( pv );
if ( setup_extradata( w, in ) )
{
// we didn't find the headers needed to set up extradata.
@@ -1168,13 +1097,14 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_close( &in );
return HB_WORK_OK;
}
- // There's a mis-feature in ffmpeg that causes the context to be
- // incorrectly initialized the 1st time avcodec_open is called.
- // If you close it and open a 2nd time, it finishes the job.
- hb_avcodec_open( pv->context, codec, 0 );
- hb_avcodec_close( pv->context );
// disable threaded decoding for scan, can cause crashes
- hb_avcodec_open( pv->context, codec, pv->job ? HB_FFMPEG_THREADS_AUTO : 0 );
+ if ( hb_avcodec_open( pv->context, codec, pv->job ? HB_FFMPEG_THREADS_AUTO : 0 ) )
+ {
+ hb_log( "decavcodecvWork: avcodec_open failed" );
+ *buf_out = hb_buffer_init( 0 );;
+ return HB_WORK_DONE;
+ }
+ pv->video_codec_opened = 1;
}
if( in->start >= 0 )
@@ -1187,283 +1117,148 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
pv->new_chap = in->new_chap;
pv->chap_time = pts >= 0? pts : pv->pts_next;
}
- decodeVideo( pv, in->data, in->size, in->sequence, pts, dts );
+ decodeVideo( w, in->data, in->size, in->sequence, pts, dts );
hb_buffer_close( &in );
*buf_out = link_buf_list( pv );
return HB_WORK_OK;
}
-static int decavcodecvInfo( hb_work_object_t *w, hb_work_info_t *info )
+static void compute_frame_duration( hb_work_private_t *pv )
{
- hb_work_private_t *pv = w->private_data;
+ double duration = 0.;
+ int64_t max_fps = 64L;
- memset( info, 0, sizeof(*info) );
+ // context->time_base may be in fields, so set the max *fields* per second
+ if ( pv->context->ticks_per_frame > 1 )
+ max_fps *= pv->context->ticks_per_frame;
- if ( pv && pv->context )
+ if ( pv->title->opaque_priv )
{
- AVCodecContext *context = pv->context;
- info->bitrate = context->bit_rate;
- info->width = context->width;
- info->height = context->height;
-
- /* ffmpeg gives the frame rate in frames per second while HB wants
- * it in units of the 27MHz MPEG clock. */
- info->rate = 27000000;
- info->rate_base = (int64_t)context->time_base.num * 27000000LL /
- context->time_base.den;
- if ( context->ticks_per_frame > 1 )
- {
- // for ffmpeg 0.5 & later, the H.264 & MPEG-2 time base is
- // field rate rather than frame rate so convert back to frames.
- info->rate_base *= context->ticks_per_frame;
- }
-
- info->pixel_aspect_width = context->sample_aspect_ratio.num;
- info->pixel_aspect_height = context->sample_aspect_ratio.den;
-
- /* Sometimes there's no pixel aspect set in the source ffmpeg context
- * which appears to come from the video stream. In that case,
- * try the pixel aspect in AVStream (which appears to come from
- * the container). Else assume a 1:1 PAR. */
- if ( info->pixel_aspect_width == 0 ||
- info->pixel_aspect_height == 0 )
+ // If ffmpeg is demuxing for us, it collects some additional
+ // information about framerates that is often more accurate
+ // than context->time_base.
+ AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv;
+ AVStream *st = ic->streams[pv->title->video_id];
+ if ( st->nb_frames && st->duration )
{
- // There will not be an ffmpeg stream if the file is TS
- AVStream *st = hb_ffmpeg_avstream( w->codec_param );
- info->pixel_aspect_width = st && st->sample_aspect_ratio.num ?
- st->sample_aspect_ratio.num : 1;
- info->pixel_aspect_height = st && st->sample_aspect_ratio.den ?
- st->sample_aspect_ratio.den : 1;
- }
- /* ffmpeg returns the Pixel Aspect Ratio (PAR). Handbrake wants the
- * Display Aspect Ratio so we convert by scaling by the Storage
- * Aspect Ratio (w/h). We do the calc in floating point to get the
- * rounding right. */
- info->aspect = (double)info->pixel_aspect_width *
- (double)context->width /
- (double)info->pixel_aspect_height /
- (double)context->height;
-
- info->profile = context->profile;
- info->level = context->level;
- info->name = context->codec->name;
- return 1;
- }
- return 0;
-}
-
-static int decavcodecvBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
- hb_work_info_t *info )
-{
- return 0;
-}
-
-hb_work_object_t hb_decavcodecv =
-{
- WORK_DECAVCODECV,
- "Video decoder (libavcodec)",
- decavcodecvInit,
- decavcodecvWork,
- decavcodecClose,
- decavcodecvInfo,
- decavcodecvBSInfo
-};
-
-
-// This is a special decoder for ffmpeg streams. The ffmpeg stream reader
-// includes a parser and passes information from the parser to the decoder
-// via a codec context kept in the AVStream of the reader's AVFormatContext.
-// We *have* to use that codec context to decode the stream or we'll get
-// garbage. ffmpeg_title_scan put a cookie that can be used to get to that
-// codec context in our codec_param.
-
-// this routine gets the appropriate context pointer from the ffmpeg
-// stream reader. it can't be called until we get the first buffer because
-// we can't guarantee that reader will be called before the our init
-// routine and if our init is called first we'll get a pointer to the
-// old scan stream (which has already been closed).
-static void init_ffmpeg_context( hb_work_object_t *w )
-{
- hb_work_private_t *pv = w->private_data;
- pv->context = hb_ffmpeg_context( w->codec_param );
-
- // during scan the decoder gets closed & reopened which will
- // close the codec so reopen it if it's not there
- if ( ! pv->context->codec )
- {
- AVCodec *codec = avcodec_find_decoder( pv->context->codec_id );
- hb_ff_set_sample_fmt( pv->context, codec );
- // disable threaded decoding for scan, can cause crashes
- hb_avcodec_open( pv->context, codec, pv->job ? HB_FFMPEG_THREADS_AUTO : 0 );
- }
- // set up our best guess at the frame duration.
- // the frame rate in the codec is usually bogus but it's sometimes
- // ok in the stream.
- AVStream *st = hb_ffmpeg_avstream( w->codec_param );
-
- if ( st->nb_frames && st->duration )
- {
- // compute the average frame duration from the total number
- // of frames & the total duration.
- pv->duration = ( (double)st->duration * (double)st->time_base.num ) /
+ // compute the average frame duration from the total number
+ // of frames & the total duration.
+ duration = ( (double)st->duration * (double)st->time_base.num ) /
( (double)st->nb_frames * (double)st->time_base.den );
- }
- else
- {
- // XXX We don't have a frame count or duration so try to use the
- // far less reliable time base info in the stream.
- // Because the time bases are so screwed up, we only take values
- // in the range 8fps - 64fps.
- AVRational tb;
- if ( st->avg_frame_rate.den * 64L > st->avg_frame_rate.num &&
- st->avg_frame_rate.num > st->avg_frame_rate.den * 8L )
- {
- tb.num = st->avg_frame_rate.den;
- tb.den = st->avg_frame_rate.num;
}
- else if ( st->time_base.num * 64L > st->time_base.den &&
- st->time_base.den > st->time_base.num * 8L )
- {
- tb = st->time_base;
- }
- else if ( st->r_frame_rate.den * 64L > st->r_frame_rate.num &&
- st->r_frame_rate.num > st->r_frame_rate.den * 8L )
+ else
{
- tb.num = st->r_frame_rate.den;
- tb.den = st->r_frame_rate.num;
+ // XXX We don't have a frame count or duration so try to use the
+ // far less reliable time base info in the stream.
+ // Because the time bases are so screwed up, we only take values
+ // in the range 8fps - 64fps.
+ AVRational *tb = NULL;
+ if ( st->avg_frame_rate.den * 64L > st->avg_frame_rate.num &&
+ st->avg_frame_rate.num > st->avg_frame_rate.den * 8L )
+ {
+ tb = &(st->avg_frame_rate);
+ duration = (double)tb->den / (double)tb->num;
+ }
+ else if ( st->time_base.num * 64L > st->time_base.den &&
+ st->time_base.den > st->time_base.num * 8L )
+ {
+ tb = &(st->time_base);
+ duration = (double)tb->num / (double)tb->den;
+ }
+ else if ( st->r_frame_rate.den * 64L > st->r_frame_rate.num &&
+ st->r_frame_rate.num > st->r_frame_rate.den * 8L )
+ {
+ tb = &(st->r_frame_rate);
+ duration = (double)tb->den / (double)tb->num;
+ }
}
- else
+ if ( !duration &&
+ pv->context->time_base.num * max_fps > pv->context->time_base.den &&
+ pv->context->time_base.den > pv->context->time_base.num * 8L )
{
- tb.num = 1001; /*XXX*/
- tb.den = 24000; /*XXX*/
+ duration = (double)pv->context->time_base.num /
+ (double)pv->context->time_base.den;
+ if ( pv->context->ticks_per_frame > 1 )
+ {
+ // for ffmpeg 0.5 & later, the H.264 & MPEG-2 time base is
+ // field rate rather than frame rate so convert back to frames.
+ duration *= pv->context->ticks_per_frame;
+ }
}
- pv->duration = (double)tb.num / (double)tb.den;
}
- pv->duration *= 90000.;
-
- // we have to wrap ffmpeg's get_buffer to be able to set the pts (?!)
- pv->context->opaque = pv;
- pv->context->get_buffer = get_frame_buf;
- pv->context->reget_buffer = reget_frame_buf;
-
- // avi, mkv and possibly mp4 containers can contain the M$ VFW packed
- // b-frames abortion that messes up frame ordering and timestamps.
- // XXX ffmpeg knows which streams are broken but doesn't expose the
- // info externally. We should patch ffmpeg to add a flag to the
- // codec context for this but until then we mark all ffmpeg streams
- // as suspicious.
- pv->brokenByMicrosoft = 1;
-}
-
-static int decavcodecviInit( hb_work_object_t * w, hb_job_t * job )
-{
-
- hb_work_private_t *pv = calloc( 1, sizeof( hb_work_private_t ) );
- int i;
- w->private_data = pv;
- pv->job = job;
- pv->list = hb_list_init();
- pv->pts_next = -1;
-
- if ( w->audio != NULL )
+ else
{
- if ( hb_need_downmix( w->audio->config.in.channel_layout,
- w->audio->config.out.mixdown) )
- {
- pv->downmix = hb_downmix_init(w->audio->config.in.channel_layout,
- w->audio->config.out.mixdown);
- hb_downmix_set_chan_map( pv->downmix, &hb_smpte_chan_map, &hb_smpte_chan_map );
- }
-
- pv->ff_audio_list = hb_list_init();
- for ( i = 0; i < hb_list_count( w->audio->priv.ff_audio_list ); i++ )
+ if ( pv->context->time_base.num * max_fps > pv->context->time_base.den &&
+ pv->context->time_base.den > pv->context->time_base.num * 8L )
{
- hb_work_private_t * ff_pv = calloc( 1, sizeof( hb_work_private_t ) );
- hb_list_add( pv->ff_audio_list, ff_pv );
-
- hb_audio_t *audio = hb_list_item( w->audio->priv.ff_audio_list, i );
-
- ff_pv->list = hb_list_init();
- ff_pv->job = job;
- ff_pv->pts_next = -1;
-
- if ( hb_need_downmix( audio->config.in.channel_layout,
- audio->config.out.mixdown) )
+ duration = (double)pv->context->time_base.num /
+ (double)pv->context->time_base.den;
+ if ( pv->context->ticks_per_frame > 1 )
{
- ff_pv->downmix = hb_downmix_init(audio->config.in.channel_layout,
- audio->config.out.mixdown);
- hb_downmix_set_chan_map( ff_pv->downmix, &hb_smpte_chan_map, &hb_smpte_chan_map );
+ // for ffmpeg 0.5 & later, the H.264 & MPEG-2 time base is
+ // field rate rather than frame rate so convert back to frames.
+ duration *= pv->context->ticks_per_frame;
}
}
}
-
- return 0;
+ if ( duration == 0 )
+ {
+ // No valid timing info found in the stream, so pick some value
+ duration = 1001. / 24000.;
+ }
+ else
+ {
+ pv->frame_duration_set = 1;
+ }
+ pv->duration = duration * 90000.;
+ pv->field_duration = pv->duration;
+ if ( pv->context->ticks_per_frame > 1 )
+ {
+ pv->field_duration /= pv->context->ticks_per_frame;
+ }
}
-static int decavcodecviWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
- hb_buffer_t ** buf_out )
+static int decavcodecvInfo( hb_work_object_t *w, hb_work_info_t *info )
{
hb_work_private_t *pv = w->private_data;
- hb_buffer_t *in = *buf_in;
- *buf_in = NULL;
- /* if we got an empty buffer signaling end-of-stream send it downstream */
- if ( in->size == 0 )
- {
- /* flush any frames left in the decoder */
- while ( pv->context && decodeFrame( pv, NULL, 0, in->sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE ) )
- {
- }
- flushDelayQueue( pv );
- hb_list_add( pv->list, in );
- *buf_out = link_buf_list( pv );
- return HB_WORK_DONE;
- }
+ memset( info, 0, sizeof(*info) );
- if ( ! pv->context )
- {
- init_ffmpeg_context( w );
- }
+ info->bitrate = pv->context->bit_rate;
+ info->width = pv->context->width;
+ info->height = pv->context->height;
- int64_t pts = in->start;
- if( pts >= 0 )
- {
- // use the first timestamp as our 'next expected' pts
- if ( pv->pts_next < 0 )
- {
- pv->pts_next = pts;
- }
- }
+ info->pixel_aspect_width = pv->context->sample_aspect_ratio.num;
+ info->pixel_aspect_height = pv->context->sample_aspect_ratio.den;
- if ( in->new_chap )
- {
- pv->new_chap = in->new_chap;
- pv->chap_time = pts >= 0? pts : pv->pts_next;
- }
- decodeFrame( pv, in->data, in->size, in->sequence, in->start, in->renderOffset );
- hb_buffer_close( &in );
- *buf_out = link_buf_list( pv );
- return HB_WORK_OK;
+ compute_frame_duration( pv );
+ info->rate = 27000000;
+ info->rate_base = pv->duration * 300.;
+
+ info->profile = pv->context->profile;
+ info->level = pv->context->level;
+ info->name = pv->context->codec->name;
+
+ return 1;
}
-static int decavcodecviInfo( hb_work_object_t *w, hb_work_info_t *info )
+static int decavcodecvBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
+ hb_work_info_t *info )
{
- if ( decavcodecvInfo( w, info ) )
- {
- hb_work_private_t *pv = w->private_data;
- if ( ! pv->context )
- {
- init_ffmpeg_context( w );
- }
- // we have the frame duration in units of the 90KHz pts clock but
- // need it in units of the 27MHz MPEG clock. */
- info->rate = 27000000;
- info->rate_base = pv->duration * 300.;
- return 1;
- }
return 0;
}
+hb_work_object_t hb_decavcodecv =
+{
+ WORK_DECAVCODECV,
+ "Video decoder (libavcodec)",
+ decavcodecvInit,
+ decavcodecvWork,
+ decavcodecClose,
+ decavcodecvInfo,
+ decavcodecvBSInfo
+};
+
static hb_buffer_t * downmixAudio(
hb_audio_t *audio,
hb_work_private_t *pv,
@@ -1499,7 +1294,6 @@ static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *dat
if ( pts != -1 )
pv->pts_next = pts;
-
while ( pos < size )
{
float *buffer = pv->buffer;
@@ -1547,21 +1341,15 @@ static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *dat
hb_buffer_t * buf;
buf = hb_buffer_init( avp.size );
- memcpy( buf->data, avp.data, len );
+ memcpy( buf->data, avp.data, avp.size );
buf->start = pv->pts_next;
buf->stop = pts_next;
hb_list_add( pv->list, buf );
-
- if ( hb_list_count( audio->priv.ff_audio_list ) == 0 )
- {
- pv->pts_next = pts_next;
- continue;
- }
- // Fall through and process the list of other audio
- // pipelines that use this ffmpeg audio stream.
+ pv->pts_next = pts_next;
+ continue;
}
- // We require signed floats for the output format. If
+ // We require floats for the output format. If
// we got something different convert it.
if ( context->sample_fmt != AV_SAMPLE_FMT_FLT )
{
@@ -1592,38 +1380,12 @@ static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *dat
av_audio_convert_free( ctx );
}
- if ( !( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) )
- {
- hb_buffer_t * buf;
- buf = downmixAudio( audio, pv, buffer, context->channels, nsamples );
- buf->start = pv->pts_next;
- buf->stop = pts_next;
- hb_list_add( pv->list, buf );
- }
+ hb_buffer_t * buf;
+ buf = downmixAudio( audio, pv, buffer, context->channels, nsamples );
+ buf->start = pv->pts_next;
+ buf->stop = pts_next;
+ hb_list_add( pv->list, buf );
- int i;
- for ( i = 0; i < hb_list_count( audio->priv.ff_audio_list ); i++ )
- {
- hb_audio_t *ff_audio = hb_list_item( audio->priv.ff_audio_list, i );
- hb_work_private_t *ff_pv = hb_list_item( pv->ff_audio_list, i );
- if ( ff_pv )
- {
- hb_buffer_t * buf;
-
- if ( !( ff_audio->config.out.codec & HB_ACODEC_PASS_FLAG ) )
- {
- buf = downmixAudio( ff_audio, ff_pv, buffer, context->channels, nsamples );
- }
- else
- {
- buf = hb_buffer_init( avp.size );
- memcpy( buf->data, avp.data, len );
- }
- buf->start = pv->pts_next;
- buf->stop = pts_next;
- hb_list_add( ff_pv->list, buf );
- }
- }
pv->pts_next = pts_next;
// if we allocated a buffer for sample format conversion, free it
@@ -1634,72 +1396,3 @@ static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *dat
}
}
}
-
-static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in,
- hb_buffer_t **buf_out )
-{
- if ( (*buf_in)->size <= 0 )
- {
- /* EOF on input stream - send it downstream & say that we're done */
- *buf_out = *buf_in;
- *buf_in = NULL;
- writeAudioEof( w );
- return HB_WORK_DONE;
- }
-
- hb_work_private_t *pv = w->private_data;
-
- if ( (*buf_in)->start < -1 && pv->pts_next <= 0 )
- {
- // discard buffers that start before video time 0
- *buf_out = NULL;
- return HB_WORK_OK;
- }
-
- if ( ! pv->context )
- {
- init_ffmpeg_context( w );
- // duration is a scaling factor to go from #bytes in the decoded
- // frame to frame time (in 90KHz mpeg ticks). 'channels' converts
- // total samples to per-channel samples. 'sample_rate' converts
- // per-channel samples to seconds per sample and the 90000
- // is mpeg ticks per second.
- pv->duration = 90000. /
- (double)( pv->context->sample_rate * pv->context->channels );
- }
- hb_buffer_t *in = *buf_in;
-
- // if the packet has a timestamp use it if we don't have a timestamp yet
- // or if there's been a timing discontinuity of more than 100ms.
- if ( in->start >= 0 )
- {
- pv->pts_next = in->start;
- }
- decodeAudio( w->audio, pv, in->data, in->size, pv->pts_next );
- writeAudioFifos( w );
- *buf_out = link_buf_list( pv );
-
- return HB_WORK_OK;
-}
-
-hb_work_object_t hb_decavcodecvi =
-{
- WORK_DECAVCODECVI,
- "Video decoder (ffmpeg streams)",
- decavcodecviInit,
- decavcodecviWork,
- decavcodeciClose,
- decavcodecviInfo,
- decavcodecvBSInfo
-};
-
-hb_work_object_t hb_decavcodecai =
-{
- WORK_DECAVCODECAI,
- "Audio decoder (ffmpeg streams)",
- decavcodecviInit,
- decavcodecaiWork,
- decavcodeciClose,
- decavcodecInfo,
- decavcodecBSInfo
-};
diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c
index 81d5c360c..c502b8fef 100644
--- a/libhb/decmpeg2.c
+++ b/libhb/decmpeg2.c
@@ -854,7 +854,6 @@ static int decmpeg2Info( hb_work_object_t *w, hb_work_info_t *info )
info->height = m->height;
info->pixel_aspect_width = m->info->sequence->pixel_width;
info->pixel_aspect_height = m->info->sequence->pixel_height;
- info->aspect = m->aspect_ratio;
// if the frame is progressive & NTSC DVD height report it as 23.976 FPS
// so that scan can autodetect NTSC film
diff --git a/libhb/hb.c b/libhb/hb.c
index a387dc917..d3d1d5668 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -439,10 +439,8 @@ hb_handle_t * hb_init( int verbose, int update_check )
hb_register( &hb_enctheora );
hb_register( &hb_deca52 );
hb_register( &hb_decdca );
- hb_register( &hb_decavcodec );
+ hb_register( &hb_decavcodeca );
hb_register( &hb_decavcodecv );
- hb_register( &hb_decavcodecvi );
- hb_register( &hb_decavcodecai );
hb_register( &hb_declpcm );
hb_register( &hb_encfaac );
hb_register( &hb_enclame );
@@ -453,6 +451,7 @@ hb_handle_t * hb_init( int verbose, int update_check )
hb_register( &hb_encca_haac );
#endif
hb_register( &hb_encavcodeca );
+ hb_register( &hb_reader );
return h;
}
@@ -541,10 +540,8 @@ hb_handle_t * hb_init_dl( int verbose, int update_check )
hb_register( &hb_enctheora );
hb_register( &hb_deca52 );
hb_register( &hb_decdca );
- hb_register( &hb_decavcodec );
+ hb_register( &hb_decavcodeca );
hb_register( &hb_decavcodecv );
- hb_register( &hb_decavcodecvi );
- hb_register( &hb_decavcodecai );
hb_register( &hb_declpcm );
hb_register( &hb_encfaac );
hb_register( &hb_enclame );
@@ -555,6 +552,7 @@ hb_handle_t * hb_init_dl( int verbose, int update_check )
hb_register( &hb_encca_haac );
#endif
hb_register( &hb_encavcodeca );
+ hb_register( &hb_reader );
return h;
}
diff --git a/libhb/internal.h b/libhb/internal.h
index b78072ad8..724ab7c2e 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -174,7 +174,7 @@ hb_thread_t * hb_scan_init( hb_handle_t *, volatile int * die,
int store_previews, uint64_t min_duration );
hb_thread_t * hb_work_init( hb_list_t * jobs,
volatile int * die, int * error, hb_job_t ** job );
-hb_thread_t * hb_reader_init( hb_job_t * );
+void ReadLoop( void * _w );
hb_work_object_t * hb_muxer_init( hb_job_t * );
hb_work_object_t * hb_get_work( int );
hb_work_object_t * hb_codec_decoder( int );
@@ -255,9 +255,9 @@ void hb_bd_set_angle( hb_bd_t * d, int angle );
int hb_bd_main_feature( hb_bd_t * d, hb_list_t * list_title );
hb_stream_t * hb_bd_stream_open( hb_title_t *title );
-hb_stream_t * hb_stream_open( char * path, hb_title_t *title );
+hb_stream_t * hb_stream_open( char * path, hb_title_t *title, int scan );
void hb_stream_close( hb_stream_t ** );
-hb_title_t * hb_stream_title_scan( hb_stream_t *);
+hb_title_t * hb_stream_title_scan( hb_stream_t *, hb_title_t *);
hb_buffer_t * hb_stream_read( hb_stream_t * );
int hb_stream_seek( hb_stream_t *, float );
int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts );
@@ -267,9 +267,6 @@ int hb_stream_chapter( hb_stream_t * );
hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt );
-void * hb_ffmpeg_context( int codec_param );
-void * hb_ffmpeg_avstream( int codec_param );
-
#define STR4_TO_UINT32(p) \
((((const uint8_t*)(p))[0] << 24) | \
(((const uint8_t*)(p))[1] << 16) | \
@@ -353,8 +350,6 @@ enum
WORK_DECDCA,
WORK_DECAVCODEC,
WORK_DECAVCODECV,
- WORK_DECAVCODECVI,
- WORK_DECAVCODECAI,
WORK_DECLPCM,
WORK_ENCFAAC,
WORK_ENCLAME,
@@ -362,7 +357,8 @@ enum
WORK_ENC_CA_AAC,
WORK_ENC_CA_HAAC,
WORK_ENCAVCODEC_AUDIO,
- WORK_MUX
+ WORK_MUX,
+ WORK_READER
};
enum
diff --git a/libhb/reader.c b/libhb/reader.c
index 037fe0931..96d09e96a 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -6,6 +6,20 @@
#include "hb.h"
+static int hb_reader_init( hb_work_object_t * w, hb_job_t * job );
+static void hb_reader_close( hb_work_object_t * w );
+
+hb_work_object_t hb_reader =
+{
+ WORK_READER,
+ "Reader",
+ hb_reader_init,
+ NULL,
+ hb_reader_close,
+ NULL,
+ NULL
+};
+
typedef struct
{
double average; // average time between packets
@@ -15,7 +29,7 @@ typedef struct
int valid; // Stream timing is not valid until next scr.
} stream_timing_t;
-typedef struct
+struct hb_work_private_s
{
hb_job_t * job;
hb_title_t * title;
@@ -37,25 +51,51 @@ typedef struct
int start_found; // found pts_to_start point
int64_t pts_to_start;
uint64_t st_first;
-} hb_reader_t;
+};
/***********************************************************************
* Local prototypes
**********************************************************************/
-static void ReaderFunc( void * );
static hb_fifo_t ** GetFifoForId( hb_job_t * job, int id );
-static void UpdateState( hb_reader_t * r, int64_t start);
+static void UpdateState( hb_work_private_t * r, int64_t start);
/***********************************************************************
* hb_reader_init
***********************************************************************
*
**********************************************************************/
-hb_thread_t * hb_reader_init( hb_job_t * job )
+static int hb_reader_open( hb_work_private_t * r )
{
- hb_reader_t * r;
+ if ( r->title->type == HB_BD_TYPE )
+ {
+ if ( !( r->bd = hb_bd_init( r->title->path ) ) )
+ return 1;
+ }
+ else if ( r->title->type == HB_DVD_TYPE )
+ {
+ if ( !( r->dvd = hb_dvd_init( r->title->path ) ) )
+ return 1;
+ }
+ else if ( r->title->type == HB_STREAM_TYPE ||
+ r->title->type == HB_FF_STREAM_TYPE )
+ {
+ if ( !( r->stream = hb_stream_open( r->title->path, r->title, 0 ) ) )
+ return 1;
+ }
+ else
+ {
+ // Unknown type, should never happen
+ return 1;
+ }
+ return 0;
+}
- r = calloc( sizeof( hb_reader_t ), 1 );
+static int hb_reader_init( hb_work_object_t * w, hb_job_t * job )
+{
+ hb_work_private_t * r;
+
+ r = calloc( sizeof( hb_work_private_t ), 1 );
+ w->private_data = r;
r->job = job;
r->title = job->title;
@@ -81,11 +121,47 @@ hb_thread_t * hb_reader_init( hb_job_t * job )
r->pts_to_start = MAX(0, job->pts_to_start - 180000);
}
- return hb_thread_init( "reader", ReaderFunc, r,
- HB_NORMAL_PRIORITY );
+ // The stream needs to be open before starting the reader thead
+ // to prevent a race with decoders that may share information
+ // with the reader. Specifically avcodec needs this.
+ if ( hb_reader_open( r ) )
+ {
+ free( r->stream_timing );
+ free( r );
+ return 1;
+ }
+ return 0;
+}
+
+
+static void hb_reader_close( hb_work_object_t * w )
+{
+ hb_work_private_t * r = w->private_data;
+
+ if (r->bd)
+ {
+ hb_bd_stop( r->bd );
+ hb_bd_close( &r->bd );
+ }
+ else if (r->dvd)
+ {
+ hb_dvd_stop( r->dvd );
+ hb_dvd_close( &r->dvd );
+ }
+ else if (r->stream)
+ {
+ hb_stream_close(&r->stream);
+ }
+
+ if ( r->stream_timing )
+ {
+ free( r->stream_timing );
+ }
+
+ free( r );
}
-static void push_buf( const hb_reader_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
+static void push_buf( const hb_work_private_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
{
while ( !*r->die && !r->job->done )
{
@@ -102,7 +178,7 @@ static void push_buf( const hb_reader_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
}
}
-static int is_audio( hb_reader_t *r, int id )
+static int is_audio( hb_work_private_t *r, int id )
{
int i;
hb_audio_t *audio;
@@ -127,7 +203,7 @@ static int is_audio( hb_reader_t *r, int id )
// find the per-stream timing state for 'buf'
-static stream_timing_t *find_st( hb_reader_t *r, const hb_buffer_t *buf )
+static stream_timing_t *find_st( hb_work_private_t *r, const hb_buffer_t *buf )
{
stream_timing_t *st = r->stream_timing;
for ( ; st->id != -1; ++st )
@@ -140,7 +216,7 @@ static stream_timing_t *find_st( hb_reader_t *r, const hb_buffer_t *buf )
// find or create the per-stream timing state for 'buf'
-static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf, int valid )
+static stream_timing_t *id_to_st( hb_work_private_t *r, const hb_buffer_t *buf, int valid )
{
stream_timing_t *st = r->stream_timing;
while ( st->id != buf->id && st->id != -1)
@@ -177,7 +253,7 @@ static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf, int va
// update the average inter-packet time of the stream associated with 'buf'
// using a recursive low-pass filter with a 16 packet time constant.
-static void update_ipt( hb_reader_t *r, const hb_buffer_t *buf )
+static void update_ipt( hb_work_private_t *r, const hb_buffer_t *buf )
{
stream_timing_t *st = id_to_st( r, buf, 1 );
double dt = buf->renderOffset - st->last;
@@ -194,7 +270,7 @@ static void update_ipt( hb_reader_t *r, const hb_buffer_t *buf )
// such that 'buf' will follow the previous packet of this stream separated
// by the average packet time of the stream.
-static void new_scr_offset( hb_reader_t *r, hb_buffer_t *buf )
+static void new_scr_offset( hb_work_private_t *r, hb_buffer_t *buf )
{
stream_timing_t *st = id_to_st( r, buf, 1 );
int64_t last;
@@ -224,9 +300,10 @@ static void new_scr_offset( hb_reader_t *r, hb_buffer_t *buf )
***********************************************************************
*
**********************************************************************/
-static void ReaderFunc( void * _r )
+void ReadLoop( void * _w )
{
- hb_reader_t * r = _r;
+ hb_work_object_t * w = _w;
+ hb_work_private_t * r = w->private_data;
hb_fifo_t ** fifos;
hb_buffer_t * buf;
hb_list_t * list;
@@ -234,28 +311,6 @@ static void ReaderFunc( void * _r )
int chapter = -1;
int chapter_end = r->job->chapter_end;
- if ( r->title->type == HB_BD_TYPE )
- {
- if ( !( r->bd = hb_bd_init( r->title->path ) ) )
- return;
- }
- else if ( r->title->type == HB_DVD_TYPE )
- {
- if ( !( r->dvd = hb_dvd_init( r->title->path ) ) )
- return;
- }
- else if ( r->title->type == HB_STREAM_TYPE ||
- r->title->type == HB_FF_STREAM_TYPE )
- {
- if ( !( r->stream = hb_stream_open( r->title->path, r->title ) ) )
- return;
- }
- else
- {
- // Unknown type, should never happen
- return;
- }
-
if (r->bd)
{
if( !hb_bd_start( r->bd, r->title ) )
@@ -601,37 +656,15 @@ static void ReaderFunc( void * _r )
}
hb_list_empty( &list );
- if (r->bd)
- {
- hb_bd_stop( r->bd );
- hb_bd_close( &r->bd );
- }
- else if (r->dvd)
- {
- hb_dvd_stop( r->dvd );
- hb_dvd_close( &r->dvd );
- }
- else if (r->stream)
- {
- hb_stream_close(&r->stream);
- }
-
- if ( r->stream_timing )
- {
- free( r->stream_timing );
- }
hb_log( "reader: done. %d scr changes", r->demux.scr_changes );
if ( r->demux.dts_drops )
{
hb_log( "reader: %d drops because DTS out of range", r->demux.dts_drops );
}
-
- free( r );
- _r = NULL;
}
-static void UpdateState( hb_reader_t * r, int64_t start)
+static void UpdateState( hb_work_private_t * r, int64_t start)
{
hb_state_t state;
uint64_t now;
diff --git a/libhb/scan.c b/libhb/scan.c
index 34137c42b..8999dc15c 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -149,14 +149,21 @@ static void ScanFunc( void * _data )
}
}
}
- else if ( (data->stream = hb_stream_open( data->path, 0 ) ) != NULL )
- {
- hb_list_add( data->list_title, hb_stream_title_scan( data->stream ) );
- }
else
{
- hb_log( "scan: unrecognized file type" );
- return;
+ hb_title_t * title = hb_title_init( data->path, 0 );
+ if ( (data->stream = hb_stream_open( data->path, title, 1 ) ) != NULL )
+ {
+ title = hb_stream_title_scan( data->stream, title );
+ if ( title )
+ hb_list_add( data->list_title, title );
+ }
+ else
+ {
+ hb_title_close( &title );
+ hb_log( "scan: unrecognized file type" );
+ return;
+ }
}
for( i = 0; i < hb_list_count( data->list_title ); )
@@ -338,17 +345,17 @@ static inline int clampBlack( int x )
return x < 16 ? 16 : x;
}
-static int row_all_dark( hb_title_t *title, uint8_t* luma, int row )
+static int row_all_dark( hb_work_info_t *info, uint8_t* luma, int row )
{
- luma += title->width * row;
+ luma += info->width * row;
// compute the average luma value of the row
int i, avg = 0;
- for ( i = 0; i < title->width; ++i )
+ for ( i = 0; i < info->width; ++i )
{
avg += clampBlack( luma[i] );
}
- avg /= title->width;
+ avg /= info->width;
if ( avg >= DARK )
return 0;
@@ -356,7 +363,7 @@ static int row_all_dark( hb_title_t *title, uint8_t* luma, int row )
// all pixels are within +-16 of the average (this range is fairly coarse
// but there's a lot of quantization noise for luma values near black
// so anything less will fail to crop because of the noise).
- for ( i = 0; i < title->width; ++i )
+ for ( i = 0; i < info->width; ++i )
{
if ( absdiff( avg, clampBlack( luma[i] ) ) > 16 )
return 0;
@@ -364,11 +371,11 @@ static int row_all_dark( hb_title_t *title, uint8_t* luma, int row )
return 1;
}
-static int column_all_dark( hb_title_t *title, uint8_t* luma, int top, int bottom,
+static int column_all_dark( hb_work_info_t *info, uint8_t* luma, int top, int bottom,
int col )
{
- int stride = title->width;
- int height = title->height - top - bottom;
+ int stride = info->width;
+ int height = info->height - top - bottom;
luma += stride * top + col;
// compute the average value of the column
@@ -495,7 +502,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
}
else if (data->batch)
{
- data->stream = hb_stream_open( title->path, title );
+ data->stream = hb_stream_open( title->path, title, 1 );
}
for( i = 0; i < data->preview_count; i++ )
@@ -670,22 +677,13 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
}
vid_decoder->close( vid_decoder );
free( vid_decoder );
+ hb_log( "scan: could not get a video information" );
continue;
}
remember_info( info_list, &vid_info );
- if ( title->video_codec_name == NULL )
- {
- title->video_codec_name = strdup( vid_info.name );
- }
- title->width = vid_info.width;
- title->height = vid_info.height;
- title->rate = vid_info.rate;
- title->rate_base = vid_info.rate_base;
- title->video_bitrate = vid_info.bitrate;
-
- if( title->rate_base == 1126125 )
+ if( vid_info.rate_base == 1126125 )
{
/* Frame FPS is 23.976 (meaning it's progressive), so
start keeping track of how many are reporting at
@@ -700,7 +698,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
which means we should be conservative and use
29.97 as the title's FPS for now.
*/
- title->rate_base = 900900;
+ vid_info.rate_base = 900900;
}
else
{
@@ -711,16 +709,16 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976");
}
- title->rate_base = 1126125;
+ vid_info.rate_base = 1126125;
}
}
- else if( title->rate_base == 900900 && progressive_count >= 6 )
+ else if( vid_info.rate_base == 900900 && progressive_count >= 6 )
{
/*
* We've already deduced that the frame rate is 23.976, so set it
* back again.
*/
- title->rate_base = 1126125;
+ vid_info.rate_base = 1126125;
}
while( ( buf_es = hb_list_item( list_es, 0 ) ) )
@@ -730,7 +728,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
}
/* Check preview for interlacing artifacts */
- if( hb_detect_comb( vid_buf, title->width, title->height, 10, 30, 9, 10, 30, 9 ) )
+ if( hb_detect_comb( vid_buf, vid_info.width, vid_info.height, 10, 30, 9, 10, 30, 9 ) )
{
hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1);
interlaced_preview_count++;
@@ -744,7 +742,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
file_preview = fopen( filename, "wb" );
if( file_preview )
{
- fwrite( vid_buf->data, title->width * title->height * 3 / 2,
+ fwrite( vid_buf->data, vid_info.width * vid_info.height * 3 / 2,
1, file_preview );
fclose( file_preview );
}
@@ -758,7 +756,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
#define Y vid_buf->data
int top, bottom, left, right;
- int h4 = title->height / 4, w4 = title->width / 4;
+ int h4 = vid_info.height / 4, w4 = vid_info.width / 4;
// When widescreen content is matted to 16:9 or 4:3 there's sometimes
// a thin border on the outer edge of the matte. On TV content it can be
@@ -768,11 +766,11 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
// we can crop the matte. The border width depends on the resolution
// (12 pixels on 1080i looks visually the same as 4 pixels on 480i)
// so we allow the border to be up to 1% of the frame height.
- const int border = title->height / 100;
+ const int border = vid_info.height / 100;
for ( top = border; top < h4; ++top )
{
- if ( ! row_all_dark( title, Y, top ) )
+ if ( ! row_all_dark( &vid_info, Y, top ) )
break;
}
if ( top <= border )
@@ -781,7 +779,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
// didn't check are dark or if we shouldn't crop at all.
for ( top = 0; top < border; ++top )
{
- if ( ! row_all_dark( title, Y, top ) )
+ if ( ! row_all_dark( &vid_info, Y, top ) )
break;
}
if ( top >= border )
@@ -791,14 +789,14 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
}
for ( bottom = border; bottom < h4; ++bottom )
{
- if ( ! row_all_dark( title, Y, title->height - 1 - bottom ) )
+ if ( ! row_all_dark( &vid_info, Y, vid_info.height - 1 - bottom ) )
break;
}
if ( bottom <= border )
{
for ( bottom = 0; bottom < border; ++bottom )
{
- if ( ! row_all_dark( title, Y, title->height - 1 - bottom ) )
+ if ( ! row_all_dark( &vid_info, Y, vid_info.height - 1 - bottom ) )
break;
}
if ( bottom >= border )
@@ -808,12 +806,12 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
}
for ( left = 0; left < w4; ++left )
{
- if ( ! column_all_dark( title, Y, top, bottom, left ) )
+ if ( ! column_all_dark( &vid_info, Y, top, bottom, left ) )
break;
}
for ( right = 0; right < w4; ++right )
{
- if ( ! column_all_dark( title, Y, top, bottom, title->width - 1 - right ) )
+ if ( ! column_all_dark( &vid_info, Y, top, bottom, vid_info.width - 1 - right ) )
break;
}
@@ -856,35 +854,46 @@ skip_preview:
hb_work_info_t vid_info;
most_common_info( info_list, &vid_info );
+ if ( title->video_codec_name == NULL )
+ {
+ title->video_codec_name = strdup( vid_info.name );
+ }
title->width = vid_info.width;
title->height = vid_info.height;
- title->pixel_aspect_width = vid_info.pixel_aspect_width;
- title->pixel_aspect_height = vid_info.pixel_aspect_height;
+ if ( vid_info.rate && vid_info.rate_base )
+ {
+ title->rate = vid_info.rate;
+ title->rate_base = vid_info.rate_base;
+ }
+ title->video_bitrate = vid_info.bitrate;
+
+ if( vid_info.pixel_aspect_width && vid_info.pixel_aspect_height )
+ {
+ title->pixel_aspect_width = vid_info.pixel_aspect_width;
+ title->pixel_aspect_height = vid_info.pixel_aspect_height;
+ }
// compute the aspect ratio based on the storage dimensions and the
// pixel aspect ratio (if supplied) or just storage dimensions if no PAR.
title->aspect = (double)title->width / (double)title->height;
- if( title->pixel_aspect_width && title->pixel_aspect_height )
+ title->aspect *= (double)title->pixel_aspect_width /
+ (double)title->pixel_aspect_height;
+
+ // For unknown reasons some French PAL DVDs put the original
+ // content's aspect ratio into the mpeg PAR even though it's
+ // the wrong PAR for the DVD. Apparently they rely on the fact
+ // that DVD players ignore the content PAR and just use the
+ // aspect ratio from the DVD metadata. So, if the aspect computed
+ // from the PAR is different from the container's aspect we use
+ // the container's aspect & recompute the PAR from it.
+ if( title->container_aspect && (int)(title->aspect * 9) != (int)(title->container_aspect * 9) )
{
- title->aspect *= (double)title->pixel_aspect_width /
- (double)title->pixel_aspect_height;
-
- // For unknown reasons some French PAL DVDs put the original
- // content's aspect ratio into the mpeg PAR even though it's
- // the wrong PAR for the DVD. Apparently they rely on the fact
- // that DVD players ignore the content PAR and just use the
- // aspect ratio from the DVD metadata. So, if the aspect computed
- // from the PAR is different from the container's aspect we use
- // the container's aspect & recompute the PAR from it.
- if( title->container_aspect && (int)(title->aspect * 9) != (int)(title->container_aspect * 9) )
- {
- hb_log("scan: content PAR gives wrong aspect %.2f; "
- "using container aspect %.2f", title->aspect,
- title->container_aspect );
- title->aspect = title->container_aspect;
- hb_reduce( &title->pixel_aspect_width, &title->pixel_aspect_height,
- (int)(title->aspect * title->height + 0.5), title->width );
- }
+ hb_log("scan: content PAR gives wrong aspect %.2f; "
+ "using container aspect %.2f", title->aspect,
+ title->container_aspect );
+ title->aspect = title->container_aspect;
+ hb_reduce( &title->pixel_aspect_width, &title->pixel_aspect_height,
+ (int)(title->aspect * title->height + 0.5), title->width );
}
// don't try to crop unless we got at least 3 previews
diff --git a/libhb/stream.c b/libhb/stream.c
index 1bf17d5f0..84d6c1a63 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -124,6 +124,7 @@ typedef struct {
struct hb_stream_s
{
+ int scan;
int frames; /* video frames so far */
int errors; /* total errors so far */
int last_error_frame; /* frame # at last error message */
@@ -164,7 +165,8 @@ struct hb_stream_s
hb_stream_type_t hb_stream_type;
hb_title_t *title;
- AVFormatContext *ffmpeg_ic;
+ AVFormatContext *ffmpeg_info_ic;
+ AVFormatContext *ffmpeg_reader_ic;
AVPacket *ffmpeg_pkt;
uint8_t ffmpeg_video_id;
@@ -209,10 +211,10 @@ static void hb_ts_stream_set_audio_list(hb_list_t *list_audio, hb_stream_t *stre
static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title);
static off_t align_to_next_packet(hb_stream_t *stream);
-static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title );
+static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title, int scan );
static void ffmpeg_close( hb_stream_t *d );
-static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream );
-static hb_buffer_t *ffmpeg_read( hb_stream_t *stream );
+static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream, hb_title_t *title );
+hb_buffer_t *hb_ffmpeg_read( hb_stream_t *stream );
static int ffmpeg_seek( hb_stream_t *stream, float frac );
static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts );
@@ -610,7 +612,7 @@ static int audio_inactive( hb_stream_t *stream, int idx )
***********************************************************************
*
**********************************************************************/
-hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
+hb_stream_t * hb_stream_open( char *path, hb_title_t *title, int scan )
{
FILE *f = fopen( path, "rb" );
if ( f == NULL )
@@ -634,7 +636,7 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
* (even if we have saved state, the stream may have changed).
*/
hb_stream_t *ss = hb_stream_lookup( path );
- if ( title && ss && ss->hb_stream_type != ffmpeg )
+ if ( !scan && ss && ss->hb_stream_type != ffmpeg )
{
/*
* copy the saved state since we might be encoding the same stream
@@ -643,6 +645,7 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
memcpy( d, ss, sizeof(*d) );
d->file_handle = f;
d->title = title;
+ d->scan = scan;
d->path = strdup( path );
if ( d->hb_stream_type == transport )
@@ -680,6 +683,7 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
}
d->file_handle = f;
d->title = title;
+ d->scan = scan;
d->path = strdup( path );
if (d->path != NULL )
{
@@ -689,7 +693,7 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
}
fclose( d->file_handle );
d->file_handle = NULL;
- if ( ffmpeg_open( d, title ) )
+ if ( ffmpeg_open( d, title, scan ) )
{
return d;
}
@@ -831,7 +835,7 @@ void hb_stream_close( hb_stream_t ** _d )
* if the stream was opened for a scan, cache the result, otherwise delete
* the state.
*/
- if ( stream->title == NULL )
+ if ( stream->scan )
{
hb_stream_delete_dynamic( stream );
if ( stream_state_list == NULL )
@@ -868,13 +872,13 @@ static void hb_stream_delete_entry(hb_stream_t *stream, int indx)
***********************************************************************
*
**********************************************************************/
-hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
+hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title)
{
if ( stream->hb_stream_type == ffmpeg )
- return ffmpeg_title_scan( stream );
+ return ffmpeg_title_scan( stream, title );
// 'Barebones Title'
- hb_title_t *aTitle = hb_title_init( stream->path, 0 );
+ hb_title_t *aTitle = title;
aTitle->type = HB_STREAM_TYPE;
aTitle->index = 1;
@@ -1388,7 +1392,7 @@ hb_buffer_t * hb_stream_read( hb_stream_t * src_stream )
{
if ( src_stream->hb_stream_type == ffmpeg )
{
- return ffmpeg_read( src_stream );
+ return hb_ffmpeg_read( src_stream );
}
if ( src_stream->hb_stream_type == dvd_program )
{
@@ -1483,28 +1487,15 @@ hb_buffer_t * hb_stream_read( hb_stream_t * src_stream )
return hb_ts_stream_decode( src_stream );
}
-void ffmpeg_flush_stream_buffers( hb_stream_t *stream )
-{
- int i;
- AVFormatContext *ic = stream->ffmpeg_ic;
-
- for ( i = 0; i < ic->nb_streams; i++ )
- {
- if ( ic->streams[i]->codec && ic->streams[i]->codec->codec )
- {
- avcodec_flush_buffers( ic->streams[i]->codec );
- }
- }
-}
-
int64_t ffmpeg_initial_timestamp( hb_stream_t * stream )
{
- AVStream *s = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id];
- if ( s->nb_index_entries < 1 )
+ AVFormatContext *ic = stream->ffmpeg_info_ic;
+ if ( ic->start_time != AV_NOPTS_VALUE && ic->start_time > 0 )
+ return ic->start_time;
+ else
return 0;
-
- return s->index_entries[0].timestamp;
}
+
int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num )
{
@@ -1537,8 +1528,11 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num )
if ( chapter_num > 1 && pos > 0 )
{
- av_seek_frame( stream->ffmpeg_ic, -1, pos, 0);
- ffmpeg_flush_stream_buffers( stream );
+ AVStream *st = stream->ffmpeg_info_ic->streams[stream->ffmpeg_video_id];
+ // timebase must be adjusted to match timebase of stream we are
+ // using for seeking.
+ pos = av_rescale(pos, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
+ avformat_seek_file( stream->ffmpeg_reader_ic, stream->ffmpeg_video_id, 0, pos, pos, AVSEEK_FLAG_BACKWARD);
}
return 1;
}
@@ -1655,7 +1649,6 @@ static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
* formatting routine in scan.c do all the stuff below.
*/
const char *codec_name;
- AVCodecContext *cc;
// Names for streams we know about.
if ( audio->config.in.stream_type == 0x80 &&
@@ -1705,24 +1698,6 @@ static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
// To distinguish, Bluray streams have a reg_desc of HDMV
codec_name = "E-AC3";
}
- // For streams demuxed and decoded by ffmpeg, we have a cached context.
- // Use it to get the name and profile information. Obtaining
- // the profile requires that ffmpeg has already probed the stream.
- else if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) &&
- ( audio->config.in.codec & HB_ACODEC_FF_I_FLAG ) &&
- ( cc = hb_ffmpeg_context( audio->config.in.codec_param ) ) &&
- avcodec_find_decoder( cc->codec_id ) )
- {
- AVCodec *codec = avcodec_find_decoder( cc->codec_id );
- codec_name = codec->name;
-
- const char *profile_name;
- profile_name = av_get_profile_name( codec, cc->profile );
- if ( profile_name )
- {
- codec_name = profile_name;
- }
- }
else if ( st2codec[audio->config.in.stream_type].kind == A )
{
codec_name = stream_type_name(audio->config.in.reg_desc,
@@ -1731,7 +1706,6 @@ static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
// For streams demuxed by us and decoded by ffmpeg, we can lookup the
// decoder name.
else if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) &&
- !( audio->config.in.codec & HB_ACODEC_FF_I_FLAG ) &&
avcodec_find_decoder( audio->config.in.codec_param ) )
{
codec_name = avcodec_find_decoder( audio->config.in.codec_param )->name;
@@ -1750,25 +1724,13 @@ static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
strlen(lang->native_name) ? lang->native_name : lang->eng_name,
codec_name );
- if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK) &&
- ( audio->config.in.codec & HB_ACODEC_FF_I_FLAG ) )
- {
- int layout = audio->config.in.channel_layout;
- char *desc = audio->config.lang.description +
- strlen( audio->config.lang.description );
- sprintf( desc, " (%d.%d ch)",
- HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(layout) +
- HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(layout),
- HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(layout) );
- }
-
snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s",
strlen(lang->native_name) ? lang->native_name : lang->eng_name );
snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ),
"%s", lang->iso639_2);
}
-static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
+static void set_audio_description( hb_stream_t * stream, hb_audio_t *audio, iso639_lang_t *lang )
{
/* XXX
* This is a duplicate of code in dvd.c - it should get factored out
@@ -1777,14 +1739,17 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
* formatting routine in scan.c do all the stuff below.
*/
const char *codec_name;
- AVCodecContext *cc;
+ AVCodecContext *cc = NULL;
+
+ if ( stream && stream->ffmpeg_info_ic )
+ {
+ cc = stream->ffmpeg_info_ic->streams[audio->id]->codec;
+ }
// For streams demuxed and decoded by ffmpeg, we have a cached context.
// Use it to get the name and profile information. Obtaining
// the profile requires that ffmpeg has already probed the stream.
- if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) &&
- ( audio->config.in.codec & HB_ACODEC_FF_I_FLAG ) &&
- ( cc = hb_ffmpeg_context( audio->config.in.codec_param ) ) &&
+ if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) && cc &&
avcodec_find_decoder( cc->codec_id ) )
{
AVCodec *codec = avcodec_find_decoder( cc->codec_id );
@@ -1800,7 +1765,6 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
// For streams demuxed by us and decoded by ffmpeg, we can lookup the
// decoder name.
else if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) &&
- !( audio->config.in.codec & HB_ACODEC_FF_I_FLAG ) &&
avcodec_find_decoder( audio->config.in.codec_param ) )
{
codec_name = avcodec_find_decoder( audio->config.in.codec_param )->name;
@@ -1818,8 +1782,7 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
strlen(lang->native_name) ? lang->native_name : lang->eng_name,
codec_name );
- if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK) &&
- ( audio->config.in.codec & HB_ACODEC_FF_I_FLAG ) )
+ if ( audio->config.in.channel_layout )
{
int layout = audio->config.in.channel_layout;
char *desc = audio->config.lang.description +
@@ -2042,7 +2005,7 @@ static void add_audio_to_title(hb_title_t *title, int id)
return;
}
- set_audio_description( audio, lang_for_code( 0 ) );
+ set_audio_description( NULL, audio, lang_for_code( 0 ) );
// Sort by id when adding to the list
int i;
@@ -3124,182 +3087,78 @@ static void hb_ts_stream_reset(hb_stream_t *stream)
// ------------------------------------------------------------------
// Support for reading media files via the ffmpeg libraries.
-static void ffmpeg_add_codec( hb_stream_t *stream, int stream_index )
+static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title, int scan )
{
- // add a codec to the context here so it will be there when we
- // read the first packet.
- AVCodecContext *context = stream->ffmpeg_ic->streams[stream_index]->codec;
- context->workaround_bugs = FF_BUG_AUTODETECT;
- context->error_recognition = 1;
- context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
- AVCodec *codec = avcodec_find_decoder( context->codec_id );
- hb_ff_set_sample_fmt( context, codec );
-}
-
-// The ffmpeg stream reader / parser shares a lot of state with the
-// decoder via a codec context kept in the AVStream of the reader's
-// AVFormatContext. Since decoding is done in a different thread we
-// have to somehow pass this codec context to the decoder and we have
-// to do it before the first packet is read (so we can't put the info
-// in the buf we'll send downstream). Decoders don't have any way to
-// get to the stream directly (they're not passed the title or job
-// pointers during a scan) so this is a back door for the decoder to
-// get the codec context. We just stick the stream pointer in the next
-// slot an array of pointers maintained as a circular list then return
-// the index into the list combined with the ffmpeg stream index as the
-// codec_param that will be passed to the decoder init routine. We make
-// the list 'big' (enough for 1024 simultaneously open ffmpeg streams)
-// so that we don't have to do a complicated allocator or worry about
-// deleting entries on close.
-//
-// Entries can only be added to this list during a scan and are never
-// deleted so the list access doesn't require locking.
-static hb_stream_t **ffmpeg_streams; // circular list of stream pointers
-static int ffmpeg_stream_cur; // where we put the last stream pointer
-#define ffmpeg_sl_bits (10) // log2 stream list size (in entries)
-#define ffmpeg_sl_size (1 << ffmpeg_sl_bits)
-
-// add a stream to the list & return the appropriate codec_param to access it
-static int ffmpeg_codec_param( hb_stream_t *stream, int stream_index )
-{
- if ( !ffmpeg_streams )
- {
- ffmpeg_streams = calloc( ffmpeg_sl_size, sizeof(stream) );
- }
-
- // the title scan adds all the ffmpeg media streams at once so we
- // only add a new entry to our stream list if the stream is different
- // than last time.
- int slot = ffmpeg_stream_cur;
- if ( ffmpeg_streams[slot] != stream )
- {
- // new stream - put it in the next slot of the stream list
- slot = ++ffmpeg_stream_cur & (ffmpeg_sl_size - 1);
- ffmpeg_streams[slot] = stream;
- }
-
- ffmpeg_add_codec( stream, stream_index );
-
- return ( stream_index << ffmpeg_sl_bits ) | slot;
-}
-
-// we're about to open 'title' to convert it - remap the stream associated
-// with the video & audio codec params of the title to refer to 'stream'
-// (the original scan stream was closed and no longer exists).
-static void ffmpeg_remap_stream( hb_stream_t *stream, hb_title_t *title )
-{
- // 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);
- ffmpeg_streams[slot] = stream;
-
- // add codecs for all the streams used by the title
- ffmpeg_add_codec( stream, title->video_codec_param >> ffmpeg_sl_bits );
-
- int i;
- hb_audio_t *audio;
- for ( i = 0; ( audio = hb_list_item( title->list_audio, i ) ); ++i )
- {
- if ( audio->config.in.codec & HB_ACODEC_FF_MASK )
- {
- ffmpeg_add_codec( stream,
- audio->config.in.codec_param >> ffmpeg_sl_bits );
- }
- }
-}
-
-void *hb_ffmpeg_context( int codec_param )
-{
- if ( ffmpeg_streams == NULL )
- return NULL;
-
- int slot = codec_param & (ffmpeg_sl_size - 1);
- int stream_index = codec_param >> ffmpeg_sl_bits;
- return ffmpeg_streams[slot]->ffmpeg_ic->streams[stream_index]->codec;
-}
-
-void *hb_ffmpeg_avstream( int codec_param )
-{
- if ( ffmpeg_streams == NULL )
- return NULL;
-
- int slot = codec_param & (ffmpeg_sl_size - 1);
- int stream_index = codec_param >> ffmpeg_sl_bits;
- return ffmpeg_streams[slot]->ffmpeg_ic->streams[stream_index];
-}
-
-static AVFormatContext *ffmpeg_deferred_close;
-
-static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title )
-{
- if ( ffmpeg_deferred_close )
- {
- av_close_input_file( ffmpeg_deferred_close );
- ffmpeg_deferred_close = NULL;
- }
- AVFormatContext *ic;
+ AVFormatContext *info_ic = NULL, *reader_ic = NULL;
av_log_set_level( AV_LOG_ERROR );
- if ( av_open_input_file( &ic, stream->path, NULL, 0, NULL ) < 0 )
+
+ // FFMpeg has issues with seeking. After av_find_stream_info, the
+ // streams are left in an indeterminate position. So a seek is
+ // necessary to force things back to the beginning of the stream.
+ // But then the seek fails for some stream types. So the safest thing
+ // to do seems to be to open 2 AVFormatContext. One for probing info
+ // and the other for reading.
+ if ( av_open_input_file( &info_ic, stream->path, NULL, 0, NULL ) < 0 )
{
return 0;
}
- if ( av_find_stream_info( ic ) < 0 )
+ if ( av_find_stream_info( info_ic ) < 0 )
goto fail;
- stream->ffmpeg_ic = ic;
+ title->opaque_priv = (void*)info_ic;
+ stream->ffmpeg_info_ic = info_ic;
+ if ( av_open_input_file( &reader_ic, stream->path, NULL, 0, NULL ) < 0 )
+ {
+ goto fail;
+ }
+ stream->ffmpeg_reader_ic = reader_ic;
stream->hb_stream_type = ffmpeg;
stream->ffmpeg_pkt = malloc(sizeof(*stream->ffmpeg_pkt));
av_init_packet( stream->ffmpeg_pkt );
stream->chapter_end = INT64_MAX;
- if ( title )
+ if ( !scan )
{
// we're opening for read. scan passed out codec params that
// indexed its stream so we need to remap them so they point
// to this stream.
stream->ffmpeg_video_id = title->video_id;
- ffmpeg_remap_stream( stream, title );
av_log_set_level( AV_LOG_ERROR );
}
else
{
// we're opening for scan. let ffmpeg put some info into the
// log about what we've got.
+ stream->ffmpeg_video_id = title->video_id;
av_log_set_level( AV_LOG_INFO );
- av_dump_format( ic, 0, stream->path, 0 );
+ av_dump_format( info_ic, 0, stream->path, 0 );
av_log_set_level( AV_LOG_ERROR );
// accept this file if it has at least one video stream we can decode
int i;
- for (i = 0; i < ic->nb_streams; ++i )
+ for (i = 0; i < info_ic->nb_streams; ++i )
{
- if ( ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
+ if ( info_ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
{
break;
}
}
- if ( i >= ic->nb_streams )
+ if ( i >= info_ic->nb_streams )
goto fail;
}
return 1;
fail:
- av_close_input_file( ic );
+ if ( info_ic ) av_close_input_file( info_ic );
+ if ( reader_ic ) av_close_input_file( reader_ic );
return 0;
}
static void ffmpeg_close( hb_stream_t *d )
{
- // XXX since we're sharing the CodecContext with the downstream
- // decoder proc we can't close the stream. We need to reference count
- // this so we can close it when both are done with their instance but
- // for now just defer the close until the next stream open or close.
- if ( ffmpeg_deferred_close )
- {
- av_close_input_file( ffmpeg_deferred_close );
- }
- ffmpeg_deferred_close = d->ffmpeg_ic;
+ av_close_input_file( d->ffmpeg_info_ic );
+ av_close_input_file( d->ffmpeg_reader_ic );
if ( d->ffmpeg_pkt != NULL )
{
free( d->ffmpeg_pkt );
@@ -3309,7 +3168,7 @@ static void ffmpeg_close( hb_stream_t *d )
static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
{
- AVStream *st = stream->ffmpeg_ic->streams[id];
+ AVStream *st = stream->ffmpeg_info_ic->streams[id];
AVCodecContext *codec = st->codec;
AVMetadataTag *tag;
int layout;
@@ -3345,13 +3204,13 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
( codec->profile == FF_PROFILE_DTS_HD_MA ||
codec->profile == FF_PROFILE_DTS_HD_HRA ) )
{
- audio->config.in.codec = HB_ACODEC_DCA_HD | HB_ACODEC_FF_I_FLAG;
+ audio->config.in.codec = HB_ACODEC_DCA_HD;
}
else
{
- audio->config.in.codec = HB_ACODEC_FFMPEG | HB_ACODEC_FF_I_FLAG;
+ audio->config.in.codec = HB_ACODEC_FFMPEG;
}
- audio->config.in.codec_param = ffmpeg_codec_param( stream, id );
+ audio->config.in.codec_param = codec->codec_id;
audio->config.in.bitrate = codec->bit_rate? codec->bit_rate : 1;
audio->config.in.samplerate = codec->sample_rate;
@@ -3360,7 +3219,7 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
}
tag = av_metadata_get( st->metadata, "language", NULL, 0 );
- set_audio_description( audio,
+ set_audio_description( stream, audio,
lang_for_code2( tag ? tag->value : "und" ) );
hb_list_add( title->list_audio, audio );
@@ -3491,7 +3350,7 @@ static int ffmpeg_parse_vobsub_extradata( AVCodecContext *codec, hb_subtitle_t *
static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id )
{
- AVStream *st = stream->ffmpeg_ic->streams[id];
+ AVStream *st = stream->ffmpeg_info_ic->streams[id];
AVCodecContext *codec = st->codec;
hb_subtitle_t *subtitle = calloc( 1, sizeof(*subtitle) );
@@ -3560,7 +3419,7 @@ static char *get_ffmpeg_metadata_value( AVMetadata *m, char *key )
static void add_ffmpeg_attachment( hb_title_t *title, hb_stream_t *stream, int id )
{
- AVStream *st = stream->ffmpeg_ic->streams[id];
+ AVStream *st = stream->ffmpeg_info_ic->streams[id];
AVCodecContext *codec = st->codec;
enum attachtype type;
@@ -3586,12 +3445,11 @@ static void add_ffmpeg_attachment( hb_title_t *title, hb_stream_t *stream, int i
hb_list_add(title->list_attachment, attachment);
}
-static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream )
+static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream, hb_title_t *title )
{
- AVFormatContext *ic = stream->ffmpeg_ic;
+ AVFormatContext *ic = stream->ffmpeg_info_ic;
// 'Barebones Title'
- hb_title_t *title = hb_title_init( stream->path, 0 );
title->type = HB_FF_STREAM_TYPE;
title->index = 1;
@@ -3629,16 +3487,18 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream )
}
title->video_id = i;
stream->ffmpeg_video_id = i;
+ if ( ic->streams[i]->sample_aspect_ratio.num &&
+ ic->streams[i]->sample_aspect_ratio.den )
+ {
+ title->pixel_aspect_width = ic->streams[i]->sample_aspect_ratio.num;
+ title->pixel_aspect_height = ic->streams[i]->sample_aspect_ratio.den;
+ }
if ( context->codec_id == CODEC_ID_H264 )
title->flags |= HBTF_NO_IDR;
- // We have to use the 'internal' avcodec decoder because
- // it needs to share the codec context from this video
- // stream. The parser internal to av_read_frame
- // passes a bunch of state info to the decoder via the context.
- title->video_codec = WORK_DECAVCODECVI;
- title->video_codec_param = ffmpeg_codec_param( stream, i );
+ title->video_codec = WORK_DECAVCODECV;
+ title->video_codec_param = context->codec_id;
}
else if ( ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
avcodec_find_decoder( ic->streams[i]->codec->codec_id ) )
@@ -3718,7 +3578,7 @@ static int ffmpeg_is_keyframe( hb_stream_t *stream )
{
uint8_t *pkt;
- switch ( stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->codec_id )
+ switch ( stream->ffmpeg_info_ic->streams[stream->ffmpeg_video_id]->codec->codec_id )
{
case CODEC_ID_VC1:
// XXX the VC1 codec doesn't mark key frames so to get previews
@@ -3740,7 +3600,7 @@ static int ffmpeg_is_keyframe( hb_stream_t *stream )
// there are bframes or not so we have to look at the sequence
// header to get that.
pkt = stream->ffmpeg_pkt->data;
- uint8_t *seqhdr = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->extradata;
+ uint8_t *seqhdr = stream->ffmpeg_info_ic->streams[stream->ffmpeg_video_id]->codec->extradata;
int pshift = 2;
if ( ( seqhdr[3] & 0x02 ) == 0 )
// no FINTERPFLAG
@@ -3760,13 +3620,13 @@ static int ffmpeg_is_keyframe( hb_stream_t *stream )
return ( stream->ffmpeg_pkt->flags & AV_PKT_FLAG_KEY );
}
-static hb_buffer_t * ffmpeg_read( hb_stream_t *stream )
+hb_buffer_t * hb_ffmpeg_read( hb_stream_t *stream )
{
int err;
hb_buffer_t * buf;
again:
- if ( ( err = av_read_frame( stream->ffmpeg_ic, stream->ffmpeg_pkt )) < 0 )
+ if ( ( err = av_read_frame( stream->ffmpeg_reader_ic, stream->ffmpeg_pkt )) < 0 )
{
// av_read_frame can return EAGAIN. In this case, it expects
// to be called again to get more data.
@@ -3818,7 +3678,7 @@ static hb_buffer_t * ffmpeg_read( hb_stream_t *stream )
{
hb_log( "ffmpeg_read: pkt too big: %d bytes", stream->ffmpeg_pkt->size );
av_free_packet( stream->ffmpeg_pkt );
- return ffmpeg_read( stream );
+ return hb_ffmpeg_read( stream );
}
buf = hb_buffer_init( stream->ffmpeg_pkt->size );
memcpy( buf->data, stream->ffmpeg_pkt->data, stream->ffmpeg_pkt->size );
@@ -3827,7 +3687,7 @@ static hb_buffer_t * ffmpeg_read( hb_stream_t *stream )
// compute a conversion factor to go from the ffmpeg
// timebase for the stream to HB's 90kHz timebase.
- AVStream *s = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index];
+ AVStream *s = stream->ffmpeg_info_ic->streams[stream->ffmpeg_pkt->stream_index];
double tsconv = 90000. * (double)s->time_base.num / (double)s->time_base.den;
buf->start = av_to_hb_pts( stream->ffmpeg_pkt->pts, tsconv );
@@ -3859,8 +3719,8 @@ static hb_buffer_t * ffmpeg_read( hb_stream_t *stream )
*/
enum CodecID ffmpeg_pkt_codec;
enum AVMediaType codec_type;
- ffmpeg_pkt_codec = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_id;
- codec_type = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_type;
+ ffmpeg_pkt_codec = stream->ffmpeg_info_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_id;
+ codec_type = stream->ffmpeg_info_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_type;
switch ( codec_type )
{
case AVMEDIA_TYPE_VIDEO:
@@ -3923,38 +3783,37 @@ static hb_buffer_t * ffmpeg_read( hb_stream_t *stream )
static int ffmpeg_seek( hb_stream_t *stream, float frac )
{
- AVFormatContext *ic = stream->ffmpeg_ic;
+ AVFormatContext *ic = stream->ffmpeg_reader_ic;
if ( frac > 0. )
{
- int64_t pos = (double)ic->duration * (double)frac;
- if ( ic->start_time != AV_NOPTS_VALUE && ic->start_time > 0 )
- {
- pos += ic->start_time;
- }
- av_seek_frame( ic, -1, pos, 0 );
- stream->need_keyframe = 1;
- ffmpeg_flush_stream_buffers( stream );
+ int64_t pos = (double)stream->ffmpeg_info_ic->duration * (double)frac +
+ ffmpeg_initial_timestamp( stream );
+ avformat_seek_file( ic, -1, 0, pos, pos, AVSEEK_FLAG_BACKWARD);
}
else
{
- av_seek_frame( ic, -1, 0LL, AVSEEK_FLAG_BACKWARD );
- ffmpeg_flush_stream_buffers( stream );
+ int64_t pos = ffmpeg_initial_timestamp( stream );
+ avformat_seek_file( ic, -1, 0, pos, pos, AVSEEK_FLAG_BACKWARD);
}
+ stream->need_keyframe = 1;
return 1;
}
// Assumes that we are always seeking forward
static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts )
{
- AVFormatContext *ic = stream->ffmpeg_ic;
+ AVFormatContext *ic = stream->ffmpeg_reader_ic;
int64_t pos;
int ret;
pos = ts * AV_TIME_BASE / 90000 + ffmpeg_initial_timestamp( stream );
+ AVStream *st = stream->ffmpeg_info_ic->streams[stream->ffmpeg_video_id];
+ // timebase must be adjusted to match timebase of stream we are
+ // using for seeking.
+ pos = av_rescale(pos, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
stream->need_keyframe = 1;
// Seek to the nearest timestamp before that requested where
// there is an I-frame
- ret = av_seek_frame( ic, -1, pos, AVSEEK_FLAG_BACKWARD );
- ffmpeg_flush_stream_buffers( stream );
+ ret = avformat_seek_file( ic, stream->ffmpeg_video_id, 0, pos, pos, 0);
return ret;
}
diff --git a/libhb/work.c b/libhb/work.c
index 90209dbd2..ffd48ec4b 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -116,10 +116,7 @@ hb_work_object_t * hb_codec_decoder( int codec )
default:
if ( codec & HB_ACODEC_FF_MASK )
{
- if ( codec & HB_ACODEC_FF_I_FLAG )
- return hb_get_work( WORK_DECAVCODECAI );
- else
- return hb_get_work( WORK_DECAVCODEC );
+ return hb_get_work( WORK_DECAVCODEC );
}
break;
}
@@ -414,27 +411,6 @@ void correct_framerate( hb_job_t * job )
interjob->vrate_base = job->vrate_base;
}
-static int check_ff_audio( hb_list_t *list_audio, hb_audio_t *ff_audio )
-{
- int i;
-
- for( i = 0; i < hb_list_count( list_audio ); i++ )
- {
- hb_audio_t * audio = hb_list_item( list_audio, i );
-
- if ( audio == ff_audio )
- break;
-
- if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) &&
- audio->id == ff_audio->id )
- {
- hb_list_add( audio->priv.ff_audio_list, ff_audio );
- return 1;
- }
- }
- return 0;
-}
-
/**
* Job initialization rountine.
* Initializes fifos.
@@ -719,19 +695,7 @@ static void do_job( hb_job_t * job )
audio->priv.fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
audio->priv.fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
audio->priv.fifo_out = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
-
- audio->priv.ff_audio_list = hb_list_init();
- if ( audio->config.in.codec & HB_ACODEC_FF_MASK )
- {
- if ( !check_ff_audio( title->list_audio, audio ) )
- {
- audio->priv.fifo_in = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
- }
- }
- else
- {
- audio->priv.fifo_in = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
- }
+ audio->priv.fifo_in = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
}
}
@@ -980,8 +944,15 @@ static void do_job( hb_job_t * job )
hb_display_job_info( job );
/* Init read & write threads */
- job->reader = hb_reader_init( job );
-
+ hb_work_object_t *reader = hb_get_work(WORK_READER);
+ if ( reader->init( reader, job ) )
+ {
+ hb_error( "Failure to initialise thread '%s'", reader->name );
+ *job->die = 1;
+ goto cleanup;
+ }
+ reader->done = &job->done;
+ reader->thread = hb_thread_init( reader->name, ReadLoop, reader, HB_NORMAL_PRIORITY );
job->done = 0;
@@ -1111,8 +1082,12 @@ cleanup:
hb_list_close( &job->list_work );
/* Stop the read thread */
- if( job->reader != NULL )
- hb_thread_close( &job->reader );
+ if( reader->thread != NULL )
+ {
+ hb_thread_close( &reader->thread );
+ reader->close( reader );
+ }
+ free( reader );
/* Close fifos */
hb_fifo_close( &job->fifo_mpeg2 );