X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fffmpeg.cc;h=b7ae04b069017d8404325939fdbbad8ba4108f26;hb=08b57ee2ed41b9de995080ff7b4fd84d8cec4002;hp=0d897abfaf27a8e361e22c8ebe1da7ce05a7add5;hpb=f1bf21a9c2581591ab80bfc997a22b93046f8c56;p=dcpomatic.git diff --git a/src/lib/ffmpeg.cc b/src/lib/ffmpeg.cc index 0d897abfa..b7ae04b06 100644 --- a/src/lib/ffmpeg.cc +++ b/src/lib/ffmpeg.cc @@ -30,20 +30,21 @@ extern "C" { #include "i18n.h" using std::string; +using std::cout; using std::stringstream; using boost::shared_ptr; +using boost::lexical_cast; boost::mutex FFmpeg::_mutex; FFmpeg::FFmpeg (boost::shared_ptr c) : _ffmpeg_content (c) + , _avio_buffer (0) + , _avio_buffer_size (4096) + , _avio_context (0) , _format_context (0) , _frame (0) , _video_stream (-1) - , _video_codec_context (0) - , _video_codec (0) - , _audio_codec_context (0) - , _audio_codec (0) { setup_general (); setup_video (); @@ -53,27 +54,51 @@ FFmpeg::FFmpeg (boost::shared_ptr c) FFmpeg::~FFmpeg () { boost::mutex::scoped_lock lm (_mutex); - - if (_audio_codec_context) { - avcodec_close (_audio_codec_context); - } - if (_video_codec_context) { - avcodec_close (_video_codec_context); + for (uint32_t i = 0; i < _format_context->nb_streams; ++i) { + AVCodecContext* context = _format_context->streams[i]->codec; + if (context->codec_type == AVMEDIA_TYPE_VIDEO || context->codec_type == AVMEDIA_TYPE_AUDIO) { + avcodec_close (context); + } } - av_free (_frame); + av_frame_free (&_frame); avformat_close_input (&_format_context); } +static int +avio_read_wrapper (void* data, uint8_t* buffer, int amount) +{ + return reinterpret_cast(data)->avio_read (buffer, amount); +} + +static int64_t +avio_seek_wrapper (void* data, int64_t offset, int whence) +{ + return reinterpret_cast(data)->avio_seek (offset, whence); +} + void FFmpeg::setup_general () { av_register_all (); - if (avformat_open_input (&_format_context, _ffmpeg_content->file().string().c_str(), 0, 0) < 0) { - throw OpenFileError (_ffmpeg_content->file().string ()); + _file_group.set_paths (_ffmpeg_content->paths ()); + _avio_buffer = static_cast (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; + /* 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 (5 * 60 * 1e6).c_str(), 0); + av_dict_set (&options, "probesize", lexical_cast (5 * 60 * 1e6).c_str(), 0); + + if (avformat_open_input (&_format_context, 0, 0, &options) < 0) { + throw OpenFileError (_ffmpeg_content->path(0).string ()); } if (avformat_find_stream_info (_format_context, 0) < 0) { @@ -93,7 +118,25 @@ FFmpeg::setup_general () 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")); } @@ -103,15 +146,16 @@ void FFmpeg::setup_video () { boost::mutex::scoped_lock lm (_mutex); - - _video_codec_context = _format_context->streams[_video_stream]->codec; - _video_codec = avcodec_find_decoder (_video_codec_context->codec_id); - if (_video_codec == 0) { + assert (_video_stream >= 0); + AVCodecContext* context = _format_context->streams[_video_stream]->codec; + AVCodec* codec = avcodec_find_decoder (context->codec_id); + + if (codec == 0) { throw DecodeError (_("could not find video decoder")); } - if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) { + if (avcodec_open2 (context, codec, 0) < 0) { throw DecodeError (N_("could not open video decoder")); } } @@ -120,19 +164,49 @@ void FFmpeg::setup_audio () { boost::mutex::scoped_lock lm (_mutex); - - if (!_ffmpeg_content->audio_stream ()) { - return; + + for (uint32_t i = 0; i < _format_context->nb_streams; ++i) { + AVCodecContext* context = _format_context->streams[i]->codec; + if (context->codec_type != AVMEDIA_TYPE_AUDIO) { + continue; + } + + AVCodec* codec = avcodec_find_decoder (context->codec_id); + if (codec == 0) { + throw DecodeError (_("could not find audio decoder")); + } + + if (avcodec_open2 (context, codec, 0) < 0) { + throw DecodeError (N_("could not open audio decoder")); + } } +} - _audio_codec_context = _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec; - _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id); - if (_audio_codec == 0) { - throw DecodeError (_("could not find audio decoder")); - } +AVCodecContext * +FFmpeg::video_codec_context () const +{ + return _format_context->streams[_video_stream]->codec; +} - if (avcodec_open2 (_audio_codec_context, _audio_codec, 0) < 0) { - throw DecodeError (N_("could not open audio decoder")); +AVCodecContext * +FFmpeg::audio_codec_context () const +{ + return _ffmpeg_content->audio_stream()->stream(_format_context)->codec; +} + +int +FFmpeg::avio_read (uint8_t* buffer, int const amount) +{ + return _file_group.read (buffer, 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); }