diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-06-21 18:48:46 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-06-25 16:32:28 +0100 |
| commit | 46cd0fe7b5b514f0d9456b25f670679cc584a218 (patch) | |
| tree | 708a5d638fff8a143502a1820925d3e31d0a1d24 /src/lib | |
| parent | f1bf21a9c2581591ab80bfc997a22b93046f8c56 (diff) | |
Basics of FFmpeg examiner works.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/ffmpeg.cc | 59 | ||||
| -rw-r--r-- | src/lib/ffmpeg.h | 15 | ||||
| -rw-r--r-- | src/lib/ffmpeg_content.cc | 14 | ||||
| -rw-r--r-- | src/lib/ffmpeg_content.h | 2 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 24 | ||||
| -rw-r--r-- | src/lib/ffmpeg_examiner.cc | 59 | ||||
| -rw-r--r-- | src/lib/ffmpeg_examiner.h | 7 |
7 files changed, 137 insertions, 43 deletions
diff --git a/src/lib/ffmpeg.cc b/src/lib/ffmpeg.cc index 0d897abfa..a39de391a 100644 --- a/src/lib/ffmpeg.cc +++ b/src/lib/ffmpeg.cc @@ -40,10 +40,6 @@ FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c) , _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,13 +49,12 @@ FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> 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); @@ -104,14 +99,14 @@ 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); + AVCodecContext* context = _format_context->streams[_video_stream]->codec; + AVCodec* codec = avcodec_find_decoder (context->codec_id); - if (_video_codec == 0) { + 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 +115,33 @@ 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 _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec; } diff --git a/src/lib/ffmpeg.h b/src/lib/ffmpeg.h index dcafe17f7..4d1a45da3 100644 --- a/src/lib/ffmpeg.h +++ b/src/lib/ffmpeg.h @@ -17,6 +17,9 @@ */ +#ifndef DCPOMATIC_FFMPEG_H +#define DCPOMATIC_FFMPEG_H + #include <vector> #include <boost/shared_ptr.hpp> #include <boost/thread/mutex.hpp> @@ -46,16 +49,16 @@ public: } protected: + AVCodecContext* video_codec_context () const; + AVCodecContext* audio_codec_context () const; + boost::shared_ptr<const FFmpegContent> _ffmpeg_content; + AVFormatContext* _format_context; AVPacket _packet; AVFrame* _frame; - int _video_stream; - AVCodecContext* _video_codec_context; - AVCodec* _video_codec; - AVCodecContext* _audio_codec_context; ///< may be 0 if there is no audio - AVCodec* _audio_codec; ///< may be 0 if there is no audio + int _video_stream; /* It would appear (though not completely verified) that one must have a mutex around calls to avcodec_open* and avcodec_close... and here @@ -68,3 +71,5 @@ private: void setup_video (); void setup_audio (); }; + +#endif diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 43e88c428..68132c5ab 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -76,6 +76,8 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::N for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) { _filters.push_back (Filter::from_id ((*i)->content ())); } + + _first_video = node->optional_number_child<Time> ("FirstVideo"); } FFmpegContent::FFmpegContent (FFmpegContent const & o) @@ -119,6 +121,10 @@ FFmpegContent::as_xml (xmlpp::Node* node) const for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) { node->add_child("Filter")->add_child_text ((*i)->id ()); } + + if (_first_video) { + node->add_child("FirstVideo")->add_child_text (lexical_cast<string> (_first_video.get ())); + } } void @@ -135,7 +141,7 @@ FFmpegContent::examine (shared_ptr<Job> job) ContentVideoFrame video_length = 0; video_length = examiner->video_length (); - film->log()->log (String::compose ("Video length obtained from header as %1 frames", examiner->video_length ())); + film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length)); { boost::mutex::scoped_lock lm (_mutex); @@ -151,6 +157,8 @@ FFmpegContent::examine (shared_ptr<Job> job) if (!_audio_streams.empty ()) { _audio_stream = _audio_streams.front (); } + + _first_video = examiner->first_video (); } take_from_video_examiner (examiner); @@ -288,6 +296,7 @@ FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node) frame_rate = node->number_child<int> ("FrameRate"); channels = node->number_child<int64_t> ("Channels"); mapping = AudioMapping (node->node_child ("Mapping")); + start = node->optional_number_child<Time> ("Start"); } void @@ -297,6 +306,9 @@ FFmpegAudioStream::as_xml (xmlpp::Node* root) const root->add_child("Id")->add_child_text (lexical_cast<string> (id)); root->add_child("FrameRate")->add_child_text (lexical_cast<string> (frame_rate)); root->add_child("Channels")->add_child_text (lexical_cast<string> (channels)); + if (start) { + root->add_child("Start")->add_child_text (lexical_cast<string> (start)); + } mapping.as_xml (root->add_child("Mapping")); } diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 98078ef3e..36c24c2b3 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -46,6 +46,7 @@ public: int frame_rate; int channels; AudioMapping mapping; + boost::optional<Time> start; }; extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b); @@ -139,6 +140,7 @@ private: boost::shared_ptr<FFmpegSubtitleStream> _subtitle_stream; std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams; boost::shared_ptr<FFmpegAudioStream> _audio_stream; + boost::optional<Time> _first_video; /** Video filters that should be used when generating DCPs */ std::vector<Filter const *> _filters; }; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index b1f8aa3d0..f1d984ee1 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -242,11 +242,11 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) AVSampleFormat FFmpegDecoder::audio_sample_format () const { - if (_audio_codec_context == 0) { + if (!_ffmpeg_content->audio_stream()) { return (AVSampleFormat) 0; } - return _audio_codec_context->sample_fmt; + return audio_codec_context()->sample_fmt; } int @@ -290,7 +290,7 @@ FFmpegDecoder::do_seek (Time t, bool backwards, bool accurate) int64_t const vt = t / (av_q2d (_format_context->streams[_video_stream]->time_base) * TIME_HZ); av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0); - avcodec_flush_buffers (_video_codec_context); + avcodec_flush_buffers (video_codec_context()); if (_subtitle_codec_context) { avcodec_flush_buffers (_subtitle_codec_context); } @@ -306,7 +306,7 @@ FFmpegDecoder::do_seek (Time t, bool backwards, bool accurate) if (_packet.stream_index == _video_stream) { int finished = 0; - int const r = avcodec_decode_video2 (_video_codec_context, _frame, &finished, &_packet); + int const r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet); if (r >= 0 && finished) { int64_t const bet = av_frame_get_best_effort_timestamp (_frame); if (bet > vt) { @@ -334,7 +334,7 @@ FFmpegDecoder::decode_audio_packet () while (copy_packet.size > 0) { int frame_finished; - int const decode_result = avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, ©_packet); + int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet); if (decode_result >= 0) { if (frame_finished) { @@ -343,10 +343,10 @@ FFmpegDecoder::decode_audio_packet () * av_frame_get_best_effort_timestamp(_frame); int const data_size = av_samples_get_buffer_size ( - 0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1 + 0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1 ); - assert (_audio_codec_context->channels == _ffmpeg_content->audio_channels()); + assert (audio_codec_context()->channels == _ffmpeg_content->audio_channels()); audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds * TIME_HZ); } @@ -360,7 +360,7 @@ bool FFmpegDecoder::decode_video_packet () { int frame_finished; - if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) < 0 || !frame_finished) { + if (avcodec_decode_video2 (video_codec_context(), _frame, &frame_finished, &_packet) < 0 || !frame_finished) { return false; } @@ -416,11 +416,11 @@ FFmpegDecoder::decode_video_packet () Time FFmpegDecoder::position () const { - if (_decode_video && _decode_audio && _audio_codec_context) { + if (_decode_video && _decode_audio && _ffmpeg_content->audio_stream()) { return min (_next_video, _next_audio); } - if (_decode_audio && _audio_codec_context) { + if (_decode_audio && _ffmpeg_content->audio_stream()) { return _next_audio; } @@ -430,7 +430,9 @@ FFmpegDecoder::position () const bool FFmpegDecoder::done () const { - return (!_decode_audio || !_audio_codec_context || audio_done()) && (!_decode_video || video_done()); + bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || audio_done(); + bool const vd = !_decode_video || video_done(); + return ad && vd; } void diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index e5d356a27..c09395e76 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -25,8 +25,10 @@ extern "C" { #include "ffmpeg_content.h" using std::string; +using std::cout; using std::stringstream; using boost::shared_ptr; +using boost::optional; FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c) : FFmpeg (c) @@ -56,6 +58,61 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c) } } + /* Run through until we find the first audio (for each stream) and video */ + + while (1) { + int r = av_read_frame (_format_context, &_packet); + if (r < 0) { + break; + } + + int frame_finished; + avcodec_get_frame_defaults (_frame); + + cout << "got packet " << _packet.stream_index << "\n"; + + AVCodecContext* context = _format_context->streams[_packet.stream_index]->codec; + + if (_packet.stream_index == _video_stream && !_first_video) { + if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + _first_video = frame_time (_video_stream); + } + } else { + for (size_t i = 0; i < _audio_streams.size(); ++i) { + if (_packet.stream_index == _audio_streams[i]->id && !_audio_streams[i]->start) { + if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + _audio_streams[i]->start = frame_time (_audio_streams[i]->id); + } + } + } + } + + bool have_all_audio = true; + size_t i = 0; + while (i < _audio_streams.size() && have_all_audio) { + have_all_audio = _audio_streams[i]->start; + ++i; + } + + if (_first_video && have_all_audio) { + break; + } + + av_free_packet (&_packet); + } +} + +optional<Time> +FFmpegExaminer::frame_time (int stream) const +{ + optional<Time> t; + + int64_t const bet = av_frame_get_best_effort_timestamp (_frame); + if (bet != AV_NOPTS_VALUE) { + t = bet * av_q2d (_format_context->streams[stream]->time_base) * TIME_HZ; + } + + return t; } float @@ -73,7 +130,7 @@ FFmpegExaminer::video_frame_rate () const libdcp::Size FFmpegExaminer::video_size () const { - return libdcp::Size (_video_codec_context->width, _video_codec_context->height); + return libdcp::Size (video_codec_context()->width, video_codec_context()->height); } /** @return Length (in video frames) according to our content's header */ diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h index 875451507..57b7775d4 100644 --- a/src/lib/ffmpeg_examiner.h +++ b/src/lib/ffmpeg_examiner.h @@ -17,6 +17,7 @@ */ +#include <boost/optional.hpp> #include "ffmpeg.h" #include "video_examiner.h" @@ -40,9 +41,15 @@ public: return _audio_streams; } + boost::optional<Time> first_video () const { + return _first_video; + } + private: std::string stream_name (AVStream* s) const; + boost::optional<Time> frame_time (int) const; std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams; std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams; + boost::optional<Time> _first_video; }; |
