Merge master.
[dcpomatic.git] / src / lib / ffmpeg.cc
index bbf853af1588aaad35a98618150a7594007152b4..5fc33348923c85d584af16fb980c6f5f50ad2dd1 100644 (file)
@@ -26,6 +26,7 @@ extern "C" {
 #include "ffmpeg.h"
 #include "ffmpeg_content.h"
 #include "exceptions.h"
+#include "util.h"
 
 #include "i18n.h"
 
@@ -37,8 +38,7 @@ using boost::lexical_cast;
 
 boost::mutex FFmpeg::_mutex;
 
-/** @param long_probe true to do a long probe of the file looking for streams */
-FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c, bool long_probe)
+FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
        : _ffmpeg_content (c)
        , _avio_buffer (0)
        , _avio_buffer_size (4096)
@@ -47,7 +47,7 @@ FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c, bool long_probe)
        , _frame (0)
        , _video_stream (-1)
 {
-       setup_general (long_probe);
+       setup_general ();
        setup_video ();
        setup_audio ();
 }
@@ -63,7 +63,7 @@ FFmpeg::~FFmpeg ()
                }
        }
 
-       avcodec_free_frame (&_frame);
+       av_frame_free (&_frame);
        
        avformat_close_input (&_format_context);
 }
@@ -77,34 +77,28 @@ avio_read_wrapper (void* data, uint8_t* buffer, int amount)
 static int64_t
 avio_seek_wrapper (void* data, int64_t offset, int whence)
 {
-       if (whence == AVSEEK_SIZE) {
-               return reinterpret_cast<FFmpeg*>(data)->avio_length ();
-       }
-                       
        return reinterpret_cast<FFmpeg*>(data)->avio_seek (offset, whence);
 }
 
 void
-FFmpeg::setup_general (bool long_probe)
+FFmpeg::setup_general ()
 {
        av_register_all ();
 
        _file_group.set_paths (_ffmpeg_content->paths ());
-       _avio_buffer = static_cast<uint8_t*> (av_malloc (_avio_buffer_size));
+       _avio_buffer = static_cast<uint8_t*> (wrapped_av_malloc (_avio_buffer_size));
        _avio_context = avio_alloc_context (_avio_buffer, _avio_buffer_size, 0, this, avio_read_wrapper, 0, avio_seek_wrapper);
        _format_context = avformat_alloc_context ();
        _format_context->pb = _avio_context;
        
        AVDictionary* options = 0;
-       if (long_probe) {
-               /* These durations are in microseconds, and represent how far into the content file
-                  we will look for streams.
-               */
-               av_dict_set (&options, "analyzeduration", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
-               av_dict_set (&options, "probesize", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
-       }
+       /* These durations are in microseconds, and represent how far into the content file
+          we will look for streams.
+       */
+       av_dict_set (&options, "analyzeduration", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
+       av_dict_set (&options, "probesize", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
        
-       if (avformat_open_input (&_format_context, _ffmpeg_content->path(0).string().c_str(), 0, &options) < 0) {
+       if (avformat_open_input (&_format_context, 0, 0, &options) < 0) {
                throw OpenFileError (_ffmpeg_content->path(0).string ());
        }
 
@@ -125,7 +119,25 @@ FFmpeg::setup_general (bool long_probe)
                throw DecodeError (N_("could not find video stream"));
        }
 
-       _frame = avcodec_alloc_frame ();
+       /* Hack: if the AVStreams have zero IDs, put some in.  We
+          use the IDs so that we can cope with VOBs, in which streams
+          move about in index but remain with the same ID in different
+          VOBs.  However, some files have all-zero IDs, hence this hack.
+       */
+          
+       uint32_t i = 0;
+       while (i < _format_context->nb_streams && _format_context->streams[i]->id == 0) {
+               ++i;
+       }
+
+       if (i == _format_context->nb_streams) {
+               /* Put in our own IDs */
+               for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
+                       _format_context->streams[i]->id = i;
+               }
+       }
+
+       _frame = av_frame_alloc ();
        if (_frame == 0) {
                throw DecodeError (N_("could not allocate frame"));
        }
@@ -135,7 +147,8 @@ void
 FFmpeg::setup_video ()
 {
        boost::mutex::scoped_lock lm (_mutex);
-       
+
+       assert (_video_stream >= 0);
        AVCodecContext* context = _format_context->streams[_video_stream]->codec;
        AVCodec* codec = avcodec_find_decoder (context->codec_id);
 
@@ -180,7 +193,11 @@ FFmpeg::video_codec_context () const
 AVCodecContext *
 FFmpeg::audio_codec_context () const
 {
-       return _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec;
+       if (!_ffmpeg_content->audio_stream ()) {
+               return 0;
+       }
+       
+       return _ffmpeg_content->audio_stream()->stream(_format_context)->codec;
 }
 
 int
@@ -192,11 +209,9 @@ FFmpeg::avio_read (uint8_t* buffer, int const amount)
 int64_t
 FFmpeg::avio_seek (int64_t const pos, int whence)
 {
+       if (whence == AVSEEK_SIZE) {
+               return _file_group.length ();
+       }
+       
        return _file_group.seek (pos, whence);
 }
-
-int64_t
-FFmpeg::avio_size ()
-{
-       return _file_group.length ();
-}