#include <iomanip>
#include <iostream>
#include <stdint.h>
+#include <boost/lexical_cast.hpp>
extern "C" {
#include <tiffio.h>
#include <libavcodec/avcodec.h>
#include "util.h"
#include "log.h"
#include "ffmpeg_decoder.h"
+#include "subtitle.h"
using namespace std;
using namespace boost;
, _format_context (0)
, _video_stream (-1)
, _audio_stream (-1)
+ , _subtitle_stream (-1)
, _frame (0)
, _video_codec_context (0)
, _video_codec (0)
, _audio_codec_context (0)
, _audio_codec (0)
+ , _subtitle_codec_context (0)
+ , _subtitle_codec (0)
{
setup_general ();
setup_video ();
setup_audio ();
+ setup_subtitle ();
}
FFmpegDecoder::~FFmpegDecoder ()
if (_video_codec_context) {
avcodec_close (_video_codec_context);
}
+
+ if (_subtitle_codec_context) {
+ avcodec_close (_subtitle_codec_context);
+ }
av_free (_frame);
avformat_close_input (&_format_context);
_video_stream = i;
} else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
_audio_stream = i;
+ } else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ _subtitle_stream = i;
}
}
if (_video_stream < 0) {
throw DecodeError ("could not find video stream");
}
- if (_audio_stream < 0) {
- throw DecodeError ("could not find audio stream");
- }
_frame = avcodec_alloc_frame ();
if (_frame == 0) {
void
FFmpegDecoder::setup_audio ()
{
+ if (_audio_stream < 0) {
+ return;
+ }
+
_audio_codec_context = _format_context->streams[_audio_stream]->codec;
_audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id);
if (_audio_codec == 0) {
throw DecodeError ("could not find audio decoder");
}
-
+
if (avcodec_open2 (_audio_codec_context, _audio_codec, 0) < 0) {
throw DecodeError ("could not open audio decoder");
}
+
+ /* This is a hack; sometimes it seems that _audio_codec_context->channel_layout isn't set up,
+ so bodge it here. No idea why we should have to do this.
+ */
+
+ if (_audio_codec_context->channel_layout == 0) {
+ _audio_codec_context->channel_layout = av_get_default_channel_layout (audio_channels ());
+ }
}
+void
+FFmpegDecoder::setup_subtitle ()
+{
+ if (_subtitle_stream < 0) {
+ return;
+ }
+
+ _subtitle_codec_context = _format_context->streams[_subtitle_stream]->codec;
+ _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
+
+ if (_subtitle_codec == 0) {
+ throw DecodeError ("could not find subtitle decoder");
+ }
+
+ if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
+ throw DecodeError ("could not open subtitle decoder");
+ }
+}
+
+
bool
FFmpegDecoder::do_pass ()
{
int r = av_read_frame (_format_context, &_packet);
if (r < 0) {
+ if (r != AVERROR_EOF) {
+ throw DecodeError ("error on av_read_frame");
+ }
+
+ /* Get any remaining frames */
+
+ _packet.data = 0;
+ _packet.size = 0;
+
+ int frame_finished;
+
+ while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ shared_ptr<Subtitle> s;
+ if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+ s = _subtitle;
+ }
+
+ process_video (_frame, s);
+ }
+
+ if (_audio_stream >= 0 && _opt->decode_audio) {
+ while (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ int const data_size = av_samples_get_buffer_size (
+ 0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
+ );
+
+ assert (_audio_codec_context->channels == _fs->audio_channels);
+ process_audio (_frame->data[0], data_size);
+ }
+ }
+
return true;
}
- if (_packet.stream_index == _video_stream && _opt->decode_video) {
-
+ if (_packet.stream_index == _video_stream) {
+
int frame_finished;
if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- process_video (_frame);
+ shared_ptr<Subtitle> s;
+ if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+ s = _subtitle;
+ }
+
+ process_video (_frame, s);
}
- } else if (_packet.stream_index == _audio_stream && _opt->decode_audio) {
+ } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio) {
avcodec_get_frame_defaults (_frame);
assert (_audio_codec_context->channels == _fs->audio_channels);
process_audio (_frame->data[0], data_size);
}
+
+ } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) {
+
+ int got_subtitle;
+ AVSubtitle sub;
+ if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
+ _subtitle.reset (new Subtitle (sub));
+ avsubtitle_free (&sub);
+ }
}
av_free_packet (&_packet);
float
FFmpegDecoder::frames_per_second () const
{
- return av_q2d (_format_context->streams[_video_stream]->avg_frame_rate);
+ AVStream* s = _format_context->streams[_video_stream];
+
+ if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
+ return av_q2d (s->avg_frame_rate);
+ }
+
+ return av_q2d (s->r_frame_rate);
}
int
if (_audio_codec_context == 0) {
return 0;
}
-
+
return _audio_codec_context->channels;
}
AVSampleFormat
FFmpegDecoder::audio_sample_format () const
{
+ if (_audio_codec_context == 0) {
+ return (AVSampleFormat) 0;
+ }
+
return _audio_codec_context->sample_fmt;
}
int64_t
FFmpegDecoder::audio_channel_layout () const
{
+ if (_audio_codec_context == 0) {
+ return 0;
+ }
+
return _audio_codec_context->channel_layout;
}
return _video_codec_context->sample_aspect_ratio.den;
}
+bool
+FFmpegDecoder::has_subtitles () const
+{
+ return (_subtitle_stream != -1);
+}