/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "media/streaming/media_streaming_utility.h" #include namespace Media { namespace Streaming { constexpr auto kFrameDisplayTimeAlreadyDone = std::numeric_limits::max(); class VideoTrackObject; class Instance; class VideoTrack final { public: // Called from some unspecified thread. // Callbacks are assumed to be thread-safe. VideoTrack( const PlaybackOptions &options, Stream &&stream, const AudioMsgId &audioId, FnMut ready, Fn error); // Thread-safe. [[nodiscard]] int streamIndex() const; [[nodiscard]] AVRational streamTimeBase() const; [[nodiscard]] crl::time streamDuration() const; // Called from the same unspecified thread. void process(std::vector &&packets); void waitForData(); // Called from the main thread. // Must be called after 'ready' was invoked. void pause(crl::time time); void resume(crl::time time); // Called from the main thread. void setSpeed(float64 speed); void setWaitForMarkAsShown(bool wait); // Called from the main thread. // Returns the position of the displayed frame. [[nodiscard]] crl::time markFrameDisplayed(crl::time now); void addTimelineDelay(crl::time delayed); bool markFrameShown(); [[nodiscard]] crl::time nextFrameDisplayTime() const; [[nodiscard]] QImage frame( const FrameRequest &request, const Instance *instance); [[nodiscard]] FrameWithInfo frameWithInfo( const FrameRequest &request, const Instance *instance); [[nodiscard]] FrameWithInfo frameWithInfo(const Instance *instance); [[nodiscard]] QImage currentFrameImage(); void unregisterInstance(not_null instance); [[nodiscard]] rpl::producer<> checkNextFrame() const; [[nodiscard]] rpl::producer<> waitingForData() const; // Called from the main thread. ~VideoTrack(); private: friend class VideoTrackObject; struct Prepared { Prepared(const FrameRequest &request) : request(request) { } FrameRequest request = FrameRequest::NonStrict(); QImage image; }; struct Frame { FFmpeg::FramePointer decoded = FFmpeg::MakeFramePointer(); FFmpeg::FramePointer transferred; QImage original; FrameYUV yuv; crl::time position = kTimeUnknown; crl::time displayed = kTimeUnknown; crl::time display = kTimeUnknown; FrameFormat format = FrameFormat::None; base::flat_map prepared; int index = 0; bool alpha = false; }; struct FrameWithIndex { not_null frame; int index = -1; }; class Shared { public: using PrepareFrame = not_null; using PrepareNextCheck = crl::time; using PrepareState = std::variant< v::null_t, PrepareFrame, PrepareNextCheck>; struct PresentFrame { crl::time displayPosition = kTimeUnknown; crl::time nextCheckDelay = 0; crl::time addedWorldTimeDelay = 0; }; // Called from the wrapped object queue. void init(QImage &&cover, bool hasAlpha, crl::time position); [[nodiscard]] bool initialized() const; [[nodiscard]] PrepareState prepareState( crl::time trackTime, bool dropStaleFrames); [[nodiscard]] PresentFrame presentFrame( not_null object, TimePoint trackTime, float64 playbackSpeed, bool dropStaleFrames); [[nodiscard]] bool firstPresentHappened() const; // Called from the main thread. // Returns the position of the displayed frame. [[nodiscard]] crl::time markFrameDisplayed(crl::time now); void addTimelineDelay(crl::time delayed); bool markFrameShown(); [[nodiscard]] crl::time nextFrameDisplayTime() const; [[nodiscard]] not_null frameForPaint(); [[nodiscard]] FrameWithIndex frameForPaintWithIndex(); private: [[nodiscard]] not_null getFrame(int index); [[nodiscard]] not_null getFrame(int index) const; [[nodiscard]] int counter() const; static constexpr auto kCounterUninitialized = -1; std::atomic _counter = kCounterUninitialized; static constexpr auto kFramesCount = 4; std::array _frames; // (_counter % 2) == 1 main thread can write _delay. // (_counter % 2) == 0 crl::queue can read _delay. crl::time _delay = kTimeUnknown; }; static void PrepareFrameByRequests( not_null frame, const AVRational &aspect, int rotation); [[nodiscard]] static bool IsDecoded(not_null frame); [[nodiscard]] static bool IsRasterized(not_null frame); [[nodiscard]] static bool IsStale( not_null frame, crl::time trackTime); [[nodiscard]] QImage frameImage( not_null frame, const FrameRequest &request, const Instance *instance); const int _streamIndex = 0; const AVRational _streamTimeBase; const crl::time _streamDuration = 0; const int _streamRotation = 0; const AVRational _streamAspect = FFmpeg::kNormalAspect; std::unique_ptr _shared; using Implementation = VideoTrackObject; crl::object_on_queue _wrapped; }; } // namespace Streaming } // namespace Media