summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-06-21 18:48:46 +0100
committerCarl Hetherington <cth@carlh.net>2013-06-25 16:32:28 +0100
commit46cd0fe7b5b514f0d9456b25f670679cc584a218 (patch)
tree708a5d638fff8a143502a1820925d3e31d0a1d24 /src/lib
parentf1bf21a9c2581591ab80bfc997a22b93046f8c56 (diff)
Basics of FFmpeg examiner works.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ffmpeg.cc59
-rw-r--r--src/lib/ffmpeg.h15
-rw-r--r--src/lib/ffmpeg_content.cc14
-rw-r--r--src/lib/ffmpeg_content.h2
-rw-r--r--src/lib/ffmpeg_decoder.cc24
-rw-r--r--src/lib/ffmpeg_examiner.cc59
-rw-r--r--src/lib/ffmpeg_examiner.h7
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, &copy_packet);
+ int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, &copy_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;
};