diff options
author | van <[email protected]> | 2008-05-31 17:00:42 +0000 |
---|---|---|
committer | van <[email protected]> | 2008-05-31 17:00:42 +0000 |
commit | afbd06d8d6151b69bbb18adf83cfab8d680c3946 (patch) | |
tree | 4e7be731854c8b8db8f87934c301c60a9155387c | |
parent | d0550da569b56a71857c34463c5b030172d47291 (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
-rw-r--r-- | Jamfile | 2 | ||||
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | contrib/Jamfile | 28 | ||||
-rw-r--r-- | contrib/patch-ffmpeg.patch | 255 | ||||
-rw-r--r-- | contrib/version_faad2.txt | 1 | ||||
-rw-r--r-- | contrib/version_ffmpeg.txt | 2 | ||||
-rw-r--r-- | libhb/Makefile | 2 | ||||
-rw-r--r-- | libhb/common.c | 3 | ||||
-rw-r--r-- | libhb/common.h | 48 | ||||
-rw-r--r-- | libhb/deblock.c | 2 | ||||
-rw-r--r-- | libhb/deca52.c | 97 | ||||
-rw-r--r-- | libhb/decavcodec.c | 684 | ||||
-rw-r--r-- | libhb/decdca.c | 100 | ||||
-rw-r--r-- | libhb/declpcm.c | 45 | ||||
-rw-r--r-- | libhb/decmpeg2.c | 65 | ||||
-rw-r--r-- | libhb/decomb.c | 18 | ||||
-rw-r--r-- | libhb/deinterlace.c | 2 | ||||
-rw-r--r-- | libhb/demuxmpeg.c | 41 | ||||
-rw-r--r-- | libhb/denoise.c | 2 | ||||
-rw-r--r-- | libhb/detelecine.c | 2 | ||||
-rw-r--r-- | libhb/encavcodec.c | 2 | ||||
-rw-r--r-- | libhb/fifo.c | 285 | ||||
-rw-r--r-- | libhb/hb.c | 12 | ||||
-rw-r--r-- | libhb/hb.h | 7 | ||||
-rw-r--r-- | libhb/internal.h | 28 | ||||
-rw-r--r-- | libhb/muxavi.c | 2 | ||||
-rw-r--r-- | libhb/reader.c | 66 | ||||
-rw-r--r-- | libhb/render.c | 4 | ||||
-rw-r--r-- | libhb/scan.c | 454 | ||||
-rwxr-xr-x | libhb/stream.c | 721 | ||||
-rw-r--r-- | libhb/sync.c | 30 | ||||
-rw-r--r-- | libhb/work.c | 351 | ||||
-rw-r--r-- | macosx/Controller.mm | 11 |
33 files changed, 2099 insertions, 1275 deletions
@@ -17,7 +17,7 @@ HANDBRAKE_LIBS = libhb.a contrib/lib/libogg.a contrib/lib/libsamplerate.a contrib/lib/libx264.a contrib/lib/libxvidcore.a contrib/lib/libmkv.a contrib/lib/libswscale.a - contrib/lib/libtheora.a ; + contrib/lib/libtheora.a contrib/lib/libfaad.a ; if $(OS) = UNKNOWN { @@ -4,7 +4,7 @@ CC="gcc" CXX="g++" CCFLAGS="$CCFLAGS -Wall -g" OPTIM="$OPTIM -O3 -funroll-loops" -LINKLIBS="-lz" +LINKLIBS="-lz -lbz2" MAKE=make # System-specific flags diff --git a/contrib/Jamfile b/contrib/Jamfile index 202df5c05..972c32817 100644 --- a/contrib/Jamfile +++ b/contrib/Jamfile @@ -57,6 +57,23 @@ actions LibA52 Wget $(SUBDIR)/a52dec.tar.gz : $(SUBDIR)/version_a52dec.txt ; LibA52 $(SUBDIR)/lib/liba52.a : $(SUBDIR)/a52dec.tar.gz ; +# FAAD2 +rule LibFaad2 +{ + Depends $(<) : $(>) ; + Depends lib : $(<) ; +} + +actions LibFaad2 +{ + cd `dirname $(>)` && CONTRIB=`pwd` && + rm -rf faad2 && (gzip -dc faad2.tar.gz | tar xf -) && + cd faad2 && + ./bootstrap && ./configure --prefix=$CONTRIB --cache-file=$CONTRIB/config.cache --disable-shared && $(MAKE) && $(MAKE) install +} +Wget $(SUBDIR)/faad2.tar.gz : $(SUBDIR)/version_faad2.txt ; +LibFaad2 $(SUBDIR)/lib/libfaad.a : $(SUBDIR)/faad2.tar.gz ; + # libavcodec rule LibAvCodec { @@ -78,10 +95,13 @@ actions LibAvCodec cd `dirname $(>)` && CONTRIB=`pwd` && rm -rf ffmpeg && (gzip -dc ffmpeg.tar.gz | tar xf -) && cd ffmpeg && $(FFMPEG_PATCH) && - ./configure --prefix=$CONTRIB --enable-gpl --enable-pthreads --enable-swscaler --disable-audio-beos --disable-shared --enable-static \ - --disable-decoders --enable-decoder=mp2 --disable-parsers --enable-parser=mpegaudio \ - --disable-encoders --enable-encoder=mpeg4 --enable-encoder=ac3 --enable-encoder=snow \ - --disable-muxers --enable-muxer=ipod --disable-demuxers --disable-protocols --disable-bsfs && + ./configure --prefix=$CONTRIB --enable-gpl --enable-pthreads --enable-swscale \ + --disable-shared --enable-static --disable-encoders \ + --enable-encoder=mpeg4 --enable-encoder=ac3 --enable-encoder=snow \ + --enable-libfaad --disable-ffmpeg --disable-ffserver \ + --disable-muxers --enable-muxer=ipod --disable-bsfs \ + --extra-cflags="-I$CONTRIB/include" \ + --extra-ldflags="-L$CONTRIB/lib" && $(MAKE) && $(MAKE) install && $(STRIP) $CONTRIB/lib/libavcodec.a } diff --git a/contrib/patch-ffmpeg.patch b/contrib/patch-ffmpeg.patch index c8b9f5288..e52731f1d 100644 --- a/contrib/patch-ffmpeg.patch +++ b/contrib/patch-ffmpeg.patch @@ -1,28 +1,3 @@ -Index: configure -=================================================================== ---- configure (revision 9814) -+++ configure (working copy) -@@ -1095,7 +1095,7 @@ - 2.9-beos-991026*|2.9-beos-000224*) echo "R5/GG gcc" - mmx="no" - ;; -- *20010315*) echo "BeBits gcc" -+ *20010315*|2.95.3*) echo "BeBits gcc" - add_cflags "-fno-expensive-optimizations" - ;; - esac -Index: libavformat/Makefile -=================================================================== ---- libavformat/Makefile (revision 9814) -+++ libavformat/Makefile (working copy) -@@ -69,6 +69,7 @@ - OBJS-$(CONFIG_IMAGE2PIPE_MUXER) += img2.o - OBJS-$(CONFIG_INGENIENT_DEMUXER) += raw.o - OBJS-$(CONFIG_IPMOVIE_DEMUXER) += ipmovie.o -+OBJS-$(CONFIG_IPOD_MUXER) += movenc.o riff.o isom.o - OBJS-$(CONFIG_M4V_DEMUXER) += raw.o - OBJS-$(CONFIG_M4V_MUXER) += raw.o - OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o riff.o Index: libavformat/tcp.c =================================================================== --- libavformat/tcp.c (revision 9814) @@ -41,233 +16,3 @@ Index: libavformat/tcp.c } s->fd = fd; return 0; -Index: libavformat/movenc.c -=================================================================== ---- libavformat/movenc.c (revision 9814) -+++ libavformat/movenc.c (working copy) -@@ -36,6 +36,7 @@ - #define MODE_PSP 3 // example working PSP command line: - // ffmpeg -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4 - #define MODE_3G2 4 -+#define MODE_IPOD 5 - - typedef struct MOVIentry { - unsigned int flags, size; -@@ -54,6 +55,7 @@ - long time; - int64_t trackDuration; - long sampleCount; -+ long sampleDuration; - long sampleSize; - int hasKeyframes; - int hasBframes; -@@ -572,6 +574,18 @@ - return tag; - } - -+static int mov_write_colr_tag(ByteIOContext *pb) -+{ -+ put_be32( pb, 0x12 ); -+ put_tag( pb, "colr" ); -+ put_tag( pb, "nclc" ); -+ put_be16( pb, 6 ); -+ put_be16( pb, 1 ); -+ put_be16( pb, 6 ); -+ put_be32( pb, 0 ); -+ return 0x12; -+} -+ - static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track) - { - offset_t pos = url_ftell(pb); -@@ -621,9 +635,22 @@ - mov_write_d263_tag(pb); - else if(track->enc->codec_id == CODEC_ID_SVQ3) - mov_write_svq3_tag(pb); -- else if(track->enc->codec_id == CODEC_ID_H264) -- mov_write_avcc_tag(pb, track); -+ else if(track->enc->codec_id == CODEC_ID_H264) { -+ mov_write_avcc_tag(pb, track); -+ if (track->mode == MODE_IPOD) { -+ put_be32(pb, 0x1C); /* size ... reports as 28 in mp4box! */ -+ put_tag(pb, "uuid"); -+ put_be32(pb, 0x6B6840F2); -+ put_be32(pb, 0x5F244FC5); -+ put_be32(pb, 0xBA39A51B); -+ put_be32(pb, 0xCF0323F3); -+ put_be32(pb, 0x00000001); -+ put_be32(pb, 0x0000039C); -+ } -+ } - -+ mov_write_colr_tag(pb); -+ - return updateSize (pb, pos); - } - -@@ -674,46 +701,18 @@ - return atom_size; - } - -+/* TODO: */ - /* Time to sample atom */ - static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track) - { -- MOV_stts_t *stts_entries; -- uint32_t entries = -1; -- uint32_t atom_size; -- int i; -- -- if (track->enc->codec_type == CODEC_TYPE_AUDIO && !track->audio_vbr) { -- stts_entries = av_malloc(sizeof(*stts_entries)); /* one entry */ -- stts_entries[0].count = track->sampleCount; -- stts_entries[0].duration = 1; -- entries = 1; -- } else { -- stts_entries = av_malloc(track->entry * sizeof(*stts_entries)); /* worst case */ -- for (i=0; i<track->entry; i++) { -- int64_t duration = i + 1 == track->entry ? -- track->trackDuration - track->cluster[i].dts + track->cluster[0].dts : /* readjusting */ -- track->cluster[i+1].dts - track->cluster[i].dts; -- if (i && duration == stts_entries[entries].duration) { -- stts_entries[entries].count++; /* compress */ -- } else { -- entries++; -- stts_entries[entries].duration = duration; -- stts_entries[entries].count = 1; -- } -- } -- entries++; /* last one */ -- } -- atom_size = 16 + (entries * 8); -- put_be32(pb, atom_size); /* size */ -+ put_be32(pb, 0x18); /* size */ - put_tag(pb, "stts"); - put_be32(pb, 0); /* version & flags */ -- put_be32(pb, entries); /* entry count */ -- for (i=0; i<entries; i++) { -- put_be32(pb, stts_entries[i].count); -- put_be32(pb, stts_entries[i].duration); -- } -- av_free(stts_entries); -- return atom_size; -+ put_be32(pb, 1); /* entry count */ -+ -+ put_be32(pb, track->sampleCount); /* sample count */ -+ put_be32(pb, track->sampleDuration); /* sample duration */ -+ return 0x18; - } - - static int mov_write_dref_tag(ByteIOContext *pb) -@@ -911,6 +910,10 @@ - /* Track width and height, for visual only */ - if(track->enc->codec_type == CODEC_TYPE_VIDEO) { - double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio); -+ if (track->mode == MODE_IPOD) { -+ /* FIXME , I do not believe this is needed, bad assumption */ -+ sample_aspect_ratio = 1; -+ } - if( !sample_aspect_ratio ) sample_aspect_ratio = 1; - put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000); - put_be32(pb, track->enc->height*0x10000); -@@ -1322,6 +1325,8 @@ - for (i=0; i<mov->nb_streams; i++) { - if(mov->tracks[i].entry <= 0) continue; - -+ mov->tracks[i].trackDuration = -+ (int64_t)mov->tracks[i].sampleCount * mov->tracks[i].sampleDuration; - mov->tracks[i].time = mov->time; - mov->tracks[i].trackID = i+1; - } -@@ -1369,6 +1374,8 @@ - put_tag(pb, "MSNV"); - else if ( mov->mode == MODE_MP4 ) - put_tag(pb, "isom"); -+ else if ( mov->mode == MODE_IPOD ) -+ put_tag(pb, "isom"); - else - put_tag(pb, "qt "); - -@@ -1380,6 +1387,8 @@ - put_tag(pb, "3g2a"); - else if ( mov->mode == MODE_PSP ) - put_tag(pb, "MSNV"); -+ else if ( mov->mode == MODE_IPOD ) -+ put_tag(pb, "mp41"); - else if ( mov->mode == MODE_MP4 ) - put_tag(pb, "mp41"); - else -@@ -1466,7 +1475,8 @@ - else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3G2; - else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV; - else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP; -- -+ else if (!strcmp("ipod", s->oformat->name)) mov->mode = MODE_IPOD; -+ - mov_write_ftyp_tag(pb,s); - if ( mov->mode == MODE_PSP ) { - if ( s->nb_streams != 2 ) { -@@ -1487,6 +1497,7 @@ - if(st->codec->codec_type == CODEC_TYPE_VIDEO){ - track->tag = mov_find_video_codec_tag(s, track); - track->timescale = st->codec->time_base.den; -+ track->sampleDuration = st->codec->time_base.num; - av_set_pts_info(st, 64, 1, st->codec->time_base.den); - if (track->timescale > 100000) - av_log(NULL, AV_LOG_WARNING, -@@ -1496,6 +1507,7 @@ - }else if(st->codec->codec_type == CODEC_TYPE_AUDIO){ - track->tag = mov_find_audio_codec_tag(s, track); - track->timescale = st->codec->sample_rate; -+ track->sampleDuration = st->codec->frame_size; - av_set_pts_info(st, 64, 1, st->codec->sample_rate); - if(!st->codec->frame_size){ - av_log(s, AV_LOG_ERROR, "track %d: codec frame size is not set\n", i); -@@ -1675,6 +1687,21 @@ - .flags = AVFMT_GLOBALHEADER, - }; - #endif -+#ifdef CONFIG_IPOD_MUXER -+AVOutputFormat ipod_muxer = { -+ "ipod", -+ "ipod mp4 format", -+ "application/mp4", -+ "mp4,m4v,ipod", -+ sizeof(MOVContext), -+ CODEC_ID_AAC, -+ CODEC_ID_MPEG4, -+ mov_write_header, -+ mov_write_packet, -+ mov_write_trailer, -+ .flags = AVFMT_GLOBALHEADER, -+}; -+#endif - #ifdef CONFIG_PSP_MUXER - AVOutputFormat psp_muxer = { - "psp", -Index: libavformat/allformats.c -=================================================================== ---- libavformat/allformats.c (revision 9814) -+++ libavformat/allformats.c (working copy) -@@ -89,6 +89,9 @@ - REGISTER_MUXDEMUX(IMAGE2PIPE, image2pipe); - REGISTER_DEMUXER (INGENIENT, ingenient); - REGISTER_DEMUXER (IPMOVIE, ipmovie); -+#ifdef CONFIG_IPOD_MUXER -+ REGISTER_MUXER (IPOD, ipod); -+#endif - if (!ENABLE_NUT_DEMUXER) REGISTER_DEMUXER (LIBNUT, libnut); - REGISTER_MUXER (LIBNUT, libnut); - REGISTER_MUXDEMUX(M4V, m4v); -Index: libavformat/allformats.h -=================================================================== ---- libavformat/allformats.h (revision 9814) -+++ libavformat/allformats.h (working copy) -@@ -142,6 +142,7 @@ - extern AVOutputFormat image2pipe_muxer; - extern AVOutputFormat image_muxer; - extern AVOutputFormat imagepipe_muxer; -+extern AVOutputFormat ipod_muxer; - extern AVOutputFormat libnut_muxer; - extern AVOutputFormat m4v_muxer; - extern AVOutputFormat mjpeg_muxer; diff --git a/contrib/version_faad2.txt b/contrib/version_faad2.txt new file mode 100644 index 000000000..ad74c36cd --- /dev/null +++ b/contrib/version_faad2.txt @@ -0,0 +1 @@ +http://download.m0k.org/handbrake/contrib/faad2-2.6.1.tar.gz diff --git a/contrib/version_ffmpeg.txt b/contrib/version_ffmpeg.txt index 15e9b5e0d..e216b4267 100644 --- a/contrib/version_ffmpeg.txt +++ b/contrib/version_ffmpeg.txt @@ -1 +1 @@ -http://download.m0k.org/handbrake/contrib/ffmpeg-9816.tar.gz +http://download.m0k.org/handbrake/contrib/ffmpeg-r13110.tar.gz 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. diff --git a/macosx/Controller.mm b/macosx/Controller.mm index 5ee6bc1fb..b2178587a 100644 --- a/macosx/Controller.mm +++ b/macosx/Controller.mm @@ -3237,11 +3237,12 @@ the user is using "Custom" settings by determining the sender*/ as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */ /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now, but this may change in the future, so they are separated for flexibility */ - int audioCodecsSupportMono = ((audio->in.codec == HB_ACODEC_AC3 || - audio->in.codec == HB_ACODEC_DCA) && acodec == HB_ACODEC_FAAC); - int audioCodecsSupport6Ch = ((audio->in.codec == HB_ACODEC_AC3 || - audio->in.codec == HB_ACODEC_DCA) && (acodec == HB_ACODEC_FAAC || - acodec == HB_ACODEC_VORBIS)); + int audioCodecsSupportMono = + (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && + (acodec != HB_ACODEC_LAME); + int audioCodecsSupport6Ch = + (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && + (acodec != HB_ACODEC_LAME); /* check for AC-3 passthru */ if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) |