/* Copyright (C) 2013-2020 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 . */ #ifndef DCPOMATIC_PLAYER_H #define DCPOMATIC_PLAYER_H #include "active_text.h" #include "atmos_metadata.h" #include "audio_merger.h" #include "audio_stream.h" #include "content_atmos.h" #include "content_audio.h" #include "content_text.h" #include "content_video.h" #include "dcp_text_track.h" #include "empty.h" #include "enum_indexed_vector.h" #include "film_property.h" #include "image.h" #include "player_text.h" #include "position_image.h" #include "shuffler.h" #include #include namespace dcp { class ReelAsset; } class AtmosContent; class AudioBuffers; class Content; class Film; class PlayerVideo; class Playlist; class ReferencedReelAsset; class PlayerProperty { public: static int constexpr VIDEO_CONTAINER_SIZE = 800; static int constexpr PLAYLIST = 801; static int constexpr FILM_CONTAINER = 802; static int constexpr FILM_VIDEO_FRAME_RATE = 803; static int constexpr DCP_DECODE_REDUCTION = 804; static int constexpr PLAYBACK_LENGTH = 805; static int constexpr IGNORE_VIDEO = 806; static int constexpr IGNORE_AUDIO = 808; static int constexpr IGNORE_TEXT = 808; static int constexpr ALWAYS_BURN_OPEN_SUBTITLES = 809; static int constexpr PLAY_REFERENCED = 810; }; /** @class Player * @brief A class which can play a Playlist. */ class Player { public: Player(std::shared_ptr, Image::Alignment subtitle_alignment, bool tolerant); Player(std::shared_ptr, std::shared_ptr playlist, bool tolerant); Player(Player const&) = delete; Player& operator=(Player const&) = delete; Player(Player&& other); Player& operator=(Player&& other); /** @return true when playback is finished, and there is no more data to come */ bool pass(); void seek(dcpomatic::DCPTime time, bool accurate); std::vector> get_subtitle_fonts(); dcp::Size video_container_size() const { return _video_container_size; } void set_video_container_size(dcp::Size); void set_ignore_video(); void set_ignore_audio(); void set_ignore_text(); void set_always_burn_open_subtitles(); void set_fast(); void set_play_referenced(); void set_dcp_decode_reduction(boost::optional reduction); void set_disable_audio_processor(); boost::optional content_time_to_dcp(std::shared_ptr content, dcpomatic::ContentTime t) const; boost::optional dcp_to_content_time(std::shared_ptr content, dcpomatic::DCPTime t) const; void signal_change(ChangeType type, int property); /** First parameter is PENDING, DONE or CANCELLED. * Second parameter is the property. * Third parameter is true if these signals are currently likely to be frequent. */ boost::signals2::signal Change; /** Emitted when a video frame is ready. These emissions happen in the correct order. */ boost::signals2::signal, dcpomatic::DCPTime)> Video; /** Emitted when audio data is ready. First parameter is the audio data, second its time, * third the frame rate. */ boost::signals2::signal, dcpomatic::DCPTime, int)> Audio; /** Emitted when a text is ready. This signal may be emitted considerably * after the corresponding Video. */ boost::signals2::signal, dcpomatic::DCPTimePeriod)> Text; boost::signals2::signal, dcpomatic::DCPTime, AtmosMetadata)> Atmos; private: friend class PlayerWrapper; friend class Piece; friend struct player_time_calculation_test1; friend struct player_time_calculation_test2; friend struct player_time_calculation_test3; friend struct player_subframe_test; friend struct empty_test1; friend struct empty_test2; friend struct check_reuse_old_data_test; friend struct overlap_video_test1; void construct(); void connect(); void setup_pieces(); void film_change(ChangeType, FilmProperty); void playlist_change(ChangeType); void playlist_content_change(ChangeType, int, bool); Frame dcp_to_content_video(std::shared_ptr piece, dcpomatic::DCPTime t) const; dcpomatic::DCPTime content_video_to_dcp(std::shared_ptr piece, Frame f) const; Frame dcp_to_resampled_audio(std::shared_ptr piece, dcpomatic::DCPTime t) const; dcpomatic::DCPTime resampled_audio_to_dcp(std::shared_ptr piece, Frame f) const; 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 emit_video_until(dcpomatic::DCPTime time); void insert_video(std::shared_ptr pv, dcpomatic::DCPTime time, dcpomatic::DCPTime end); std::pair, boost::optional> earliest_piece_and_time() const; void video(std::weak_ptr, ContentVideo); void audio(std::weak_ptr, AudioStreamPtr, ContentAudio); void bitmap_text_start(std::weak_ptr, std::weak_ptr, ContentBitmapText); void plain_text_start(std::weak_ptr, std::weak_ptr, ContentStringText); void text_stop(std::weak_ptr, std::weak_ptr, dcpomatic::ContentTime); void atmos(std::weak_ptr, ContentAtmos); dcpomatic::DCPTime one_video_frame() const; void fill_audio(dcpomatic::DCPTimePeriod period); std::pair, dcpomatic::DCPTime> discard_audio( std::shared_ptr audio, dcpomatic::DCPTime time, dcpomatic::DCPTime discard_to ) const; boost::optional open_texts_for_frame(dcpomatic::DCPTime time) const; void emit_video(std::shared_ptr pv, dcpomatic::DCPTime time); void use_video(std::shared_ptr pv, dcpomatic::DCPTime time, dcpomatic::DCPTime end); void emit_audio(std::shared_ptr data, dcpomatic::DCPTime time); std::shared_ptr playlist() const; boost::optional should_store( std::weak_ptr weak_piece, std::weak_ptr weak_content, dcpomatic::ContentTime subtitle_from ) const; /** Mutex to protect the player state that is not using std::atomic. * When the player is used for the preview we have seek() and pass() * called from the Butler thread and lots of other stuff called * from the GUI thread. */ mutable boost::mutex _mutex; std::weak_ptr _film; /** Playlist, or 0 if we are using the one from the _film */ std::shared_ptr _playlist; /** > 0 if we are suspended (i.e. pass() and seek() do nothing) */ std::atomic _suspended; std::vector> _pieces; /** Size of the image we are rendering to; this may be the DCP frame size, or * the size of preview in a window. */ std::atomic _video_container_size; mutable boost::mutex _black_image_mutex; std::shared_ptr _black_image; /** true if the player should ignore all video; i.e. never produce any */ std::atomic _ignore_video; std::atomic _ignore_audio; /** true if the player should ignore all text; i.e. never produce any */ std::atomic _ignore_text; std::atomic _always_burn_open_subtitles; /** true if we should try to be fast rather than high quality */ std::atomic _fast; /** true if we should keep going in the face of `survivable' errors */ bool _tolerant; /** true if we should `play' (i.e output) referenced DCP data (e.g. for preview) */ std::atomic _play_referenced; /** Time of the next video that we will emit, or the time of the last accurate seek */ boost::optional _next_video_time; /** Time of the next audio that we will emit, or the time of the last accurate seek */ boost::optional _next_audio_time; boost::optional _dcp_decode_reduction; EnumIndexedVector, dcpomatic::DCPTime>, Eyes> _last_video; AudioMerger _audio_merger; std::unique_ptr _shuffler; std::list, dcpomatic::DCPTime>> _delay; class StreamState { public: StreamState() {} explicit StreamState(std::shared_ptr p) : piece(p) {} std::shared_ptr piece; boost::optional last_push_end; }; std::map _stream_states; Empty _black; Empty _silent; EnumIndexedVector _active_texts; std::shared_ptr _audio_processor; bool _disable_audio_processor = false; std::atomic _playback_length; /** Alignment for subtitle images that we create */ Image::Alignment _subtitle_alignment = Image::Alignment::PADDED; boost::signals2::scoped_connection _film_changed_connection; boost::signals2::scoped_connection _playlist_change_connection; boost::signals2::scoped_connection _playlist_content_change_connection; }; #endif