OSDN Git Service

This patch adds mingw32 cross-compilation support to HandBrake trunk to
[handbrake-jp/handbrake-jp-git.git] / libhb / decavcodec.c
index 7f68100..8b09afc 100644 (file)
  */
 
 #include "hb.h"
-
-#include "libavcodec/avcodec.h"
-#include "libavformat/avformat.h"
-#include "libswscale/swscale.h"
+#include "hbffmpeg.h"
+#include "libavcodec/audioconvert.h"
 
 static int  decavcodecInit( hb_work_object_t *, hb_job_t * );
 static int  decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
@@ -195,7 +193,7 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job )
     pv->parser = av_parser_init( codec_id );
 
     pv->context = avcodec_alloc_context();
-    avcodec_open( pv->context, codec );
+    hb_avcodec_open( pv->context, codec );
 
     return 0;
 }
@@ -227,7 +225,7 @@ static void decavcodecClose( hb_work_object_t * w )
         }
         if ( pv->context && pv->context->codec )
         {
-            avcodec_close( pv->context );
+            hb_avcodec_close( pv->context );
         }
         if ( pv->list )
         {
@@ -269,6 +267,12 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
 
     *buf_out = NULL;
 
+    if ( in->start < -1 && pv->pts_next <= 0 )
+    {
+        // discard buffers that start before video time 0
+        return HB_WORK_OK;
+    }
+
     cur = ( in->start < 0 )? pv->pts_next : in->start;
 
     pos = 0;
@@ -356,10 +360,23 @@ static int decavcodecInfo( hb_work_object_t *w, hb_work_info_t *info )
     return 0;
 }
 
+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,
+};
+
 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;
+    int ret = 0;
 
     memset( info, 0, sizeof(*info) );
 
@@ -372,18 +389,52 @@ static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
     // 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 )
+    if ( codec )
     {
-        static char codec_name[64];
+        // there's no ffmpeg codec for this audio type - give up
+        return -1;
+    }
 
-        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;
+    static char codec_name[64];
+    info->name =  strncpy( codec_name, codec->name, sizeof(codec_name)-1 );
+
+    AVCodecParserContext *parser = av_parser_init( codec->id );
+    AVCodecContext *context = avcodec_alloc_context();
+    hb_avcodec_open( context, codec );
+#if defined( SYS_CYGWIN )
+    uint8_t *buffer = memalign(16, AVCODEC_MAX_AUDIO_FRAME_SIZE);
+#else
+    uint8_t *buffer = malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE );
+#endif
+    int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
+    unsigned char *pbuffer;
+    int pos = 0, pbuffer_size;
+
+    while ( pos < buf->size )
+    {
+        int len = av_parser_parse( parser, context, &pbuffer, &pbuffer_size,
+                                   buf->data + pos, buf->size - pos,
+                                   buf->start, buf->start );
+        pos += len;
+        if ( pbuffer_size > 0 )
+        {
+            len = avcodec_decode_audio2( context, (int16_t*)buffer, &out_size,
+                                         pbuffer, pbuffer_size );
+            if ( len > 0 && context->sample_rate > 0 )
+            {
+                info->bitrate = context->bit_rate;
+                info->rate = context->sample_rate;
+                info->rate_base = 1;
+                info->channel_layout = chan2layout[context->channels & 7];
+                ret = 1;
+                break;
+            }
+        }
     }
-    return -1;
+    free( buffer );
+    av_parser_close( parser );
+    hb_avcodec_close( context );
+    return ret;
 }
 
 /* -------------------------------------------------------------
@@ -497,13 +548,22 @@ static void flushDelayQueue( hb_work_private_t *pv )
 
 static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size )
 {
-    int got_picture;
+    int got_picture, oldlevel = 0;
     AVFrame frame;
 
+    if ( global_verbosity_level <= 1 )
+    {
+        oldlevel = av_log_get_level();
+        av_log_set_level( AV_LOG_QUIET );
+    }
     if ( avcodec_decode_video( pv->context, &frame, &got_picture, data, size ) < 0 )
     {
         ++pv->decode_errors;     
     }
+    if ( global_verbosity_level <= 1 )
+    {
+        av_log_set_level( oldlevel );
+    }
     if( got_picture )
     {
         // ffmpeg makes it hard to attach a pts to a frame. if the MPEG ES
@@ -796,9 +856,9 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
         // There's a mis-feature in ffmpeg that causes the context to be 
         // incorrectly initialized the 1st time avcodec_open is called.
         // If you close it and open a 2nd time, it finishes the job.
-        avcodec_open( pv->context, codec );
-        avcodec_close( pv->context );
-        avcodec_open( pv->context, codec );
+        hb_avcodec_open( pv->context, codec );
+        hb_avcodec_close( pv->context );
+        hb_avcodec_open( pv->context, codec );
     }
 
     if( in->start >= 0 )
@@ -835,6 +895,12 @@ static int decavcodecvInfo( hb_work_object_t *w, hb_work_info_t *info )
         info->rate = 27000000;
         info->rate_base = (int64_t)context->time_base.num * 27000000LL /
                           context->time_base.den;
+        if ( context->ticks_per_frame > 1 )
+        {
+            // for ffmpeg 0.5 & later, the H.264 & MPEG-2 time base is
+            // field rate rather than frame rate so convert back to frames.
+            info->rate_base *= context->ticks_per_frame;
+        }
         
         /* Sometimes there's no pixel aspect set in the source. In that case,
            assume a 1:1 PAR. Otherwise, preserve the source PAR.             */
@@ -900,7 +966,7 @@ static void init_ffmpeg_context( hb_work_object_t *w )
     if ( ! pv->context->codec )
     {
         AVCodec *codec = avcodec_find_decoder( pv->context->codec_id );
-        avcodec_open( pv->context, codec );
+        hb_avcodec_open( pv->context, codec );
     }
     // set up our best guess at the frame duration.
     // the frame rate in the codec is usually bogus but it's sometimes
@@ -1066,7 +1132,7 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
             // complicated, machine dependent alignment here we use the
             // fact that malloc returns an aligned pointer on most architectures.
 
-            #ifdef SYS_CYGWIN
+            #if defined( SYS_CYGWIN )
                 // Cygwin's malloc doesn't appear to return 16-byte aligned memory so use memalign instead.
                pv->buffer = memalign(16, AVCODEC_MAX_AUDIO_FRAME_SIZE);
             #else
@@ -1085,6 +1151,35 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
         pos += len;
         if( out_size > 0 )
         {
+            // We require signed 16-bit ints for the output format. If
+            // we got something different convert it.
+            if ( context->sample_fmt != SAMPLE_FMT_S16 )
+            {
+                // Note: av_audio_convert seems to be a work-in-progress but
+                //       looks like it will eventually handle general audio
+                //       mixdowns which would allow us much more flexibility
+                //       in handling multichannel audio in HB. If we were doing
+                //       anything more complicated than a one-for-one format
+                //       conversion we'd probably want to cache the converter
+                //       context in the pv.
+                int isamp = av_get_bits_per_sample_format( context->sample_fmt ) / 8;
+                AVAudioConvert *ctx = av_audio_convert_alloc( SAMPLE_FMT_S16, 1,
+                                                              context->sample_fmt, 1,
+                                                              NULL, 0 );
+                // get output buffer size (in 2-byte samples) then malloc a buffer
+                out_size = ( out_size * 2 ) / isamp;
+                buffer = malloc( out_size );
+
+                // we're doing straight sample format conversion which behaves as if
+                // there were only one channel.
+                const void * const ibuf[6] = { pv->buffer };
+                void * const obuf[6] = { buffer };
+                const int istride[6] = { isamp };
+                const int ostride[6] = { 2 };
+
+                av_audio_convert( ctx, obuf, ostride, ibuf, istride, out_size >> 1 );
+                av_audio_convert_free( ctx );
+            }
             hb_buffer_t *buf = hb_buffer_init( 2 * out_size );
 
             // convert from bytes to total samples
@@ -1103,6 +1198,12 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size )
                 fl32[i] = buffer[i];
             }
             hb_list_add( pv->list, buf );
+
+            // if we allocated a buffer for sample format conversion, free it
+            if ( buffer != pv->buffer )
+            {
+                free( buffer );
+            }
         }
     }
 }
@@ -1119,6 +1220,14 @@ static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in,
     }
 
     hb_work_private_t *pv = w->private_data;
+
+    if ( (*buf_in)->start < -1 && pv->pts_next <= 0 )
+    {
+        // discard buffers that start before video time 0
+        *buf_out = NULL;
+        return HB_WORK_OK;
+    }
+
     if ( ! pv->context )
     {
         init_ffmpeg_context( w );