X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=8370a3f9fa4373fc957238cf7d7fd3c88d93c0e9;hb=fc5f541024dbc8269714dfb28384b41e1d97cb61;hp=70d6fa8773a445977da35edd2d2b286a042221dd;hpb=1284dd144452ec7fd2e7dca9d5f8f8a77bbff1ac;p=dcpomatic.git diff --git a/src/lib/player.cc b/src/lib/player.cc index 70d6fa877..8370a3f9f 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -48,8 +48,6 @@ using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; -//#define DEBUG_PLAYER 1 - class Piece { public: @@ -57,6 +55,8 @@ public: : content (c) , video_position (c->position ()) , audio_position (c->position ()) + , repeat_to_do (0) + , repeat_done (0) {} Piece (shared_ptr c, shared_ptr d) @@ -65,29 +65,50 @@ public: , video_position (c->position ()) , audio_position (c->position ()) {} + + /** Set this piece to repeat a video frame a given number of times */ + void set_repeat (IncomingVideo video, int num) + { + repeat_video = video; + repeat_to_do = num; + repeat_done = 0; + } + + void reset_repeat () + { + repeat_video.image.reset (); + repeat_to_do = 0; + repeat_done = 0; + } + + bool repeating () const + { + return repeat_done != repeat_to_do; + } + + void repeat (Player* player) + { + player->process_video ( + repeat_video.weak_piece, + repeat_video.image, + repeat_video.eyes, + repeat_done > 0, + repeat_video.frame, + (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ()) + ); + + ++repeat_done; + } 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 + IncomingVideo repeat_video; + int repeat_to_do; + int repeat_done; +}; Player::Player (shared_ptr f, shared_ptr p) : _film (f) @@ -103,7 +124,7 @@ Player::Player (shared_ptr f, shared_ptr p) _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 ())); + set_video_container_size (fit_ratio_within (_film->container()->ratio (), _film->full_frame ())); } void @@ -126,10 +147,6 @@ Player::pass () _have_valid_pieces = true; } -#ifdef DEBUG_PLAYER - cout << "= PASS\n"; -#endif - Time earliest_t = TIME_MAX; shared_ptr earliest; enum { @@ -142,7 +159,10 @@ Player::pass () continue; } - if (_video && dynamic_pointer_cast ((*i)->decoder)) { + shared_ptr vd = dynamic_pointer_cast ((*i)->decoder); + shared_ptr ad = dynamic_pointer_cast ((*i)->decoder); + + if (_video && vd) { if ((*i)->video_position < earliest_t) { earliest_t = (*i)->video_position; earliest = *i; @@ -150,7 +170,7 @@ Player::pass () } } - if (_audio && dynamic_pointer_cast ((*i)->decoder)) { + if (_audio && ad && ad->has_audio ()) { if ((*i)->audio_position < earliest_t) { earliest_t = (*i)->audio_position; earliest = *i; @@ -160,10 +180,6 @@ Player::pass () } if (!earliest) { -#ifdef DEBUG_PLAYER - cout << "no earliest piece.\n"; -#endif - flush (); return true; } @@ -171,28 +187,20 @@ Player::pass () 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 (); + if (earliest->repeating ()) { + earliest->repeat (this); + } else { + 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()) { @@ -223,16 +231,21 @@ Player::pass () _audio_position += _film->audio_frames_to_time (tb.audio->frames ()); } -#ifdef DEBUG_PLAYER - cout << "\tpost pass _video_position=" << _video_position << " _audio_position=" << _audio_position << "\n"; -#endif - return false; } +/** @param extra Amount of extra time to add to the content frame's time (for repeat) */ void -Player::process_video (weak_ptr weak_piece, shared_ptr image, Eyes eyes, bool same, VideoContent::Frame frame) +Player::process_video (weak_ptr weak_piece, shared_ptr image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra) { + /* Keep a note of what came in so that we can repeat it if required */ + _last_incoming_video.weak_piece = weak_piece; + _last_incoming_video.image = image; + _last_incoming_video.eyes = eyes; + _last_incoming_video.same = same; + _last_incoming_video.frame = frame; + _last_incoming_video.extra = extra; + shared_ptr piece = weak_piece.lock (); if (!piece) { return; @@ -256,11 +269,12 @@ Player::process_video (weak_ptr weak_piece, shared_ptr image work_image = work_image->crop (content->crop(), true); - libdcp::Size const image_size = content->ratio()->size (_video_container_size); + float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio(); + libdcp::Size image_size = fit_ratio_within (ratio, _video_container_size); work_image = work_image->scale (image_size, _film->scaler(), PIX_FMT_RGB24, true); - Time time = content->position() + relative_time - content->trim_start (); + Time time = content->position() + relative_time + extra - content->trim_start (); if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) { work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position); @@ -280,16 +294,14 @@ Player::process_video (weak_ptr weak_piece, shared_ptr image #endif Video (work_image, eyes, content->colour_conversion(), same, time); - time += TIME_HZ / _film->video_frame_rate(); - - if (frc.repeat) { - Video (work_image, eyes, content->colour_conversion(), true, time); - time += TIME_HZ / _film->video_frame_rate(); - } + time += TIME_HZ / _film->video_frame_rate(); _last_emit_was_black = false; - _video_position = piece->video_position = time; + + if (frc.repeat > 1 && !piece->repeating ()) { + piece->set_repeat (_last_incoming_video, frc.repeat - 1); + } } void @@ -324,7 +336,7 @@ Player::process_audio (weak_ptr weak_piece, shared_ptrposition() + (content->audio_delay() * TIME_HZ / 1000) + relative_time; + Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start (); /* Remap channels */ shared_ptr dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames())); @@ -410,10 +422,12 @@ Player::seek (Time t, bool accurate) */ VideoContent::Frame f = (s + vc->trim_start ()) * _film->video_frame_rate() / (frc.factor() * TIME_HZ); dynamic_pointer_cast((*i)->decoder)->seek (f, accurate); + + (*i)->reset_repeat (); } _video_position = _audio_position = t; - + /* XXX: don't seek audio because we don't need to... */ } @@ -437,7 +451,7 @@ Player::setup_pieces () if (fc) { shared_ptr fd (new FFmpegDecoder (_film, fc, _video, _audio)); - fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4)); + fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4, 0)); fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2)); fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4)); @@ -458,7 +472,7 @@ Player::setup_pieces () if (!id) { id.reset (new StillImageDecoder (_film, ic)); - id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4)); + id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4, 0)); } piece->decoder = id; @@ -470,7 +484,7 @@ Player::setup_pieces () if (!md) { md.reset (new MovingImageDecoder (_film, mc)); - md->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4)); + md->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4, 0)); } piece->decoder = md; @@ -486,13 +500,6 @@ Player::setup_pieces () _pieces.push_back (piece); } - -#ifdef DEBUG_PLAYER - cout << "=== Player setup:\n"; - for (list >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) { - cout << *(i->get()) << "\n"; - } -#endif } void @@ -505,17 +512,26 @@ Player::content_changed (weak_ptr w, int property, bool frequent) if ( property == ContentProperty::POSITION || property == ContentProperty::LENGTH || - property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END || - property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO + property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ) { _have_valid_pieces = false; Changed (frequent); } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) { + update_subtitle (); Changed (frequent); - } else if (property == VideoContentProperty::VIDEO_FRAME_TYPE) { + + } else if ( + property == VideoContentProperty::VIDEO_FRAME_TYPE || property == VideoContentProperty::VIDEO_CROP || + property == VideoContentProperty::VIDEO_RATIO + ) { + + Changed (frequent); + + } else if (property == ContentProperty::PATH) { + Changed (frequent); } } @@ -558,7 +574,7 @@ Player::emit_black () #ifdef DCPOMATIC_DEBUG _last_video.reset (); #endif - + Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position); _video_position += _film->video_frames_to_time (1); _last_emit_was_black = true; @@ -652,3 +668,25 @@ Player::update_subtitle () _out_subtitle.from = _in_subtitle.from + piece->content->position (); _out_subtitle.to = _in_subtitle.to + piece->content->position (); } + +/** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles. + * @return false if this could not be done. + */ +bool +Player::repeat_last_video () +{ + if (!_last_incoming_video.image) { + return false; + } + + process_video ( + _last_incoming_video.weak_piece, + _last_incoming_video.image, + _last_incoming_video.eyes, + _last_incoming_video.same, + _last_incoming_video.frame, + _last_incoming_video.extra + ); + + return true; +}