wip: black pieces; sad part is that Shuffler can't cope with content that doesn't... 2253-3d-empty
authorCarl Hetherington <cth@carlh.net>
Thu, 19 May 2022 19:35:46 +0000 (21:35 +0200)
committerCarl Hetherington <cth@carlh.net>
Thu, 19 May 2022 19:35:46 +0000 (21:35 +0200)
src/lib/black_content.h [new file with mode: 0644]
src/lib/black_decoder.cc [new file with mode: 0644]
src/lib/black_decoder.h [new file with mode: 0644]
src/lib/frame_interval_checker.h
src/lib/player.cc
src/lib/player.h
src/lib/video_decoder.h
src/lib/wscript
test/overlap_video_test.cc
test/player_test.cc
test/threed_test.cc

diff --git a/src/lib/black_content.h b/src/lib/black_content.h
new file mode 100644 (file)
index 0000000..756bec5
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+    Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
+
+    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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "content.h"
+#include "dcpomatic_time.h"
+#include "video_content.h"
+#include <iostream>
+#include <string>
+
+
+class BlackContent : public Content
+{
+public:
+       BlackContent (dcpomatic::DCPTimePeriod period, Eyes eyes)
+               : Content(period.from)
+               , _length(period.duration())
+       {
+               video = std::make_shared<VideoContent>(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 Film>) 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 (file)
index 0000000..f316767
--- /dev/null
@@ -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<const Film> film, shared_ptr<const BlackContent> content)
+       : Decoder(film)
+       , _content(content)
+       , _video_frame_rate(film->video_frame_rate())
+{
+       video = make_shared<VideoDecoder>(this, content);
+       _image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size(128, 128), Image::Alignment::PADDED);
+       _image->make_black ();
+       _proxy = make_shared<const RawImageProxy>(_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 (file)
index 0000000..58741e1
--- /dev/null
@@ -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<const Film> film, std::shared_ptr<const BlackContent> 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<const BlackContent> _content;
+       int _video_frame_rate;
+       dcpomatic::DCPTime _position;
+       std::shared_ptr<Image> _image;
+       std::shared_ptr<const RawImageProxy> _proxy;
+};
index e8f537c1a6a5ef67b360ac3a26196805e96503bf..8d8c6743e37d169e65bc6380f9eb303050eeff49 100644 (file)
 */
 
 
+#ifndef DCPOMATIC_FRAME_INTERVAL_CHECKER_H
+#define DCPOMATIC_FRAME_INTERVAL_CHECKER_H
+
+
 #include "dcpomatic_time.h"
 #include <boost/optional.hpp>
 #include <vector>
@@ -53,3 +57,6 @@ private:
        static int const _frames;
 };
 
+
+#endif
+
index feafd6f1f279ea40d532bf94c1228c609906cd0d..43589c70a5ae1ab997056208a04fb63a8a566a0b 100644 (file)
@@ -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<DCPTimePeriod> full_left;
+       list<DCPTimePeriod> full_right;
+       list<DCPTimePeriod> 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<pair<DCPTimePeriod, Eyes>> 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<DCPTimePeriod, Eyes> const& a, std::pair<DCPTimePeriod, Eyes> 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<BlackContent>(period.first, period.second);
+               auto decoder = make_shared<BlackDecoder>(_film, content);
+               _pieces.push_back(make_shared<Piece>(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<Piece>(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>(piece), _1));
+                               piece->decoder->video->Data.connect(bind(&Shuffler::video, &_shuffler.get(), weak_ptr<Piece>(piece), _1));
                        } else {
-                               decoder->video->Data.connect (bind(&Player::video, this, weak_ptr<Piece>(piece), _1));
+                               piece->decoder->video->Data.connect(bind(&Player::video, this, weak_ptr<Piece>(piece), _1));
                        }
                }
 
-               if (decoder->audio) {
-                       decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1, _2));
+               if (piece->decoder->audio) {
+                       piece->decoder->audio->Data.connect(bind(&Player::audio, this, weak_ptr<Piece>(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>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
+               for (auto j: piece->decoder->text) {
+                       j->BitmapStart.connect (
+                               bind(&Player::bitmap_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>(j->content()), _1)
                                );
-                       (*j)->PlainStart.connect (
-                               bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
+                       j->PlainStart.connect (
+                               bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>(j->content()), _1)
                                );
-                       (*j)->Stop.connect (
-                               bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
+                       j->Stop.connect (
+                               bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>(j->content()), _1)
                                );
-
-                       ++j;
                }
 
-               if (decoder->atmos) {
-                       decoder->atmos->Data.connect (bind(&Player::atmos, this, weak_ptr<Piece>(piece), _1));
+               if (piece->decoder->atmos) {
+                       piece->decoder->atmos->Data.connect(bind(&Player::atmos, this, weak_ptr<Piece>(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<DCPContent>(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 ();
index 3f83d9547554b3fdc5c4f92aa8cfe86bf7ae2c06..ed002742408819e8b236a30dc10436bd6f4f0d42 100644 (file)
@@ -143,6 +143,7 @@ private:
        dcpomatic::ContentTime dcp_to_content_time (std::shared_ptr<const Piece> piece, dcpomatic::DCPTime t) const;
        dcpomatic::DCPTime content_time_to_dcp (std::shared_ptr<const Piece> piece, dcpomatic::ContentTime t) const;
        std::shared_ptr<PlayerVideo> black_player_video_frame (Eyes eyes) const;
+       void add_black_pieces ();
 
        void video (std::weak_ptr<Piece>, ContentVideo);
        void audio (std::weak_ptr<Piece>, AudioStreamPtr, ContentAudio);
@@ -226,7 +227,6 @@ private:
        };
        std::map<AudioStreamPtr, StreamState> _stream_states;
 
-       EmptyVideo _black;
        EmptyAudio _silent;
 
        ActiveText _active_texts[static_cast<int>(TextType::COUNT)];
index 828ac66a20af1cbbff7785f039b9c1fd8a32bddb..d8ed025e6a19e781a570ea5c7685eb037a3babb1 100644 (file)
 #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 <boost/signals2.hpp>
 
 
-class VideoContent;
-class ImageProxy;
 class Image;
+class ImageProxy;
 class Log;
-class FrameIntervalChecker;
+class VideoContent;
 
 
 /** @class VideoDecoder
index f83adedbdf729f3fb786d0711045c249974dbec3..566d87c329846295533bce15431bd71fd758f655 100644 (file)
@@ -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
index c23444c4abe6756f92299b3cacb4eea6a37f2587..9b2f4249b2d19407228323124bda26f6861c9819 100644 (file)
@@ -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()));
index 3954fbbaeaebe6ff3cc9f7e0439ec2465fd0b41a..d7a8035e83e6d8874434f489a2f1b02c2911fd20 100644 (file)
@@ -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<Player>(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<std::shared_ptr<Piece>> black;
+       std::copy_if(
+               player->_pieces.begin(),
+               player->_pieces.end(),
+               std::back_inserter(black),
+               [](shared_ptr<Piece> piece) {
+                       return static_cast<bool>(dynamic_pointer_cast<BlackContent>(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)));
 }
index 78316be086b4159ad62c3756329ef18e1b91f027..e42c4ef4f4fe47fc2e490ab52c2df34e56ffe57e 100644 (file)
@@ -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<FFmpegContent>("test/data/test.mp4");
        film->examine_and_add_content (L);