X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=70d6fa8773a445977da35edd2d2b286a042221dd;hb=56139fd8f98ad306c32567c4b8070fe7ff18ab1e;hp=e38b12ec35286d64e299d499ba2f43a375a3b487;hpb=f2caad0df1a451e2aff68dfd37277faa72116e12;p=dcpomatic.git diff --git a/src/lib/player.cc b/src/lib/player.cc index e38b12ec3..70d6fa877 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -1,5 +1,3 @@ -/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */ - /* Copyright (C) 2013 Carl Hetherington @@ -19,41 +17,93 @@ */ +#include #include "player.h" #include "film.h" #include "ffmpeg_decoder.h" #include "ffmpeg_content.h" -#include "imagemagick_decoder.h" -#include "imagemagick_content.h" +#include "still_image_decoder.h" +#include "still_image_content.h" +#include "moving_image_decoder.h" +#include "moving_image_content.h" #include "sndfile_decoder.h" #include "sndfile_content.h" +#include "subtitle_content.h" #include "playlist.h" #include "job.h" #include "image.h" +#include "ratio.h" +#include "resampler.h" +#include "log.h" +#include "scaler.h" using std::list; using std::cout; using std::min; +using std::max; using std::vector; +using std::pair; +using std::map; using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; +//#define DEBUG_PLAYER 1 + +class Piece +{ +public: + Piece (shared_ptr c) + : content (c) + , video_position (c->position ()) + , audio_position (c->position ()) + {} + + Piece (shared_ptr c, shared_ptr d) + : content (c) + , decoder (d) + , video_position (c->position ()) + , audio_position (c->position ()) + {} + + shared_ptr content; + shared_ptr decoder; + Time video_position; + Time audio_position; +}; + +#ifdef DEBUG_PLAYER +std::ostream& operator<<(std::ostream& s, Piece const & p) +{ + if (dynamic_pointer_cast (p.content)) { + s << "\tffmpeg "; + } else if (dynamic_pointer_cast (p.content)) { + s << "\tstill image"; + } else if (dynamic_pointer_cast (p.content)) { + s << "\tsndfile "; + } + + s << " at " << p.content->position() << " until " << p.content->end(); + + return s; +} +#endif + Player::Player (shared_ptr f, shared_ptr p) : _film (f) , _playlist (p) , _video (true) , _audio (true) - , _subtitles (true) - , _have_valid_decoders (false) - , _position (0) - , _audio_buffers (MAX_AUDIO_CHANNELS, 0) - , _last_video (0) - , _last_was_black (false) - , _last_audio (0) + , _have_valid_pieces (false) + , _video_position (0) + , _audio_position (0) + , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1)) + , _last_emit_was_black (false) { - _playlist->Changed.connect (bind (&Player::playlist_changed, this)); - _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2)); + _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this)); + _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3)); + _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1)); + set_video_container_size (_film->container()->size (_film->full_frame ())); } void @@ -68,244 +118,537 @@ Player::disable_audio () _audio = false; } -void -Player::disable_subtitles () -{ - _subtitles = false; -} - bool Player::pass () { - if (!_have_valid_decoders) { - setup_decoders (); - _have_valid_decoders = true; - } - - /* Here we are just finding the active decoder with the earliest last emission time, then - calling pass on it. If there is no decoder, we skip our position on until there is. - Hence this method will cause video and audio to be emitted, and it is up to the - process_{video,audio} methods to tidy it up. - */ - - Time earliest_pos = TIME_MAX; - shared_ptr earliest; - Time next_wait = TIME_MAX; - - for (list >::iterator i = _decoders.begin(); i != _decoders.end(); ++i) { - Time const ts = (*i)->region.time; - Time const te = (*i)->region.time + (*i)->region.content->length (_film); - if (ts <= _position && te > _position) { - Time const pos = ts + (*i)->last; - if (pos < earliest_pos) { - earliest_pos = pos; - earliest = *i; - } - } - - if (ts > _position) { - next_wait = min (next_wait, ts - _position); - } - } - - if (earliest) { - earliest->decoder->pass (); - _position = earliest->last; - } else if (next_wait < TIME_MAX) { - _position += next_wait; - } else { - return true; - } - - return false; + if (!_have_valid_pieces) { + setup_pieces (); + _have_valid_pieces = true; + } + +#ifdef DEBUG_PLAYER + cout << "= PASS\n"; +#endif + + Time earliest_t = TIME_MAX; + shared_ptr earliest; + enum { + VIDEO, + AUDIO + } type = VIDEO; + + for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { + if ((*i)->decoder->done ()) { + continue; + } + + if (_video && dynamic_pointer_cast ((*i)->decoder)) { + if ((*i)->video_position < earliest_t) { + earliest_t = (*i)->video_position; + earliest = *i; + type = VIDEO; + } + } + + if (_audio && dynamic_pointer_cast ((*i)->decoder)) { + if ((*i)->audio_position < earliest_t) { + earliest_t = (*i)->audio_position; + earliest = *i; + type = AUDIO; + } + } + } + + if (!earliest) { +#ifdef DEBUG_PLAYER + cout << "no earliest piece.\n"; +#endif + + flush (); + return true; + } + + switch (type) { + case VIDEO: + if (earliest_t > _video_position) { +#ifdef DEBUG_PLAYER + cout << "no video here; emitting black frame (earliest=" << earliest_t << ", video_position=" << _video_position << ").\n"; +#endif + emit_black (); + } else { +#ifdef DEBUG_PLAYER + cout << "Pass video " << *earliest << "\n"; +#endif + earliest->decoder->pass (); + } + break; + + case AUDIO: + if (earliest_t > _audio_position) { +#ifdef DEBUG_PLAYER + cout << "no audio here (none until " << earliest_t << "); emitting silence.\n"; +#endif + emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position)); + } else { +#ifdef DEBUG_PLAYER + cout << "Pass audio " << *earliest << "\n"; +#endif + earliest->decoder->pass (); + + if (earliest->decoder->done()) { + shared_ptr ac = dynamic_pointer_cast (earliest->content); + assert (ac); + shared_ptr re = resampler (ac, false); + if (re) { + shared_ptr b = re->flush (); + if (b->frames ()) { + process_audio (earliest, b, ac->audio_length ()); + } + } + } + } + break; + } + + if (_audio) { + Time audio_done_up_to = TIME_MAX; + for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { + if (dynamic_pointer_cast ((*i)->decoder)) { + audio_done_up_to = min (audio_done_up_to, (*i)->audio_position); + } + } + + TimedAudioBuffers