#include "transcoder.h"
#include "job.h"
#include "filter.h"
-#include "film_state.h"
#include "options.h"
#include "exceptions.h"
#include "image.h"
using namespace std;
using namespace boost;
-FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Job* j, Log* l, bool minimal, bool ignore_length)
- : Decoder (s, o, j, l, minimal, ignore_length)
+FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, Job* j, bool minimal, bool ignore_length)
+ : Decoder (f, o, j, minimal, ignore_length)
, _format_context (0)
, _video_stream (-1)
, _audio_stream (-1)
, _audio_codec (0)
, _subtitle_codec_context (0)
, _subtitle_codec (0)
- , _first_video_pts (-1)
- , _first_audio_pts (-1)
{
setup_general ();
setup_video ();
av_register_all ();
- if ((r = avformat_open_input (&_format_context, _fs->content_path().c_str(), 0, 0)) != 0) {
- throw OpenFileError (_fs->content_path ());
+ if ((r = avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0)) != 0) {
+ throw OpenFileError (_film->content_path ());
}
if (avformat_find_stream_info (_format_context, 0) < 0) {
/* Now override audio and subtitle streams with those from the Film, if it has any */
- if (_fs->audio_stream_index() != -1) {
- _audio_stream = _fs->audio_stream().id();
+ if (_film->audio_stream_index() != -1) {
+ _audio_stream = _film->audio_stream().id();
}
- if (_fs->subtitle_stream_index() != -1) {
- _subtitle_stream = _fs->subtitle_stream().id ();
+ if (_film->subtitle_stream_index() != -1) {
+ _subtitle_stream = _film->subtitle_stream().id ();
}
if (_video_stream < 0) {
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");
0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
);
- assert (_audio_codec_context->channels == _fs->audio_channels());
+ assert (_audio_codec_context->channels == _film->audio_channels());
process_audio (_frame->data[0], data_size);
}
}
return true;
}
+ double const pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base) * _packet.pts;
+
if (_packet.stream_index == _video_stream) {
- if (_first_video_pts == -1) {
- _first_video_pts = _packet.pts;
+ if (!_first_video) {
+ _first_video = pts_seconds;
}
int frame_finished;
process_video (_frame);
}
- } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio && (_first_video_pts != -1 && _packet.pts > _first_video_pts)) {
+ } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio && _first_video && _first_video.get() <= pts_seconds) {
- /* Note: We only decode audio if we've had our first video packet through, and if this
- packet comes after it. Until then it is thrown away.
+ /* Note: We only decode audio if we've had our first video packet through, and if it
+ was before this packet. Until then audio is thrown away.
*/
-
- if (_first_audio_pts == -1) {
- _first_audio_pts = _packet.pts;
-
+
+ if (!_first_audio) {
+ _first_audio = pts_seconds;
+
/* This is our first audio packet, and if we've arrived here we must have had our
first video packet. Push some silence to make up the gap between our first
video packet and our first audio.
*/
- AVStream* v = _format_context->streams[_video_stream];
- AVStream* a = _format_context->streams[_audio_stream];
+ /* frames of silence that we must push */
+ int const s = rint ((_first_audio.get() - _first_video.get()) * audio_sample_rate ());
- assert (v->time_base.num == a->time_base.num);
- assert (v->time_base.den == a->time_base.den);
-
- /* samples of silence that we must push */
- int const s = rint (av_q2d (v->time_base) * (_first_audio_pts - _first_video_pts) * audio_sample_rate ());
-
_log->log (
String::compose (
- "First video at %1, first audio at %2, pushing %3 samples of silence",
- _first_video_pts, _first_audio_pts, s
+ "First video at %1, first audio at %2, pushing %3 frames of silence for %4 channels (%5 bytes per sample)",
+ _first_video.get(), _first_audio.get(), s, audio_channels(), bytes_per_audio_sample()
)
);
-
+
/* hence bytes */
int const b = s * audio_channels() * bytes_per_audio_sample();
-
+
/* XXX: this assumes that it won't be too much, and there are shaky assumptions
that all sound representations are silent with memset()ed zero data.
*/
memset (silence, 0, b);
process_audio (silence, b);
}
-
+
avcodec_get_frame_defaults (_frame);
int 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());
+
+ assert (_audio_codec_context->channels == _film->audio_channels());
process_audio (_frame->data[0], data_size);
}
-
- } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) {
+
+ } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles && _first_video) {
int got_subtitle;
AVSubtitle sub;
if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
- /* I'm not entirely sure why, but sometimes we get an AVSubtitle with
- no AVSubtitleRects.
+ /* Sometimes we get an empty AVSubtitle, which is used by some codecs to
+ indicate that the previous subtitle should stop.
*/
if (sub.num_rects > 0) {
- process_subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub)));
+ process_subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub, _first_video.get())));
+ } else {
+ process_subtitle (shared_ptr<TimedSubtitle> ());
}
avsubtitle_free (&sub);
}
return false;
}
-int
-FFmpegDecoder::length_in_frames () const
-{
- return (_format_context->duration / AV_TIME_BASE) * frames_per_second ();
-}
-
float
FFmpegDecoder::frames_per_second () const
{