/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <iomanip>
#include <iostream>
#include <stdint.h>
-#include <boost/lexical_cast.hpp>
#include <sndfile.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include "util.h"
#include "log.h"
#include "ffmpeg_decoder.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
#include "filter_graph.h"
#include "audio_buffers.h"
#include "ffmpeg_content.h"
+#include "image_proxy.h"
+#include "film.h"
#include "i18n.h"
+#define LOG_GENERAL(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_WARNING(...) _video_content->film()->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+
using std::cout;
using std::string;
using std::vector;
FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log> log)
: VideoDecoder (c)
, AudioDecoder (c)
+ , SubtitleDecoder (c)
, FFmpeg (c)
, _log (log)
, _subtitle_codec_context (0)
, _subtitle_codec (0)
{
- setup_subtitle ();
-
/* Audio and video frame PTS values may not start with 0. We want
to fiddle them so that:
/* Maybe we should fail here, but for now we'll just finish off instead */
char buf[256];
av_strerror (r, buf, sizeof(buf));
- _log->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
+ LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r);
}
flush ();
avcodec_get_frame_defaults (_frame);
- int finished = 0;
- r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
- if (r >= 0 && finished) {
+ int got_picture = 0;
+ r = avcodec_decode_video2 (video_codec_context(), _frame, &got_picture, &_packet);
+ if (r >= 0 && got_picture) {
last_video = ContentTime::from_seconds (av_frame_get_best_effort_timestamp (_frame) * time_base) + _pts_offset;
}
AVPacket copy_packet = _packet;
while (copy_packet.size > 0) {
- int finished;
- r = avcodec_decode_audio4 (audio_codec_context(), _frame, &finished, &_packet);
- if (r >= 0 && finished) {
+ int got_frame;
+ r = avcodec_decode_audio4 (audio_codec_context(), _frame, &got_frame, &_packet);
+ if (r >= 0 && got_frame) {
last_audio = ContentTime::from_seconds (av_frame_get_best_effort_timestamp (_frame) * time_base) + _pts_offset;
}
VideoDecoder::seek (time, accurate);
AudioDecoder::seek (time, accurate);
- /* If we are doing an accurate seek, our initial shot will be 200ms (200 being
+ /* If we are doing an accurate seek, our initial shot will be 2s (2 being
a number plucked from the air) earlier than we want to end up. The loop below
will hopefully then step through to where we want to be.
*/
- ContentTime pre_roll = accurate ? ContentTime::from_seconds (0.2) : ContentTime (0);
+ ContentTime pre_roll = accurate ? ContentTime::from_seconds (2) : ContentTime (0);
ContentTime initial_seek = time - pre_roll;
if (initial_seek < ContentTime (0)) {
initial_seek = ContentTime (0);
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet);
if (decode_result < 0) {
- _log->log (String::compose ("avcodec_decode_audio4 failed (%1)", decode_result));
+ LOG_ERROR ("avcodec_decode_audio4 failed (%1)", decode_result);
return;
}
if (i == _filter_graphs.end ()) {
graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
_filter_graphs.push_back (graph);
- _log->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
+ LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
} else {
graph = *i;
}
if (i->second != AV_NOPTS_VALUE) {
double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds ();
- video (image, rint (pts * _ffmpeg_content->video_frame_rate ()));
+ video (
+ shared_ptr<ImageProxy> (new RawImageProxy (image, _video_content->film()->log())),
+ rint (pts * _ffmpeg_content->video_frame_rate ())
+ );
} else {
- _log->log ("Dropping frame without PTS");
+ LOG_WARNING ("Dropping frame without PTS");
}
}
return true;
}
-
-void
-FFmpegDecoder::setup_subtitle ()
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- if (!_ffmpeg_content->subtitle_stream()) {
- return;
- }
-
- _subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
- if (_subtitle_codec_context == 0) {
- throw DecodeError (N_("could not find subtitle stream"));
- }
-
- _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
-
- if (_subtitle_codec == 0) {
- throw DecodeError (N_("could not find subtitle decoder"));
- }
-
- if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
- throw DecodeError (N_("could not open subtitle decoder"));
- }
-}
-
void
FFmpegDecoder::decode_subtitle_packet ()
{
indicate that the previous subtitle should stop.
*/
if (sub.num_rects <= 0) {
- image_subtitle (ContentTime (), ContentTime (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
+ image_subtitle (ContentTimePeriod (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
return;
} else if (sub.num_rects > 1) {
throw DecodeError (_("multi-part subtitles not yet supported"));
/* Subtitle PTS (within the source, not taking into account any of the
source that we may have chopped off for the DCP)
*/
- ContentTime packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE) + _pts_offset;
-
- /* hence start time for this sub */
- ContentTime const from = packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3);
- ContentTime const to = packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3);
+ ContentTimePeriod period = subtitle_period (sub) + _pts_offset;
AVSubtitleRect const * rect = sub.rects[0];
if (rect->type != SUBTITLE_BITMAP) {
- throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ /* XXX */
+ // throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ return;
}
/* Note RGBA is expressed little-endian, so the first byte in the word is R, second
dcp::Size const vs = _ffmpeg_content->video_size ();
image_subtitle (
- from,
- to,
+ period,
image,
dcpomatic::Rect<double> (
static_cast<double> (rect->x) / vs.width,