X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=8b785f0fe7d7c040dba0a6598dbe53c83a0e7cd9;hb=1b998e92e4313e728389a39408bd67919649c5cb;hp=9348214497ae4706950a329026c370f5e0760fd9;hpb=e1afd317dc2660feb121eb1b2ad298c3f3a5b06b;p=dcpomatic.git diff --git a/src/lib/player.cc b/src/lib/player.cc index 934821449..8b785f0fe 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -20,38 +20,37 @@ #include "atmos_decoder.h" -#include "player.h" -#include "film.h" #include "audio_buffers.h" -#include "content_audio.h" +#include "audio_content.h" +#include "audio_decoder.h" +#include "audio_processor.h" +#include "compose.hpp" +#include "config.h" #include "dcp_content.h" +#include "dcp_decoder.h" #include "dcpomatic_log.h" -#include "job.h" +#include "decoder.h" +#include "decoder_factory.h" +#include "ffmpeg_content.h" +#include "film.h" +#include "frame_rate_change.h" #include "image.h" -#include "raw_image_proxy.h" -#include "ratio.h" +#include "image_decoder.h" +#include "job.h" #include "log.h" -#include "render_text.h" -#include "config.h" -#include "content_video.h" +#include "piece_video.h" +#include "player.h" #include "player_video.h" -#include "frame_rate_change.h" -#include "audio_processor.h" #include "playlist.h" +#include "ratio.h" +#include "raw_image_proxy.h" #include "referenced_reel_asset.h" -#include "decoder_factory.h" -#include "decoder.h" -#include "video_decoder.h" -#include "audio_decoder.h" +#include "render_text.h" +#include "shuffler.h" #include "text_content.h" #include "text_decoder.h" -#include "ffmpeg_content.h" -#include "audio_content.h" -#include "dcp_decoder.h" -#include "image_decoder.h" -#include "compose.hpp" -#include "shuffler.h" #include "timer.h" +#include "video_decoder.h" #include #include #include @@ -70,6 +69,7 @@ using std::dynamic_pointer_cast; using std::list; using std::make_pair; using std::make_shared; +using std::make_shared; using std::map; using std::max; using std::min; @@ -78,9 +78,8 @@ using std::pair; using std::shared_ptr; using std::vector; using std::weak_ptr; -using std::make_shared; +using std::unique_ptr; using boost::optional; -using boost::scoped_ptr; #if BOOST_VERSION >= 106100 using namespace boost::placeholders; #endif @@ -95,6 +94,10 @@ int const PlayerProperty::DCP_DECODE_REDUCTION = 704; int const PlayerProperty::PLAYBACK_LENGTH = 705; +/** About 0.01dB */ +#define AUDIO_GAIN_EPSILON 0.001 + + Player::Player (shared_ptr film) : _film (film) , _suspended (0) @@ -134,12 +137,6 @@ Player::construct () } -Player::~Player () -{ - delete _shuffler; -} - - void Player::setup_pieces () { @@ -162,6 +159,66 @@ have_audio (shared_ptr content) } +vector>> +collect (shared_ptr film, ContentList content) +{ + vector> ungrouped; + vector>> grouped; + + auto same_settings = [](shared_ptr film, shared_ptr a, shared_ptr b) { + + auto a_streams = a->streams(); + auto b_streams = b->streams(); + + if (a_streams.size() != b_streams.size()) { + return false; + } + + for (size_t i = 0; i < a_streams.size(); ++i) { + auto a_stream = a_streams[i]; + auto b_stream = b_streams[i]; + if ( + !a_stream->mapping().equals(b_stream->mapping(), AUDIO_GAIN_EPSILON) || + a_stream->frame_rate() != b_stream->frame_rate() || + a_stream->channels() != b_stream->channels()) { + return false; + } + } + + return ( + fabs(a->gain() - b->gain()) < AUDIO_GAIN_EPSILON && + a->delay() == b->delay() && + a->language() == b->language() && + a->resampled_frame_rate(film) == b->resampled_frame_rate(film) && + a->channel_names() == b->channel_names() + ); + }; + + for (auto i: content) { + if (i->video || !i->audio || !i->text.empty()) { + ungrouped.push_back (i); + } else { + bool done = false; + for (auto& g: grouped) { + if (same_settings(film, g.front()->audio, i->audio) && i->position() == g.back()->end(film)) { + g.push_back (i); + done = true; + } + } + if (!done) { + grouped.push_back ({i}); + } + } + } + + for (auto i: ungrouped) { + grouped.push_back({i}); + } + + return grouped; +} + + void Player::setup_pieces_unlocked () { @@ -170,8 +227,7 @@ Player::setup_pieces_unlocked () auto old_pieces = _pieces; _pieces.clear (); - delete _shuffler; - _shuffler = new Shuffler(); + _shuffler.reset (new Shuffler()); _shuffler->Video.connect(bind(&Player::video, this, _1, _2)); for (auto i: playlist()->content()) { @@ -194,7 +250,7 @@ Player::setup_pieces_unlocked () } } - auto decoder = decoder_factory (_film, i, _fast, _tolerant, old_decoder); + auto decoder = decoder_factory (_film, i, _tolerant, old_decoder); DCPOMATIC_ASSERT (decoder); FrameRateChange frc (_film, i); @@ -221,41 +277,30 @@ Player::setup_pieces_unlocked () } } - auto piece = make_shared(i, decoder, frc); + vector content = { + Piece::Pair(i, decoder) + }; + + auto piece = make_shared(_film, content, frc, _fast); _pieces.push_back (piece); - if (decoder->video) { + if (i->video) { if (i->video->frame_type() == VideoFrameType::THREE_D_LEFT || i->video->frame_type() == VideoFrameType::THREE_D_RIGHT) { /* We need a Shuffler to cope with 3D L/R video data arriving out of sequence */ - decoder->video->Data.connect (bind(&Shuffler::video, _shuffler, weak_ptr(piece), _1)); + piece->Video.connect (bind(&Shuffler::video, _shuffler.get(), weak_ptr(piece), _1)); } else { - decoder->video->Data.connect (bind(&Player::video, this, weak_ptr(piece), _1)); + piece->Video.connect (bind(&Player::video, this, weak_ptr(piece), _1)); } } - if (decoder->audio) { - decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr (piece), _1, _2)); + if (i->audio) { + piece->Audio.connect (bind(&Player::audio, this, weak_ptr(piece), _1)); } - auto j = decoder->text.begin(); - - while (j != decoder->text.end()) { - (*j)->BitmapStart.connect ( - bind(&Player::bitmap_text_start, this, weak_ptr(piece), i, weak_ptr((*j)->content()), _1) - ); - (*j)->PlainStart.connect ( - bind(&Player::plain_text_start, this, weak_ptr(piece), i, weak_ptr((*j)->content()), _1) - ); - (*j)->Stop.connect ( - bind(&Player::subtitle_stop, this, weak_ptr(piece), i, weak_ptr((*j)->content()), _1) - ); - - ++j; - } - - if (decoder->atmos) { - decoder->atmos->Data.connect (bind(&Player::atmos, this, weak_ptr(piece), _1)); - } + piece->BitmapTextStart.connect (bind(&Player::bitmap_text_start, this, piece, _1)); + piece->StringTextStart.connect (bind(&Player::string_text_start, this, piece, _1)); + piece->TextStop.connect (bind(&Player::subtitle_stop, this, piece, _1)); + piece->Atmos.connect (bind(&Player::atmos, this, piece, _1)); } for (auto i = _pieces.begin(); i != _pieces.end(); ++i) { @@ -263,7 +308,7 @@ Player::setup_pieces_unlocked () /* Look for content later in the content list with in-use video that overlaps this */ for (auto j = std::next(i); j != _pieces.end(); ++j) { if ((*j)->use_video()) { - (*i)->set_ignore_video ((*j)->period(_film).overlap((*i)->period(_film))); + (*i)->set_ignore_video ((*j)->period().overlap((*i)->period())); } } } @@ -513,9 +558,9 @@ Player::get_reel_assets () continue; } - scoped_ptr decoder; + unique_ptr decoder; try { - decoder.reset (new DCPDecoder(_film, j, false, false, shared_ptr())); + decoder.reset (new DCPDecoder(_film, j, false, shared_ptr())); } catch (...) { return a; } @@ -595,7 +640,7 @@ Player::pass () optional earliest_time; for (auto i: _pieces) { - auto time = i->decoder_before(_film, earliest_time); + auto time = i->decoder_before(earliest_time); if (time) { earliest_time = *time; earliest_content = i; @@ -634,7 +679,7 @@ Player::pass () to `hide' the fact that no audio was emitted during the referenced DCP (though we need to behave as though it was). */ - _last_audio_time = earliest_content->end (_film); + _last_audio_time = earliest_content->end (); } break; } @@ -764,42 +809,27 @@ Player::open_subtitles_for_frame (DCPTime time) const void -Player::video (weak_ptr wp, ContentVideo video) +Player::video (weak_ptr wp, PieceVideo video) { auto piece = wp.lock (); if (!piece) { return; } - if (!piece->use_video()) { - return; - } - - auto frc = piece->frame_rate_change(); - if (frc.skip && (video.frame % 2) == 1) { - return; - } - - /* Time of the first frame we will emit */ - DCPTime const time = piece->content_video_to_dcp (video.frame); - LOG_DEBUG_PLAYER("Received video frame %1 at %2", video.frame, to_string(time)); + LOG_DEBUG_PLAYER("Received video frame %1 at %2", video.frame, to_string(video.time)); /* Discard if it's before the content's period or the last accurate seek. We can't discard if it's after the content's period here as in that case we still need to fill any gap between `now' and the end of the content's period. */ - if (time < piece->position() || (_last_video_time && time < *_last_video_time)) { - return; - } - - if (piece->ignore_video_at(time)) { + if (video.time < piece->position() || (_last_video_time && video.time < *_last_video_time)) { return; } /* Fill gaps that we discover now that we have some video which needs to be emitted. This is where we need to fill to. */ - DCPTime fill_to = min (time, piece->end(_film)); + DCPTime fill_to = min (video.time, piece->end()); if (_last_video_time) { DCPTime fill_from = max (*_last_video_time, piece->position()); @@ -812,7 +842,7 @@ Player::video (weak_ptr wp, ContentVideo video) if (fill_to_eyes == Eyes::BOTH) { fill_to_eyes = Eyes::LEFT; } - if (fill_to == piece->end(_film)) { + if (fill_to == piece->end()) { /* Don't fill after the end of the content */ fill_to_eyes = Eyes::LEFT; } @@ -848,11 +878,12 @@ Player::video (weak_ptr wp, ContentVideo video) } } - _last_video[wp] = piece->player_video (video, _film, _video_container_size); + _last_video[wp] = piece->player_video (video, _video_container_size); - DCPTime t = time; + DCPTime t = video.time; + auto const frc = piece->frame_rate_change(); for (int i = 0; i < frc.repeat; ++i) { - if (t < piece->end(_film)) { + if (t < piece->end()) { emit_video (_last_video[wp], t); } t += one_video_frame (); @@ -861,77 +892,75 @@ Player::video (weak_ptr wp, ContentVideo video) void -Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_audio) +Player::audio (weak_ptr wp, PieceAudio audio) { - DCPOMATIC_ASSERT (content_audio.audio->frames() > 0); + DCPOMATIC_ASSERT (audio.audio->frames() > 0); auto piece = wp.lock (); if (!piece) { return; } - int const rfr = piece->resampled_audio_frame_rate (_film); - - /* Compute time in the DCP */ - auto time = piece->resampled_audio_to_dcp (content_audio.frame, _film); - LOG_DEBUG_PLAYER("Received audio frame %1 at %2", content_audio.frame, to_string(time)); + LOG_DEBUG_PLAYER("Received audio at %1", to_string(audio.time)); - /* And the end of this block in the DCP */ - auto end = time + DCPTime::from_frames(content_audio.audio->frames(), rfr); + /* The end of this block in the DCP */ + int const rfr = piece->resampled_audio_frame_rate (); + auto end = audio.time + DCPTime::from_frames(audio.audio->frames(), rfr); + std::cout << "Player gets " << to_string(audio.time) << "\n"; /* Remove anything that comes before the start or after the end of the content */ - if (time < piece->position()) { - auto cut = discard_audio (content_audio.audio, time, piece->position()); + if (audio.time < piece->position()) { + auto cut = discard_audio (audio.audio, audio.time, piece->position()); if (!cut.first) { /* This audio is entirely discarded */ return; } - content_audio.audio = cut.first; - time = cut.second; - } else if (time > piece->end(_film)) { + audio.audio = cut.first; + audio.time = cut.second; + } else if (audio.time > piece->end()) { /* Discard it all */ return; - } else if (end > piece->end(_film)) { - Frame const remaining_frames = DCPTime(piece->end(_film) - time).frames_round(rfr); + } else if (end > piece->end()) { + Frame const remaining_frames = DCPTime(piece->end() - audio.time).frames_round(rfr); if (remaining_frames == 0) { return; } - content_audio.audio = make_shared(content_audio.audio, remaining_frames, 0); + audio.audio = make_shared(audio.audio, remaining_frames, 0); } - DCPOMATIC_ASSERT (content_audio.audio->frames() > 0); + DCPOMATIC_ASSERT (audio.audio->frames() > 0); /* Gain */ if (piece->audio_gain() != 0) { - auto gain = make_shared(content_audio.audio); + auto gain = make_shared(audio.audio); gain->apply_gain (piece->audio_gain()); - content_audio.audio = gain; + audio.audio = gain; } /* Remap */ - content_audio.audio = remap (content_audio.audio, _film->audio_channels(), stream->mapping()); + audio.audio = remap (audio.audio, _film->audio_channels(), audio.mapping); /* Process */ if (_audio_processor) { - content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ()); + audio.audio = _audio_processor->run (audio.audio, _film->audio_channels()); } /* Push */ - _audio_merger.push (content_audio.audio, time); - piece->set_last_push_end (stream, time + DCPTime::from_frames(content_audio.audio->frames(), _film->audio_frame_rate())); + _audio_merger.push (audio.audio, audio.time); + piece->set_last_push_end (audio.stream, audio.time + DCPTime::from_frames(audio.audio->frames(), _film->audio_frame_rate())); } void -Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, weak_ptr wt, ContentBitmapText subtitle) +Player::bitmap_text_start (weak_ptr wp, PieceBitmapTextStart subtitle) { auto piece = wp.lock (); - auto content = wc.lock (); - auto text = wt.lock (); + auto content = subtitle.content().lock(); + auto text = subtitle.text().lock(); if (!piece || !content || !text) { return; } @@ -960,34 +989,34 @@ Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, weak_ dcp::Size scaled_size (width, height); ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUVToRGB::REC601, image->pixel_format(), true, _fast), subtitle.sub.rectangle)); - auto from = piece->content_time_to_dcp(content, subtitle.from()); + auto from = piece->content_time_to_dcp(content, subtitle.time()); DCPOMATIC_ASSERT (from); - _active_texts[static_cast(text->type())].add_from (wt, ps, *from); + _active_texts[static_cast(text->type())].add_from(text, ps, *from); } void -Player::plain_text_start (weak_ptr wp, weak_ptr wc, weak_ptr wt, ContentStringText subtitle) +Player::string_text_start (weak_ptr wp, PieceStringTextStart subtitle) { auto piece = wp.lock (); - auto content = wc.lock (); - auto text = wt.lock (); + auto content = subtitle.content().lock(); + auto text = subtitle.text().lock(); if (!piece || !content || !text) { return; } PlayerText ps; - auto const from = piece->content_time_to_dcp(content, subtitle.from()); + auto const from = piece->content_time_to_dcp(content, subtitle.time()); DCPOMATIC_ASSERT (from); - if (from > piece->end(_film)) { + if (from > piece->end()) { return; } for (auto s: subtitle.subs) { - s.set_h_position (s.h_position() + text->x_offset ()); - s.set_v_position (s.v_position() + text->y_offset ()); + s.set_h_position (s.h_position() + text->x_offset()); + s.set_v_position (s.v_position() + text->y_offset()); float const xs = text->x_scale(); float const ys = text->y_scale(); float size = s.size(); @@ -1010,20 +1039,20 @@ Player::plain_text_start (weak_ptr wp, weak_ptr wc, weak_p ps.add_fonts (text->fonts ()); } - _active_texts[static_cast(text->type())].add_from (wt, ps, *from); + _active_texts[static_cast(text->type())].add_from(text, ps, *from); } void -Player::subtitle_stop (weak_ptr wp, weak_ptr wc, weak_ptr wt, ContentTime to) +Player::subtitle_stop (weak_ptr wp, PieceTextStop stop) { - auto content = wc.lock (); - auto text = wt.lock (); + auto content = stop.content().lock(); + auto text = stop.text().lock(); if (!text) { return; } - if (!_active_texts[static_cast(text->type())].have(wt)) { + if (!_active_texts[static_cast(text->type())].have(stop.text())) { return; } @@ -1032,14 +1061,14 @@ Player::subtitle_stop (weak_ptr wp, weak_ptr wc, weak_ptr< return; } - auto const dcp_to = piece->content_time_to_dcp(content, to); + auto const dcp_to = piece->content_time_to_dcp(content, stop.time()); DCPOMATIC_ASSERT (dcp_to); - if (*dcp_to > piece->end(_film)) { + if (*dcp_to > piece->end()) { return; } - auto from = _active_texts[static_cast(text->type())].add_to(wt, *dcp_to); + auto from = _active_texts[static_cast(text->type())].add_to(stop.text(), *dcp_to); bool const always = (text->type() == TextType::OPEN_SUBTITLE && _always_burn_open_subtitles); if (text->use() && !always && !text->burn()) { @@ -1075,7 +1104,7 @@ Player::seek (DCPTime time, bool accurate) } for (auto i: _pieces) { - i->seek (_film, time, accurate); + i->seek (time, accurate); } if (accurate) { @@ -1235,7 +1264,7 @@ Player::playlist () const void -Player::atmos (weak_ptr, ContentAtmos data) +Player::atmos (weak_ptr, PieceAtmos data) { Atmos (data.data, DCPTime::from_frames(data.frame, _film->video_frame_rate()), data.metadata); }