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"
33 #include "resampler.h"
43 using boost::shared_ptr;
44 using boost::weak_ptr;
45 using boost::dynamic_pointer_cast;
47 #define DEBUG_PLAYER 1
52 Piece (shared_ptr<Content> c)
54 , video_position (c->start ())
55 , audio_position (c->start ())
58 Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
61 , video_position (c->start ())
62 , audio_position (c->start ())
65 shared_ptr<Content> content;
66 shared_ptr<Decoder> decoder;
72 std::ostream& operator<<(std::ostream& s, Piece const & p)
74 if (dynamic_pointer_cast<FFmpegContent> (p.content)) {
76 } else if (dynamic_pointer_cast<ImageMagickContent> (p.content)) {
78 } else if (dynamic_pointer_cast<SndfileContent> (p.content)) {
82 s << " at " << p.content->start() << " until " << p.content->end();
88 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
93 , _have_valid_pieces (false)
96 , _audio_buffers (f->dcp_audio_channels(), 0)
98 _playlist->Changed.connect (bind (&Player::playlist_changed, this));
99 _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2));
100 _film->Changed.connect (bind (&Player::film_changed, this, _1));
101 set_video_container_size (_film->container()->size (_film->full_frame ()));
105 Player::disable_video ()
111 Player::disable_audio ()
119 if (!_have_valid_pieces) {
121 _have_valid_pieces = true;
128 Time earliest_t = TIME_MAX;
129 shared_ptr<Piece> earliest;
135 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
136 if ((*i)->decoder->done ()) {
140 if (dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
141 if ((*i)->video_position < earliest_t) {
142 earliest_t = (*i)->video_position;
148 if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) {
149 if ((*i)->audio_position < earliest_t) {
150 earliest_t = (*i)->audio_position;
159 cout << "no earliest piece.\n";
168 if (earliest_t > _video_position) {
170 cout << "no video here; emitting black frame.\n";
175 cout << "Pass " << *earliest << "\n";
177 earliest->decoder->pass ();
182 if (earliest_t > _audio_position) {
184 cout << "no audio here; emitting silence.\n";
186 emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
189 cout << "Pass " << *earliest << "\n";
191 earliest->decoder->pass ();
197 cout << "\tpost pass " << _video_position << " " << _audio_position << "\n";
204 Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
206 shared_ptr<Piece> piece = weak_piece.lock ();
211 shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
214 FrameRateConversion frc (content->video_frame_rate(), _film->dcp_video_frame_rate());
215 if (frc.skip && (frame % 2) == 1) {
219 shared_ptr<Image> work_image = image->crop (content->crop(), true);
221 libdcp::Size const image_size = content->ratio()->size (_video_container_size);
223 work_image = work_image->scale_and_convert_to_rgb (image_size, _film->scaler(), true);
225 Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate());
227 if (_film->with_subtitles ()) {
228 shared_ptr<Subtitle> sub;
229 if (_subtitle && _subtitle->displayed_at (time - _subtitle_offset)) {
230 sub = _subtitle->subtitle ();
234 dcpomatic::Rect const tx = subtitle_transformed_area (
235 float (image_size.width) / content->video_size().width,
236 float (image_size.height) / content->video_size().height,
237 sub->area(), _film->subtitle_offset(), _film->subtitle_scale()
240 shared_ptr<Image> im = sub->image()->scale (tx.size(), _film->scaler(), true);
241 work_image->alpha_blend (im, tx.position());
245 if (image_size != _video_container_size) {
246 assert (image_size.width <= _video_container_size.width);
247 assert (image_size.height <= _video_container_size.height);
248 shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
250 im->copy (work_image, Position ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2));
254 Video (work_image, same, time);
255 time += TIME_HZ / _film->dcp_video_frame_rate();
258 Video (work_image, true, time);
259 time += TIME_HZ / _film->dcp_video_frame_rate();
262 _video_position = piece->video_position = time;
266 Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
268 shared_ptr<Piece> piece = weak_piece.lock ();
273 shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
276 if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
277 audio = resampler(content)->run (audio);
281 shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames()));
282 dcp_mapped->make_silent ();
283 list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
284 for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
285 dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
290 /* The time of this audio may indicate that some of our buffered audio is not going to
291 be added to any more, so it can be emitted.
294 Time const time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate());
296 if (time > _audio_position) {
297 /* We can emit some audio from our buffers */
298 OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
299 assert (N <= _audio_buffers.frames());
300 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
301 emit->copy_from (&_audio_buffers, N, 0, 0);
302 Audio (emit, _audio_position);
303 _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N);
305 /* And remove it from our buffers */
306 if (_audio_buffers.frames() > N) {
307 _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
309 _audio_buffers.set_frames (_audio_buffers.frames() - N);
312 /* Now accumulate the new audio into our buffers */
313 _audio_buffers.ensure_size (_audio_buffers.frames() + audio->frames());
314 _audio_buffers.accumulate_frames (audio.get(), 0, 0, audio->frames ());
315 _audio_buffers.set_frames (_audio_buffers.frames() + audio->frames());
321 if (_audio_buffers.frames() > 0) {
322 shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
323 emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
324 Audio (emit, _audio_position);
325 _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
326 _audio_buffers.set_frames (0);
329 while (_video_position < _audio_position) {
333 while (_audio_position < _video_position) {
334 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
339 /** @return true on error */
341 Player::seek (Time t, bool accurate)
343 if (!_have_valid_pieces) {
345 _have_valid_pieces = true;
348 if (_pieces.empty ()) {
352 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
353 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
358 Time s = t - vc->start ();
359 s = max (static_cast<Time> (0), s);
360 s = min (vc->length(), s);
362 FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate());
363 VideoContent::Frame f = s * vc->video_frame_rate() / (frc.factor() * TIME_HZ);
364 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
367 /* XXX: don't seek audio because we don't need to... */
371 Player::setup_pieces ()
373 list<shared_ptr<Piece> > old_pieces = _pieces;
377 Playlist::ContentList content = _playlist->content ();
378 sort (content.begin(), content.end(), ContentSorter ());
380 for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
382 shared_ptr<Piece> piece (new Piece (*i));
384 /* XXX: into content? */
386 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
388 shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
390 fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
391 fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
392 fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1));
397 shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
399 shared_ptr<ImageMagickDecoder> id;
401 /* See if we can re-use an old ImageMagickDecoder */
402 for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
403 shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*j)->decoder);
404 if (imd && imd->content() == ic) {
410 id.reset (new ImageMagickDecoder (_film, ic));
411 id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3));
417 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
419 shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
420 sd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2));
425 _pieces.push_back (piece);
429 cout << "=== Player setup:\n";
430 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
431 cout << *(i->get()) << "\n";
437 Player::content_changed (weak_ptr<Content> w, int p)
439 shared_ptr<Content> c = w.lock ();
445 p == ContentProperty::START || p == ContentProperty::LENGTH ||
446 p == VideoContentProperty::VIDEO_CROP || p == VideoContentProperty::VIDEO_RATIO
449 _have_valid_pieces = false;
455 Player::playlist_changed ()
457 _have_valid_pieces = false;
462 Player::set_video_container_size (libdcp::Size s)
464 _video_container_size = s;
465 _black_frame.reset (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
466 _black_frame->make_black ();
469 shared_ptr<Resampler>
470 Player::resampler (shared_ptr<AudioContent> c)
472 map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
473 if (i != _resamplers.end ()) {
477 shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
483 Player::emit_black ()
485 /* XXX: use same here */
486 Video (_black_frame, false, _video_position);
487 _video_position += _film->video_frames_to_time (1);
491 Player::emit_silence (OutputAudioFrame most)
493 OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2);
494 shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N));
495 silence->make_silent ();
496 Audio (silence, _audio_position);
497 _audio_position += _film->audio_frames_to_time (N);
501 Player::film_changed (Film::Property p)
503 /* Here we should notice Film properties that affect our output, and
504 alert listeners that our output now would be different to how it was
505 last time we were run.
509 p == Film::SCALER || p == Film::WITH_SUBTITLES ||
510 p == Film::SUBTITLE_SCALE || p == Film::SUBTITLE_OFFSET ||
519 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<TimedSubtitle> sub)
521 shared_ptr<Piece> piece = weak_piece.lock ();
527 _subtitle_offset = piece->content->start ();