OSDN Git Service

- support blu-ray, avchd & dvb x264
authorvan <van@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 31 May 2008 17:00:42 +0000 (17:00 +0000)
committervan <van@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 31 May 2008 17:00:42 +0000 (17:00 +0000)
- support video files handled by ffmpeg (avi, mkv, mp4, etc.)

git-svn-id: svn://localhost/HandBrake/trunk@1480 b64f7644-9d1e-0410-96f1-a4d463321fa5

33 files changed:
Jamfile
configure
contrib/Jamfile
contrib/patch-ffmpeg.patch
contrib/version_faad2.txt [new file with mode: 0644]
contrib/version_ffmpeg.txt
libhb/Makefile
libhb/common.c
libhb/common.h
libhb/deblock.c
libhb/deca52.c
libhb/decavcodec.c
libhb/decdca.c
libhb/declpcm.c
libhb/decmpeg2.c
libhb/decomb.c
libhb/deinterlace.c
libhb/demuxmpeg.c
libhb/denoise.c
libhb/detelecine.c
libhb/encavcodec.c
libhb/fifo.c
libhb/hb.c
libhb/hb.h
libhb/internal.h
libhb/muxavi.c
libhb/reader.c
libhb/render.c
libhb/scan.c
libhb/stream.c
libhb/sync.c
libhb/work.c
macosx/Controller.mm

diff --git a/Jamfile b/Jamfile
index 39f3afb..1abb22f 100644 (file)
--- a/Jamfile
+++ b/Jamfile
@@ -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
 {
index 2018e79..f1f6189 100755 (executable)
--- a/configure
+++ b/configure
@@ -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
index 202df5c..972c328 100644 (file)
@@ -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
 }
index c8b9f52..e52731f 100644 (file)
@@ -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 (file)
index 0000000..ad74c36
--- /dev/null
@@ -0,0 +1 @@
+http://download.m0k.org/handbrake/contrib/faad2-2.6.1.tar.gz
index 15e9b5e..e216b42 100644 (file)
@@ -1 +1 @@
-http://download.m0k.org/handbrake/contrib/ffmpeg-9816.tar.gz
+http://download.m0k.org/handbrake/contrib/ffmpeg-r13110.tar.gz
index f0d4aeb..72f4a29 100644 (file)
@@ -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)
 
index 00810dd..bd183e9 100644 (file)
@@ -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;
 }
index fc0001f..d772877 100644 (file)
@@ -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;
index dbf39c6..42b7d8c 100644 (file)
@@ -17,7 +17,7 @@
 */
 
 #include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
 #include "mpeg2dec/mpeg2.h"
 
 #define PP7_QP_DEFAULT    0
index 6695d36..1495611 100644 (file)
@@ -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;
+}
index 5ca114e..04b12f6 100644 (file)
@@ -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
+};
index 2808190..a326031 100644 (file)
@@ -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;
+}
index 49b15bb..3220d3d 100644 (file)
@@ -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;
+}
index 3bdceed..e5fcdc5 100644 (file)
 #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
 };
 
index e5779c8..e29f253 100644 (file)
@@ -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] ||
index 44593f4..4cf3a2f 100644 (file)
@@ -17,7 +17,7 @@
 */
 
 #include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
 #include "mpeg2dec/mpeg2.h"
 
 #define SUPPRESS_AV_LOG
index 35d941a..0538679 100644 (file)
@@ -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;
+}
index 5cf88ea..6d335fc 100644 (file)
@@ -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
index ada5e79..768cc2c 100644 (file)
@@ -1,5 +1,5 @@
 #include "hb.h"
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
 #include "mpeg2dec/mpeg2.h"
 
 /*
index dca246d..5e55b00 100644 (file)
@@ -6,7 +6,7 @@
 
 #include "hb.h"
 
-#include "ffmpeg/avcodec.h"
+#include "libavcodec/avcodec.h"
 
 struct hb_work_private_s
 {
index 69b8b90..1bf2a9d 100644 (file)
 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 )
index 825b2de..bcd77e7 100644 (file)
@@ -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 );
index 9ca3306..a47e4e2 100644 (file)
@@ -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 ); \
 
index 39364fc..95545ac 100644 (file)
@@ -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,
index 83d3d05..9960433 100644 (file)
@@ -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
index 2333198..9ad2867 100644 (file)
@@ -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 )
         {
index eead0a7..0fc5816 100644 (file)
@@ -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
 {
index 674a1e3..6834d46 100644 (file)
@@ -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 );
 }
 
 /*
index 065a051..c6dce85 100755 (executable)
@@ -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;
+}
index 34b426f..f9c738b 100644 (file)
@@ -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) )
         {
index 9c2ecbf..cf801e4 100644 (file)
@@ -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.
index 5ee6bc1..b217858 100644 (file)
@@ -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)