summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2014-01-03 00:47:32 +0000
committerCarl Hetherington <cth@carlh.net>2014-01-03 00:47:32 +0000
commit821b556975c44bcb2c8607fc26462b7b79db2fe6 (patch)
tree14a2ce9227c8ab385ee2b86cc57e54ab08a9a8cd /src/lib
parent3dfdd5795cf6514e15fdbece54c28f3bddc2aadc (diff)
Various fix-ups.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/audio_decoder.cc27
-rw-r--r--src/lib/audio_decoder.h8
-rw-r--r--src/lib/decoded.h52
-rw-r--r--src/lib/decoder.cc2
-rw-r--r--src/lib/ffmpeg_decoder.cc60
-rw-r--r--src/lib/ffmpeg_decoder.h4
-rw-r--r--src/lib/image_decoder.cc4
-rw-r--r--src/lib/image_decoder.h2
-rw-r--r--src/lib/player.cc4
-rw-r--r--src/lib/sndfile_decoder.cc6
-rw-r--r--src/lib/sndfile_decoder.h6
-rw-r--r--src/lib/video_decoder.cc14
-rw-r--r--src/lib/video_decoder.h2
13 files changed, 109 insertions, 82 deletions
diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc
index aecf39644..d85bfaee2 100644
--- a/src/lib/audio_decoder.cc
+++ b/src/lib/audio_decoder.cc
@@ -22,6 +22,8 @@
#include "exceptions.h"
#include "log.h"
#include "resampler.h"
+#include "util.h"
+#include "film.h"
#include "i18n.h"
@@ -35,22 +37,26 @@ using boost::shared_ptr;
AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content)
: Decoder (film)
, _audio_content (content)
- , _last_audio (0)
+ , _audio_position (0)
{
if (content->output_audio_frame_rate() != content->content_audio_frame_rate() && content->audio_channels ()) {
_resampler.reset (new Resampler (content->content_audio_frame_rate(), content->output_audio_frame_rate(), content->audio_channels ()));
}
}
+/** Audio timestamping is made hard by many factors, but the final nail in the coffin is resampling.
+ * We have to assume that we are feeding continuous data into the resampler, and so we get continuous
+ * data out. Hence we do the timestamping here, post-resampler, just by counting samples.
+ */
void
-AudioDecoder::audio (shared_ptr<const AudioBuffers> data, ContentTime time)
+AudioDecoder::audio (shared_ptr<const AudioBuffers> data)
{
if (_resampler) {
data = _resampler->run (data);
}
- _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (data, time)));
- _last_audio = time + (data->frames() * TIME_HZ / _audio_content->output_audio_frame_rate());
+ _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (data, _audio_position)));
+ _audio_position += data->frames ();
}
void
@@ -62,6 +68,17 @@ AudioDecoder::flush ()
shared_ptr<const AudioBuffers> b = _resampler->flush ();
if (b) {
- audio (b, _last_audio);
+ _pending.push_back (shared_ptr<DecodedAudio> (new DecodedAudio (b, _audio_position)));
+ _audio_position += b->frames ();
}
}
+
+void
+AudioDecoder::seek (ContentTime t, bool)
+{
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ FrameRateChange frc = film->active_frame_rate_change (_audio_content->position ());
+ _audio_position = (t + first_audio()) / frc.speed_up;
+}
diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h
index a295df0cc..12f8505f6 100644
--- a/src/lib/audio_decoder.h
+++ b/src/lib/audio_decoder.h
@@ -44,15 +44,17 @@ public:
return _audio_content;
}
+ void seek (ContentTime time, bool accurate);
+
protected:
- void audio (boost::shared_ptr<const AudioBuffers>, ContentTime);
+ virtual ContentTime first_audio () const = 0;
+ void audio (boost::shared_ptr<const AudioBuffers>);
void flush ();
boost::shared_ptr<const AudioContent> _audio_content;
boost::shared_ptr<Resampler> _resampler;
- /* End time of last audio that we wrote to _pending; only used for flushing the resampler */
- ContentTime _last_audio;
+ AudioFrame _audio_position;
};
#endif
diff --git a/src/lib/decoded.h b/src/lib/decoded.h
index 1de2ff79e..ff32e43f2 100644
--- a/src/lib/decoded.h
+++ b/src/lib/decoded.h
@@ -22,6 +22,7 @@
#include "types.h"
#include "rect.h"
+#include "util.h"
class Image;
@@ -29,20 +30,13 @@ class Decoded
{
public:
Decoded ()
- : content_time (0)
- , dcp_time (0)
- {}
-
- Decoded (DCPTime ct)
- : content_time (ct)
- , dcp_time (0)
+ : dcp_time (0)
{}
virtual ~Decoded () {}
- virtual void set_dcp_times (float speed_up, DCPTime offset) = 0;
+ virtual void set_dcp_times (VideoFrame, AudioFrame, FrameRateChange, DCPTime) = 0;
- ContentTime content_time;
DCPTime dcp_time;
};
@@ -53,62 +47,70 @@ public:
DecodedVideo ()
: eyes (EYES_BOTH)
, same (false)
+ , frame (0)
{}
- DecodedVideo (boost::shared_ptr<const Image> im, Eyes e, bool s, ContentTime ct)
- : Decoded (ct)
- , image (im)
+ DecodedVideo (boost::shared_ptr<const Image> im, Eyes e, bool s, VideoFrame f)
+ : image (im)
, eyes (e)
, same (s)
+ , frame (f)
{}
- void set_dcp_times (float speed_up, DCPTime offset) {
- dcp_time = rint (content_time / speed_up) + offset;
+ void set_dcp_times (VideoFrame video_frame_rate, AudioFrame, FrameRateChange frc, DCPTime offset)
+ {
+ dcp_time = frame * TIME_HZ * frc.factor() / video_frame_rate + offset;
}
boost::shared_ptr<const Image> image;
Eyes eyes;
bool same;
+ VideoFrame frame;
};
class DecodedAudio : public Decoded
{
public:
- DecodedAudio (boost::shared_ptr<const AudioBuffers> d, ContentTime ct)
- : Decoded (ct)
- , data (d)
+ DecodedAudio (boost::shared_ptr<const AudioBuffers> d, AudioFrame f)
+ : data (d)
+ , frame (f)
{}
- void set_dcp_times (float speed_up, DCPTime offset) {
- dcp_time = rint (content_time / speed_up) + offset;
+ void set_dcp_times (VideoFrame, AudioFrame audio_frame_rate, FrameRateChange, DCPTime offset)
+ {
+ dcp_time = frame * TIME_HZ / audio_frame_rate + offset;
}
boost::shared_ptr<const AudioBuffers> data;
+ AudioFrame frame;
};
class DecodedSubtitle : public Decoded
{
public:
DecodedSubtitle ()
- : content_time_to (0)
+ : content_time (0)
+ , content_time_to (0)
, dcp_time_to (0)
{}
DecodedSubtitle (boost::shared_ptr<Image> im, dcpomatic::Rect<double> r, ContentTime f, ContentTime t)
- : Decoded (f)
- , image (im)
+ : image (im)
, rect (r)
+ , content_time (f)
, content_time_to (t)
, dcp_time_to (0)
{}
- void set_dcp_times (float speed_up, DCPTime offset) {
- dcp_time = rint (content_time / speed_up) + offset;
- dcp_time_to = rint (content_time_to / speed_up) + offset;
+ void set_dcp_times (VideoFrame, AudioFrame, FrameRateChange frc, DCPTime offset)
+ {
+ dcp_time = rint (content_time / frc.speed_up) + offset;
+ dcp_time_to = rint (content_time_to / frc.speed_up) + offset;
}
boost::shared_ptr<Image> image;
dcpomatic::Rect<double> rect;
+ ContentTime content_time;
ContentTime content_time_to;
DCPTime dcp_time_to;
};
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc
index 4e136d619..30244b40b 100644
--- a/src/lib/decoder.cc
+++ b/src/lib/decoder.cc
@@ -47,7 +47,7 @@ Decoder::peek ()
_done = pass ();
}
- if (_done) {
+ if (_done && _pending.empty ()) {
return shared_ptr<Decoded> ();
}
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 298284512..25fe655be 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -68,8 +68,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
, _subtitle_codec (0)
, _decode_video (video)
, _decode_audio (audio)
- , _video_pts_offset (0)
- , _audio_pts_offset (0)
+ , _pts_offset (0)
{
setup_subtitle ();
@@ -82,9 +81,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
Then we remove big initial gaps in PTS and we allow our
insertion of black frames to work.
- We will do:
- audio_pts_to_use = audio_pts_from_ffmpeg + audio_pts_offset;
- video_pts_to_use = video_pts_from_ffmpeg + video_pts_offset;
+ We will do pts_to_use = pts_from_ffmpeg + pts_offset;
*/
bool const have_video = video && c->first_video();
@@ -93,16 +90,16 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
/* First, make one of them start at 0 */
if (have_audio && have_video) {
- _video_pts_offset = _audio_pts_offset = - min (c->first_video().get(), c->audio_stream()->first_audio.get());
+ _pts_offset = - min (c->first_video().get(), c->audio_stream()->first_audio.get());
} else if (have_video) {
- _video_pts_offset = - c->first_video().get();
+ _pts_offset = - c->first_video().get();
} else if (have_audio) {
- _audio_pts_offset = - c->audio_stream()->first_audio.get();
+ _pts_offset = - c->audio_stream()->first_audio.get();
}
/* Now adjust both so that the video pts starts on a frame */
if (have_video && have_audio) {
- double first_video = c->first_video().get() + _video_pts_offset;
+ double first_video = c->first_video().get() + _pts_offset;
double const old_first_video = first_video;
/* Round the first video up to a frame boundary */
@@ -110,8 +107,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
first_video = ceil (first_video * c->video_frame_rate()) / c->video_frame_rate ();
}
- _video_pts_offset += first_video - old_first_video;
- _audio_pts_offset += first_video - old_first_video;
+ _pts_offset += first_video - old_first_video;
}
}
@@ -325,10 +321,10 @@ FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optiona
r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
if (r >= 0 && finished) {
last_video = rint (
- (av_frame_get_best_effort_timestamp (_frame) * time_base + _video_pts_offset) * TIME_HZ
+ (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * TIME_HZ
);
}
-
+
} else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->index (_format_context)) {
AVPacket copy_packet = _packet;
while (copy_packet.size > 0) {
@@ -337,7 +333,7 @@ FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optiona
r = avcodec_decode_audio4 (audio_codec_context(), _frame, &finished, &_packet);
if (r >= 0 && finished) {
last_audio = rint (
- (av_frame_get_best_effort_timestamp (_frame) * time_base + _audio_pts_offset) * TIME_HZ
+ (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * TIME_HZ
);
}
@@ -367,18 +363,21 @@ FFmpegDecoder::seek_final_finished (int n, int done) const
void
FFmpegDecoder::seek_and_flush (ContentTime t)
{
- int64_t s = ((double (t) / TIME_HZ) - _video_pts_offset) /
+ int64_t s = ((double (t) / TIME_HZ) - _pts_offset) /
av_q2d (_format_context->streams[_video_stream]->time_base);
-
+
if (_ffmpeg_content->audio_stream ()) {
s = min (
s, int64_t (
- ((double (t) / TIME_HZ) - _audio_pts_offset) /
+ ((double (t) / TIME_HZ) - _pts_offset) /
av_q2d (_ffmpeg_content->audio_stream()->stream(_format_context)->time_base)
)
);
}
+ /* Ridiculous empirical hack */
+ s--;
+
av_seek_frame (_format_context, _video_stream, s, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers (video_codec_context());
@@ -394,6 +393,7 @@ void
FFmpegDecoder::seek (ContentTime time, bool accurate)
{
Decoder::seek (time, accurate);
+ AudioDecoder::seek (time, accurate);
/* If we are doing an accurate seek, our initial shot will be 200ms (200 being
a number plucked from the air) earlier than we want to end up. The loop below
@@ -405,7 +405,7 @@ FFmpegDecoder::seek (ContentTime time, bool accurate)
if (initial_seek < 0) {
initial_seek = 0;
}
-
+
/* Initial seek time in the video stream's timebase */
seek_and_flush (initial_seek);
@@ -416,7 +416,7 @@ FFmpegDecoder::seek (ContentTime time, bool accurate)
}
int const N = minimal_run (boost::bind (&FFmpegDecoder::seek_overrun_finished, this, time, _1, _2));
-
+
seek_and_flush (initial_seek);
if (N > 0) {
minimal_run (boost::bind (&FFmpegDecoder::seek_final_finished, this, N - 1, _3));
@@ -445,16 +445,11 @@ FFmpegDecoder::decode_audio_packet ()
}
if (frame_finished) {
- ContentTime const t = rint (
- (av_q2d (_format_context->streams[copy_packet.stream_index]->time_base)
- * av_frame_get_best_effort_timestamp(_frame) + _audio_pts_offset) * TIME_HZ
- );
-
int const data_size = av_samples_get_buffer_size (
0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1
);
- audio (deinterleave_audio (_frame->data, data_size), t);
+ audio (deinterleave_audio (_frame->data, data_size));
}
copy_packet.data += decode_result;
@@ -503,8 +498,9 @@ FFmpegDecoder::decode_video_packet ()
}
if (i->second != AV_NOPTS_VALUE) {
- ContentTime const t = rint ((i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _video_pts_offset) * TIME_HZ);
- video (image, false, t);
+ double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset;
+ VideoFrame const f = rint (pts * _ffmpeg_content->video_frame_rate ());
+ video (image, false, f);
} else {
shared_ptr<const Film> film = _film.lock ();
assert (film);
@@ -607,3 +603,13 @@ FFmpegDecoder::decode_subtitle_packet ()
avsubtitle_free (&sub);
}
+
+ContentTime
+FFmpegDecoder::first_audio () const
+{
+ if (!_ffmpeg_content->audio_stream ()) {
+ return 0;
+ }
+
+ return _ffmpeg_content->audio_stream()->first_audio.get_value_or(0) + _pts_offset;
+}
diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h
index 55b624bd6..680e39c39 100644
--- a/src/lib/ffmpeg_decoder.h
+++ b/src/lib/ffmpeg_decoder.h
@@ -58,6 +58,7 @@ private:
bool pass ();
void flush ();
+ ContentTime first_audio () const;
void setup_subtitle ();
@@ -85,6 +86,5 @@ private:
bool _decode_video;
bool _decode_audio;
- double _video_pts_offset;
- double _audio_pts_offset;
+ double _pts_offset;
};
diff --git a/src/lib/image_decoder.cc b/src/lib/image_decoder.cc
index a5ca67e0d..204849ecf 100644
--- a/src/lib/image_decoder.cc
+++ b/src/lib/image_decoder.cc
@@ -49,7 +49,7 @@ ImageDecoder::pass ()
}
if (_image && _image_content->still ()) {
- video (_image, true, _video_position * TIME_HZ / _video_content->video_frame_rate ());
+ video (_image, true, _video_position);
++_video_position;
return false;
}
@@ -82,7 +82,7 @@ ImageDecoder::pass ()
delete magick_image;
- video (_image, false, _video_position * TIME_HZ / _video_content->video_frame_rate ());
+ video (_image, false, _video_position);
++_video_position;
return false;
diff --git a/src/lib/image_decoder.h b/src/lib/image_decoder.h
index f4d81dfb5..63b4c58e3 100644
--- a/src/lib/image_decoder.h
+++ b/src/lib/image_decoder.h
@@ -41,6 +41,6 @@ private:
boost::shared_ptr<const ImageContent> _image_content;
boost::shared_ptr<Image> _image;
- ContentTime _video_position;
+ VideoFrame _video_position;
};
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 4fe81d4b2..da7e7c147 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -119,10 +119,10 @@ Player::pass ()
break;
}
- dec->set_dcp_times ((*i)->frc.speed_up, offset);
+ dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
DCPTime const t = dec->dcp_time - offset;
if (t >= (*i)->content->full_length() - (*i)->content->trim_end ()) {
- /* In the end-trimmed part; decoder has nothing eles to give us */
+ /* In the end-trimmed part; decoder has nothing else to give us */
dec.reset ();
done = true;
} else if (t >= (*i)->content->trim_start ()) {
diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc
index 5e3f3313b..1c651e614 100644
--- a/src/lib/sndfile_decoder.cc
+++ b/src/lib/sndfile_decoder.cc
@@ -45,7 +45,6 @@ SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const Sndfi
throw DecodeError (_("could not open audio file for reading"));
}
- _done = 0;
_remaining = _info.frames;
}
@@ -94,8 +93,7 @@ SndfileDecoder::pass ()
}
data->set_frames (this_time);
- audio (data, _done * TIME_HZ / audio_frame_rate ());
- _done += this_time;
+ audio (data);
_remaining -= this_time;
return _remaining == 0;
@@ -123,7 +121,7 @@ void
SndfileDecoder::seek (ContentTime t, bool accurate)
{
Decoder::seek (t, accurate);
+ AudioDecoder::seek (t, accurate);
- _done = t * audio_frame_rate() / TIME_HZ;
_remaining = _info.frames - _done;
}
diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h
index 9cbddc9f6..4ecea0846 100644
--- a/src/lib/sndfile_decoder.h
+++ b/src/lib/sndfile_decoder.h
@@ -36,15 +36,15 @@ public:
int audio_frame_rate () const;
private:
- bool has_audio () const {
- return true;
+ ContentTime first_audio () const {
+ return 0;
}
+
bool pass ();
boost::shared_ptr<const SndfileContent> _sndfile_content;
SNDFILE* _sndfile;
SF_INFO _info;
- AudioFrame _done;
AudioFrame _remaining;
float* _deinterleave_buffer;
};
diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc
index 6a16557cd..a3b45716c 100644
--- a/src/lib/video_decoder.cc
+++ b/src/lib/video_decoder.cc
@@ -35,25 +35,27 @@ VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoCont
/** Called by subclasses when they have a video frame ready */
void
-VideoDecoder::video (shared_ptr<const Image> image, bool same, ContentTime time)
+VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoFrame frame)
{
switch (_video_content->video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
- _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image, EYES_BOTH, same, time)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image, EYES_BOTH, same, frame)));
break;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
{
int const half = image->size().width / 2;
- _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, time)));
- _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, time)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, frame)));
break;
}
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
{
int const half = image->size().height / 2;
- _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, time)));
- _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, time)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, frame)));
+ _pending.push_back (shared_ptr<DecodedVideo> (new DecodedVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, frame)));
break;
}
+ default:
+ assert (false);
}
}
diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h
index d8c362354..8947c2708 100644
--- a/src/lib/video_decoder.h
+++ b/src/lib/video_decoder.h
@@ -41,7 +41,7 @@ public:
protected:
- void video (boost::shared_ptr<const Image>, bool, ContentTime);
+ void video (boost::shared_ptr<const Image>, bool, VideoFrame);
boost::shared_ptr<const VideoContent> _video_content;
};