summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorvan <[email protected]>2008-05-31 17:00:42 +0000
committervan <[email protected]>2008-05-31 17:00:42 +0000
commitafbd06d8d6151b69bbb18adf83cfab8d680c3946 (patch)
tree4e7be731854c8b8db8f87934c301c60a9155387c /libhb
parentd0550da569b56a71857c34463c5b030172d47291 (diff)
- support blu-ray, avchd & dvb x264
- support video files handled by ffmpeg (avi, mkv, mp4, etc.) git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1480 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/Makefile2
-rw-r--r--libhb/common.c3
-rw-r--r--libhb/common.h48
-rw-r--r--libhb/deblock.c2
-rw-r--r--libhb/deca52.c97
-rw-r--r--libhb/decavcodec.c684
-rw-r--r--libhb/decdca.c100
-rw-r--r--libhb/declpcm.c45
-rw-r--r--libhb/decmpeg2.c65
-rw-r--r--libhb/decomb.c18
-rw-r--r--libhb/deinterlace.c2
-rw-r--r--libhb/demuxmpeg.c41
-rw-r--r--libhb/denoise.c2
-rw-r--r--libhb/detelecine.c2
-rw-r--r--libhb/encavcodec.c2
-rw-r--r--libhb/fifo.c285
-rw-r--r--libhb/hb.c12
-rw-r--r--libhb/hb.h7
-rw-r--r--libhb/internal.h28
-rw-r--r--libhb/muxavi.c2
-rw-r--r--libhb/reader.c66
-rw-r--r--libhb/render.c4
-rw-r--r--libhb/scan.c454
-rwxr-xr-xlibhb/stream.c721
-rw-r--r--libhb/sync.c30
-rw-r--r--libhb/work.c351
26 files changed, 2065 insertions, 1008 deletions
diff --git a/libhb/Makefile b/libhb/Makefile
index f0d4aebc4..72f4a29d8 100644
--- a/libhb/Makefile
+++ b/libhb/Makefile
@@ -39,7 +39,7 @@ CONTRIBS = ../contrib/lib/liba52.a ../contrib/lib/libavformat.a \
../contrib/lib/libsamplerate.a ../contrib/lib/libx264.a \
../contrib/lib/libxvidcore.a ../contrib/lib/libmp4v2.a \
../contrib/lib/libmkv.a ../contrib/lib/libswscale.a \
- ../contrib/lib/libtheora.a
+ ../contrib/lib/libtheora.a ../contrib/lib/libfaad.a
CFLAGS += -I../contrib/include -D__LIBHB__ -DUSE_PTHREAD -DHB_VERSION=\"$(HB_VERSION)\" -DHB_BUILD=$(HB_BUILD) $(SYSDEF)
diff --git a/libhb/common.c b/libhb/common.c
index 00810dd79..bd183e9f9 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -593,6 +593,9 @@ hb_title_t * hb_title_init( char * dvd, int index )
t->list_chapter = hb_list_init();
t->list_subtitle = hb_list_init();
strcat( t->dvd, dvd );
+ // default to decoding mpeg2
+ t->video_id = 0xE0;
+ t->video_codec = WORK_DECMPEG2;
return t;
}
diff --git a/libhb/common.h b/libhb/common.h
index fc0001fac..d7728770e 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -251,6 +251,7 @@ struct hb_job_s
#define HB_ACODEC_MPGA 0x001000
#define HB_ACODEC_LPCM 0x002000
#define HB_ACODEC_DCA 0x004000
+#define HB_ACODEC_FFMPEG 0x008000
/* Audio Mixdown */
/* define some masks, used to extract the various information from the HB_AMIXDOWN_XXXX values */
@@ -329,8 +330,9 @@ struct hb_audio_config_s
/* Input */
struct
{
- int track; /* Input track number */
- PRIVATE uint32_t codec; /* Input audio codec */
+ int track; /* Input track number */
+ PRIVATE uint32_t codec; /* Input audio codec */
+ PRIVATE uint32_t codec_param; /* per-codec config info */
PRIVATE int samplerate; /* Input sample rate (Hz) */
PRIVATE int bitrate; /* Input bitrate (kbps) */
PRIVATE int channel_layout;
@@ -345,6 +347,7 @@ struct hb_audio_config_s
PRIVATE int ac3; /* flags.ac3 is only set when the source audio format is HB_ACODEC_AC3 */
PRIVATE int dca; /* flags.dca is only set when the source audio format is HB_ACODEC_DCA */
} flags;
+#define AUDIO_F_DOLBY (1 << 31) /* set if source uses Dolby Surround */
struct
{
@@ -437,7 +440,11 @@ struct hb_title_s
int rate;
int rate_base;
int crop[4];
+ enum { HB_MPEG2_DEMUXER = 0, HB_NULL_DEMUXER } demuxer;
int detected_interlacing;
+ int video_id; /* demuxer stream id for video */
+ int video_codec; /* worker object id of video codec */
+ int video_codec_param; /* codec specific config */
uint32_t palette[16];
@@ -501,6 +508,27 @@ struct hb_state_s
} param;
};
+typedef struct hb_work_info_s
+{
+ const char *name;
+ int profile;
+ int level;
+ int bitrate;
+ int rate;
+ int rate_base;
+ int flags;
+ union {
+ struct { // info only valid for video decoders
+ int width;
+ int height;
+ double aspect;
+ };
+ struct { // info only valid for audio decoders
+ int channel_layout;
+ };
+ };
+} hb_work_info_t;
+
struct hb_work_object_s
{
int id;
@@ -511,6 +539,18 @@ struct hb_work_object_s
int (* work) ( hb_work_object_t *, hb_buffer_t **,
hb_buffer_t ** );
void (* close) ( hb_work_object_t * );
+ /* the info entry point is used by scan to get bitstream information
+ * during a decode (i.e., it should only be called after at least one
+ * call to the 'work' entry point). currently it's only called for
+ * video streams & can be null for other work objects. */
+ int (* info) ( hb_work_object_t *, hb_work_info_t * );
+ /* the bitstream info entry point is used by scan to get bitstream
+ * information from a buffer. it doesn't have to be called during a
+ * decode (it can be called even if init & work haven't been).
+ * currently it's only called for audio streams & can be null for
+ * other work objects. */
+ int (* bsinfo) ( hb_work_object_t *, const hb_buffer_t *,
+ hb_work_info_t * );
hb_fifo_t * fifo_in;
hb_fifo_t * fifo_out;
@@ -524,6 +564,7 @@ struct hb_work_object_s
hb_thread_t * thread;
volatile int * done;
int status;
+ int codec_param;
hb_work_object_t * next;
int thread_sleep_interval;
@@ -541,6 +582,9 @@ 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_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;
diff --git a/libhb/deblock.c b/libhb/deblock.c
index dbf39c606..42b7d8c4c 100644
--- a/libhb/deblock.c
+++ b/libhb/deblock.c
@@ -17,7 +17,7 @@
*/
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#include "mpeg2dec/mpeg2.h"
#define PP7_QP_DEFAULT 0
diff --git a/libhb/deca52.c b/libhb/deca52.c
index 6695d3687..1495611da 100644
--- a/libhb/deca52.c
+++ b/libhb/deca52.c
@@ -38,9 +38,11 @@ struct hb_work_private_s
};
-int deca52Init( hb_work_object_t *, hb_job_t * );
-int deca52Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
-void deca52Close( hb_work_object_t * );
+static int deca52Init( hb_work_object_t *, hb_job_t * );
+static int deca52Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+static void deca52Close( hb_work_object_t * );
+static int deca52BSInfo( hb_work_object_t * , const hb_buffer_t *,
+ hb_work_info_t * );
hb_work_object_t hb_deca52 =
{
@@ -48,7 +50,9 @@ hb_work_object_t hb_deca52 =
"AC3 decoder",
deca52Init,
deca52Work,
- deca52Close
+ deca52Close,
+ 0,
+ deca52BSInfo
};
/***********************************************************************
@@ -85,7 +89,7 @@ static sample_t dynrng_call (sample_t c, void *data)
***********************************************************************
* Allocate the work object, initialize liba52
**********************************************************************/
-int deca52Init( hb_work_object_t * w, hb_job_t * job )
+static int deca52Init( hb_work_object_t * w, hb_job_t * job )
{
hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
hb_audio_t * audio = w->audio;
@@ -119,7 +123,7 @@ int deca52Init( hb_work_object_t * w, hb_job_t * job )
***********************************************************************
* Free memory
**********************************************************************/
-void deca52Close( hb_work_object_t * w )
+static void deca52Close( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
a52_free( pv->state );
@@ -134,7 +138,7 @@ void deca52Close( hb_work_object_t * w )
* Add the given buffer to the data we already have, and decode as much
* as we can
**********************************************************************/
-int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -276,3 +280,82 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
return buf;
}
+static int deca52BSInfo( hb_work_object_t *w, const hb_buffer_t *b,
+ hb_work_info_t *info )
+{
+ int i, rate, bitrate, flags;
+
+ memset( info, 0, sizeof(*info) );
+
+ /* since AC3 frames don't line up with MPEG ES frames scan the
+ * entire frame for an AC3 sync pattern. */
+ for ( i = 0; i < b->size - 7; ++i )
+ {
+ if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) != 0 )
+ {
+ break;
+ }
+ }
+ if ( i >= b->size - 7 )
+ {
+ /* didn't find AC3 sync */
+ return 0;
+ }
+
+ info->name = "AC-3";
+ info->rate = rate;
+ info->rate_base = 1;
+ info->bitrate = bitrate;
+ info->flags = flags;
+ if ( (flags & A52_CHANNEL_MASK) == A52_DOLBY )
+ {
+ info->flags |= AUDIO_F_DOLBY;
+ }
+
+ switch( flags & A52_CHANNEL_MASK )
+ {
+ /* mono sources */
+ case A52_MONO:
+ case A52_CHANNEL1:
+ case A52_CHANNEL2:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_MONO;
+ break;
+ /* stereo input */
+ case A52_CHANNEL:
+ case A52_STEREO:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ break;
+ /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */
+ case A52_DOLBY:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_DOLBY;
+ break;
+ /* 3F/2R input */
+ case A52_3F2R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_3F2R;
+ break;
+ /* 3F/1R input */
+ case A52_3F1R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_3F1R;
+ break;
+ /* other inputs */
+ case A52_3F:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_3F;
+ break;
+ case A52_2F1R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_2F1R;
+ break;
+ case A52_2F2R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_2F2R;
+ break;
+ /* unknown */
+ default:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ }
+
+ if (flags & A52_LFE)
+ {
+ info->channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE;
+ }
+
+ return 1;
+}
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index 5ca114eee..04b12f678 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -6,11 +6,14 @@
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
-int decavcodecInit( hb_work_object_t *, hb_job_t * );
-int decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
-void decavcodecClose( hb_work_object_t * );
+static int decavcodecInit( hb_work_object_t *, hb_job_t * );
+static int decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+static void decavcodecClose( 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 * );
hb_work_object_t hb_decavcodec =
{
@@ -18,25 +21,34 @@ hb_work_object_t hb_decavcodec =
"MPGA decoder (libavcodec)",
decavcodecInit,
decavcodecWork,
- decavcodecClose
+ decavcodecClose,
+ decavcodecInfo,
+ decavcodecBSInfo
};
struct hb_work_private_s
{
- hb_job_t * job;
-
- AVCodecContext * context;
- int64_t pts_last;
+ hb_job_t *job;
+ AVCodecContext *context;
AVCodecParserContext *parser;
+ hb_list_t *list;
+ double pts_next; // next pts we expect to generate
+ int64_t pts; // (video) pts passing from parser to decoder
+ int64_t chap_time; // time of next chap mark (if new_chap != 0)
+ int new_chap;
+ int ignore_pts; // workaround M$ bugs
+ int nframes;
+ double duration; // frame duration (for video)
};
+
/***********************************************************************
* hb_work_decavcodec_init
***********************************************************************
*
**********************************************************************/
-int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
+static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
{
AVCodec * codec;
@@ -45,12 +57,15 @@ int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
pv->job = job;
- codec = avcodec_find_decoder( CODEC_ID_MP2 );
- pv->parser = av_parser_init(CODEC_ID_MP2);
+ int codec_id = w->codec_param;
+ /*XXX*/
+ if ( codec_id == 0 )
+ codec_id = CODEC_ID_MP2;
+ codec = avcodec_find_decoder( codec_id );
+ pv->parser = av_parser_init( codec_id );
pv->context = avcodec_alloc_context();
avcodec_open( pv->context, codec );
- pv->pts_last = -1;
return 0;
}
@@ -60,11 +75,21 @@ int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
***********************************************************************
*
**********************************************************************/
-void decavcodecClose( hb_work_object_t * w )
+static void decavcodecClose( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
- av_parser_close(pv->parser);
- avcodec_close( pv->context );
+ if ( pv->parser )
+ {
+ av_parser_close(pv->parser);
+ }
+ if ( pv->context && pv->context->codec )
+ {
+ avcodec_close( pv->context );
+ }
+ if ( pv->list )
+ {
+ hb_list_close( &pv->list );
+ }
}
/***********************************************************************
@@ -72,7 +97,7 @@ void decavcodecClose( hb_work_object_t * w )
***********************************************************************
*
**********************************************************************/
-int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -85,28 +110,24 @@ int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
*buf_out = NULL;
- if( in->start < 0 ||
- ( pv->pts_last > 0 &&
- in->start > pv->pts_last &&
- in->start - pv->pts_last < 5000 ) ) /* Hacky */
- {
- cur = pv->pts_last;
- }
- else
- {
- cur = in->start;
- }
+ cur = ( in->start < 0 )? pv->pts_next : in->start;
pos = 0;
while( pos < in->size )
{
- len = av_parser_parse(pv->parser, pv->context,&parser_output_buffer,&parser_output_buffer_len,in->data + pos,in->size - pos,cur,cur);
-
+ len = av_parser_parse( pv->parser, pv->context,
+ &parser_output_buffer, &parser_output_buffer_len,
+ in->data + pos, in->size - pos, cur, cur );
out_size = 0;
uncompressed_len = 0;
if (parser_output_buffer_len)
- uncompressed_len = avcodec_decode_audio( pv->context, buffer, &out_size,
- parser_output_buffer, parser_output_buffer_len );
+ {
+ out_size = sizeof(buffer);
+ uncompressed_len = avcodec_decode_audio2( pv->context, buffer,
+ &out_size,
+ parser_output_buffer,
+ parser_output_buffer_len );
+ }
if( out_size )
{
short * s16;
@@ -152,8 +173,605 @@ int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
pos += len;
}
- pv->pts_last = cur;
+ pv->pts_next = cur;
+
+ return HB_WORK_OK;
+}
+
+static int decavcodecInfo( hb_work_object_t *w, hb_work_info_t *info )
+{
+ hb_work_private_t *pv = w->private_data;
+
+ memset( info, 0, sizeof(*info) );
+
+ if ( pv && pv->context )
+ {
+ AVCodecContext *context = pv->context;
+ info->bitrate = context->bit_rate;
+ info->rate = context->time_base.num;
+ info->rate_base = context->time_base.den;
+ info->profile = context->profile;
+ info->level = context->level;
+ return 1;
+ }
+ return 0;
+}
+
+static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
+ hb_work_info_t *info )
+{
+ hb_work_private_t *pv = w->private_data;
+
+ memset( info, 0, sizeof(*info) );
+
+ if ( pv && pv->context )
+ {
+ return decavcodecInfo( w, info );
+ }
+ // XXX
+ // We should parse the bitstream to find its parameters but for right
+ // now we just return dummy values if there's a codec that will handle it.
+ AVCodec *codec = avcodec_find_decoder( w->codec_param? w->codec_param :
+ CODEC_ID_MP2 );
+ if ( codec )
+ {
+ static char codec_name[64];
+
+ info->name = strncpy( codec_name, codec->name, sizeof(codec_name)-1 );
+ info->bitrate = 384000;
+ info->rate = 48000;
+ info->rate_base = 1;
+ info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ return 1;
+ }
+ return -1;
+}
+
+/* -------------------------------------------------------------
+ * General purpose video decoder using libavcodec
+ */
+
+static uint8_t *copy_plane( uint8_t *dst, uint8_t* src, int dstride, int sstride,
+ int h )
+{
+ if ( dstride == sstride )
+ {
+ memcpy( dst, src, dstride * h );
+ return dst + dstride * h;
+ }
+ int lbytes = dstride <= sstride? dstride : sstride;
+ while ( --h >= 0 )
+ {
+ memcpy( dst, src, lbytes );
+ src += sstride;
+ dst += dstride;
+ }
+ return dst;
+}
+
+/* Note: assumes frame format is PIX_FMT_YUV420P */
+static hb_buffer_t *copy_frame( AVCodecContext *context, AVFrame *frame )
+{
+ int w = context->width, h = context->height;
+ hb_buffer_t *buf = hb_buffer_init( w * h * 3 / 2 );
+ uint8_t *dst = buf->data;
+
+ dst = copy_plane( dst, frame->data[0], w, frame->linesize[0], h );
+ w >>= 1; h >>= 1;
+ dst = copy_plane( dst, frame->data[1], w, frame->linesize[1], h );
+ dst = copy_plane( dst, frame->data[2], w, frame->linesize[2], h );
+
+ return buf;
+}
+
+static int get_frame_buf( AVCodecContext *context, AVFrame *frame )
+{
+ hb_work_private_t *pv = context->opaque;
+ frame->pts = pv->pts;
+ pv->pts = -1;
+
+ return avcodec_default_get_buffer( context, frame );
+}
+
+static void log_chapter( hb_work_private_t *pv, int chap_num, int64_t pts )
+{
+ hb_chapter_t *c = hb_list_item( pv->job->title->list_chapter, chap_num - 1 );
+ hb_log( "%s: \"%s\" (%d) at frame %u time %lld", pv->context->codec->name,
+ c->title, chap_num, pv->nframes, pts );
+}
+
+static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size )
+{
+ int got_picture;
+ AVFrame frame;
+
+ avcodec_decode_video( pv->context, &frame, &got_picture, data, size );
+ if( got_picture )
+ {
+ // ffmpeg makes it hard to attach a pts to a frame. if the MPEG ES
+ // packet had a pts we handed it to av_parser_parse (if the packet had
+ // no pts we set it to -1 but before the parse we can't distinguish between
+ // the start of a video frame with no pts & an intermediate packet of
+ // some frame which never has a pts). we hope that when parse returns
+ // the frame to us the pts we originally handed it will be in parser->pts.
+ // we put this pts into pv->pts so that when a avcodec_decode_video
+ // finally gets around to allocating an AVFrame to hold the decoded
+ // frame we can stuff that pts into the frame. if all of these relays
+ // worked at this point frame.pts should hold the frame's pts from the
+ // original data stream or -1 if it didn't have one. in the latter case
+ // we generate the next pts in sequence for it.
+ double pts = frame.pts;
+ if ( pts < 0 )
+ {
+ pts = pv->pts_next;
+ }
+ if ( pv->duration == 0 )
+ {
+ pv->duration = 90000. * pv->context->time_base.num /
+ pv->context->time_base.den;
+ }
+ double frame_dur = pv->duration;
+ frame_dur += frame.repeat_pict * frame_dur * 0.5;
+ pv->pts_next = pts + frame_dur;
+
+ hb_buffer_t *buf = copy_frame( pv->context, &frame );
+ buf->start = pts;
+
+ if ( pv->new_chap && buf->start >= pv->chap_time )
+ {
+ buf->new_chap = pv->new_chap;
+ pv->new_chap = 0;
+ pv->chap_time = 0;
+ if ( pv->job )
+ {
+ log_chapter( pv, buf->new_chap, buf->start );
+ }
+ }
+ else if ( pv->job && pv->nframes == 0 )
+ {
+ log_chapter( pv, pv->job->chapter_start, buf->start );
+ }
+ hb_list_add( pv->list, buf );
+ ++pv->nframes;
+ }
+ return got_picture;
+}
+
+static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size,
+ int64_t pts, int64_t dts )
+{
+ /*
+ * 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
+ * generally a frame in the parser & one or more frames in the decoder
+ * (depending on the bframes setting).
+ */
+ int pos = 0;
+ do {
+ uint8_t *pout;
+ int pout_len;
+ int len = av_parser_parse( pv->parser, pv->context, &pout, &pout_len,
+ data + pos, size - pos, pts, dts );
+ pos += len;
+
+ if ( pout_len > 0 )
+ {
+ pv->pts = pv->parser->pts;
+ decodeFrame( pv, pout, pout_len );
+ }
+ } while ( pos < size );
+
+ /* the stuff above flushed the parser, now flush the decoder */
+ while ( size == 0 && decodeFrame( pv, NULL, 0 ) )
+ {
+ }
+}
+
+static hb_buffer_t *link_buf_list( hb_work_private_t *pv )
+{
+ hb_buffer_t *head = hb_list_item( pv->list, 0 );
+
+ if ( head )
+ {
+ hb_list_rem( pv->list, head );
+
+ hb_buffer_t *last = head, *buf;
+
+ while ( ( buf = hb_list_item( pv->list, 0 ) ) != NULL )
+ {
+ hb_list_rem( pv->list, buf );
+ last->next = buf;
+ last = buf;
+ }
+ }
+ return head;
+}
+
+
+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;
+ pv->list = hb_list_init();
+
+ int codec_id = w->codec_param;
+ pv->parser = av_parser_init( codec_id );
+ pv->context = avcodec_alloc_context2( CODEC_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;
+
+ AVCodec *codec = avcodec_find_decoder( codec_id );
+
+ // we can't call the avstream funcs but the read_header func in the
+ // AVInputFormat may set up some state in the AVContext. In particular
+ // vc1t_read_header allocates 'extradata' to deal with header issues
+ // related to Microsoft's bizarre engineering notions. We alloc a chunk
+ // of space to make vc1 work then associate the codec with the context.
+ pv->context->extradata_size = 32;
+ pv->context->extradata = av_malloc(pv->context->extradata_size);
+ avcodec_open( pv->context, codec );
+
+ return 0;
+}
+
+static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out )
+{
+ hb_work_private_t *pv = w->private_data;
+ hb_buffer_t *in = *buf_in;
+ int64_t pts = -1;
+ int64_t dts = pts;
+
+ *buf_in = NULL;
+
+ /* if we got an empty buffer signaling end-of-stream send it downstream */
+ if ( in->size == 0 )
+ {
+ decodeVideo( pv, in->data, in->size, pts, dts );
+ hb_list_add( pv->list, in );
+ *buf_out = link_buf_list( pv );
+ hb_log( "%s done: %d frames", pv->context->codec->name, pv->nframes );
+ return HB_WORK_DONE;
+ }
+
+ if( in->start >= 0 )
+ {
+ pts = in->start;
+ dts = in->renderOffset;
+ }
+ if ( in->new_chap )
+ {
+ pv->new_chap = in->new_chap;
+ pv->chap_time = pts >= 0? pts : pv->pts_next;
+ }
+ decodeVideo( pv, in->data, in->size, 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 )
+{
+ hb_work_private_t *pv = w->private_data;
+
+ memset( info, 0, sizeof(*info) );
+
+ if ( pv && pv->context )
+ {
+ 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;
+
+ /* 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. We round in the second decimal digit because we
+ * scale the (integer) aspect by 9 to preserve the 1st digit. */
+ info->aspect = ( (double)context->sample_aspect_ratio.num *
+ (double)context->width /
+ (double)context->sample_aspect_ratio.den /
+ (double)context->height + 0.05 ) * HB_ASPECT_BASE;
+
+ if( context->sample_aspect_ratio.num == 0 )
+ {
+ info->aspect = (double)context->width / (double)context->height * HB_ASPECT_BASE;
+ }
+ 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 );
+ avcodec_open( pv->context, codec );
+ }
+ // set up our best guess at the frame duration.
+ // the frame rate in the codec seems to be bogus but it's ok in the stream.
+ AVStream *st = hb_ffmpeg_avstream( w->codec_param );
+ AVRational tb = st->time_base;
+ if ( st->r_frame_rate.den && st->r_frame_rate.num )
+ {
+ tb.num = st->r_frame_rate.den;
+ tb.den = st->r_frame_rate.num;
+ }
+ pv->duration = 90000. * tb.num / tb.den;
+
+ // 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;
+}
+
+static void prepare_ffmpeg_buffer( hb_buffer_t * in )
+{
+ // ffmpeg requires an extra 8 bytes of zero at the end of the buffer and
+ // will seg fault in odd, data dependent ways if it's not there. (my guess
+ // is this is a case of a local performance optimization creating a global
+ // performance degradation since all the time wasted by extraneous data
+ // copies & memory zeroing has to be huge compared to the minor reduction
+ // in inner-loop instructions this affords - modern cpus bottleneck on
+ // memory bandwidth not instruction bandwidth).
+ if ( in->size + FF_INPUT_BUFFER_PADDING_SIZE > in->alloc )
+ {
+ // have to realloc to add the padding
+ hb_buffer_realloc( in, in->size + FF_INPUT_BUFFER_PADDING_SIZE );
+ }
+ memset( in->data + in->size, 0, FF_INPUT_BUFFER_PADDING_SIZE );
+}
+
+static int decavcodecviInit( 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;
+ pv->list = hb_list_init();
+
+ return 0;
+}
+
+static int decavcodecviWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out )
+{
+ hb_work_private_t *pv = w->private_data;
+ if ( ! pv->context )
+ {
+ init_ffmpeg_context( w );
+
+ switch ( pv->context->codec_id )
+ {
+ // These are the only formats whose timestamps we'll believe.
+ // All others are treated as CFR (i.e., we take the first timestamp
+ // then generate all the others from the frame rate). The reason for
+ // this is that the M$ encoders are so frigging buggy with garbage
+ // like packed b-frames (vfw divx mpeg4) that believing their timestamps
+ // results in discarding more than half the video frames because they'll
+ // be out of sequence (and attempting to reseqence them doesn't work
+ // because it's the timestamps that are wrong, not the decoded frame
+ // order). All hail Redmond, ancestral home of the rich & stupid.
+ case CODEC_ID_MPEG2VIDEO:
+ case CODEC_ID_RAWVIDEO:
+ case CODEC_ID_H264:
+ case CODEC_ID_VC1:
+ break;
+
+ default:
+ pv->ignore_pts = 1;
+ break;
+ }
+ }
+ hb_buffer_t *in = *buf_in;
+ int64_t pts = -1;
+
+ *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 ( decodeFrame( pv, NULL, 0 ) )
+ {
+ }
+ hb_list_add( pv->list, in );
+ *buf_out = link_buf_list( pv );
+ hb_log( "%s done: %d frames", pv->context->codec->name, pv->nframes );
+ return HB_WORK_DONE;
+ }
+
+ if( in->start >= 0 )
+ {
+ // use the first timestamp as our 'next expected' pts
+ if ( pv->pts_next <= 0 )
+ {
+ pv->pts_next = in->start;
+ }
+
+ if ( ! pv->ignore_pts )
+ {
+ pts = in->start;
+ if ( pv->pts > 0 )
+ {
+ hb_log( "overwriting pts %lld with %lld (diff %d)",
+ pv->pts, pts, pts - pv->pts );
+ }
+ if ( pv->pts_next - pts >= 10.)
+ {
+ hb_log( "time reversal next %.0f pts %lld (diff %g)",
+ pv->pts_next, pts, pv->pts_next - pts );
+ }
+ pv->pts = pts;
+ }
+ }
+
+ if ( in->new_chap )
+ {
+ pv->new_chap = in->new_chap;
+ pv->chap_time = pts >= 0? pts : pv->pts_next;
+ }
+ prepare_ffmpeg_buffer( in );
+ decodeFrame( pv, in->data, in->size );
+ hb_buffer_close( &in );
+ *buf_out = link_buf_list( pv );
+ return HB_WORK_OK;
+}
+
+static int decavcodecviInfo( hb_work_object_t *w, hb_work_info_t *info )
+{
+ if ( decavcodecvInfo( w, info ) )
+ {
+ // the frame rate in the codec seems to be bogus but it's ok in the stream.
+ AVStream *st = hb_ffmpeg_avstream( w->codec_param );
+ AVRational tb;
+ if ( st->r_frame_rate.den && st->r_frame_rate.num )
+ {
+ tb.num = st->r_frame_rate.den;
+ tb.den = st->r_frame_rate.num;
+ }
+ else
+ {
+ tb = st->time_base;
+ }
+
+ // 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)tb.num * 27000000LL / tb.den;
+ return 1;
+ }
+ return 0;
+}
+
+static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
+{
+ AVCodecContext *context = pv->context;
+ int pos = 0;
+
+ while ( pos < size )
+ {
+ int16_t buffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
+ int out_size = sizeof(buffer);
+ int len = avcodec_decode_audio2( context, buffer, &out_size,
+ data + pos, size - pos );
+ if ( len <= 0 )
+ {
+ return;
+ }
+ pos += len;
+ if( out_size > 0 )
+ {
+ hb_buffer_t *buf = hb_buffer_init( 2 * out_size );
+
+ double pts = pv->pts_next;
+ buf->start = pts;
+ out_size >>= 1;
+ pts += out_size * pv->duration;
+ buf->stop = pts;
+ pv->pts_next = pts;
+
+ float *fl32 = (float *)buf->data;
+ int i;
+ for( i = 0; i < out_size; ++i )
+ {
+ fl32[i] = buffer[i];
+ }
+ hb_list_add( pv->list, buf );
+ }
+ }
+}
+
+static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in,
+ hb_buffer_t **buf_out )
+{
+ hb_work_private_t *pv = w->private_data;
+ if ( ! pv->context )
+ {
+ init_ffmpeg_context( w );
+ pv->duration = 90000. /
+ (double)( pv->context->sample_rate * pv->context->channels );
+ }
+ hb_buffer_t *in = *buf_in;
+
+ if ( in->start >= 0 &&
+ ( pv->pts_next < 0 || ( in->start - pv->pts_next ) > 90*100 ) )
+ {
+ pv->pts_next = in->start;
+ }
+ prepare_ffmpeg_buffer( in );
+ decodeAudio( pv, in->data, in->size );
+ *buf_out = link_buf_list( pv );
return HB_WORK_OK;
}
+hb_work_object_t hb_decavcodecvi =
+{
+ WORK_DECAVCODECVI,
+ "Video decoder (ffmpeg streams)",
+ decavcodecviInit,
+ decavcodecviWork,
+ decavcodecClose,
+ decavcodecviInfo,
+ decavcodecvBSInfo
+};
+
+hb_work_object_t hb_decavcodecai =
+{
+ WORK_DECAVCODECAI,
+ "Audio decoder (ffmpeg streams)",
+ decavcodecviInit,
+ decavcodecaiWork,
+ decavcodecClose,
+ decavcodecInfo,
+ decavcodecBSInfo
+};
diff --git a/libhb/decdca.c b/libhb/decdca.c
index 28081900d..a326031dd 100644
--- a/libhb/decdca.c
+++ b/libhb/decdca.c
@@ -35,9 +35,11 @@ struct hb_work_private_s
};
-int decdcaInit( hb_work_object_t *, hb_job_t * );
-int decdcaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
-void decdcaClose( hb_work_object_t * );
+static int decdcaInit( hb_work_object_t *, hb_job_t * );
+static int decdcaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+static void decdcaClose( hb_work_object_t * );
+static int decdcaBSInfo( hb_work_object_t *, const hb_buffer_t *,
+ hb_work_info_t * );
hb_work_object_t hb_decdca =
{
@@ -45,7 +47,9 @@ hb_work_object_t hb_decdca =
"DCA decoder",
decdcaInit,
decdcaWork,
- decdcaClose
+ decdcaClose,
+ 0,
+ decdcaBSInfo
};
/***********************************************************************
@@ -58,7 +62,7 @@ static hb_buffer_t * Decode( hb_work_object_t * w );
***********************************************************************
* Allocate the work object, initialize libdca
**********************************************************************/
-int decdcaInit( hb_work_object_t * w, hb_job_t * job )
+static int decdcaInit( hb_work_object_t * w, hb_job_t * job )
{
hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
hb_audio_t * audio = w->audio;
@@ -88,7 +92,7 @@ int decdcaInit( hb_work_object_t * w, hb_job_t * job )
***********************************************************************
* Free memory
**********************************************************************/
-void decdcaClose( hb_work_object_t * w )
+static void decdcaClose( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
dca_free( pv->state );
@@ -103,7 +107,7 @@ void decdcaClose( hb_work_object_t * w )
* Add the given buffer to the data we already have, and decode as much
* as we can
**********************************************************************/
-int decdcaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int decdcaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -214,3 +218,85 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
return buf;
}
+
+static int decdcaBSInfo( hb_work_object_t *w, const hb_buffer_t *b,
+ hb_work_info_t *info )
+{
+ int i, flags, rate, bitrate, frame_length;
+ dca_state_t * state = dca_init( 0 );
+
+ memset( info, 0, sizeof(*info) );
+
+ /* since DCA frames don't line up with MPEG ES frames scan the
+ * entire frame for an DCA sync pattern. */
+ for ( i = 0; i < b->size - 7; ++i )
+ {
+ if( dca_syncinfo( state, &b->data[i], &flags, &rate, &bitrate,
+ &frame_length ) )
+ {
+ break;
+ }
+ }
+ if ( i >= b->size - 7 )
+ {
+ /* didn't find DCA sync */
+ return 0;
+ }
+
+ info->name = "DCA";
+ info->rate = rate;
+ info->rate_base = 1;
+ info->bitrate = bitrate;
+ info->flags = flags;
+
+ if ( ( flags & DCA_CHANNEL_MASK) == DCA_DOLBY )
+ {
+ info->flags |= AUDIO_F_DOLBY;
+ }
+
+ switch( flags & DCA_CHANNEL_MASK )
+ {
+ /* mono sources */
+ case DCA_MONO:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_MONO;
+ break;
+ /* stereo input */
+ case DCA_CHANNEL:
+ case DCA_STEREO:
+ case DCA_STEREO_SUMDIFF:
+ case DCA_STEREO_TOTAL:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ break;
+ /* 3F/2R input */
+ case DCA_3F2R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_3F2R;
+ break;
+ /* 3F/1R input */
+ case DCA_3F1R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_3F1R;
+ break;
+ /* other inputs */
+ case DCA_3F:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_3F;
+ break;
+ case DCA_2F1R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_2F1R;
+ break;
+ case DCA_2F2R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_2F2R;
+ break;
+ case DCA_4F2R:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_4F2R;
+ break;
+ /* unknown */
+ default:
+ info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ }
+
+ if (flags & DCA_LFE)
+ {
+ info->channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE;
+ }
+
+ return 1;
+}
diff --git a/libhb/declpcm.c b/libhb/declpcm.c
index 49b15bb82..3220d3da5 100644
--- a/libhb/declpcm.c
+++ b/libhb/declpcm.c
@@ -27,9 +27,11 @@ struct hb_work_private_s
};
static hb_buffer_t * Decode( hb_work_object_t * w );
-int declpcmInit( hb_work_object_t *, hb_job_t * );
-int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
-void declpcmClose( hb_work_object_t * );
+static int declpcmInit( hb_work_object_t *, hb_job_t * );
+static int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+static void declpcmClose( hb_work_object_t * );
+static int declpcmBSInfo( hb_work_object_t *, const hb_buffer_t *,
+ hb_work_info_t * );
hb_work_object_t hb_declpcm =
{
@@ -37,11 +39,19 @@ hb_work_object_t hb_declpcm =
"LPCM decoder",
declpcmInit,
declpcmWork,
- declpcmClose
+ declpcmClose,
+ 0,
+ declpcmBSInfo
};
static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 };
static const int hdr2samplesize[] = { 16, 20, 24, 16 };
+static const int hdr2layout[] = {
+ HB_INPUT_CH_LAYOUT_MONO, HB_INPUT_CH_LAYOUT_STEREO,
+ HB_INPUT_CH_LAYOUT_2F1R, HB_INPUT_CH_LAYOUT_2F2R,
+ HB_INPUT_CH_LAYOUT_3F2R, HB_INPUT_CH_LAYOUT_4F2R,
+ HB_INPUT_CH_LAYOUT_STEREO, HB_INPUT_CH_LAYOUT_STEREO,
+};
static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in )
{
@@ -103,7 +113,7 @@ static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in )
pv->next_pts = in->start;
}
-int declpcmInit( hb_work_object_t * w, hb_job_t * job )
+static int declpcmInit( 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;
@@ -119,7 +129,7 @@ int declpcmInit( hb_work_object_t * w, hb_job_t * job )
* to DVD PES boundaries, this routine has to reconstruct then extract the audio
* frames. Because of the arbitrary alignment, it can output zero, one or two buf's.
*/
-int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -225,7 +235,7 @@ static hb_buffer_t *Decode( hb_work_object_t *w )
return out;
}
-void declpcmClose( hb_work_object_t * w )
+static void declpcmClose( hb_work_object_t * w )
{
if ( w->private_data )
{
@@ -233,3 +243,24 @@ void declpcmClose( hb_work_object_t * w )
w->private_data = 0;
}
}
+
+static int declpcmBSInfo( hb_work_object_t *w, const hb_buffer_t *b,
+ hb_work_info_t *info )
+{
+ int nchannels = ( b->data[4] & 7 ) + 1;
+ int sample_size = hdr2samplesize[b->data[4] >> 6];
+
+ int rate = hdr2samplerate[ ( b->data[4] >> 4 ) & 0x3 ];
+ int bitrate = rate * sample_size * nchannels;
+
+ memset( info, 0, sizeof(*info) );
+
+ info->name = "LPCM";
+ info->rate = rate;
+ info->rate_base = 1;
+ info->bitrate = bitrate;
+ info->flags = ( b->data[3] << 16 ) | ( b->data[4] << 8 ) | b->data[5];
+ info->channel_layout = hdr2layout[nchannels - 1];
+
+ return 1;
+}
diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c
index 3bdceed70..e5fcdc56d 100644
--- a/libhb/decmpeg2.c
+++ b/libhb/decmpeg2.c
@@ -25,15 +25,13 @@
#define BTB_PROG 64
#define TB_PROG 128
#define TBT_PROG 256
-int cadence[12];
-int flag = 0;
+static int cadence[12];
+static int flag = 0;
/**********************************************************************
* hb_libmpeg2_t
- **********************************************************************
- * A convenient libmpeg wrapper, used both here and in scan.c
*********************************************************************/
-struct hb_libmpeg2_s
+typedef struct hb_libmpeg2_s
{
mpeg2dec_t * libmpeg2;
const mpeg2_info_t * info;
@@ -47,14 +45,14 @@ struct hb_libmpeg2_s
int look_for_break; /* need gop start to add chap break */
uint32_t nframes; /* number of frames we've decoded */
int64_t last_pts;
-};
+} hb_libmpeg2_t;
/**********************************************************************
* hb_libmpeg2_init
**********************************************************************
*
*********************************************************************/
-hb_libmpeg2_t * hb_libmpeg2_init()
+static hb_libmpeg2_t * hb_libmpeg2_init()
{
hb_libmpeg2_t * m = calloc( sizeof( hb_libmpeg2_t ), 1 );
@@ -70,7 +68,7 @@ hb_libmpeg2_t * hb_libmpeg2_init()
**********************************************************************
*
*********************************************************************/
-int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
+static int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
hb_list_t * list_raw )
{
mpeg2_state_t state;
@@ -312,7 +310,7 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
**********************************************************************
*
*********************************************************************/
-void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height,
+static void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height,
int * rate, int *aspect_ratio )
{
*width = m->width;
@@ -334,19 +332,12 @@ void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height,
*aspect_ratio = m->aspect_ratio;
}
-int hb_libmpeg2_clear_aspect_ratio( hb_libmpeg2_t * m )
-{
- int ar = m->aspect_ratio;
- m->aspect_ratio = 0;
- return ar;
-}
-
/**********************************************************************
* hb_libmpeg2_close
**********************************************************************
*
*********************************************************************/
-void hb_libmpeg2_close( hb_libmpeg2_t ** _m )
+static void hb_libmpeg2_close( hb_libmpeg2_t ** _m )
{
hb_libmpeg2_t * m = *_m;
@@ -372,7 +363,7 @@ struct hb_work_private_s
**********************************************************************
*
*********************************************************************/
-int decmpeg2Init( hb_work_object_t * w, hb_job_t * job )
+static int decmpeg2Init( hb_work_object_t * w, hb_job_t * job )
{
hb_work_private_t * pv;
@@ -392,7 +383,7 @@ int decmpeg2Init( hb_work_object_t * w, hb_job_t * job )
**********************************************************************
*
*********************************************************************/
-int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -441,21 +432,51 @@ int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
**********************************************************************
*
*********************************************************************/
-void decmpeg2Close( hb_work_object_t * w )
+static void decmpeg2Close( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
- hb_log( "mpeg2 done: %d frames", pv->libmpeg2->nframes );
+
+ // don't log during scan
+ if ( pv->libmpeg2->job )
+ {
+ hb_log( "mpeg2 done: %d frames", pv->libmpeg2->nframes );
+ }
hb_list_close( &pv->list );
hb_libmpeg2_close( &pv->libmpeg2 );
free( pv );
}
+static int decmpeg2Info( hb_work_object_t *w, hb_work_info_t *info )
+{
+ hb_work_private_t *pv = w->private_data;
+
+ if ( pv && pv->libmpeg2 )
+ {
+ int aspect;
+ hb_libmpeg2_t *m = pv->libmpeg2;
+
+ hb_libmpeg2_info( m, &info->width, &info->height, &info->rate_base,
+ &aspect );
+
+ info->aspect = (double)aspect;
+ info->rate = 27000000;
+
+ info->bitrate = m->info->sequence->byte_rate * 8;
+ info->profile = m->info->sequence->profile_level_id >> 4;
+ info->level = m->info->sequence->profile_level_id & 0xf;
+ info->name = "mpeg2";
+ return 1;
+ }
+ return 0;
+}
+
hb_work_object_t hb_decmpeg2 =
{
WORK_DECMPEG2,
"MPEG-2 decoder (libmpeg2)",
decmpeg2Init,
decmpeg2Work,
- decmpeg2Close
+ decmpeg2Close,
+ decmpeg2Info
};
diff --git a/libhb/decomb.c b/libhb/decomb.c
index e5779c8fa..e29f253e6 100644
--- a/libhb/decomb.c
+++ b/libhb/decomb.c
@@ -6,7 +6,7 @@
The yadif algorithm was created by Michael Niedermayer. */
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#include "mpeg2dec/mpeg2.h"
#define SUPPRESS_AV_LOG
@@ -69,7 +69,7 @@ hb_filter_private_t * hb_decomb_init( int pix_fmt,
int height,
char * settings );
-int hb_decomb_work( hb_buffer_t * buf_in,
+int hb_decomb_work( const hb_buffer_t * buf_in,
hb_buffer_t ** buf_out,
int pix_fmt,
int width,
@@ -825,13 +825,15 @@ void hb_decomb_close( hb_filter_private_t * pv )
free( pv );
}
-int hb_decomb_work( hb_buffer_t * buf_in,
- hb_buffer_t ** buf_out,
- int pix_fmt,
- int width,
- int height,
- hb_filter_private_t * pv )
+int hb_decomb_work( const hb_buffer_t * cbuf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv )
{
+ hb_buffer_t * buf_in = (hb_buffer_t *)cbuf_in;
+
if( !pv ||
pix_fmt != pv->pix_fmt ||
width != pv->width[0] ||
diff --git a/libhb/deinterlace.c b/libhb/deinterlace.c
index 44593f45c..4cf3a2fe1 100644
--- a/libhb/deinterlace.c
+++ b/libhb/deinterlace.c
@@ -17,7 +17,7 @@
*/
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#include "mpeg2dec/mpeg2.h"
#define SUPPRESS_AV_LOG
diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c
index 35d941ad0..053867931 100644
--- a/libhb/demuxmpeg.c
+++ b/libhb/demuxmpeg.c
@@ -6,7 +6,7 @@
#include "hb.h"
-/* Basic MPEG demuxer, only works with DVDs (2048 bytes packets) */
+/* Basic MPEG demuxer */
int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state )
{
@@ -91,7 +91,7 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state
int pes_header_d_length;
int pes_header_end;
int has_pts;
- int64_t pts = -1;
+ int64_t pts = -1, dts = -1;
pos += 3; /* packet_start_code_prefix */
id = d[pos];
@@ -109,7 +109,7 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state
continue;
}
- has_pts = ( ( d[pos+1] >> 6 ) & 0x2 ) ? 1 : 0;
+ has_pts = d[pos+1] >> 6;
pos += 2; /* Required headers */
pes_header_d_length = d[pos];
@@ -118,11 +118,23 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state
if( has_pts )
{
- pts = ( ( ( (uint64_t) d[pos] >> 1 ) & 0x7 ) << 30 ) +
+ pts = ( (uint64_t)(d[pos] & 0xe ) << 29 ) +
( d[pos+1] << 22 ) +
( ( d[pos+2] >> 1 ) << 15 ) +
( d[pos+3] << 7 ) +
( d[pos+4] >> 1 );
+ if ( has_pts & 1 )
+ {
+ dts = ( (uint64_t)(d[pos+5] & 0xe ) << 29 ) +
+ ( d[pos+6] << 22 ) +
+ ( ( d[pos+7] >> 1 ) << 15 ) +
+ ( d[pos+8] << 7 ) +
+ ( d[pos+9] >> 1 );
+ }
+ else
+ {
+ dts = pts;
+ }
}
pos = pes_header_end;
@@ -153,6 +165,7 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state
buf_es->id = id;
buf_es->start = pts;
+ buf_es->renderOffset = dts;
buf_es->stop = -1;
if (id == 0xE0) {
// Consume a chapter break, and apply it to the ES.
@@ -170,3 +183,23 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state
return 1;
}
+
+// "null" demuxer (makes a copy of input buf & returns it in list)
+// used when the reader for some format includes its own demuxer.
+// for example, ffmpeg.
+int hb_demux_null( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state )
+{
+ hb_buffer_t *buf = hb_buffer_init( buf_ps->size );
+
+ // copy everything from the old to the new except the data ptr & alloc
+ uint8_t *data = buf->data;
+ int alloc = buf->alloc;
+ *buf = *buf_ps;
+ buf->data = data;
+ buf->alloc = alloc;
+
+ // now copy the data
+ memcpy( buf->data, buf_ps->data, buf_ps->size );
+ hb_list_add( list_es, buf );
+ return 1;
+}
diff --git a/libhb/denoise.c b/libhb/denoise.c
index 5cf88ea78..6d335fcdc 100644
--- a/libhb/denoise.c
+++ b/libhb/denoise.c
@@ -17,7 +17,7 @@
*/
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#include "mpeg2dec/mpeg2.h"
#define HQDN3D_SPATIAL_LUMA_DEFAULT 4.0f
diff --git a/libhb/detelecine.c b/libhb/detelecine.c
index ada5e793c..768cc2c91 100644
--- a/libhb/detelecine.c
+++ b/libhb/detelecine.c
@@ -1,5 +1,5 @@
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#include "mpeg2dec/mpeg2.h"
/*
diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c
index dca246d25..5e55b0083 100644
--- a/libhb/encavcodec.c
+++ b/libhb/encavcodec.c
@@ -6,7 +6,7 @@
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
struct hb_work_private_s
{
diff --git a/libhb/fifo.c b/libhb/fifo.c
index 69b8b908b..1bf2a9de6 100644
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -14,40 +14,50 @@
struct hb_fifo_s
{
hb_lock_t * lock;
- int capacity;
- int size;
- int buffer_size;
+ uint32_t capacity;
+ uint32_t size;
+ uint32_t buffer_size;
hb_buffer_t * first;
hb_buffer_t * last;
};
-#define MAX_BUFFER_POOLS 15
-#define BUFFER_POOL_MAX_ELEMENTS 2048
+/* we round the requested buffer size up to the next power of 2 so there can
+ * be at most 32 possible pools when the size is a 32 bit int. To avoid a lot
+ * of slow & error-prone run-time checking we allow for all 32. */
+#define MAX_BUFFER_POOLS 32
+/* the buffer pool only exists to avoid the two malloc and two free calls that
+ * it would otherwise take to allocate & free a buffer. but we don't want to
+ * tie up a lot of memory in the pool because this allocator isn't as general
+ * as malloc so memory tied up here puts more pressure on the malloc pool.
+ * A pool of 16 elements will avoid 94% of the malloc/free calls without wasting
+ * too much memory. */
+#define BUFFER_POOL_MAX_ELEMENTS 32
struct hb_buffer_pools_s
{
- int entries;
- int allocated;
- hb_fifo_t *pool[MAX_BUFFER_POOLS];
+ int64_t allocated;
hb_lock_t *lock;
-};
+ hb_fifo_t *pool[MAX_BUFFER_POOLS];
+} buffers;
-struct hb_buffer_pools_s buffers;
void hb_buffer_pool_init( void )
{
- hb_fifo_t *buffer_pool;
- int size = 512;
- int max_size = 32768;;
-
- buffers.entries = 0;
buffers.lock = hb_lock_init();
buffers.allocated = 0;
- while(size <= max_size) {
- buffer_pool = buffers.pool[buffers.entries++] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS);
- buffer_pool->buffer_size = size;
- size *= 2;
+ /* we allocate pools for sizes 2^10 through 2^25. requests larger than
+ * 2^25 will get passed through to malloc. */
+ int i;
+ for ( i = 10; i < 26; ++i )
+ {
+ buffers.pool[i] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS);
+ buffers.pool[i]->buffer_size = 1 << i;
+ }
+ /* requests smaller than 2^10 are satisfied from the 2^10 pool. */
+ for ( i = 1; i < 10; ++i )
+ {
+ buffers.pool[i] = buffers.pool[10];
}
}
@@ -55,12 +65,12 @@ void hb_buffer_pool_free( void )
{
int i;
int count;
- int freed = 0;
+ int64_t freed = 0;
hb_buffer_t *b;
hb_lock(buffers.lock);
- for( i = 0; i < buffers.entries; i++)
+ for( i = 10; i < 26; ++i)
{
count = 0;
while( ( b = hb_fifo_get(buffers.pool[i]) ) )
@@ -69,70 +79,42 @@ void hb_buffer_pool_free( void )
if( b->data )
{
free( b->data );
- b->data = NULL;
}
free( b );
count++;
}
- hb_log("Freed %d buffers of size %d", count, buffers.pool[i]->buffer_size);
+ if ( count )
+ {
+ hb_log("Freed %d buffers of size %d", count,
+ buffers.pool[i]->buffer_size);
+ }
}
- hb_log("Allocated %d bytes of buffers on this pass and Freed %d bytes, %d bytes leaked",
- buffers.allocated, freed, buffers.allocated - freed);
+ hb_log("Allocated %lld bytes of buffers on this pass and Freed %lld bytes, "
+ "%lld bytes leaked", buffers.allocated, freed, buffers.allocated - freed);
buffers.allocated = 0;
hb_unlock(buffers.lock);
}
-
-hb_buffer_t * hb_buffer_init( int size )
+static hb_fifo_t *size_to_pool( int size )
{
- hb_buffer_t * b;
int i;
- hb_fifo_t *buffer_pool = NULL;
- uint8_t *data;
- int b_alloc;
- int resize = 0;
-
- if( size > 0 )
+ for ( i = 0; i < 30; ++i )
{
- /*
- * The buffer pools are allocated in increasing size
- */
- for( i = 0; i < buffers.entries; i++ )
+ if ( size <= (1 << i) )
{
- if( buffers.pool[i]->buffer_size >= size )
- {
- /*
- * This pool is big enough, but are there any buffers in it?
- */
- if( hb_fifo_size( buffers.pool[i] ) )
- {
- /*
- * We've found a matching buffer pool, with buffers.
- */
- buffer_pool = buffers.pool[i];
- resize = buffers.pool[i]->buffer_size;
- } else {
- /*
- * Buffer pool is empty,
- */
- if( resize ) {
- /*
- * This is the second time through, so break
- * out of here to avoid using too large a
- * buffer for a small job.
- */
- break;
- }
- resize = buffers.pool[i]->buffer_size;
- }
- }
+ return buffers.pool[i];
}
}
- /*
- * Don't reuse the 0 size buffers, not much gain.
- */
- if( size != 0 && buffer_pool )
+ return NULL;
+}
+
+hb_buffer_t * hb_buffer_init( int size )
+{
+ hb_buffer_t * b;
+ hb_fifo_t *buffer_pool = size_to_pool( size );
+
+ if( buffer_pool )
{
b = hb_fifo_get( buffer_pool );
@@ -141,15 +123,10 @@ hb_buffer_t * hb_buffer_init( int size )
/*
* Zero the contents of the buffer, would be nice if we
* didn't have to do this.
- *
- hb_log("Reused buffer size %d for size %d from pool %d depth %d",
- b->alloc, size, smallest_pool->buffer_size,
- hb_fifo_size(smallest_pool));
- */
- data = b->data;
- b_alloc = b->alloc;
+ */
+ uint8_t *data = b->data;
memset( b, 0, sizeof(hb_buffer_t) );
- b->alloc = b_alloc;
+ b->alloc = buffer_pool->buffer_size;
b->size = size;
b->data = data;
return( b );
@@ -166,152 +143,66 @@ hb_buffer_t * hb_buffer_init( int size )
}
b->size = size;
+ b->alloc = buffer_pool? buffer_pool->buffer_size : size;
- if( resize )
+ if (size)
{
- size = resize;
- }
- b->alloc = size;
-
- /*
- hb_log("Allocating new buffer of size %d for size %d",
- b->alloc,
- b->size);
- */
-
- if (!size)
- return b;
#if defined( SYS_DARWIN ) || defined( SYS_FREEBSD )
- b->data = malloc( b->alloc );
+ b->data = malloc( b->alloc );
#elif defined( SYS_CYGWIN )
- /* FIXME */
- b->data = malloc( b->alloc + 17 );
+ /* FIXME */
+ b->data = malloc( b->alloc + 17 );
#else
- b->data = memalign( 16, b->alloc );
+ b->data = memalign( 16, b->alloc );
#endif
-
- if( !b->data )
- {
- hb_log( "out of memory" );
- free( b );
- return NULL;
+ if( !b->data )
+ {
+ hb_log( "out of memory" );
+ free( b );
+ return NULL;
+ }
+ hb_lock(buffers.lock);
+ buffers.allocated += b->alloc;
+ hb_unlock(buffers.lock);
}
-
- buffers.allocated += b->alloc;
-
return b;
}
void hb_buffer_realloc( hb_buffer_t * b, int size )
{
- /* No more alignment, but we don't care */
- if( size < 2048 ) {
- size = 2048;
+ if ( size > b->alloc )
+ {
+ uint32_t orig = b->alloc;
+ size = size_to_pool( size )->buffer_size;
+ b->data = realloc( b->data, size );
+ b->alloc = size;
+
+ hb_lock(buffers.lock);
+ buffers.allocated += size - orig;
+ hb_unlock(buffers.lock);
}
- b->data = realloc( b->data, size );
- buffers.allocated -= b->alloc;
- b->alloc = size;
- buffers.allocated += b->alloc;
}
void hb_buffer_close( hb_buffer_t ** _b )
{
hb_buffer_t * b = *_b;
- hb_fifo_t *buffer_pool = NULL;
- int i;
+ hb_fifo_t *buffer_pool = size_to_pool( b->alloc );
- /*
- * Put the buffer into our free list in the matching buffer pool, if there is one.
- */
- if( b->alloc != 0 )
+ if( buffer_pool && b->data && !hb_fifo_is_full( buffer_pool ) )
{
- for( i = 0; i < buffers.entries; i++ )
- {
- if( b->alloc == buffers.pool[i]->buffer_size )
- {
- buffer_pool = buffers.pool[i];
- break;
- }
- }
+ hb_fifo_push( buffer_pool, b );
+ return;
}
-
- if( buffer_pool )
+ /* either the pool is full or this size doesn't use a pool - free the buf */
+ if( b->data )
{
- if( !hb_fifo_is_full( buffer_pool ) )
- {
- if(b->data)
- {
- /*
- hb_log("Putting a buffer of size %d on pool %d, depth %d",
- b->alloc,
- buffer_pool->buffer_size,
- hb_fifo_size(buffer_pool));
- */
- hb_fifo_push( buffer_pool, b );
- } else {
- free(b);
- }
- } else {
- /*
- * Got a load of these size ones already, free this buffer.
- *
- hb_log("Buffer pool for size %d full, freeing buffer", b->alloc);
- */
- if( b->data )
- {
- free( b->data );
- }
- buffers.allocated -= b->alloc;
- free( b );
- }
- } else {
- /*
- * Need a new buffer pool for this size.
- */
+ free( b->data );
hb_lock(buffers.lock);
- if ( b->alloc != 0 && buffers.entries < MAX_BUFFER_POOLS)
- {
- buffer_pool = buffers.pool[buffers.entries++] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS);
- buffer_pool->buffer_size = b->alloc;
- hb_fifo_push( buffer_pool, b );
- /*
- hb_log("*** Allocated a new buffer pool for size %d [%d]", b->alloc,
- buffers.entries );
- */
- } else {
- if( b->alloc != 0 )
- {
- for( i = buffers.entries-1; i >= 0; i-- )
- {
- if( hb_fifo_size(buffers.pool[i]) == 0 )
- {
- /*
- * Reuse this pool as it is empty.
- */
- buffers.pool[i]->buffer_size = b->alloc;
- hb_fifo_push( buffers.pool[i], b );
- b = NULL;
- break;
- }
- }
- }
-
- if( b )
- {
- if( b->data )
- {
- free( b->data );
- b->data = NULL;
- buffers.allocated -= b->alloc;
- }
- free( b );
- }
- }
+ buffers.allocated -= b->alloc;
hb_unlock(buffers.lock);
}
-
+ free( b );
*_b = NULL;
-
}
void hb_buffer_copy_settings( hb_buffer_t * dst, const hb_buffer_t * src )
diff --git a/libhb/hb.c b/libhb/hb.c
index 825b2de47..bcd77e7a9 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -1,7 +1,8 @@
#include "hb.h"
-#include "ffmpeg/avcodec.h"
-#include "ffmpeg/swscale.h"
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
+#include "libswscale/swscale.h"
struct hb_handle_s
{
@@ -122,9 +123,7 @@ hb_handle_t * hb_init_real( int verbose, int update_check )
h->pause_lock = hb_lock_init();
/* libavcodec */
- avcodec_init();
- avcodec_register_all();
- av_register_codec_parser( &mpegaudio_parser);
+ av_register_all();
/* Start library thread */
hb_log( "hb_init: starting libhb thread" );
@@ -220,6 +219,9 @@ hb_handle_t * hb_init_dl( int verbose, int update_check )
hb_register( &hb_deca52 );
hb_register( &hb_decdca );
hb_register( &hb_decavcodec );
+ hb_register( &hb_decavcodecv );
+ hb_register( &hb_decavcodecvi );
+ hb_register( &hb_decavcodecai );
hb_register( &hb_declpcm );
hb_register( &hb_encfaac );
hb_register( &hb_enclame );
diff --git a/libhb/hb.h b/libhb/hb.h
index 9ca330612..a47e4e2e5 100644
--- a/libhb/hb.h
+++ b/libhb/hb.h
@@ -5,6 +5,7 @@
extern "C" {
#endif
+#include "hbversion.h"
#include "common.h"
/* hb_init()
@@ -29,6 +30,9 @@ hb_register( &hb_enctheora ); \
hb_register( &hb_deca52 ); \
hb_register( &hb_decdca ); \
hb_register( &hb_decavcodec ); \
+hb_register( &hb_decavcodecv ); \
+hb_register( &hb_decavcodecvi ); \
+hb_register( &hb_decavcodecai ); \
hb_register( &hb_declpcm ); \
hb_register( &hb_encfaac ); \
hb_register( &hb_enclame ); \
@@ -45,6 +49,9 @@ hb_register( &hb_encx264 ); \
hb_register( &hb_deca52 ); \
hb_register( &hb_decdca ); \
hb_register( &hb_decavcodec ); \
+hb_register( &hb_decavcodecv ); \
+hb_register( &hb_decavcodecvi ); \
+hb_register( &hb_decavcodecai ); \
hb_register( &hb_declpcm ); \
hb_register( &hb_encfaac ); \
diff --git a/libhb/internal.h b/libhb/internal.h
index 39364fcd3..95545acc2 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -97,22 +97,9 @@ hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count,
volatile int * die, int * error, hb_job_t ** job );
hb_thread_t * hb_reader_init( hb_job_t * );
hb_thread_t * hb_muxer_init( hb_job_t * );
-
-/***********************************************************************
- * libmpeg2 wrapper
- ***********************************************************************
- * It is exported here because it is used at several places
- **********************************************************************/
-typedef struct hb_libmpeg2_s hb_libmpeg2_t;
-
-hb_libmpeg2_t * hb_libmpeg2_init();
-int hb_libmpeg2_decode( hb_libmpeg2_t *,
- hb_buffer_t * es_buf,
- hb_list_t * raw_list );
-void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width,
- int * height, int * rate, int * aspect_ratio );
-void hb_libmpeg2_close( hb_libmpeg2_t ** );
-int hb_libmpeg2_clear_aspect_ratio( hb_libmpeg2_t * );
+hb_work_object_t * hb_get_work( int );
+hb_work_object_t * hb_codec_decoder( int );
+hb_work_object_t * hb_codec_encoder( int );
/***********************************************************************
* mpegdemux.c
@@ -124,6 +111,7 @@ typedef struct {
} hb_psdemux_t;
int hb_demux_ps( hb_buffer_t * ps_buf, hb_list_t * es_list, hb_psdemux_t * );
+int hb_demux_null( hb_buffer_t * ps_buf, hb_list_t * es_list, hb_psdemux_t * );
/***********************************************************************
* dvd.c
@@ -142,12 +130,15 @@ int hb_dvd_chapter( hb_dvd_t * );
int hb_dvd_is_break( hb_dvd_t * d );
void hb_dvd_close( hb_dvd_t ** );
-hb_stream_t * hb_stream_open( char * path, int opentype );
+hb_stream_t * hb_stream_open( char * path, hb_title_t *title );
void hb_stream_close( hb_stream_t ** );
hb_title_t * hb_stream_title_scan( hb_stream_t *);
int hb_stream_read( hb_stream_t *, hb_buffer_t *);
int hb_stream_seek( hb_stream_t *, float );
+void * hb_ffmpeg_context( int codec_param );
+void * hb_ffmpeg_avstream( int codec_param );
+
/***********************************************************************
* Work objects
**********************************************************************/
@@ -213,6 +204,9 @@ enum
WORK_DECA52,
WORK_DECDCA,
WORK_DECAVCODEC,
+ WORK_DECAVCODECV,
+ WORK_DECAVCODECVI,
+ WORK_DECAVCODECAI,
WORK_DECLPCM,
WORK_ENCFAAC,
WORK_ENCLAME,
diff --git a/libhb/muxavi.c b/libhb/muxavi.c
index 83d3d05d2..99604335d 100644
--- a/libhb/muxavi.c
+++ b/libhb/muxavi.c
@@ -5,7 +5,7 @@
It may be used under the terms of the GNU General Public License. */
#include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#define AVIF_HASINDEX 0x10
#define AVIIF_KEYFRAME 0x10
diff --git a/libhb/reader.c b/libhb/reader.c
index 233319833..9ad2867e5 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -47,6 +47,19 @@ hb_thread_t * hb_reader_init( hb_job_t * job )
HB_NORMAL_PRIORITY );
}
+static void push_buf( hb_reader_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
+{
+ while( !*r->die && !r->job->done && hb_fifo_is_full( fifo ) )
+ {
+ /*
+ * Loop until the incoming fifo is reaqdy to receive
+ * this buffer.
+ */
+ hb_snooze( 50 );
+ }
+ hb_fifo_push( fifo, buf );
+}
+
/***********************************************************************
* ReaderFunc
***********************************************************************
@@ -57,7 +70,6 @@ static void ReaderFunc( void * _r )
hb_reader_t * r = _r;
hb_fifo_t ** fifos;
hb_buffer_t * buf;
- hb_buffer_t * buf_old;
hb_list_t * list;
int n;
int chapter = -1;
@@ -65,7 +77,7 @@ static void ReaderFunc( void * _r )
if( !( r->dvd = hb_dvd_init( r->title->dvd ) ) )
{
- if ( !( r->stream = hb_stream_open( r->title->dvd, 1 ) ) )
+ if ( !( r->stream = hb_stream_open( r->title->dvd, r->title ) ) )
{
return;
}
@@ -158,7 +170,14 @@ static void ReaderFunc( void * _r )
hb_set_state( r->job->h, &state );
}
- hb_demux_ps( r->ps, list, &r->demux );
+ if ( r->title->demuxer == HB_NULL_DEMUXER )
+ {
+ hb_demux_null( r->ps, list, &r->demux );
+ }
+ else
+ {
+ hb_demux_ps( r->ps, list, &r->demux );
+ }
while( ( buf = hb_list_item( list, 0 ) ) )
{
@@ -169,10 +188,10 @@ static void ReaderFunc( void * _r )
{
/* The first video packet defines 'time zero' so discard
data until we get a video packet with a PTS */
- if ( buf->id == 0xE0 && buf->start != -1 )
+ if ( buf->id == r->title->video_id && buf->start != -1 )
{
r->saw_video = 1;
- r->demux.scr_offset = buf->start;
+ r->demux.scr_offset = buf->renderOffset;
hb_log( "reader: first SCR %llu scr_offset %llu",
r->demux.last_scr, r->demux.scr_offset );
}
@@ -190,34 +209,21 @@ static void ReaderFunc( void * _r )
everything after this sees a continuous clock with 0
being the time of the first video packet. */
buf->start -= r->demux.scr_offset;
+ buf->renderOffset -= r->demux.scr_offset;
}
buf->sequence = r->sequence++;
- for( n = 0; fifos[n] != NULL; n++)
+ /* if there are mutiple output fifos, send a copy of the
+ * buffer down all but the first (we have to not ship the
+ * original buffer or we'll race with the thread that's
+ * consuming the buffer & inject garbage into the data stream). */
+ for( n = 1; fifos[n] != NULL; n++)
{
- if( n != 0 )
- {
- /*
- * Replace the buffer with a new copy of itself for when
- * it is being sent down multiple fifos.
- */
- buf_old = buf;
- buf = hb_buffer_init(buf_old->size);
- memcpy( buf->data, buf_old->data, buf->size );
- hb_buffer_copy_settings( buf, buf_old );
- }
-
- while( !*r->die && !r->job->done &&
- hb_fifo_is_full( fifos[n] ) )
- {
- /*
- * Loop until the incoming fifo is reaqdy to receive
- * this buffer.
- */
- hb_snooze( 50 );
- }
-
- hb_fifo_push( fifos[n], buf );
+ hb_buffer_t *buf_copy = hb_buffer_init( buf->size );
+ hb_buffer_copy_settings( buf_copy, buf );
+ memcpy( buf_copy->data, buf->data, buf->size );
+ push_buf( r, fifos[n], buf_copy );
}
+ push_buf( r, fifos[0], buf );
}
else
{
@@ -262,7 +268,7 @@ static hb_fifo_t ** GetFifoForId( hb_job_t * job, int id )
memset(fifos, 0, sizeof(fifos));
- if( id == 0xE0 )
+ if( id == title->video_id )
{
if( job->indepth_scan )
{
diff --git a/libhb/render.c b/libhb/render.c
index eead0a7c3..0fc5816a9 100644
--- a/libhb/render.c
+++ b/libhb/render.c
@@ -6,8 +6,8 @@
#include "hb.h"
-#include "ffmpeg/avcodec.h"
-#include "ffmpeg/swscale.h"
+#include "libavcodec/avcodec.h"
+#include "libswscale/swscale.h"
struct hb_work_private_s
{
diff --git a/libhb/scan.c b/libhb/scan.c
index 674a1e35f..6834d4614 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -237,16 +237,15 @@ static void ScanFunc( void * _data )
static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
int i, npreviews = 0;
- hb_buffer_t * buf_ps, * buf_es, * buf_raw;
- hb_list_t * list_es, * list_raw;
- hb_libmpeg2_t * mpeg2;
+ hb_buffer_t * buf_ps, * buf_es;
+ hb_list_t * list_es;
int progressive_count = 0;
int interlaced_preview_count = 0;
- int last_ar = 0, ar16_count = 0, ar4_count = 0;
+ double last_ar = 0;
+ int ar16_count = 0, ar4_count = 0;
buf_ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
list_es = hb_list_init();
- list_raw = hb_list_init();
hb_log( "scan: decoding previews for title %d", title->index );
@@ -263,7 +262,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) )
{
- goto error;
+ continue;
}
}
else if (data->stream)
@@ -273,13 +272,17 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
* file and we need it to decode any previews. */
if (!hb_stream_seek(data->stream, (float) i / 11.0 ) )
{
- goto error;
+ continue;
}
}
hb_log( "scan: preview %d", i + 1 );
- mpeg2 = hb_libmpeg2_init();
+ int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2;
+ hb_work_object_t *vid_decoder = hb_get_work( vcodec );
+ vid_decoder->codec_param = title->video_codec_param;
+ vid_decoder->init( vid_decoder, NULL );
+ hb_buffer_t * vid_buf = NULL;
for( j = 0; j < 10240 ; j++ )
{
@@ -299,82 +302,76 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
goto skip_preview;
}
}
- hb_demux_ps( buf_ps, list_es, 0 );
+ if ( title->demuxer == HB_NULL_DEMUXER )
+ {
+ hb_demux_null( buf_ps, list_es, 0 );
+ }
+ else
+ {
+ hb_demux_ps( buf_ps, list_es, 0 );
+ }
while( ( buf_es = hb_list_item( list_es, 0 ) ) )
{
hb_list_rem( list_es, buf_es );
- if( buf_es->id == 0xE0 && !hb_list_count( list_raw ) )
+ if( buf_es->id == title->video_id && vid_buf == NULL )
{
- hb_libmpeg2_decode( mpeg2, buf_es, list_raw );
- int ar = hb_libmpeg2_clear_aspect_ratio( mpeg2 );
- if ( ar != 0 )
- {
- if ( ar != last_ar && last_ar != 0 )
- {
- hb_log( "aspect ratio changed from %d to %d",
- last_ar, ar );
- }
- switch ( ar )
- {
- case HB_ASPECT_BASE * 4 / 3:
- ++ar4_count;
- break;
- case HB_ASPECT_BASE * 16 / 9:
- ++ar16_count;
- break;
- default:
- hb_log( "unknown aspect ratio %d", ar );
- /* if the aspect is closer to 4:3 use that
- * otherwise use 16:9 */
- if ( ar < HB_ASPECT_BASE * 14 / 9 )
- {
- ++ar4_count;
- }
- else
- {
- ++ar16_count;
- }
- break;
- }
- }
- last_ar = ar;
+ vid_decoder->work( vid_decoder, &buf_es, &vid_buf );
}
else if( ! AllAudioOK( title ) )
{
LookForAudio( title, buf_es );
}
- hb_buffer_close( &buf_es );
-
- if( hb_list_count( list_raw ) && AllAudioOK( title ) )
- {
- /* We got a picture */
- break;
- }
+ if ( buf_es )
+ hb_buffer_close( &buf_es );
}
- if( hb_list_count( list_raw ) && AllAudioOK( title ) )
- {
+ if( vid_buf && AllAudioOK( title ) )
break;
- }
}
- if( !hb_list_count( list_raw ) )
+ if( ! vid_buf )
{
hb_log( "scan: could not get a decoded picture" );
continue;
}
/* Get size and rate infos */
- title->rate = 27000000;
- int ar;
- hb_libmpeg2_info( mpeg2, &title->width, &title->height,
- &title->rate_base, &ar );
- /* if we found mostly 4:3 previews use that as the aspect ratio otherwise
- use 16:9 */
- title->aspect = ar4_count > ar16_count ?
- HB_ASPECT_BASE * 4 / 3 : HB_ASPECT_BASE * 16 / 9;
+ hb_work_info_t vid_info;
+ vid_decoder->info( vid_decoder, &vid_info );
+ vid_decoder->close( vid_decoder );
+ free( vid_decoder );
+
+ title->width = vid_info.width;
+ title->height = vid_info.height;
+ title->rate = vid_info.rate;
+ title->rate_base = vid_info.rate_base;
+ if ( vid_info.aspect != 0 )
+ {
+ if ( vid_info.aspect != last_ar && last_ar != 0 )
+ {
+ hb_log( "aspect ratio changed from %g to %g",
+ last_ar, vid_info.aspect );
+ }
+ switch ( (int)vid_info.aspect )
+ {
+ case HB_ASPECT_BASE * 4 / 3:
+ ++ar4_count;
+ break;
+ case HB_ASPECT_BASE * 16 / 9:
+ ++ar16_count;
+ break;
+ default:
+ hb_log( "unknown aspect ratio %g", vid_info.aspect );
+ /* if the aspect is closer to 4:3 use that
+ * otherwise use 16:9 */
+ vid_info.aspect < HB_ASPECT_BASE * 14 / 9 ? ++ar4_count :
+ ++ar16_count;
+ break;
+ }
+ last_ar = vid_info.aspect;
+ }
if( title->rate_base == 1126125 )
{
@@ -421,18 +418,14 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
title->crop[2] = title->crop[3] = title->width / 2;
}
- hb_libmpeg2_close( &mpeg2 );
-
while( ( buf_es = hb_list_item( list_es, 0 ) ) )
{
hb_list_rem( list_es, buf_es );
hb_buffer_close( &buf_es );
}
- buf_raw = hb_list_item( list_raw, 0 );
-
/* Check preview for interlacing artifacts */
- if( hb_detect_comb( buf_raw, title->width, title->height, 10, 30, 9, 10, 30, 9 ) )
+ if( hb_detect_comb( vid_buf, title->width, title->height, 10, 30, 9, 10, 30, 9 ) )
{
hb_log("Interlacing detected in preview frame %i", i);
interlaced_preview_count++;
@@ -444,7 +437,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
file_preview = fopen( filename, "w" );
if( file_preview )
{
- fwrite( buf_raw->data, title->width * title->height * 3 / 2,
+ fwrite( vid_buf->data, title->width * title->height * 3 / 2,
1, file_preview );
fclose( file_preview );
}
@@ -453,14 +446,14 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
hb_log( "scan: fopen failed (%s)", filename );
}
-#define Y buf_raw->data
+#define Y vid_buf->data
#define DARK 64
/* Detect black borders */
for( j = 0; j < title->width; j++ )
{
- for( k = 0; k < title->crop[0]; k++ )
+ for( k = 2; k < title->crop[0]; k++ )
if( Y[ k * title->width + j ] > DARK )
{
title->crop[0] = k;
@@ -493,13 +486,15 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
++npreviews;
skip_preview:
- while( ( buf_raw = hb_list_item( list_raw, 0 ) ) )
- {
- hb_list_rem( list_raw, buf_raw );
- hb_buffer_close( &buf_raw );
- }
+ if ( vid_buf )
+ hb_buffer_close( &vid_buf );
}
+ /* if we found mostly 4:3 previews use that as the aspect ratio otherwise
+ use 16:9 */
+ title->aspect = ar4_count > ar16_count ?
+ HB_ASPECT_BASE * 4 / 3 : HB_ASPECT_BASE * 16 / 9;
+
title->crop[0] = EVEN( title->crop[0] );
title->crop[1] = EVEN( title->crop[1] );
title->crop[2] = EVEN( title->crop[2] );
@@ -523,12 +518,6 @@ skip_preview:
title->detected_interlacing = 0;
}
- goto cleanup;
-
-error:
- npreviews = 0;
-
-cleanup:
hb_buffer_close( &buf_ps );
while( ( buf_es = hb_list_item( list_es, 0 ) ) )
{
@@ -536,236 +525,12 @@ cleanup:
hb_buffer_close( &buf_es );
}
hb_list_close( &list_es );
- while( ( buf_raw = hb_list_item( list_raw, 0 ) ) )
- {
- hb_list_rem( list_raw, buf_raw );
- hb_buffer_close( &buf_raw );
- }
- hb_list_close( &list_raw );
if (data->dvd)
hb_dvd_stop( data->dvd );
return npreviews;
}
-static void update_audio_description( const char *codec, hb_audio_t *audio,
- int is_dolby )
-{
- hb_log( "scan: %s, rate=%dHz, bitrate=%d", codec, audio->config.in.samplerate,
- audio->config.in.bitrate );
-
- /* XXX */
- if ( is_dolby )
- {
- strcat( audio->config.lang.description, " (Dolby Surround)" );
- return;
- }
-
- char *desc = audio->config.lang.description +
- strlen( audio->config.lang.description );
- sprintf( desc, " (%d.%d ch)",
- HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->config.in.channel_layout) +
- HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->config.in.channel_layout),
- HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->config.in.channel_layout));
-}
-
-static int hb_setup_a52_audio( hb_audio_t *audio, hb_buffer_t *b )
-{
- int i, rate, bitrate, flags;
-
- /* since AC3 frames don't line up with MPEG ES frames scan the
- * entire frame for an AC3 sync pattern. */
- for ( i = 0; i < b->size - 7; ++i )
- {
- if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) != 0 )
- {
- break;
- }
- }
- if ( i >= b->size - 7 )
- {
- /* didn't find AC3 sync */
- return 0;
- }
-
- audio->config.in.samplerate = rate;
- audio->config.in.bitrate = bitrate;
-
- switch( flags & A52_CHANNEL_MASK )
- {
- /* mono sources */
- case A52_MONO:
- case A52_CHANNEL1:
- case A52_CHANNEL2:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO;
- break;
- /* stereo input */
- case A52_CHANNEL:
- case A52_STEREO:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
- break;
- /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */
- case A52_DOLBY:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_DOLBY;
- break;
- /* 3F/2R input */
- case A52_3F2R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R;
- break;
- /* 3F/1R input */
- case A52_3F1R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R;
- break;
- /* other inputs */
- case A52_3F:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F;
- break;
- case A52_2F1R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R;
- break;
- case A52_2F2R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R;
- break;
- /* unknown */
- default:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
- }
-
- if (flags & A52_LFE)
- {
- audio->config.in.channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE;
- }
-
- /* store the AC3 flags for future reference
- * This enables us to find out if we had a stereo or Dolby source later on
- * Store the ac3 flags in the public ac3flags property too, so we can access
- * it from the GUI
- */
- audio->config.flags.ac3 = audio->priv.config.a52.ac3flags = flags;
- update_audio_description( "AC3", audio, (flags & A52_CHANNEL_MASK) == A52_DOLBY );
- return 1;
-}
-
-static int hb_setup_dca_audio( hb_audio_t *audio, hb_buffer_t *b )
-{
- int i, flags, rate, bitrate, frame_length;
- dca_state_t * state = dca_init( 0 );
-
- /* since DCA frames don't line up with MPEG ES frames scan the
- * entire frame for an DCA sync pattern. */
- for ( i = 0; i < b->size - 7; ++i )
- {
- if( dca_syncinfo( state, &b->data[i], &flags, &rate, &bitrate,
- &frame_length ) )
- {
- break;
- }
- }
- if ( i >= b->size - 7 )
- {
- /* didn't find DCA sync */
- return 0;
- }
-
- audio->config.in.samplerate = rate;
- audio->config.in.bitrate = bitrate;
- switch( flags & DCA_CHANNEL_MASK )
- {
- /* mono sources */
- case DCA_MONO:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO;
- break;
- /* stereo input */
- case DCA_CHANNEL:
- case DCA_STEREO:
- case DCA_STEREO_SUMDIFF:
- case DCA_STEREO_TOTAL:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
- break;
- /* 3F/2R input */
- case DCA_3F2R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R;
- break;
- /* 3F/1R input */
- case DCA_3F1R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R;
- break;
- /* other inputs */
- case DCA_3F:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F;
- break;
- case DCA_2F1R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R;
- break;
- case DCA_2F2R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R;
- break;
- case DCA_4F2R:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_4F2R;
- break;
- /* unknown */
- default:
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
- }
-
- if (flags & DCA_LFE)
- {
- audio->config.in.channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE;
- }
-
- /* store the DCA flags for future reference
- * This enables us to find out if we had a stereo or Dolby source later on
- * store the dca flags in the public dcaflags property too, so we can access
- * it from the GUI
- */
- audio->config.flags.dca = audio->priv.config.dca.dcaflags = flags;
- update_audio_description( "DCA", audio, (flags & DCA_CHANNEL_MASK) == DCA_DOLBY );
- return 1;
-}
-
-static int hb_setup_pcm_audio( hb_audio_t *audio, hb_buffer_t *b )
-{
- // LPCM doesn't have a sync pattern like AC3 or DCA but every
- // LPCM elementary stream packet starts with a 7 byte header
- // giving the characteristics of the stream.
- // See libhb/declpcm.c for a description of the LPCM header.
-
- static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 };
- static const int hdr2samplesize[] = { 16, 20, 24, 16 };
- static const int hdr2layout[] = {
- HB_INPUT_CH_LAYOUT_MONO, HB_INPUT_CH_LAYOUT_STEREO,
- HB_INPUT_CH_LAYOUT_2F1R, HB_INPUT_CH_LAYOUT_2F2R,
- HB_INPUT_CH_LAYOUT_3F2R, HB_INPUT_CH_LAYOUT_4F2R,
- HB_INPUT_CH_LAYOUT_STEREO, HB_INPUT_CH_LAYOUT_STEREO,
- };
-
- int nchannels = ( b->data[4] & 7 ) + 1;
- int sample_size = hdr2samplesize[b->data[4] >> 6];
-
- int rate = hdr2samplerate[ ( b->data[4] >> 4 ) & 0x3 ];
- int bitrate = rate * sample_size * nchannels;
-
- audio->config.in.samplerate = rate;
- audio->config.in.bitrate = bitrate;
- audio->config.in.channel_layout = hdr2layout[nchannels - 1];
- update_audio_description( "LPCM", audio, 0 );
- return 1;
-}
-
-static int hb_setup_mpg_audio( hb_audio_t *audio, hb_buffer_t *b )
-{
- /* XXX
- * This is a placeholder to get the audio sample rate set.
- * It should be replaced by something that extracts the correct info from
- * the mpeg audio bitstream.
- */
- audio->config.in.samplerate = 48000;
- audio->config.in.bitrate = 384000;
- audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
- update_audio_description( "MPGA", audio, 0 );
- return 1;
-}
-
/*
* This routine is called for every frame from a non-video elementary stream.
* These are a mix of audio & subtitle streams, some of which we want & some
@@ -804,29 +569,70 @@ static void LookForAudio( hb_title_t * title, hb_buffer_t * b )
return;
}
- switch ( audio->config.in.codec )
- {
- case HB_ACODEC_AC3:
- hb_setup_a52_audio( audio, b );
- break;
+ hb_work_object_t *w = hb_codec_decoder( audio->config.in.codec );
- case HB_ACODEC_DCA:
- hb_setup_dca_audio( audio, b );
- break;
-
- case HB_ACODEC_LPCM:
- hb_setup_pcm_audio( audio, b );
- break;
+ if ( w == NULL || w->bsinfo == NULL )
+ {
+ hb_log( "Internal error in scan: unhandled audio type %d for id 0x%x",
+ audio->config.in.codec, audio->id );
+ goto drop_audio;
+ }
- case HB_ACODEC_MPGA:
- hb_setup_mpg_audio( audio, b );
- break;
+ hb_work_info_t info;
+ w->audio = audio;
+ w->codec_param = audio->config.in.codec_param;
+ int ret = w->bsinfo( w, b, &info );
+ if ( ret < 0 )
+ {
+ hb_log( "no info on audio type %d/0x%x for id 0x%x",
+ audio->config.in.codec, audio->config.in.codec_param,
+ audio->id );
+ goto drop_audio;
+ }
+ if ( !info.bitrate )
+ {
+ /* didn't find any info */
+ return;
+ }
+ audio->config.in.samplerate = info.rate;
+ audio->config.in.bitrate = info.bitrate;
+ audio->config.in.channel_layout = info.channel_layout;
+ audio->config.flags.ac3 = info.flags;
- default:
- hb_log( "Internal error in scan: unhandled audio type %d for 0x%x",
- audio->config.in.codec, audio->id );
- break;
+ // update the audio description string based on the info we found
+ if ( audio->config.flags.ac3 & AUDIO_F_DOLBY )
+ {
+ strcat( audio->config.lang.description, " (Dolby Surround)" );
}
+ else
+ {
+ 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) );
+ }
+
+ hb_log( "scan: audio 0x%x: %s, rate=%dHz, bitrate=%d %s", audio->id,
+ info.name, audio->config.in.samplerate, audio->config.in.bitrate,
+ audio->config.lang.description );
+
+ free( w );
+ return;
+
+ // We get here if there's no hope of finding info on an audio bitstream,
+ // either because we don't have a decoder (or a decoder with a bitstream
+ // info proc) or because the decoder's info proc said that the stream
+ // wasn't something it could handle. Delete the item from the title's
+ // audio list so we won't keep reading packets while trying to get its
+ // bitstream info.
+ drop_audio:
+ if ( w )
+ free( w );
+
+ hb_list_rem( title->list_audio, audio );
}
/*
diff --git a/libhb/stream.c b/libhb/stream.c
index 065a051e5..c6dce850b 100755
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -7,16 +7,84 @@
#include "hb.h"
#include "lang.h"
#include "a52dec/a52.h"
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
#include <string.h>
+#include <ctype.h>
#define min(a, b) a < b ? a : b
+/*
+ * This table defines how ISO MPEG stream type codes map to HandBrake
+ * codecs. It is indexed by the 8 bit stream type and contains the codec
+ * worker object id and a parameter for that worker proc (ignored except
+ * for the ffmpeg-based codecs in which case it is the ffmpeg codec id).
+ *
+ * Entries with a worker proc id of 0 or a kind of 'U' indicate that HB
+ * doesn't handle the stream type.
+ */
+typedef struct {
+ enum { U, A, V } kind; /* unknown / audio / video */
+ int codec; /* HB worker object id of codec */
+ int codec_param; /* param for codec (usually ffmpeg codec id) */
+ const char* name; /* description of type */
+} stream2codec_t;
+
+#define st(id, kind, codec, codec_param, name) \
+ [id] = { kind, codec, codec_param, name }
+
+static const stream2codec_t st2codec[256] = {
+ st(0x01, V, WORK_DECMPEG2, 0, "MPEG1"),
+ st(0x02, V, WORK_DECMPEG2, 0, "MPEG2"),
+ st(0x03, A, HB_ACODEC_MPGA, CODEC_ID_MP2, "MPEG1"),
+ st(0x04, A, HB_ACODEC_MPGA, CODEC_ID_MP2, "MPEG2"),
+ st(0x05, U, 0, 0, "ISO 13818-1 private section"),
+ st(0x06, U, 0, 0, "ISO 13818-1 PES private data"),
+ st(0x07, U, 0, 0, "ISO 13522 MHEG"),
+ st(0x08, U, 0, 0, "ISO 13818-1 DSM-CC"),
+ st(0x09, U, 0, 0, "ISO 13818-1 auxiliary"),
+ st(0x0a, U, 0, 0, "ISO 13818-6 encap"),
+ st(0x0b, U, 0, 0, "ISO 13818-6 DSM-CC U-N msgs"),
+ st(0x0c, U, 0, 0, "ISO 13818-6 Stream descriptors"),
+ st(0x0d, U, 0, 0, "ISO 13818-6 Sections"),
+ st(0x0e, U, 0, 0, "ISO 13818-1 auxiliary"),
+ st(0x0f, A, HB_ACODEC_MPGA, CODEC_ID_AAC, "ISO 13818-7 AAC Audio"),
+ st(0x10, V, WORK_DECAVCODECV, CODEC_ID_MPEG4, "MPEG4"),
+ st(0x11, A, HB_ACODEC_MPGA, CODEC_ID_AAC, "MPEG4 LATM AAC"),
+ st(0x12, U, 0, 0, "MPEG4 generic"),
+
+ st(0x14, U, 0, 0, "ISO 13818-6 DSM-CC download"),
+
+ st(0x1b, V, WORK_DECAVCODECV, CODEC_ID_H264, "H.264"),
+
+ st(0x80, U, 0, 0, "DigiCipher II Video"),
+ st(0x81, A, HB_ACODEC_AC3, 0, "AC-3"),
+ st(0x82, A, HB_ACODEC_MPGA, CODEC_ID_DTS, "HDMV DTS"),
+ st(0x83, A, HB_ACODEC_LPCM, 0, "LPCM"),
+ st(0x84, A, 0, 0, "SDDS"),
+ st(0x85, U, 0, 0, "ATSC Program ID"),
+ st(0x86, U, 0, 0, "SCTE 35 splice info"),
+ st(0x87, A, 0, 0, "E-AC-3"),
+
+ st(0x8a, A, HB_ACODEC_DCA, 0, "DTS"),
+
+ st(0x91, A, HB_ACODEC_AC3, 0, "AC-3"),
+ st(0x92, U, 0, 0, "Subtitle"),
+
+ st(0x94, A, 0, 0, "SDDS"),
+ st(0xa0, V, 0, 0, "MSCODEC"),
+
+ st(0xea, V, WORK_DECAVCODECV, CODEC_ID_VC1, "VC1"),
+};
+#undef st
+
typedef enum {
hb_stream_type_unknown = 0,
transport,
program,
- dvd_program
+ dvd_program,
+ ffmpeg
} hb_stream_type_t;
#define kMaxNumberVideoPIDS 1
@@ -65,7 +133,9 @@ struct hb_stream_s
char *path;
FILE *file_handle;
hb_stream_type_t hb_stream_type;
- int opentype;
+ hb_title_t *title;
+
+ AVFormatContext *ffmpeg_ic;
struct {
int lang_code;
@@ -116,6 +186,12 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
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 void ffmpeg_close( hb_stream_t *d );
+static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream );
+static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf );
+static int ffmpeg_seek( hb_stream_t *stream, float frac );
+
/*
* streams have a bunch of state that's learned during the scan. We don't
* want to throw away the state when scan does a close then relearn
@@ -342,9 +418,8 @@ static void hb_stream_delete( hb_stream_t *d )
***********************************************************************
*
**********************************************************************/
-hb_stream_t * hb_stream_open( char *path, int opentype )
+hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
{
-
FILE *f = fopen( path, "r" );
if ( f == NULL )
{
@@ -367,23 +442,15 @@ hb_stream_t * hb_stream_open( char *path, int opentype )
* (even if we have saved state, the stream may have changed).
*/
hb_stream_t *ss = hb_stream_lookup( path );
- if ( opentype == 1 )
+ if ( title && ss && ss->hb_stream_type != ffmpeg )
{
- /* opening to read - we must have saved state */
- if ( ss == NULL )
- {
- hb_log( "hb_stream_open: error: re-opening %s but no scan state", path );
- fclose( f );
- free( d );
- return NULL;
- }
/*
* copy the saved state since we might be encoding the same stream
* multiple times.
*/
memcpy( d, ss, sizeof(*d) );
d->file_handle = f;
- d->opentype = opentype;
+ d->title = title;
d->path = strdup( path );
if ( d->hb_stream_type == transport )
@@ -410,11 +477,20 @@ hb_stream_t * hb_stream_open( char *path, int opentype )
hb_stream_state_delete( ss );
}
d->file_handle = f;
- d->opentype = opentype;
+ d->title = title;
d->path = strdup( path );
- if (d->path != NULL && hb_stream_get_type( d ) != 0 )
+ if (d->path != NULL )
{
- return d;
+ if ( hb_stream_get_type( d ) != 0 )
+ {
+ return d;
+ }
+ fclose( d->file_handle );
+ d->file_handle = NULL;
+ if ( ffmpeg_open( d, title ) )
+ {
+ return d;
+ }
}
fclose( d->file_handle );
if (d->path)
@@ -434,17 +510,27 @@ hb_stream_t * hb_stream_open( char *path, int opentype )
void hb_stream_close( hb_stream_t ** _d )
{
hb_stream_t *stream = * _d;
+
+ if ( stream->hb_stream_type == ffmpeg )
+ {
+ ffmpeg_close( stream );
+ hb_stream_delete( stream );
+ *_d = NULL;
+ return;
+ }
+
if ( stream->frames )
{
hb_log( "stream: %d good frames, %d errors (%.0f%%)", stream->frames,
stream->errors, (double)stream->errors * 100. /
(double)stream->frames );
}
+
/*
* if the stream was opened for a scan, cache the result, otherwise delete
* the state.
*/
- if ( stream->opentype == 0 )
+ if ( stream->title == NULL )
{
hb_stream_delete_dynamic( stream );
if ( stream_state_list == NULL )
@@ -501,6 +587,9 @@ static int index_of_pid(int pid, hb_stream_t *stream)
**********************************************************************/
hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
{
+ if ( stream->hb_stream_type == ffmpeg )
+ return ffmpeg_title_scan( stream );
+
// 'Barebones Title'
hb_title_t *aTitle = hb_title_init( stream->path, 0 );
aTitle->index = 1;
@@ -557,6 +646,10 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
stream->ts_audio_pids[stream->ts_number_audio_pids++] =
stream->pmt_info.PCR_PID;
}
+
+ // set up the video codec to use for this title
+ aTitle->video_codec = st2codec[stream->ts_stream_type[0]].codec;
+ aTitle->video_codec_param = st2codec[stream->ts_stream_type[0]].codec_param;
}
else
{
@@ -868,6 +961,10 @@ static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle)
**********************************************************************/
int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
{
+ if ( src_stream->hb_stream_type == ffmpeg )
+ {
+ return ffmpeg_read( src_stream, b );
+ }
if ( src_stream->hb_stream_type == dvd_program )
{
size_t amt_read = fread(b->data, HB_DVD_READ_BUFFER_SIZE, 1,
@@ -931,6 +1028,10 @@ int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
**********************************************************************/
int hb_stream_seek( hb_stream_t * src_stream, float f )
{
+ if ( src_stream->hb_stream_type == ffmpeg )
+ {
+ return ffmpeg_seek( src_stream, f );
+ }
off_t stream_size, cur_pos, new_pos;
double pos_ratio = f;
cur_pos = ftello( src_stream->file_handle );
@@ -960,6 +1061,21 @@ int hb_stream_seek( hb_stream_t * src_stream, float f )
return 1;
}
+static const char* make_upper( const char* s )
+{
+ static char name[8];
+ char *cp = name;
+ char *ep = cp + sizeof(name)-1;
+
+ while ( *s && cp < ep )
+ {
+ *cp++ = islower(*s)? toupper(*s) : *s;
+ ++s;
+ }
+ *cp = 0;
+ return name;
+}
+
static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
{
/* XXX
@@ -968,12 +1084,37 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
* code or a lang pointer into the audio config & let the common description
* formatting routine in scan.c do all the stuff below.
*/
+ const char *codec_name;
+ AVCodecContext *cc;
+
+ if ( audio->config.in.codec == HB_ACODEC_FFMPEG &&
+ ( cc = hb_ffmpeg_context( audio->config.in.codec_param ) ) &&
+ avcodec_find_decoder( cc->codec_id ) )
+ {
+ codec_name = make_upper( avcodec_find_decoder( cc->codec_id )->name );
+ if ( !strcmp( codec_name, "LIBFAAD" ) )
+ {
+ codec_name = "AAC";
+ }
+ }
+ else if ( audio->config.in.codec == HB_ACODEC_MPGA &&
+ avcodec_find_decoder( audio->config.in.codec_param ) )
+ {
+ codec_name = avcodec_find_decoder( audio->config.in.codec_param )->name;
+ }
+ else
+ {
+ codec_name = audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" :
+ audio->config.in.codec == HB_ACODEC_DCA ? "DTS" :
+ audio->config.in.codec == HB_ACODEC_MPGA ? "MPEG" :
+ audio->config.in.codec == HB_ACODEC_LPCM ? "LPCM" :
+ audio->config.in.codec == HB_ACODEC_FFMPEG ? "FFMPEG" :
+ "Unknown";
+ }
snprintf( audio->config.lang.description,
sizeof( audio->config.lang.description ), "%s (%s)",
strlen(lang->native_name) ? lang->native_name : lang->eng_name,
- audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" :
- audio->config.in.codec == HB_ACODEC_DCA ? "DTS" :
- audio->config.in.codec == HB_ACODEC_MPGA ? "MPEG" : "LPCM" );
+ codec_name );
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 ),
@@ -992,65 +1133,52 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
buf = hb_ts_stream_getPEStype(stream, stream->ts_audio_pids[aud_pid_index]);
/* check that we found a PES header */
+ uint8_t stype = 0;
if (buf && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01)
{
- if (buf[3] == 0xbd)
+ // 0xbd is the normal container for AC3/DCA/PCM/etc. 0xfd indicates an
+ // extended stream id (ISO 13818-1(2007)). If we cared about the
+ // real id we'd have to look inside the PES extension to find it.
+ // But since we remap stream id's when we generate PS packets from
+ // the TS packets we can just ignore the actual id.
+ if ( buf[3] == 0xbd || buf[3] == 0xfd )
{
audio->id = 0x80bd | (aud_pid_index << 8);
- audio->config.in.codec = HB_ACODEC_AC3;
- hb_log("transport stream pid 0x%x (type 0x%x) is AC-3 audio id 0x%x",
- stream->ts_audio_pids[aud_pid_index],
- stream->ts_stream_type[1 + aud_pid_index],
- audio->id);
- stream->ts_stream_type[1 + aud_pid_index] = 0x81;
- stream->ts_streamid[1 + aud_pid_index] = 0xbd;
- }
- else if (buf[3] == 0xfd)
- {
- /* XXX Extended stream id (ISO 13818-1(2000) Amd 2) - we have to look
- * inside the PES extension to find out the real stream id then
- * figure out what the heck it means. For now we just use the stream
- * type if one was specified. */
- const char *atype = 0;
- switch (stream->ts_stream_type[1 + aud_pid_index])
+ stype = stream->ts_stream_type[1 + aud_pid_index];
+ if ( st2codec[stype].kind == U )
{
- case 0x81: // AC-3
- atype = "AC-3";
- audio->config.in.codec = HB_ACODEC_AC3;
- break;
- case 0x82: // HDMV DTS
- case 0x8a: // DTS
- atype = "DTS";
- audio->config.in.codec = HB_ACODEC_DCA;
- break;
- case 0x83: // LPCM
- atype = "PCM";
- audio->config.in.codec = HB_ACODEC_LPCM;
- break;
+ // XXX assume unknown stream types are AC-3 (if they're not
+ // audio we'll find that out during the scan but if they're
+ // some other type of audio we'll end up ignoring them).
+ stype = 0x81;
+ stream->ts_stream_type[1 + aud_pid_index] = 0x81;
}
- audio->id = 0x80bd | (aud_pid_index << 8);
stream->ts_streamid[1 + aud_pid_index] = 0xbd;
- hb_log("transport stream pid 0x%x (type 0x%x) is %s audio id 0x%x",
- stream->ts_audio_pids[aud_pid_index],
- stream->ts_stream_type[1 + aud_pid_index], atype, audio->id);
}
else if ((buf[3] & 0xe0) == 0xc0)
{
- audio->id = buf[3] | aud_pid_index;
- audio->config.in.codec = HB_ACODEC_MPGA;
- hb_log("transport stream pid 0x%x (type 0x%x) is MPEG audio id 0x%x",
- stream->ts_audio_pids[aud_pid_index],
- stream->ts_stream_type[1 + aud_pid_index],
- audio->id);
- stream->ts_stream_type[1 + aud_pid_index] = 0x03;
- stream->ts_streamid[1 + aud_pid_index] = buf[3];
+ audio->id = 0xc0 | aud_pid_index;
+ stype = stream->ts_stream_type[1 + aud_pid_index];
+ if ( st2codec[stype].kind == U )
+ {
+ // XXX assume unknown stream types are MPEG audio
+ stype = 0x03;
+ stream->ts_stream_type[1 + aud_pid_index] = 0x03;
+ }
}
}
- fseeko(stream->file_handle, cur_pos, SEEK_SET);
- if ( audio->config.in.codec )
+ // if we found an audio stream type & HB has a codec that can decode it
+ // finish configuring the audio so we'll add it to the title's list.
+ if ( st2codec[stype].kind == A && st2codec[stype].codec )
{
+ stream->ts_streamid[1 + aud_pid_index] = audio->id;
+ audio->config.in.codec = st2codec[stype].codec;
+ audio->config.in.codec_param = st2codec[stype].codec_param;
set_audio_description( audio,
lang_for_code( stream->a52_info[aud_pid_index].lang_code ) );
+ hb_log("transport stream pid 0x%x (type 0x%x) is %s audio id 0x%x",
+ stream->ts_audio_pids[aud_pid_index],
+ stype, st2codec[stype].name, audio->id);
}
else
{
@@ -1058,6 +1186,7 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
stream->ts_audio_pids[aud_pid_index],
stream->ts_stream_type[1 + aud_pid_index]);
}
+ fseeko(stream->file_handle, cur_pos, SEEK_SET);
return audio;
}
@@ -1294,54 +1423,9 @@ static void decode_element_descriptors(hb_stream_t* stream, int esindx,
}
}
-static const char * stream_type_name (uint8_t stream_type)
+static const char *stream_type_name (uint8_t stream_type)
{
- switch( stream_type )
- {
- case 0x01: return("ISO 11172 (MPEG1) Video");
- case 0x02: return("ISO 13818-2 (MPEG2) Video");
- case 0x03: return("ISO 11172 (MPEG1) Audio");
- case 0x04: return("ISO 13818-3 (MPEG2) Audio");
- case 0x05: return("ISO 13818-1 private section");
- case 0x06: return("ISO 13818-1 PES private data");
- case 0x07: return("ISO 13522 MHEG");
- case 0x08: return("ISO 13818-1 DSM-CC");
- case 0x09: return("ISO 13818-1 auxiliary");
- case 0x0a: return("ISO 13818-6 multi-protocol encap");
- case 0x0b: return("ISO 13818-6 DSM-CC U-N msgs");
- case 0x0c: return("ISO 13818-6 Stream descriptors");
- case 0x0d: return("ISO 13818-6 Sections");
- case 0x0e: return("ISO 13818-1 auxiliary");
- case 0x0f: return("ISO 13818-7 AAC Audio");
- case 0x10: return("MPEG4 Video");
- case 0x11: return("MPEG4 Audio");
- case 0x12: return("MPEG4 generic");
-
- case 0x14: return("ISO 13818-6 DSM-CC download");
-
- case 0x1b: return("H.264 Video");
-
- case 0x80: return("DigiCipher II Video");
- case 0x81: return("A52/AC-3 Audio");
- case 0x82: return("HDMV DTS Audio");
- case 0x83: return("LPCM Audio");
- case 0x84: return("SDDS Audio");
- case 0x85: return("ATSC Program ID");
- case 0x86: return("SCTE 35 splice info");
- case 0x87: return("ATSC E-AC-3");
-
- case 0x8a: return("DTS Audio");
-
- case 0x91: return("A52b/AC-3 Audio");
- case 0x92: return("Subtitle");
-
- case 0x94: return("SDDS Audio");
- case 0xa0: return("MSCODEC Video");
-
- case 0xea: return("VC-1 Video");
-
- default: return("Unknown");
- }
+ return st2codec[stream_type].name? st2codec[stream_type].name : "Unknown";
}
int decode_program_map(hb_stream_t* stream)
@@ -1393,8 +1477,7 @@ int decode_program_map(hb_stream_t* stream)
}
- if (stream->ts_number_video_pids == 0 &&
- ( stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B ) )
+ if (stream->ts_number_video_pids == 0 && st2codec[stream_type].kind == V )
{
stream->ts_video_pids[0] = elementary_PID;
stream->ts_stream_type[0] = stream_type;
@@ -1765,7 +1848,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
int len;
// we always ship a PACK header plus all the data in our demux buf.
- // AC3 audio also always needs it substream header.
+ // AC3 audio also always needs its substream header.
len = 14 + stream->ts_pos[curstream];
if ( stream->ts_stream_type[curstream] == 0x81)
{
@@ -1876,37 +1959,82 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
stream->ts_pos[curstream] = 0;
}
-static int isIframe( const uint8_t *buf, int adapt_len )
+static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len )
{
- // Look for the Group of Pictures packet
+ // For mpeg2: look for a gop start or i-frame picture start
+ // for h.264: look for idr nal type or a slice header for an i-frame
+ // for vc1: ???
int i;
uint32_t strid = 0;
- for (i = 4 + adapt_len; i < 188; i++)
+
+ if ( stream->ts_stream_type[0] <= 2 )
{
- strid = (strid << 8) | buf[i];
- switch ( strid )
+ // This section of the code handles MPEG-1 and MPEG-2 video streams
+ for (i = 13 + adapt_len; i < 188; i++)
{
- case 0x000001B8: // group_start_code (GOP header)
- case 0x000001B3: // sequence_header code
- return 1;
+ strid = (strid << 8) | buf[i];
+ if ( ( strid >> 8 ) == 1 )
+ {
+ // we found a start code
+ uint8_t id = strid;
+ switch ( id )
+ {
+ case 0xB8: // group_start_code (GOP header)
+ case 0xB3: // sequence_header code
+ return 1;
+
+ case 0x00: // picture_start_code
+ // picture_header, let's see if it's an I-frame
+ if (i<185)
+ {
+ // check if picture_coding_type == 1
+ if ((buf[i+2] & (0x7 << 3)) == (1 << 3))
+ {
+ // found an I-frame picture
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ }
+ // didn't find an I-frame
+ return 0;
+ }
+ if ( stream->ts_stream_type[0] == 0x1b )
+ {
+ // we have an h.264 stream
+ for (i = 13 + adapt_len; i < 188; i++)
+ {
+ strid = (strid << 8) | buf[i];
+ if ( ( strid >> 8 ) == 1 )
+ {
+ // we found a start code - remove the ref_idc from the nal type
+ uint8_t nal_type = strid & 0x1f;
+ if ( nal_type == 0x05 )
+ // h.264 IDR picture start
+ return 1;
- case 0x00000100: // picture_start_code
- // picture_header, let's see if it's an I-frame
- if (i<185)
+ if ( nal_type == 0x01 )
{
- // check if picture_coding_type == 1
- if ((buf[i+2] & (0x7 << 3)) == (1 << 3))
+ // h.264 slice: has to be start MB 0 & type I (2, 4, 7 or 9)
+ uint8_t id = buf[i+1];
+ if ( ( id >> 4 ) == 0x0b || ( id >> 2 ) == 0x25 ||
+ id == 0x88 || id == 0x8a )
{
- // found an I-frame picture
return 1;
}
}
- break;
+ }
}
+ // didn't find an I-frame
+ return 0;
}
- // didn't find an I frame
- return 0;
+
+ // we don't understand the stream type so just say "yes" otherwise
+ // we'll discard all the video.
+ return 1;
}
/***********************************************************************
@@ -2034,7 +2162,7 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
{
// video skips to an iframe after a bad packet to minimize
// screen corruption
- if ( curstream == 0 && !isIframe( buf, adapt_len ) )
+ if ( curstream == 0 && !isIframe( stream, buf, adapt_len ) )
{
continue;
}
@@ -2047,8 +2175,7 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
{
if ( !stream->ts_foundfirst[0] )
{
- if ( stream->ts_stream_type[0] == 2 &&
- !isIframe( buf, adapt_len ) )
+ if ( !isIframe( stream, buf, adapt_len ) )
{
// didn't find an I frame
continue;
@@ -2103,11 +2230,6 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
}
}
-/***********************************************************************
- * hb_ts_stream_reset
- ***********************************************************************
- *
- **********************************************************************/
static void hb_ts_stream_reset(hb_stream_t *stream)
{
int i;
@@ -2132,3 +2254,322 @@ static void hb_ts_stream_reset(hb_stream_t *stream)
align_to_next_packet(stream);
}
+// ------------------------------------------------------------------
+// Support for reading media files via the ffmpeg libraries.
+
+static void ffmpeg_add_codec( hb_stream_t *stream, int stream_index )
+{
+ // 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_resilience = 1;
+ context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
+ AVCodec *codec = avcodec_find_decoder( context->codec_id );
+ avcodec_open( 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_FFMPEG )
+ {
+ ffmpeg_add_codec( stream,
+ audio->config.in.codec_param >> ffmpeg_sl_bits );
+ }
+ }
+}
+
+void *hb_ffmpeg_context( int codec_param )
+{
+ 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 )
+{
+ 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;
+
+ av_log_set_level( AV_LOG_ERROR );
+ if ( av_open_input_file( &ic, stream->path, NULL, 0, NULL ) < 0 )
+ {
+ return 0;
+ }
+ if ( av_find_stream_info( ic ) < 0 )
+ goto fail;
+
+ stream->ffmpeg_ic = ic;
+ stream->hb_stream_type = ffmpeg;
+
+ if ( title )
+ {
+ // 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.
+ ffmpeg_remap_stream( stream, title );
+ ffmpeg_seek( stream, 0. );
+ 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.
+ av_log_set_level( AV_LOG_INFO );
+ dump_format( 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 )
+ {
+ if ( ic->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
+ {
+ break;
+ }
+ }
+ if ( i >= ic->nb_streams )
+ goto fail;
+ }
+ return 1;
+
+ fail:
+ av_close_input_file( 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;
+}
+
+static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
+{
+ AVStream *st = stream->ffmpeg_ic->streams[id];
+ AVCodecContext *codec = st->codec;
+
+ // scan will ignore any audio without a bitrate. Since we've already
+ // typed the audio in order to determine its codec we set up the audio
+ // paramters here.
+ if ( codec->bit_rate || codec->sample_rate )
+ {
+ static const int chan2layout[] = {
+ HB_INPUT_CH_LAYOUT_MONO, // We should allow no audio really.
+ HB_INPUT_CH_LAYOUT_MONO,
+ HB_INPUT_CH_LAYOUT_STEREO,
+ HB_INPUT_CH_LAYOUT_2F1R,
+ HB_INPUT_CH_LAYOUT_2F2R,
+ HB_INPUT_CH_LAYOUT_3F2R,
+ HB_INPUT_CH_LAYOUT_4F2R,
+ HB_INPUT_CH_LAYOUT_STEREO,
+ HB_INPUT_CH_LAYOUT_STEREO,
+ };
+
+ hb_audio_t *audio = calloc( 1, sizeof(*audio) );;
+
+ audio->id = id;
+ if ( codec->codec_id == CODEC_ID_AC3 )
+ {
+ audio->config.in.codec = HB_ACODEC_AC3;
+ }
+ else
+ {
+ audio->config.in.codec = HB_ACODEC_FFMPEG;
+ audio->config.in.codec_param = ffmpeg_codec_param( stream, id );
+
+ audio->config.in.bitrate = codec->bit_rate? codec->bit_rate : 1;
+ audio->config.in.samplerate = codec->sample_rate;
+ audio->config.in.channel_layout = chan2layout[codec->channels & 7];
+ }
+
+ set_audio_description( audio, lang_for_code2( st->language ) );
+
+ hb_list_add( title->list_audio, audio );
+ }
+}
+
+static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream )
+{
+ AVFormatContext *ic = stream->ffmpeg_ic;
+
+ // 'Barebones Title'
+ hb_title_t *title = hb_title_init( stream->path, 0 );
+ title->index = 1;
+
+ // Copy part of the stream path to the title name
+ char *sep = strrchr(stream->path, '/');
+ if (sep)
+ strcpy(title->name, sep+1);
+ char *dot_term = strrchr(title->name, '.');
+ if (dot_term)
+ *dot_term = '\0';
+
+ uint64_t dur = ic->duration * 90000 / AV_TIME_BASE;
+ title->duration = dur;
+ dur /= 90000;
+ title->hours = dur / 3600;
+ title->minutes = ( dur % 3600 ) / 60;
+ title->seconds = dur % 60;
+
+ // One Chapter
+ hb_chapter_t * chapter;
+ chapter = calloc( sizeof( hb_chapter_t ), 1 );
+ chapter->index = 1;
+ chapter->duration = title->duration;
+ chapter->hours = title->hours;
+ chapter->minutes = title->minutes;
+ chapter->seconds = title->seconds;
+ hb_list_add( title->list_chapter, chapter );
+
+ // set the title to decode the first video stream in the file
+ title->demuxer = HB_NULL_DEMUXER;
+ title->video_codec = 0;
+ int i;
+ for (i = 0; i < ic->nb_streams; ++i )
+ {
+ if ( ic->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO &&
+ avcodec_find_decoder( ic->streams[i]->codec->codec_id ) &&
+ title->video_codec == 0 )
+ {
+ title->video_id = i;
+
+ // 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 );
+ }
+ else if ( ic->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO &&
+ avcodec_find_decoder( ic->streams[i]->codec->codec_id ) )
+ {
+ add_ffmpeg_audio( title, stream, i );
+ }
+ }
+
+ return title;
+}
+
+static int64_t av_to_hb_pts( int64_t pts, double conv_factor )
+{
+ if ( pts == AV_NOPTS_VALUE )
+ return -1;
+ return (int64_t)( (double)pts * conv_factor );
+}
+
+static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf )
+{
+ AVPacket pkt;
+
+ if ( av_read_frame( stream->ffmpeg_ic, &pkt ) < 0 )
+ {
+ return 0;
+ }
+ if ( pkt.size > buf->alloc )
+ {
+ // need to expand buffer
+ hb_buffer_realloc( buf, pkt.size );
+ }
+ memcpy( buf->data, pkt.data, pkt.size );
+ buf->id = pkt.stream_index;
+ buf->size = pkt.size;
+ int64_t pts = pkt.pts != AV_NOPTS_VALUE? pkt.pts :
+ pkt.dts != AV_NOPTS_VALUE? pkt.dts : -1;
+ buf->start = av_to_hb_pts( pts,
+ av_q2d(stream->ffmpeg_ic->streams[pkt.stream_index]->time_base)*90000. );
+ buf->renderOffset = av_to_hb_pts( pkt.pts,
+ av_q2d(stream->ffmpeg_ic->streams[pkt.stream_index]->time_base)*90000. );
+ av_free_packet( &pkt );
+ return 1;
+}
+
+static int ffmpeg_seek( hb_stream_t *stream, float frac )
+{
+ AVFormatContext *ic = stream->ffmpeg_ic;
+ int64_t pos = (double)ic->duration * (double)frac;
+ av_seek_frame( ic, -1, pos, pos? 0 : AVSEEK_FLAG_BACKWARD );
+ return 1;
+}
diff --git a/libhb/sync.c b/libhb/sync.c
index 34b426f6d..f9c738bdf 100644
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -8,7 +8,7 @@
#include <stdio.h>
#include "samplerate.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
#ifdef INT64_MIN
#undef INT64_MIN /* Because it isn't defined correctly in Zeta */
@@ -729,28 +729,34 @@ static void SyncAudio( hb_work_object_t * w, int i )
{
if ( (int64_t)( buf->start - sync->next_pts ) < 0 )
{
- /*
- * audio time went backwards by more than a frame time (this can
- * happen when we reset the PTS because of lost data).
- * Discard data that's in the past.
- */
- if ( sync->first_drop == 0 )
+ // audio time went backwards.
+ // If our output clock is more than a half frame ahead of the
+ // input clock drop this frame to move closer to sync.
+ // Otherwise drop frames until the input clock matches the output clock.
+ if ( sync->first_drop || sync->next_start - buf->start > 90*15 )
{
- sync->first_drop = buf->start;
+ // Discard data that's in the past.
+ if ( sync->first_drop == 0 )
+ {
+ sync->first_drop = sync->next_pts;
+ }
+ ++sync->drop_count;
+ buf = hb_fifo_get( audio->priv.fifo_raw );
+ hb_buffer_close( &buf );
+ continue;
}
- ++sync->drop_count;
- buf = hb_fifo_get( audio->priv.fifo_raw );
- hb_buffer_close( &buf );
- continue;
+ sync->next_pts = buf->start;
}
if ( sync->first_drop )
{
+ // we were dropping old data but input buf time is now current
hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
"(next %lld, current %lld)", i,
(int)( sync->next_pts - sync->first_drop ) / 90,
sync->drop_count, sync->first_drop, sync->next_pts );
sync->first_drop = 0;
sync->drop_count = 0;
+ sync->next_pts = buf->start;
}
if ( buf->start - sync->next_pts >= (90 * 70) )
{
diff --git a/libhb/work.c b/libhb/work.c
index 9c2ecbf9a..cf801e48b 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -70,19 +70,45 @@ static void work_func( void * _work )
free( work );
}
-static hb_work_object_t * getWork( int id )
+hb_work_object_t * hb_get_work( int id )
{
hb_work_object_t * w;
for( w = hb_objects; w; w = w->next )
{
if( w->id == id )
{
- return w;
+ hb_work_object_t *wc = malloc( sizeof(*w) );
+ *wc = *w;
+ return wc;
}
}
return NULL;
}
+hb_work_object_t * hb_codec_decoder( int codec )
+{
+ switch( codec )
+ {
+ case HB_ACODEC_AC3: return hb_get_work( WORK_DECA52 );
+ case HB_ACODEC_DCA: return hb_get_work( WORK_DECDCA );
+ case HB_ACODEC_MPGA: return hb_get_work( WORK_DECAVCODEC );
+ case HB_ACODEC_LPCM: return hb_get_work( WORK_DECLPCM );
+ case HB_ACODEC_FFMPEG: return hb_get_work( WORK_DECAVCODECAI );
+ }
+ return NULL;
+}
+
+hb_work_object_t * hb_codec_encoder( int codec )
+{
+ switch( codec )
+ {
+ case HB_ACODEC_FAAC: return hb_get_work( WORK_ENCFAAC );
+ case HB_ACODEC_LAME: return hb_get_work( WORK_ENCLAME );
+ case HB_ACODEC_VORBIS: return hb_get_work( WORK_ENCVORBIS );
+ }
+ return NULL;
+}
+
/**
* Job initialization rountine.
* Initializes fifos.
@@ -99,10 +125,6 @@ static void do_job( hb_job_t * job, int cpu_count )
hb_title_t * title;
int i, j;
hb_work_object_t * w;
-
- /* FIXME: This feels really hackish, anything better? */
- hb_work_object_t * audio_w = NULL;
- hb_work_object_t * sub_w = NULL;
hb_work_object_t * final_w = NULL;
hb_audio_t * audio;
@@ -250,17 +272,19 @@ static void do_job( hb_job_t * job, int cpu_count )
job->fifo_mpeg4 = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
/* Synchronization */
- hb_list_add( job->list_work, ( w = getWork( WORK_SYNC ) ) );
+ hb_list_add( job->list_work, ( w = hb_get_work( WORK_SYNC ) ) );
w->fifo_in = NULL;
w->fifo_out = NULL;
/* Video decoder */
- hb_list_add( job->list_work, ( w = getWork( WORK_DECMPEG2 ) ) );
+ int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2;
+ hb_list_add( job->list_work, ( w = hb_get_work( vcodec ) ) );
+ w->codec_param = title->video_codec_param;
w->fifo_in = job->fifo_mpeg2;
w->fifo_out = job->fifo_raw;
/* Video renderer */
- hb_list_add( job->list_work, ( w = getWork( WORK_RENDER ) ) );
+ hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) );
w->fifo_in = job->fifo_sync;
w->fifo_out = job->fifo_render;
if ( job->indepth_scan )
@@ -278,21 +302,21 @@ static void do_job( hb_job_t * job, int cpu_count )
{
case HB_VCODEC_FFMPEG:
hb_log( " + encoder FFmpeg" );
- w = getWork( WORK_ENCAVCODEC );
+ w = hb_get_work( WORK_ENCAVCODEC );
break;
case HB_VCODEC_XVID:
hb_log( " + encoder XviD" );
- w = getWork( WORK_ENCXVID );
+ w = hb_get_work( WORK_ENCXVID );
break;
case HB_VCODEC_X264:
hb_log( " + encoder x264" );
if( job->x264opts != NULL && *job->x264opts != '\0' )
hb_log( " + x264 options: %s", job->x264opts);
- w = getWork( WORK_ENCX264 );
+ w = hb_get_work( WORK_ENCX264 );
break;
case HB_VCODEC_THEORA:
hb_log( " + encoder Theora" );
- w = getWork( WORK_ENCTHEORA );
+ w = hb_get_work( WORK_ENCTHEORA );
break;
}
w->fifo_in = job->fifo_render;
@@ -354,20 +378,10 @@ static void do_job( hb_job_t * job, int cpu_count )
* Don't add threads for subtitles when we are scanning, unless
* looking for forced subtitles.
*/
- if( sub_w != NULL )
- {
- /*
- * Need to copy the prior subtitle structure so that we
- * don't overwrite the fifos.
- */
- sub_w = calloc( sizeof( hb_work_object_t ), 1 );
- sub_w = memcpy( sub_w, w, sizeof( hb_work_object_t ));
- } else {
- w = sub_w = getWork( WORK_DECSUB );
- }
- hb_list_add( job->list_work, sub_w );
- sub_w->fifo_in = subtitle->fifo_in;
- sub_w->fifo_out = subtitle->fifo_raw;
+ w = hb_get_work( WORK_DECSUB );
+ w->fifo_in = subtitle->fifo_in;
+ w->fifo_out = subtitle->fifo_raw;
+ hb_list_add( job->list_work, w );
}
}
}
@@ -427,113 +441,129 @@ static void do_job( hb_job_t * job, int cpu_count )
/* sense-check the requested mixdown */
- if( audio->config.out.mixdown == 0 && audio->config.out.codec != HB_ACODEC_AC3 )
+ if( audio->config.out.mixdown == 0 &&
+ audio->config.out.codec != HB_ACODEC_AC3 )
+ {
+ /*
+ * Mixdown wasn't specified and this is not pass-through,
+ * set a default mixdown of stereo.
+ */
+ audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
+ }
+
+ // Here we try to sanitize the audio input to output mapping.
+ // Constraints are:
+ // 1. only the AC3 & DCA decoder libraries currently support mixdown
+ // 2. the lame encoder library only supports stereo.
+ // So if the encoder is lame we need the output to be stereo (or multichannel
+ // matrixed into stereo like dpl). If the decoder is not AC3 or DCA the
+ // encoder has to handle the input format since we can't do a mixdown.
+#define CAN_MIXDOWN(a) ( a->config.in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA) )
+#define STEREO_ONLY(a) ( a->config.out.codec & HB_ACODEC_LAME )
+
+ switch (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK)
+ {
+ // stereo input or something not handled below
+ default:
+ case HB_INPUT_CH_LAYOUT_STEREO:
+ // mono gets mixed up to stereo & more than stereo gets mixed down
+ if ( STEREO_ONLY( audio ) ||
+ audio->config.out.mixdown > HB_AMIXDOWN_STEREO)
{
- /*
- * Mixdown wasn't specified and this is not pass-through, set a default mixdown
- * of stereo.
- */
audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
}
+ break;
- /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
- but this may change in the future, so they are separated for flexibility */
- int audioCodecsSupportMono = ( (audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA) &&
- (audio->config.out.codec == HB_ACODEC_FAAC || audio->config.out.codec == HB_ACODEC_VORBIS) );
- int audioCodecsSupport6Ch = ( (audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA) &&
- (audio->config.out.codec == HB_ACODEC_FAAC || audio->config.out.codec == HB_ACODEC_VORBIS));
-
- /* find out what the format of our source audio is */
- switch (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK) {
-
- /* mono sources */
+ // mono input
case HB_INPUT_CH_LAYOUT_MONO:
- /* regardless of what stereo mixdown we've requested, a mono source always get mixed down
- to mono if we can, and mixed up to stereo if we can't */
- if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 1) {
- audio->config.out.mixdown = HB_AMIXDOWN_MONO;
- } else {
+ if ( STEREO_ONLY( audio ) )
+ {
+ if ( !CAN_MIXDOWN( audio ) )
+ {
+ // XXX we're hosed - we can't mix up & lame can't handle
+ // the input format. The user shouldn't be able to make
+ // this choice. It's too late to do anything about it now
+ // so complain in the log & let things abort in lame.
+ hb_log( "ERROR - can't use lame mp3 audio output with "
+ "mono audio stream %x - output will be messed up",
+ audio->id );
+ }
audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
}
- break;
-
- /* stereo input */
- case HB_INPUT_CH_LAYOUT_STEREO:
- /* if we've requested a mono mixdown, and it is supported, then do the mix */
- /* use stereo if not supported */
- if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) {
- audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
- /* otherwise, preserve stereo regardless of if we requested something higher */
- } else if (audio->config.out.mixdown > HB_AMIXDOWN_STEREO) {
- audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
+ else
+ {
+ // everything else passes through
+ audio->config.out.mixdown = HB_AMIXDOWN_MONO;
}
break;
- /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */
- /* the A52 flags don't allow for a way to distinguish between DPL1 and DPL2 on a DVD,
- so we always assume a DPL1 source for A52_DOLBY */
+ // dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input
+ // the A52 flags don't allow for a way to distinguish between DPL1 and
+ // DPL2 on a DVD so we always assume a DPL1 source for A52_DOLBY.
case HB_INPUT_CH_LAYOUT_DOLBY:
- /* if we've requested a mono mixdown, and it is supported, then do the mix */
- /* preserve dolby if not supported */
- if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
- /* otherwise, preserve dolby even if we requested something higher */
- /* a stereo mixdown will still be honoured here */
- } else if (audio->config.out.mixdown > HB_AMIXDOWN_DOLBY) {
+ if ( STEREO_ONLY( audio ) || !CAN_MIXDOWN( audio ) ||
+ audio->config.out.mixdown > HB_AMIXDOWN_DOLBY )
+ {
audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
}
break;
- /* 3F/2R input */
+ // 4 channel discrete
+ case HB_INPUT_CH_LAYOUT_2F2R:
+ case HB_INPUT_CH_LAYOUT_3F1R:
+ if ( CAN_MIXDOWN( audio ) )
+ {
+ if ( STEREO_ONLY( audio ) ||
+ audio->config.out.mixdown > HB_AMIXDOWN_DOLBY )
+ {
+ audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
+ }
+ }
+ else
+ {
+ // XXX we can't mixdown & don't have any way to specify
+ // 4 channel discrete output so we're hosed.
+ hb_log( "ERROR - can't handle 4 channel discrete audio stream "
+ "%x - output will be messed up", audio->id );
+ }
+ break;
+
+ // 5 or 6 channel discrete
case HB_INPUT_CH_LAYOUT_3F2R:
- /* if we've requested a mono mixdown, and it is supported, then do the mix */
- /* use dpl2 if not supported */
- if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
- } else {
- /* check if we have 3F2R input and also have an LFE - i.e. we have a 5.1 source) */
- if (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE) {
- /* we have a 5.1 source */
- /* if we requested 6ch, but our audio format doesn't support it, then mix to DPLII instead */
- if (audio->config.out.mixdown == HB_AMIXDOWN_6CH && audioCodecsSupport6Ch == 0) {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
+ if ( CAN_MIXDOWN( audio ) )
+ {
+ if ( STEREO_ONLY( audio ) )
+ {
+ if ( audio->config.out.mixdown < HB_AMIXDOWN_STEREO )
+ {
+ audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
}
- } else {
- /* we have a 5.0 source, so we can't do 6ch conversion
- default to DPL II instead */
- if (audio->config.out.mixdown > HB_AMIXDOWN_DOLBYPLII) {
+ else if ( audio->config.out.mixdown > HB_AMIXDOWN_DOLBYPLII )
+ {
audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
}
}
- }
- /* all other mixdowns will have been preserved here */
- break;
-
- /* 3F/1R input */
- case HB_INPUT_CH_LAYOUT_3F1R:
- /* if we've requested a mono mixdown, and it is supported, then do the mix */
- /* use dpl1 if not supported */
- if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
- } else {
- /* we have a 4.0 or 4.1 source, so we can't do DPLII or 6ch conversion
- default to DPL I instead */
- if (audio->config.out.mixdown > HB_AMIXDOWN_DOLBY) {
- audio->config.out.mixdown = HB_AMIXDOWN_DOLBY;
+ else if ( ! ( audio->config.in.channel_layout &
+ HB_INPUT_CH_LAYOUT_HAS_LFE ) )
+ {
+ // we don't do 5 channel discrete so mixdown to DPLII
+ audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII;
}
}
- /* all other mixdowns will have been preserved here */
- break;
-
- default:
- /* if we've requested a mono mixdown, and it is supported, then do the mix */
- if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 1) {
- audio->config.out.mixdown = HB_AMIXDOWN_MONO;
- /* mix everything else down to stereo */
- } else {
- audio->config.out.mixdown = HB_AMIXDOWN_STEREO;
+ else if ( ! ( audio->config.in.channel_layout &
+ HB_INPUT_CH_LAYOUT_HAS_LFE ) )
+ {
+ // XXX we can't mixdown & don't have any way to specify
+ // 5 channel discrete output so we're hosed.
+ hb_log( "ERROR - can't handle 5 channel discrete audio stream "
+ "%x - output will be messed up", audio->id );
}
-
+ else
+ {
+ // we can't mixdown so force 6 channel discrete
+ audio->config.out.mixdown = HB_AMIXDOWN_6CH;
+ }
+ break;
}
/* log the output mixdown */
@@ -557,79 +587,42 @@ static void do_job( hb_job_t * job, int cpu_count )
/*
* Audio Decoder Thread
*/
- switch( audio->config.in.codec )
+ if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL )
{
- case HB_ACODEC_AC3:
- w = getWork( WORK_DECA52 );
- break;
- case HB_ACODEC_DCA:
- w = getWork( WORK_DECDCA );
- break;
- case HB_ACODEC_MPGA:
- w = getWork( WORK_DECAVCODEC );
- break;
- case HB_ACODEC_LPCM:
- w = getWork( WORK_DECLPCM );
- break;
- default:
- /* Invalid input codec */
- hb_error("Invalid input codec: %d", audio->config.in.codec);
- *job->die = 1;
- goto cleanup;
+ hb_error("Invalid input codec: %d", audio->config.in.codec);
+ *job->die = 1;
+ goto cleanup;
}
- w->fifo_in = audio->priv.fifo_in;
- w->fifo_out = audio->priv.fifo_raw;
- w->config = &audio->priv.config;
- w->audio = audio;
+ w->fifo_in = audio->priv.fifo_in;
+ w->fifo_out = audio->priv.fifo_raw;
+ w->config = &audio->priv.config;
+ w->audio = audio;
+ w->codec_param = audio->config.in.codec_param;
- /* FIXME: This feels really hackish, anything better? */
- audio_w = calloc( sizeof( hb_work_object_t ), 1 );
- audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t ));
-
- hb_list_add( job->list_work, audio_w );
+ hb_list_add( job->list_work, w );
/*
* Audio Encoder Thread
*/
- switch( audio->config.out.codec )
- {
- case HB_ACODEC_FAAC:
- w = getWork( WORK_ENCFAAC );
- break;
- case HB_ACODEC_LAME:
- w = getWork( WORK_ENCLAME );
- break;
- case HB_ACODEC_VORBIS:
- w = getWork( WORK_ENCVORBIS );
- break;
- case HB_ACODEC_AC3:
- break;
- case HB_ACODEC_DCA: /* These are all invalid output codecs. */
- default:
- hb_error("Invalid audio codec: %#x", audio->config.out.codec);
- w = NULL;
- *job->die = 1;
- goto cleanup;
- }
-
if( audio->config.out.codec != HB_ACODEC_AC3 )
{
/*
* Add the encoder thread if not doing AC-3 pass through
*/
- w->fifo_in = audio->priv.fifo_sync;
- w->fifo_out = audio->priv.fifo_out;
- w->config = &audio->priv.config;
- w->audio = audio;
-
- /* FIXME: This feels really hackish, anything better? */
- audio_w = calloc( sizeof( hb_work_object_t ), 1 );
- audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t ));
+ if ( ( w = hb_codec_encoder( audio->config.out.codec ) ) == NULL )
+ {
+ hb_error("Invalid audio codec: %#x", audio->config.out.codec);
+ w = NULL;
+ *job->die = 1;
+ goto cleanup;
+ }
+ w->fifo_in = audio->priv.fifo_sync;
+ w->fifo_out = audio->priv.fifo_out;
+ w->config = &audio->priv.config;
+ w->audio = audio;
- hb_list_add( job->list_work, audio_w );
+ hb_list_add( job->list_work, w );
}
-
-
}
/* Init read & write threads */
@@ -671,6 +664,7 @@ static void do_job( hb_job_t * job, int cpu_count )
}
hb_list_rem( job->list_work, w );
w->close( w );
+ free( w );
job->done = 1;
cleanup:
@@ -678,23 +672,12 @@ cleanup:
while( ( w = hb_list_item( job->list_work, 0 ) ) )
{
hb_list_rem( job->list_work, w );
- if( w != NULL && w->thread != NULL )
+ if( w->thread != NULL )
{
hb_thread_close( &w->thread );
w->close( w );
}
-
- /* FIXME: This feels really hackish, anything better? */
- if ( w->id == WORK_DECA52 ||
- w->id == WORK_DECDCA ||
- w->id == WORK_DECLPCM ||
- w->id == WORK_ENCFAAC ||
- w->id == WORK_ENCLAME ||
- w->id == WORK_ENCVORBIS )
- {
- free( w );
- w = NULL;
- }
+ free( w );
}
hb_list_close( &job->list_work );
@@ -847,7 +830,7 @@ cleanup:
}
/**
- * Performs the work objects specific work function.
+ * Performs the work object's specific work function.
* Loops calling work function for associated work object. Sleeps when fifo is full.
* Monitors work done indicator.
* Exits loop when work indiactor is set.