From: Carl Hetherington Date: Thu, 19 May 2022 19:35:46 +0000 (+0200) Subject: wip: black pieces; sad part is that Shuffler can't cope with content that doesn't... X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=8d67ad2e74b6d2c88b52a397a2c2823254ec09fb wip: black pieces; sad part is that Shuffler can't cope with content that doesn't start at the same time. --- diff --git a/src/lib/black_content.h b/src/lib/black_content.h new file mode 100644 index 000000000..756bec55f --- /dev/null +++ b/src/lib/black_content.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2013-2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "content.h" +#include "dcpomatic_time.h" +#include "video_content.h" +#include +#include + + +class BlackContent : public Content +{ +public: + BlackContent (dcpomatic::DCPTimePeriod period, Eyes eyes) + : Content(period.from) + , _length(period.duration()) + { + video = std::make_shared(this); + if (eyes == Eyes::BOTH) { + video->set_frame_type(VideoFrameType::TWO_D); + } else if (eyes == Eyes::LEFT) { + video->set_frame_type(VideoFrameType::THREE_D_LEFT); + } else if (eyes == Eyes::RIGHT) { + video->set_frame_type(VideoFrameType::THREE_D_RIGHT); + } + } + + std::string summary () const override { + return "black"; + } + + dcpomatic::DCPTime full_length (std::shared_ptr) const override { + return _length; + } + + dcpomatic::DCPTime approximate_length () const override { + return _length; + } + +private: + dcpomatic::DCPTime _length; +}; + diff --git a/src/lib/black_decoder.cc b/src/lib/black_decoder.cc new file mode 100644 index 000000000..f316767bf --- /dev/null +++ b/src/lib/black_decoder.cc @@ -0,0 +1,43 @@ +#include "black_content.h" +#include "black_decoder.h" +#include "image.h" +#include "raw_image_proxy.h" +#include "video_decoder.h" + + +using std::make_shared; +using std::shared_ptr; +using namespace dcpomatic; + + +BlackDecoder::BlackDecoder (shared_ptr film, shared_ptr content) + : Decoder(film) + , _content(content) + , _video_frame_rate(film->video_frame_rate()) +{ + video = make_shared(this, content); + _image = make_shared(AV_PIX_FMT_RGB24, dcp::Size(128, 128), Image::Alignment::PADDED); + _image->make_black (); + _proxy = make_shared(_image); +} + + +void +BlackDecoder::seek (ContentTime time, bool) +{ + _position = DCPTime(time.get()); +} + + +bool +BlackDecoder::pass () +{ + if (_position >= _content->end(film())) { + return true; + } + + video->emit(film(), _proxy, _position.frames_round(_video_frame_rate)); + _position += DCPTime::from_frames(1, _video_frame_rate); + return false; +} + diff --git a/src/lib/black_decoder.h b/src/lib/black_decoder.h new file mode 100644 index 000000000..58741e11f --- /dev/null +++ b/src/lib/black_decoder.h @@ -0,0 +1,28 @@ +#include "dcpomatic_time.h" +#include "decoder.h" + + +class BlackContent; +class Image; +class RawImageProxy; + + +class BlackDecoder : public Decoder +{ +public: + BlackDecoder (std::shared_ptr film, std::shared_ptr content); + + bool pass () override; + void seek (dcpomatic::ContentTime time, bool accurate) override; + + dcpomatic::ContentTime position () const override { + return dcpomatic::ContentTime(_position.get()); + } + +private: + std::shared_ptr _content; + int _video_frame_rate; + dcpomatic::DCPTime _position; + std::shared_ptr _image; + std::shared_ptr _proxy; +}; diff --git a/src/lib/frame_interval_checker.h b/src/lib/frame_interval_checker.h index e8f537c1a..8d8c6743e 100644 --- a/src/lib/frame_interval_checker.h +++ b/src/lib/frame_interval_checker.h @@ -19,6 +19,10 @@ */ +#ifndef DCPOMATIC_FRAME_INTERVAL_CHECKER_H +#define DCPOMATIC_FRAME_INTERVAL_CHECKER_H + + #include "dcpomatic_time.h" #include #include @@ -53,3 +57,6 @@ private: static int const _frames; }; + +#endif + diff --git a/src/lib/player.cc b/src/lib/player.cc index feafd6f1f..43589c70a 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -24,6 +24,8 @@ #include "audio_content.h" #include "audio_decoder.h" #include "audio_processor.h" +#include "black_content.h" +#include "black_decoder.h" #include "compose.hpp" #include "config.h" #include "content_audio.h" @@ -144,6 +146,58 @@ Player::setup_pieces () } +void +Player::add_black_pieces () +{ + list full_left; + list full_right; + list full_both; + for (auto i: playlist()->content()) { + if (i->video && i->video->use() && i->can_be_played() && i->paths_valid()) { + auto period = DCPTimePeriod(i->position(), i->end(_film)); + if (i->video->frame_type() == VideoFrameType::THREE_D_LEFT) { + full_left.push_back (period); + } else if (i->video->frame_type() == VideoFrameType::THREE_D_RIGHT) { + full_right.push_back (period); + } else { + full_both.push_back (period); + } + } + } + + auto const whole = DCPTimePeriod(DCPTime(), _playback_length); + + auto empty_both = subtract(subtract(subtract({whole}, full_left), full_right), full_both); + auto empty_left = subtract(subtract(subtract({whole}, full_left), full_both), empty_both); + auto empty_right = subtract(subtract(subtract({whole}, full_right), full_both), empty_both); + + list> periods; + + for (auto left: empty_left) { + periods.push_back (make_pair(left, Eyes::LEFT)); + } + + for (auto right: empty_right) { + periods.push_back (make_pair(right, Eyes::RIGHT)); + } + + for (auto both: empty_both) { + periods.push_back (make_pair(both, Eyes::BOTH)); + } + + periods.sort([](std::pair const& a, std::pair const& b) { + return a.first < b.first; + }); + + for (auto period: periods) { + LOG_DEBUG_PLAYER("BlackContent in period %1", to_string(period.first)); + auto content = make_shared(period.first, period.second); + auto decoder = make_shared(_film, content); + _pieces.push_back(make_shared(content, decoder, FrameRateChange(_film->video_frame_rate(), _film->video_frame_rate()))); + } +} + + void Player::setup_pieces_unlocked () { @@ -214,38 +268,39 @@ Player::setup_pieces_unlocked () auto piece = make_shared(i, decoder, frc); _pieces.push_back (piece); + } + + + add_black_pieces (); - if (decoder->video) { + for (auto piece: _pieces) { + if (piece->decoder->video) { if (have_threed) { /* We need a Shuffler to cope with 3D L/R video data arriving out of sequence */ - decoder->video->Data.connect (bind(&Shuffler::video, &_shuffler.get(), weak_ptr(piece), _1)); + piece->decoder->video->Data.connect(bind(&Shuffler::video, &_shuffler.get(), weak_ptr(piece), _1)); } else { - decoder->video->Data.connect (bind(&Player::video, this, weak_ptr(piece), _1)); + piece->decoder->video->Data.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 (piece->decoder->audio) { + piece->decoder->audio->Data.connect(bind(&Player::audio, this, weak_ptr(piece), _1, _2)); } - auto j = decoder->text.begin(); - - while (j != decoder->text.end()) { - (*j)->BitmapStart.connect ( - bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + for (auto j: piece->decoder->text) { + j->BitmapStart.connect ( + bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr(j->content()), _1) ); - (*j)->PlainStart.connect ( - bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + j->PlainStart.connect ( + bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr(j->content()), _1) ); - (*j)->Stop.connect ( - bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + j->Stop.connect ( + bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr(j->content()), _1) ); - - ++j; } - if (decoder->atmos) { - decoder->atmos->Data.connect (bind(&Player::atmos, this, weak_ptr(piece), _1)); + if (piece->decoder->atmos) { + piece->decoder->atmos->Data.connect(bind(&Player::atmos, this, weak_ptr(piece), _1)); } } @@ -274,7 +329,6 @@ Player::setup_pieces_unlocked () } } - _black = EmptyVideo (_film, playlist(), _playback_length); _silent = EmptyAudio (_film, playlist(), _playback_length); _next_video_time = boost::none; @@ -676,7 +730,6 @@ Player::pass () enum { NONE, CONTENT, - BLACK, SILENT } which = NONE; @@ -684,11 +737,6 @@ Player::pass () which = CONTENT; } - if (!_black.done() && !_ignore_video && (!earliest_time || _black.position() < *earliest_time)) { - earliest_time = _black.position (); - which = BLACK; - } - if (!_silent.done() && !_ignore_audio && (!earliest_time || _silent.position() < *earliest_time)) { earliest_time = _silent.position (); which = SILENT; @@ -697,7 +745,9 @@ Player::pass () switch (which) { case CONTENT: { - LOG_DEBUG_PLAYER ("Calling pass() on %1", earliest_content->content->path(0)); + if (earliest_content->content->number_of_paths()) { + LOG_DEBUG_PLAYER ("Calling pass() on %1", earliest_content->content->path(0)); + } earliest_content->done = earliest_content->decoder->pass (); auto dcp = dynamic_pointer_cast(earliest_content->content); if (dcp && !_play_referenced && dcp->reference_audio()) { @@ -709,14 +759,6 @@ Player::pass () } break; } - case BLACK: - { - LOG_DEBUG_PLAYER ("Emit black for gap at %1", to_string(_black.position())); - auto period = _black.period_at_position(); - emit_video (black_player_video_frame(period.second), _black.position()); - _black.set_position (_black.position() + one_video_frame()); - break; - } case SILENT: { LOG_DEBUG_PLAYER ("Emit silence for gap at %1", to_string(_silent.position())); @@ -1270,7 +1312,6 @@ Player::seek (DCPTime time, bool accurate) _next_audio_time = boost::none; } - _black.set_position (time); _silent.set_position (time); _last_video.clear (); diff --git a/src/lib/player.h b/src/lib/player.h index 3f83d9547..ed0027424 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -143,6 +143,7 @@ private: dcpomatic::ContentTime dcp_to_content_time (std::shared_ptr piece, dcpomatic::DCPTime t) const; dcpomatic::DCPTime content_time_to_dcp (std::shared_ptr piece, dcpomatic::ContentTime t) const; std::shared_ptr black_player_video_frame (Eyes eyes) const; + void add_black_pieces (); void video (std::weak_ptr, ContentVideo); void audio (std::weak_ptr, AudioStreamPtr, ContentAudio); @@ -226,7 +227,6 @@ private: }; std::map _stream_states; - EmptyVideo _black; EmptyAudio _silent; ActiveText _active_texts[static_cast(TextType::COUNT)]; diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 828ac66a2..d8ed025e6 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -28,19 +28,19 @@ #define DCPOMATIC_VIDEO_DECODER_H -#include "decoder.h" -#include "video_content.h" -#include "util.h" +#include "frame_interval_checker.h" #include "content_video.h" +#include "decoder.h" #include "decoder_part.h" +#include "util.h" +#include "video_content.h" #include -class VideoContent; -class ImageProxy; class Image; +class ImageProxy; class Log; -class FrameIntervalChecker; +class VideoContent; /** @class VideoDecoder diff --git a/src/lib/wscript b/src/lib/wscript index f83adedbd..566d87c32 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -44,6 +44,7 @@ sources = """ audio_processor.cc audio_ring_buffers.cc audio_stream.cc + black_decoder.cc butler.cc text_content.cc text_decoder.cc diff --git a/test/overlap_video_test.cc b/test/overlap_video_test.cc index c23444c4a..9b2f4249b 100644 --- a/test/overlap_video_test.cc +++ b/test/overlap_video_test.cc @@ -65,8 +65,6 @@ BOOST_AUTO_TEST_CASE (overlap_video_test1) BOOST_CHECK (pieces.front()->ignore_video); BOOST_CHECK (pieces.front()->ignore_video.get() == dcpomatic::DCPTimePeriod(dcpomatic::DCPTime::from_seconds(1), dcpomatic::DCPTime::from_seconds(1) + B->length_after_trim(film))); - BOOST_CHECK (player->_black.done()); - make_and_verify_dcp (film); dcp::DCP back (film->dir(film->dcp_name())); diff --git a/test/player_test.cc b/test/player_test.cc index 3954fbbae..d7a8035e8 100644 --- a/test/player_test.cc +++ b/test/player_test.cc @@ -26,6 +26,7 @@ #include "lib/audio_buffers.h" +#include "lib/black_content.h" #include "lib/butler.h" #include "lib/compose.hpp" #include "lib/content_factory.h" @@ -35,6 +36,7 @@ #include "lib/ffmpeg_content.h" #include "lib/film.h" #include "lib/image_content.h" +#include "lib/piece.h" #include "lib/player.h" #include "lib/ratio.h" #include "lib/string_text_file_content.h" @@ -47,6 +49,7 @@ using std::cout; +using std::dynamic_pointer_cast; using std::list; using std::shared_ptr; using std::make_shared; @@ -164,9 +167,20 @@ BOOST_AUTO_TEST_CASE (player_subframe_test) BOOST_CHECK (film->length() == DCPTime::from_frames(3 * 24 + 1, 24)); auto player = std::make_shared(film, Image::Alignment::COMPACT); + player->setup_pieces (); - BOOST_REQUIRE_EQUAL (player->_black._periods.size(), 1U); - BOOST_CHECK (player->_black._periods.front().first == DCPTimePeriod(DCPTime::from_frames(3 * 24, 24), DCPTime::from_frames(3 * 24 + 1, 24))); + list> black; + std::copy_if( + player->_pieces.begin(), + player->_pieces.end(), + std::back_inserter(black), + [](shared_ptr piece) { + return static_cast(dynamic_pointer_cast(piece->content)); + }); + BOOST_REQUIRE_EQUAL (black.size(), 1U); + BOOST_CHECK (black.front()->content->position() == DCPTime::from_frames(3 * 24, 24)); + BOOST_CHECK (black.front()->content->full_length(film) == DCPTime::from_frames(1, 24)); + BOOST_REQUIRE_EQUAL (player->_silent._periods.size(), 1U); BOOST_CHECK (player->_silent._periods.front() == DCPTimePeriod(DCPTime(289920), DCPTime::from_frames(3 * 24 + 1, 24))); } diff --git a/test/threed_test.cc b/test/threed_test.cc index 78316be08..e42c4ef4f 100644 --- a/test/threed_test.cc +++ b/test/threed_test.cc @@ -247,6 +247,12 @@ BOOST_AUTO_TEST_CASE (threed_test_separate_files_slightly_different_lengths) */ BOOST_AUTO_TEST_CASE (threed_test_separate_files_very_different_lengths) { + dcpomatic_log->set_types ( + LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | + LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK | LogEntry::TYPE_DEBUG_PLAYER | + LogEntry::TYPE_DEBUG_THREE_D + ); + auto film = new_test_film2 ("threed_test3"); auto L = make_shared("test/data/test.mp4"); film->examine_and_add_content (L);