2 Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "ffmpeg_decoder.h"
24 #include "ffmpeg_content.h"
25 #include "imagemagick_decoder.h"
26 #include "imagemagick_content.h"
27 #include "sndfile_decoder.h"
28 #include "sndfile_content.h"
29 #include "subtitle_content.h"
34 #include "resampler.h"
44 using boost::shared_ptr;
45 using boost::weak_ptr;
46 using boost::dynamic_pointer_cast;
48 //#define DEBUG_PLAYER 1
53 Piece (shared_ptr<Content> c)
55 , video_position (c->start ())
56 , audio_position (c->start ())
59 Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
62 , video_position (c->start ())
63 , audio_position (c->start ())
66 shared_ptr<Content> content;
67 shared_ptr<Decoder> decoder;
73 std::ostream& operator<<(std::ostream& s, Piece const & p)
75 if (dynamic_pointer_cast<FFmpegContent> (p.content)) {
77 } else if (dynamic_pointer_cast<ImageMagickContent> (p.content)) {
79 } else if (dynamic_pointer_cast<SndfileContent> (p.content)) {
83 s << " at " << p.content->start() << " until " << p.content->end();
89 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
94 , _have_valid_pieces (false)
97 , _audio_buffers (f->dcp_audio_channels(), 0)
99 _playlist->Changed.connect (bind (&Player::playlist_changed, this));
100 _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2));
101 _film->Changed.connect (bind (&Player::film_changed, this, _1));
102 set_video_container_size (_film->container()->size (_film->full_frame ()));
106 Player::disable_video ()
112 Player::disable_audio ()
120 if (!_have_valid_pieces) {
122 _have_valid_pieces = true;
129 Time earliest_t = TIME_MAX;
130 shared_ptr<Piece> earliest;
136 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
137 if ((*i)->decoder->done ()) {
141 if (dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
142 if ((*i)->video_position < earliest_t) {
143 earliest_t = (*i)->video_position;
149 if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) {
150 if ((*i)->audio_position < earliest_t) {
151 earliest_t = (*i)->audio_position;
160 cout << "no earliest piece.\n";
169 if (earliest_t > _video_position) {
171 cout << "no video here; emitting black frame.\n";
176 cout << "Pass " << *earliest << "\n";
178 earliest->decoder->pass ();
183 if (earliest_t > _audio_position) {
185 cout << "no audio here; emitting silence.\n";
187 emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
190 cout << "Pass " << *earliest << "\n";
192 earliest->decoder->pass ();
198 cout << "\tpost pass " << _video_position << " " << _audio_position << "\n";
205 Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
207 shared_ptr<Piece> piece = weak_piece.lock ();
212 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
215 FrameRateConversion frc (content->video_frame_rate(), _film->dcp_video_frame_rate());
216 if (frc.skip && (frame % 2) == 1) {
220 shared_ptr<Image> work_image = image->crop (content->crop(), true);
222 libdcp::Size const image_size = content->ratio()->size (_video_container_size);
224 work_image = work_image->scale_and_convert_to_rgb (image_size, _film->scaler(), true);
226 Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate());
228 if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
229 work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position);
232 if (image_size != _video_container_size) {
233 assert (image_size.width <= _video_container_size.width);
234 assert (image_size.height <= _video_container_size.height);
235 shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
237 im->copy (work_image, Position<int> ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2));
241 Video (work_image, same, time);
242 time += TIME_HZ / _film->dcp_video_frame_rate();
245 Video (work_image, true, time);
246 time += TIME_HZ / _film->dcp_video_frame_rate();
249 _video_position = piece->video_position = time;
253 Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
255 shared_ptr<Piece> piece = weak_piece.lock ();
260 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
263 if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
264 audio = resampler(content)->run (audio);
268 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames()));
269 dcp_mapped->make_silent ();
270 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
271 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
272 dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
277 /* The time of this audio may indicate that some of our buffered audio is not going to
278 be added to any more, so it can be emitted.
281 Time const time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate());
283 if (time > _audio_position) {
284 /* We can emit some audio from our buffers */
285 OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
286 assert (N <= _audio_buffers.frames());
287 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
288 emit->copy_from (&_audio_buffers, N, 0, 0);
289 Audio (emit, _audio_position);
290 _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N);
292 /* And remove it from our buffers */
293 if (_audio_buffers.frames() > N) {
294 _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
296 _audio_buffers.set_frames (_audio_buffers.frames() - N);
299 /* Now accumulate the new audio into our buffers */
300 _audio_buffers.ensure_size (_audio_buffers.frames() + audio->frames());
301 _audio_buffers.accumulate_frames (audio.get(), 0, 0, audio->frames ());
302 _audio_buffers.set_frames (_audio_buffers.frames() + audio->frames());
308 if (_audio_buffers.frames() > 0) {
309 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
310 emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
311 Audio (emit, _audio_position);
312 _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
313 _audio_buffers.set_frames (0);
316 while (_video_position < _audio_position) {
320 while (_audio_position < _video_position) {
321 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
326 /** @return true on error */
328 Player::seek (Time t, bool accurate)
330 if (!_have_valid_pieces) {
332 _have_valid_pieces = true;
335 if (_pieces.empty ()) {
339 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
340 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
345 Time s = t - vc->start ();
346 s = max (static_cast<Time> (0), s);
347 s = min (vc->length(), s);
349 FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate());
350 VideoContent::Frame f = s * vc->video_frame_rate() / (frc.factor() * TIME_HZ);
351 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
354 /* XXX: don't seek audio because we don't need to... */
358 Player::setup_pieces ()
360 list<shared_ptr<Piece> > old_pieces = _pieces;
364 Playlist::ContentList content = _playlist->content ();
365 sort (content.begin(), content.end(), ContentSorter ());
367 for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
369 shared_ptr<Piece> piece (new Piece (*i));
371 /* XXX: into content? */
373 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
375 shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
377 fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
378 fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
379 fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4));
384 shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
386 shared_ptr<ImageMagickDecoder> id;
388 /* See if we can re-use an old ImageMagickDecoder */
389 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
390 shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*j)->decoder);
391 if (imd && imd->content() == ic) {
397 id.reset (new ImageMagickDecoder (_film, ic));
398 id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
404 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
406 shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
407 sd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
412 _pieces.push_back (piece);
416 cout << "=== Player setup:\n";
417 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
418 cout << *(i->get()) << "\n";
424 Player::content_changed (weak_ptr<Content> w, int p)
426 shared_ptr<Content> c = w.lock ();
432 p == ContentProperty::START || p == ContentProperty::LENGTH ||
433 p == VideoContentProperty::VIDEO_CROP || p == VideoContentProperty::VIDEO_RATIO
436 _have_valid_pieces = false;
439 } else if (p == SubtitleContentProperty::SUBTITLE_OFFSET || p == SubtitleContentProperty::SUBTITLE_SCALE) {
446 Player::playlist_changed ()
448 _have_valid_pieces = false;
453 Player::set_video_container_size (libdcp::Size s)
455 _video_container_size = s;
456 _black_frame.reset (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
457 _black_frame->make_black ();
460 shared_ptr<Resampler>
461 Player::resampler (shared_ptr<AudioContent> c)
463 map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
464 if (i != _resamplers.end ()) {
468 shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
474 Player::emit_black ()
476 /* XXX: use same here */
477 Video (_black_frame, false, _video_position);
478 _video_position += _film->video_frames_to_time (1);
482 Player::emit_silence (OutputAudioFrame most)
484 OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2);
485 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N));
486 silence->make_silent ();
487 Audio (silence, _audio_position);
488 _audio_position += _film->audio_frames_to_time (N);
492 Player::film_changed (Film::Property p)
494 /* Here we should notice Film properties that affect our output, and
495 alert listeners that our output now would be different to how it was
496 last time we were run.
499 if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) {
505 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
507 _in_subtitle.piece = weak_piece;
508 _in_subtitle.image = image;
509 _in_subtitle.rect = rect;
510 _in_subtitle.from = from;
511 _in_subtitle.to = to;
517 Player::update_subtitle ()
519 shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
524 if (!_in_subtitle.image) {
525 _out_subtitle.image.reset ();
529 shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
532 dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
533 libdcp::Size scaled_size;
535 in_rect.y += sc->subtitle_offset ();
537 /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
538 scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
539 scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
541 /* Then we need a corrective translation, consisting of two parts:
543 * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
544 * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
546 * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
547 * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
548 * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
550 * Combining these two translations gives these expressions.
553 _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
554 _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
556 _out_subtitle.image = _in_subtitle.image->scale (libdcp::Size (scaled_size.width, scaled_size.height), Scaler::from_id ("bicubic"), true);
557 _out_subtitle.from = _in_subtitle.from + piece->content->start ();
558 _out_subtitle.to = _in_subtitle.to + piece->content->start ();