| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346 |
- /*
- 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
- */
- #include "media/streaming/media_streaming_video_track.h"
- #include "ffmpeg/ffmpeg_utility.h"
- #include "media/audio/media_audio.h"
- #include "base/concurrent_timer.h"
- #include "core/crash_reports.h"
- #include "base/debug_log.h"
- namespace Media {
- namespace Streaming {
- namespace {
- constexpr auto kMaxFrameArea = 3840 * 2160; // usual 4K
- constexpr auto kDisplaySkipped = crl::time(-1);
- constexpr auto kFinishedPosition = std::numeric_limits<crl::time>::max();
- static_assert(kDisplaySkipped != kTimeUnknown);
- [[nodiscard]] QImage ConvertToARGB32(
- FrameFormat format,
- const FrameYUV &data) {
- Expects(data.y.data != nullptr);
- Expects(data.u.data != nullptr);
- Expects((format == FrameFormat::NV12) || (data.v.data != nullptr));
- Expects(!data.size.isEmpty());
- //if (FFmpeg::RotationSwapWidthHeight(stream.rotation)) {
- // resize.transpose();
- //}
- auto result = FFmpeg::CreateFrameStorage(data.size);
- const auto swscale = FFmpeg::MakeSwscalePointer(
- data.size,
- (format == FrameFormat::YUV420
- ? AV_PIX_FMT_YUV420P
- : AV_PIX_FMT_NV12),
- data.size,
- AV_PIX_FMT_BGRA);
- if (!swscale) {
- return QImage();
- }
- // AV_NUM_DATA_POINTERS defined in AVFrame struct
- const uint8_t *srcData[AV_NUM_DATA_POINTERS] = {
- static_cast<const uint8_t*>(data.y.data),
- static_cast<const uint8_t*>(data.u.data),
- static_cast<const uint8_t*>(data.v.data),
- nullptr,
- };
- int srcLinesize[AV_NUM_DATA_POINTERS] = {
- data.y.stride,
- data.u.stride,
- data.v.stride,
- 0,
- };
- uint8_t *dstData[AV_NUM_DATA_POINTERS] = { result.bits(), nullptr };
- int dstLinesize[AV_NUM_DATA_POINTERS] = { int(result.bytesPerLine()), 0 };
- sws_scale(
- swscale.get(),
- srcData,
- srcLinesize,
- 0,
- data.size.height(),
- dstData,
- dstLinesize);
- return result;
- }
- } // namespace
- class VideoTrackObject final {
- public:
- using Frame = VideoTrack::Frame;
- using Shared = VideoTrack::Shared;
- VideoTrackObject(
- crl::weak_on_queue<VideoTrackObject> weak,
- const PlaybackOptions &options,
- not_null<Shared*> shared,
- Stream &&stream,
- const AudioMsgId &audioId,
- FnMut<void(const Information &)> ready,
- Fn<void(Error)> error);
- void process(std::vector<FFmpeg::Packet> &&packets);
- [[nodiscard]] rpl::producer<> checkNextFrame() const;
- [[nodiscard]] rpl::producer<> waitingForData() const;
- void pause(crl::time time);
- void resume(crl::time time);
- void setSpeed(float64 speed);
- void setWaitForMarkAsShown(bool wait);
- void interrupt();
- void frameShown();
- void addTimelineDelay(crl::time delayed);
- void updateFrameRequest(
- const Instance *instance,
- const FrameRequest &request);
- void removeFrameRequest(const Instance *instance);
- void rasterizeFrame(not_null<Frame*> frame);
- [[nodiscard]] bool requireARGB32() const;
- private:
- enum class FrameResult {
- Done,
- Error,
- Waiting,
- Looped,
- Finished,
- };
- using ReadEnoughState = std::variant<
- v::null_t,
- FrameResult,
- Shared::PrepareNextCheck>;
- void fail(Error error);
- [[nodiscard]] bool interrupted() const;
- [[nodiscard]] bool tryReadFirstFrame(FFmpeg::Packet &&packet);
- [[nodiscard]] bool fillStateFromFrame();
- [[nodiscard]] bool processFirstFrame();
- void queueReadFrames(crl::time delay = 0);
- void readFrames();
- [[nodiscard]] ReadEnoughState readEnoughFrames(crl::time trackTime);
- [[nodiscard]] FrameResult readFrame(not_null<Frame*> frame);
- void fillRequests(not_null<Frame*> frame) const;
- [[nodiscard]] QSize chooseOriginalResize(QSize encoded) const;
- void presentFrameIfNeeded();
- void callReady();
- [[nodiscard]] bool loopAround();
- [[nodiscard]] crl::time computeDuration() const;
- [[nodiscard]] int durationByPacket(const FFmpeg::Packet &packet);
- // Force frame position to be clamped to [0, duration] and monotonic.
- [[nodiscard]] crl::time currentFramePosition() const;
- [[nodiscard]] TimePoint trackTime() const;
- const crl::weak_on_queue<VideoTrackObject> _weak;
- PlaybackOptions _options;
- // Main thread wrapper destructor will set _shared back to nullptr.
- // All queued method calls after that should be discarded.
- Shared *_shared = nullptr;
- Stream _stream;
- AudioMsgId _audioId;
- bool _readTillEnd = false;
- FnMut<void(const Information &)> _ready;
- Fn<void(Error)> _error;
- crl::time _pausedTime = kTimeUnknown;
- crl::time _resumedTime = kTimeUnknown;
- int _frameIndex = 0;
- int _durationByLastPacket = 0;
- mutable TimePoint _syncTimePoint;
- crl::time _loopingShift = 0;
- rpl::event_stream<> _checkNextFrame;
- rpl::event_stream<> _waitingForData;
- base::flat_map<const Instance*, FrameRequest> _requests;
- bool _queued = false;
- base::ConcurrentTimer _readFramesTimer;
- // For initial frame skipping for an exact seek.
- FFmpeg::FramePointer _initialSkippingFrame;
- };
- VideoTrackObject::VideoTrackObject(
- crl::weak_on_queue<VideoTrackObject> weak,
- const PlaybackOptions &options,
- not_null<Shared*> shared,
- Stream &&stream,
- const AudioMsgId &audioId,
- FnMut<void(const Information &)> ready,
- Fn<void(Error)> error)
- : _weak(std::move(weak))
- , _options(options)
- , _shared(shared)
- , _stream(std::move(stream))
- , _audioId(audioId)
- , _ready(std::move(ready))
- , _error(std::move(error))
- , _readFramesTimer(_weak, [=] { readFrames(); }) {
- Expects(_stream.duration > 1);
- Expects(_ready != nullptr);
- Expects(_error != nullptr);
- }
- rpl::producer<> VideoTrackObject::checkNextFrame() const {
- return interrupted()
- ? (rpl::complete<>() | rpl::type_erased())
- : !_shared->firstPresentHappened()
- ? (_checkNextFrame.events() | rpl::type_erased())
- : _checkNextFrame.events_starting_with({});
- }
- rpl::producer<> VideoTrackObject::waitingForData() const {
- return interrupted()
- ? (rpl::never() | rpl::type_erased())
- : _waitingForData.events();
- }
- void VideoTrackObject::process(std::vector<FFmpeg::Packet> &&packets) {
- if (interrupted() || packets.empty()) {
- return;
- }
- if (packets.front().empty()) {
- Assert(packets.size() == 1);
- _readTillEnd = true;
- } else if (!_readTillEnd) {
- //for (const auto &packet : packets) {
- // // Maybe it is enough to count by list.back()?.. hope so.
- // accumulate_max(
- // _durationByLastPacket,
- // durationByPacket(packet));
- // if (interrupted()) {
- // return;
- // }
- //}
- accumulate_max(
- _durationByLastPacket,
- durationByPacket(packets.back()));
- if (interrupted()) {
- return;
- }
- }
- for (auto i = begin(packets), e = end(packets); i != e; ++i) {
- if (_shared->initialized()) {
- _stream.queue.insert(
- end(_stream.queue),
- std::make_move_iterator(i),
- std::make_move_iterator(e));
- queueReadFrames();
- break;
- } else if (!tryReadFirstFrame(std::move(*i))) {
- fail(Error::InvalidData);
- break;
- }
- }
- }
- int VideoTrackObject::durationByPacket(const FFmpeg::Packet &packet) {
- // We've set this value on the first cycle.
- if (_loopingShift || _stream.duration != kDurationUnavailable) {
- return 0;
- }
- const auto result = FFmpeg::DurationByPacket(packet, _stream.timeBase);
- if (result < 0) {
- fail(Error::InvalidData);
- return 0;
- }
- Ensures(result > 0);
- return result;
- }
- void VideoTrackObject::queueReadFrames(crl::time delay) {
- if (delay > 0) {
- _readFramesTimer.callOnce(delay);
- } else if (!_queued) {
- _queued = true;
- _weak.with([](VideoTrackObject &that) {
- that._queued = false;
- that.readFrames();
- });
- }
- }
- void VideoTrackObject::readFrames() {
- if (interrupted()) {
- return;
- }
- auto time = trackTime().trackTime;
- while (true) {
- const auto result = readEnoughFrames(time);
- v::match(result, [&](FrameResult result) {
- if (result == FrameResult::Done
- || result == FrameResult::Finished) {
- presentFrameIfNeeded();
- } else if (result == FrameResult::Looped) {
- const auto duration = computeDuration();
- Assert(duration != kDurationUnavailable);
- time -= duration;
- }
- }, [&](Shared::PrepareNextCheck delay) {
- Expects(delay == kTimeUnknown || delay > 0);
- if (delay != kTimeUnknown) {
- queueReadFrames(delay);
- }
- }, [](v::null_t) {
- });
- if (!v::is_null(result)) {
- break;
- }
- }
- }
- auto VideoTrackObject::readEnoughFrames(crl::time trackTime)
- -> ReadEnoughState {
- const auto dropStaleFrames = !_options.waitForMarkAsShown;
- const auto state = _shared->prepareState(trackTime, dropStaleFrames);
- return v::match(state, [&](Shared::PrepareFrame frame)
- -> ReadEnoughState {
- while (true) {
- const auto result = readFrame(frame);
- if (result != FrameResult::Done) {
- return result;
- } else if (!dropStaleFrames
- || !VideoTrack::IsStale(frame, trackTime)) {
- return v::null;
- }
- }
- }, [&](Shared::PrepareNextCheck delay) -> ReadEnoughState {
- return delay;
- }, [&](v::null_t) -> ReadEnoughState {
- return FrameResult::Done;
- });
- }
- bool VideoTrackObject::loopAround() {
- const auto duration = computeDuration();
- if (duration == kDurationUnavailable) {
- LOG(("Streaming Error: "
- "Couldn't find out the real video stream duration."));
- return false;
- }
- avcodec_flush_buffers(_stream.codec.get());
- _frameIndex = 0;
- _loopingShift += duration;
- _readTillEnd = false;
- return true;
- }
- crl::time VideoTrackObject::computeDuration() const {
- if (_stream.duration != kDurationUnavailable) {
- return _stream.duration;
- } else if ((_loopingShift || _readTillEnd) && _durationByLastPacket) {
- // We looped, so it already holds full stream duration.
- return _durationByLastPacket;
- }
- return kDurationUnavailable;
- }
- auto VideoTrackObject::readFrame(not_null<Frame*> frame) -> FrameResult {
- if (const auto error = ReadNextFrame(_stream)) {
- if (error.code() == AVERROR_EOF) {
- if (!_options.loop) {
- frame->position = kFinishedPosition;
- frame->displayed = kTimeUnknown;
- return FrameResult::Finished;
- } else if (loopAround()) {
- return FrameResult::Looped;
- } else {
- fail(Error::InvalidData);
- return FrameResult::Error;
- }
- } else if (error.code() != AVERROR(EAGAIN) || _readTillEnd) {
- fail(Error::InvalidData);
- return FrameResult::Error;
- }
- Assert(_stream.queue.empty());
- _waitingForData.fire({});
- return FrameResult::Waiting;
- }
- const auto position = currentFramePosition();
- if (position == kTimeUnknown) {
- fail(Error::InvalidData);
- return FrameResult::Error;
- }
- std::swap(frame->decoded, _stream.decodedFrame);
- std::swap(frame->transferred, _stream.transferredFrame);
- frame->index = _frameIndex++;
- frame->position = position;
- frame->displayed = kTimeUnknown;
- return FrameResult::Done;
- }
- void VideoTrackObject::fillRequests(not_null<Frame*> frame) const {
- auto i = frame->prepared.begin();
- for (const auto &[instance, request] : _requests) {
- while (i != frame->prepared.end() && i->first < instance) {
- i = frame->prepared.erase(i);
- }
- if (i == frame->prepared.end() || i->first > instance) {
- i = frame->prepared.emplace(instance, request).first;
- }
- ++i;
- }
- while (i != frame->prepared.end()) {
- i = frame->prepared.erase(i);
- }
- }
- QSize VideoTrackObject::chooseOriginalResize(QSize encoded) const {
- auto chosen = QSize();
- if (FFmpeg::RotationSwapWidthHeight(_stream.rotation)) {
- encoded.transpose();
- }
- for (const auto &[_, request] : _requests) {
- const auto resize = request.blurredBackground
- ? CalculateResizeFromOuter(request.outer, encoded)
- : request.resize;
- if (resize.isEmpty()) {
- return QSize();
- }
- const auto byWidth = (resize.width() >= chosen.width());
- const auto byHeight = (resize.height() >= chosen.height());
- if (byWidth && byHeight) {
- chosen = resize;
- } else if (byWidth || byHeight) {
- return QSize();
- }
- }
- return chosen;
- }
- bool VideoTrackObject::requireARGB32() const {
- for (const auto &[_, request] : _requests) {
- if (!request.requireARGB32) {
- return false;
- }
- }
- return true;
- }
- void VideoTrackObject::rasterizeFrame(not_null<Frame*> frame) {
- Expects(frame->position != kFinishedPosition);
- fillRequests(frame);
- frame->format = FrameFormat::None;
- if (frame->decoded->hw_frames_ctx) {
- if (!frame->transferred) {
- frame->transferred = FFmpeg::MakeFramePointer();
- }
- const auto success = TransferFrame(
- _stream,
- frame->decoded.get(),
- frame->transferred.get());
- if (!success) {
- frame->prepared.clear();
- fail(Error::InvalidData);
- return;
- }
- } else {
- frame->transferred = nullptr;
- }
- const auto frameWithData = frame->transferred
- ? frame->transferred.get()
- : frame->decoded.get();
- if ((frameWithData->format == AV_PIX_FMT_YUV420P
- || frameWithData->format == AV_PIX_FMT_NV12) && !requireARGB32()) {
- const auto nv12 = (frameWithData->format == AV_PIX_FMT_NV12);
- frame->alpha = false;
- frame->yuv = ExtractYUV(_stream, frameWithData);
- if (frame->yuv.size.isEmpty()
- || frame->yuv.chromaSize.isEmpty()
- || !frame->yuv.y.data
- || !frame->yuv.u.data
- || (!nv12 && !frame->yuv.v.data)) {
- frame->prepared.clear();
- fail(Error::InvalidData);
- return;
- }
- if (!frame->original.isNull()) {
- frame->original = QImage();
- for (auto &[_, prepared] : frame->prepared) {
- prepared.image = QImage();
- }
- }
- frame->format = nv12 ? FrameFormat::NV12 : FrameFormat::YUV420;
- } else {
- frame->alpha = (frameWithData->format == AV_PIX_FMT_BGRA)
- || (frameWithData->format == AV_PIX_FMT_YUVA420P);
- frame->yuv.size = {
- frameWithData->width,
- frameWithData->height
- };
- frame->original = ConvertFrame(
- _stream,
- frameWithData,
- chooseOriginalResize(
- { frameWithData->width, frameWithData->height }),
- std::move(frame->original));
- if (frame->original.isNull()) {
- frame->prepared.clear();
- fail(Error::InvalidData);
- return;
- }
- frame->format = FrameFormat::ARGB32;
- }
- VideoTrack::PrepareFrameByRequests(
- frame,
- _stream.aspect,
- _stream.rotation);
- Ensures(VideoTrack::IsRasterized(frame));
- }
- void VideoTrackObject::presentFrameIfNeeded() {
- if (_pausedTime != kTimeUnknown || _resumedTime == kTimeUnknown) {
- return;
- }
- const auto dropStaleFrames = !_options.waitForMarkAsShown;
- const auto time = trackTime();
- const auto presented = _shared->presentFrame(
- this,
- time,
- _options.speed,
- dropStaleFrames);
- addTimelineDelay(presented.addedWorldTimeDelay);
- if (presented.displayPosition == kFinishedPosition) {
- interrupt();
- _checkNextFrame = rpl::event_stream<>();
- return;
- } else if (presented.displayPosition != kTimeUnknown) {
- _checkNextFrame.fire({});
- }
- if (presented.nextCheckDelay != kTimeUnknown) {
- Assert(presented.nextCheckDelay >= 0);
- queueReadFrames(presented.nextCheckDelay);
- }
- }
- void VideoTrackObject::pause(crl::time time) {
- Expects(_syncTimePoint.valid());
- if (interrupted()) {
- return;
- } else if (_pausedTime == kTimeUnknown) {
- _pausedTime = time;
- }
- }
- void VideoTrackObject::resume(crl::time time) {
- Expects(_syncTimePoint.trackTime != kTimeUnknown);
- if (interrupted()) {
- return;
- }
- // Resumed time used to validate sync to audio.
- _resumedTime = time;
- if (_pausedTime != kTimeUnknown) {
- Assert(_pausedTime <= time);
- _syncTimePoint.worldTime += (time - _pausedTime);
- _pausedTime = kTimeUnknown;
- } else {
- _syncTimePoint.worldTime = time;
- }
- queueReadFrames();
- Ensures(_syncTimePoint.valid());
- Ensures(_pausedTime == kTimeUnknown);
- }
- void VideoTrackObject::setSpeed(float64 speed) {
- if (interrupted()) {
- return;
- }
- if (_syncTimePoint.valid()) {
- const auto time = trackTime();
- _syncTimePoint = time;
- }
- _options.speed = speed;
- }
- void VideoTrackObject::setWaitForMarkAsShown(bool wait) {
- if (interrupted()) {
- return;
- }
- _options.waitForMarkAsShown = wait;
- }
- bool VideoTrackObject::interrupted() const {
- return !_shared;
- }
- void VideoTrackObject::frameShown() {
- if (interrupted()) {
- return;
- }
- queueReadFrames();
- }
- void VideoTrackObject::addTimelineDelay(crl::time delayed) {
- Expects(_syncTimePoint.valid());
- if (!delayed) {
- return;
- }
- _syncTimePoint.worldTime += delayed;
- }
- void VideoTrackObject::updateFrameRequest(
- const Instance *instance,
- const FrameRequest &request) {
- _requests[instance] = request;
- }
- void VideoTrackObject::removeFrameRequest(const Instance *instance) {
- _requests.remove(instance);
- }
- bool VideoTrackObject::tryReadFirstFrame(FFmpeg::Packet &&packet) {
- if (ProcessPacket(_stream, std::move(packet)).failed()) {
- return false;
- }
- while (true) {
- if (const auto error = ReadNextFrame(_stream)) {
- if (error.code() == AVERROR_EOF) {
- if (!_initialSkippingFrame) {
- return false;
- }
- // Return the last valid frame if we seek too far.
- _stream.decodedFrame = std::move(_initialSkippingFrame);
- return processFirstFrame();
- } else if (error.code() != AVERROR(EAGAIN) || _readTillEnd) {
- return false;
- } else {
- // Waiting for more packets.
- return true;
- }
- } else if (!fillStateFromFrame()) {
- return false;
- } else if (_syncTimePoint.trackTime >= _options.position) {
- return processFirstFrame();
- }
- // Seek was with AVSEEK_FLAG_BACKWARD so first we get old frames.
- // Try skipping frames until one is after the requested position.
- std::swap(_initialSkippingFrame, _stream.decodedFrame);
- if (!_stream.decodedFrame) {
- _stream.decodedFrame = FFmpeg::MakeFramePointer();
- }
- }
- }
- bool VideoTrackObject::processFirstFrame() {
- const auto decodedFrame = _stream.decodedFrame.get();
- if (decodedFrame->width * decodedFrame->height > kMaxFrameArea) {
- return false;
- } else if (decodedFrame->hw_frames_ctx) {
- if (!_stream.transferredFrame) {
- _stream.transferredFrame = FFmpeg::MakeFramePointer();
- }
- const auto success = TransferFrame(
- _stream,
- decodedFrame,
- _stream.transferredFrame.get());
- if (!success) {
- LOG(("Video Error: Failed accelerated decoding from format %1."
- ).arg(int(decodedFrame->format)));
- return false;
- }
- DEBUG_LOG(("Video Info: "
- "Using accelerated decoding from format %1 to format %2."
- ).arg(int(decodedFrame->format)
- ).arg(int(_stream.transferredFrame->format)));
- } else {
- _stream.transferredFrame = nullptr;
- }
- const auto frameWithData = _stream.transferredFrame
- ? _stream.transferredFrame.get()
- : decodedFrame;
- const auto alpha = (frameWithData->format == AV_PIX_FMT_BGRA)
- || (frameWithData->format == AV_PIX_FMT_YUVA420P);
- auto frame = ConvertFrame(
- _stream,
- frameWithData,
- QSize(),
- QImage());
- if (frame.isNull()) {
- return false;
- }
- _shared->init(std::move(frame), alpha, _syncTimePoint.trackTime);
- callReady();
- queueReadFrames();
- return true;
- }
- crl::time VideoTrackObject::currentFramePosition() const {
- const auto position = FramePosition(_stream);
- if (position == kTimeUnknown || position == kFinishedPosition) {
- return kTimeUnknown;
- }
- return _loopingShift + std::clamp(
- position,
- crl::time(0),
- computeDuration() - 1);
- }
- bool VideoTrackObject::fillStateFromFrame() {
- const auto position = currentFramePosition();
- if (position == kTimeUnknown) {
- return false;
- }
- _syncTimePoint.trackTime = position;
- return true;
- }
- void VideoTrackObject::callReady() {
- Expects(_ready != nullptr);
- const auto frame = _shared->frameForPaint();
- ++_frameIndex;
- base::take(_ready)({ VideoInformation{
- .state = {
- .position = _syncTimePoint.trackTime,
- .receivedTill = (_readTillEnd
- ? _stream.duration
- : _syncTimePoint.trackTime),
- .duration = _stream.duration,
- },
- .size = FFmpeg::TransposeSizeByRotation(
- FFmpeg::CorrectByAspect(frame->original.size(), _stream.aspect),
- _stream.rotation),
- .cover = frame->original,
- .rotation = _stream.rotation,
- .alpha = frame->alpha,
- } });
- }
- TimePoint VideoTrackObject::trackTime() const {
- auto result = TimePoint();
- result.worldTime = (_pausedTime != kTimeUnknown)
- ? _pausedTime
- : crl::now();
- if (!_syncTimePoint) {
- result.trackTime = _syncTimePoint.trackTime;
- return result;
- }
- Assert(_resumedTime != kTimeUnknown);
- if (_options.syncVideoByAudio && _audioId.externalPlayId()) {
- const auto mixer = Media::Player::mixer();
- const auto point = mixer->getExternalSyncTimePoint(_audioId);
- if (point && point.worldTime > _resumedTime) {
- _syncTimePoint = point;
- }
- }
- const auto adjust = (result.worldTime - _syncTimePoint.worldTime);
- const auto adjustSpeed = adjust * _options.speed;
- const auto roundAdjustSpeed = base::SafeRound(adjustSpeed);
- const auto timeRoundAdjustSpeed = crl::time(roundAdjustSpeed);
- result.trackTime = _syncTimePoint.trackTime + timeRoundAdjustSpeed;
- return result;
- }
- void VideoTrackObject::interrupt() {
- _shared = nullptr;
- }
- void VideoTrackObject::fail(Error error) {
- interrupt();
- _error(error);
- }
- void VideoTrack::Shared::init(
- QImage &&cover,
- bool hasAlpha,
- crl::time position) {
- Expects(!initialized());
- _frames[0].original = std::move(cover);
- _frames[0].position = position;
- _frames[0].format = FrameFormat::ARGB32;
- _frames[0].alpha = hasAlpha;
- // Usually main thread sets displayed time before _counter increment.
- // But in this case we update _counter, so we set a fake displayed time.
- _frames[0].displayed = kDisplaySkipped;
- _delay = 0;
- _counter.store(0, std::memory_order_release);
- }
- int VideoTrack::Shared::counter() const {
- return _counter.load(std::memory_order_acquire);
- }
- bool VideoTrack::Shared::initialized() const {
- return (counter() != kCounterUninitialized);
- }
- not_null<VideoTrack::Frame*> VideoTrack::Shared::getFrame(int index) {
- Expects(index >= 0 && index < kFramesCount);
- return &_frames[index];
- }
- not_null<const VideoTrack::Frame*> VideoTrack::Shared::getFrame(
- int index) const {
- Expects(index >= 0 && index < kFramesCount);
- return &_frames[index];
- }
- auto VideoTrack::Shared::prepareState(
- crl::time trackTime,
- bool dropStaleFrames)
- -> PrepareState {
- const auto prepareNext = [&](int index) -> PrepareState {
- const auto frame = getFrame(index);
- const auto next = getFrame((index + 1) % kFramesCount);
- if (!IsDecoded(frame)) {
- return frame;
- } else if (!IsDecoded(next)) {
- return next;
- } else if (next->position < frame->position) {
- std::swap(*frame, *next);
- }
- if (next->position == kFinishedPosition || !dropStaleFrames) {
- return PrepareNextCheck(kTimeUnknown);
- } else if (IsStale(frame, trackTime)) {
- std::swap(*frame, *next);
- next->displayed = kDisplaySkipped;
- return next;
- } else {
- if (frame->position - trackTime + 1 <= 0) { // Debugging crash.
- CrashReports::SetAnnotation(
- "DelayValues",
- (QString::number(frame->position)
- + " + 1 <= "
- + QString::number(trackTime)));
- }
- Assert(frame->position >= trackTime);
- Assert(frame->position - trackTime + 1 > 0);
- return PrepareNextCheck(frame->position - trackTime + 1);
- }
- };
- const auto finishPrepare = [&](int index) -> PrepareState {
- // If player already awaits next frame - we ignore if it's stale.
- dropStaleFrames = false;
- const auto result = prepareNext(index);
- return v::is<PrepareNextCheck>(result) ? PrepareState() : result;
- };
- switch (counter()) {
- case 0: return finishPrepare(1);
- case 1: return prepareNext(2);
- case 2: return finishPrepare(2);
- case 3: return prepareNext(3);
- case 4: return finishPrepare(3);
- case 5: return prepareNext(0);
- case 6: return finishPrepare(0);
- case 7: return prepareNext(1);
- }
- Unexpected("Counter value in VideoTrack::Shared::prepareState.");
- }
- // Sometimes main thread subscribes to check frame requests before
- // the first frame is ready and presented and sometimes after.
- bool VideoTrack::Shared::firstPresentHappened() const {
- switch (counter()) {
- case 0: return false;
- case 1: return true;
- }
- Unexpected("Counter value in VideoTrack::Shared::firstPresentHappened.");
- }
- auto VideoTrack::Shared::presentFrame(
- not_null<VideoTrackObject*> object,
- TimePoint time,
- float64 playbackSpeed,
- bool dropStaleFrames)
- -> PresentFrame {
- const auto present = [&](int counter, int index) -> PresentFrame {
- const auto frame = getFrame(index);
- const auto position = frame->position;
- const auto addedWorldTimeDelay = base::take(_delay);
- if (position == kFinishedPosition) {
- return { kFinishedPosition, kTimeUnknown, addedWorldTimeDelay };
- }
- object->rasterizeFrame(frame);
- if (!IsRasterized(frame)) {
- // Error happened during frame prepare.
- return { kTimeUnknown, kTimeUnknown, addedWorldTimeDelay };
- }
- const auto trackLeft = position - time.trackTime;
- const auto adjustedBySpeed = trackLeft / playbackSpeed;
- const auto roundedAdjustedBySpeed = base::SafeRound(adjustedBySpeed);
- frame->display = time.worldTime
- + addedWorldTimeDelay
- + crl::time(roundedAdjustedBySpeed);
- // Release this frame to the main thread for rendering.
- _counter.store(
- counter + 1,
- std::memory_order_release);
- return { position, crl::time(0), addedWorldTimeDelay };
- };
- const auto nextCheckDelay = [&](int index) -> PresentFrame {
- const auto frame = getFrame(index);
- if (frame->position == kFinishedPosition) {
- return { kFinishedPosition, kTimeUnknown };
- }
- const auto next = getFrame((index + 1) % kFramesCount);
- if (!IsDecoded(frame) || !IsDecoded(next)) {
- return { kTimeUnknown, crl::time(0) };
- } else if (next->position == kFinishedPosition
- || !dropStaleFrames
- || IsStale(frame, time.trackTime)) {
- return { kTimeUnknown, kTimeUnknown };
- }
- return { kTimeUnknown, (frame->position - time.trackTime + 1) };
- };
- switch (counter()) {
- case 0: return present(0, 1);
- case 1: return nextCheckDelay(2);
- case 2: return present(2, 2);
- case 3: return nextCheckDelay(3);
- case 4: return present(4, 3);
- case 5: return nextCheckDelay(0);
- case 6: return present(6, 0);
- case 7: return nextCheckDelay(1);
- }
- Unexpected("Counter value in VideoTrack::Shared::prepareState.");
- }
- crl::time VideoTrack::Shared::nextFrameDisplayTime() const {
- const auto frameDisplayTime = [&](int counter) {
- const auto next = (counter + 1) % (2 * kFramesCount);
- const auto index = next / 2;
- const auto frame = getFrame(index);
- if (frame->displayed != kTimeUnknown) {
- // Frame already displayed, but not yet shown.
- return kFrameDisplayTimeAlreadyDone;
- }
- Assert(IsRasterized(frame));
- Assert(frame->display != kTimeUnknown);
- return frame->display;
- };
- switch (counter()) {
- case 0: return kTimeUnknown;
- case 1: return frameDisplayTime(1);
- case 2: return kTimeUnknown;
- case 3: return frameDisplayTime(3);
- case 4: return kTimeUnknown;
- case 5: return frameDisplayTime(5);
- case 6: return kTimeUnknown;
- case 7: return frameDisplayTime(7);
- }
- Unexpected("Counter value in VideoTrack::Shared::nextFrameDisplayTime.");
- }
- crl::time VideoTrack::Shared::markFrameDisplayed(crl::time now) {
- const auto mark = [&](int counter) {
- const auto next = (counter + 1) % (2 * kFramesCount);
- const auto index = next / 2;
- const auto frame = getFrame(index);
- Assert(frame->position != kTimeUnknown);
- if (frame->displayed == kTimeUnknown) {
- frame->displayed = now;
- }
- return frame->position;
- };
- switch (counter()) {
- case 0: Unexpected("Value 0 in VideoTrack::Shared::markFrameDisplayed.");
- case 1: return mark(1);
- case 2: Unexpected("Value 2 in VideoTrack::Shared::markFrameDisplayed.");
- case 3: return mark(3);
- case 4: Unexpected("Value 4 in VideoTrack::Shared::markFrameDisplayed.");
- case 5: return mark(5);
- case 6: Unexpected("Value 6 in VideoTrack::Shared::markFrameDisplayed.");
- case 7: return mark(7);
- }
- Unexpected("Counter value in VideoTrack::Shared::markFrameDisplayed.");
- }
- void VideoTrack::Shared::addTimelineDelay(crl::time delayed) {
- if (!delayed) {
- return;
- }
- const auto recountCurrentFrame = [&](int counter) {
- _delay += delayed;
- //const auto next = (counter + 1) % (2 * kFramesCount);
- //const auto index = next / 2;
- //const auto frame = getFrame(index);
- //if (frame->displayed != kTimeUnknown) {
- // // Frame already displayed.
- // return;
- //}
- //Assert(IsRasterized(frame));
- //Assert(frame->display != kTimeUnknown);
- //frame->display = countFrameDisplayTime(frame->index);
- };
- switch (counter()) {
- case 0: Unexpected("Value 0 in VideoTrack::Shared::addTimelineDelay.");
- case 1: return recountCurrentFrame(1);
- case 2: Unexpected("Value 2 in VideoTrack::Shared::addTimelineDelay.");
- case 3: return recountCurrentFrame(3);
- case 4: Unexpected("Value 4 in VideoTrack::Shared::addTimelineDelay.");
- case 5: return recountCurrentFrame(5);
- case 6: Unexpected("Value 6 in VideoTrack::Shared::addTimelineDelay.");
- case 7: return recountCurrentFrame(7);
- }
- Unexpected("Counter value in VideoTrack::Shared::addTimelineDelay.");
- }
- bool VideoTrack::Shared::markFrameShown() {
- const auto jump = [&](int counter) {
- const auto next = (counter + 1) % (2 * kFramesCount);
- const auto index = next / 2;
- const auto frame = getFrame(index);
- if (frame->displayed == kTimeUnknown) {
- return false;
- }
- _counter.store(
- next,
- std::memory_order_release);
- return true;
- };
- switch (counter()) {
- case 0: return false;
- case 1: return jump(1);
- case 2: return false;
- case 3: return jump(3);
- case 4: return false;
- case 5: return jump(5);
- case 6: return false;
- case 7: return jump(7);
- }
- Unexpected("Counter value in VideoTrack::Shared::markFrameShown.");
- }
- not_null<VideoTrack::Frame*> VideoTrack::Shared::frameForPaint() {
- return frameForPaintWithIndex().frame;
- }
- VideoTrack::FrameWithIndex VideoTrack::Shared::frameForPaintWithIndex() {
- const auto index = counter() / 2;
- const auto frame = getFrame(index);
- Assert(frame->format != FrameFormat::None);
- Assert(frame->position != kTimeUnknown);
- Assert(frame->displayed != kTimeUnknown);
- return {
- .frame = frame,
- .index = frame->index,
- };
- }
- VideoTrack::VideoTrack(
- const PlaybackOptions &options,
- Stream &&stream,
- const AudioMsgId &audioId,
- FnMut<void(const Information &)> ready,
- Fn<void(Error)> error)
- : _streamIndex(stream.index)
- , _streamTimeBase(stream.timeBase)
- , _streamDuration(stream.duration)
- , _streamRotation(stream.rotation)
- , _streamAspect(stream.aspect)
- , _shared(std::make_unique<Shared>())
- , _wrapped(
- options,
- _shared.get(),
- std::move(stream),
- audioId,
- std::move(ready),
- std::move(error)) {
- }
- int VideoTrack::streamIndex() const {
- return _streamIndex;
- }
- AVRational VideoTrack::streamTimeBase() const {
- return _streamTimeBase;
- }
- crl::time VideoTrack::streamDuration() const {
- return _streamDuration;
- }
- void VideoTrack::process(std::vector<FFmpeg::Packet> &&packets) {
- _wrapped.with([
- packets = std::move(packets)
- ](Implementation &unwrapped) mutable {
- unwrapped.process(std::move(packets));
- });
- }
- void VideoTrack::waitForData() {
- }
- void VideoTrack::pause(crl::time time) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.pause(time);
- });
- }
- void VideoTrack::resume(crl::time time) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.resume(time);
- });
- }
- void VideoTrack::setSpeed(float64 speed) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.setSpeed(speed);
- });
- }
- void VideoTrack::setWaitForMarkAsShown(bool wait) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.setWaitForMarkAsShown(wait);
- });
- }
- crl::time VideoTrack::nextFrameDisplayTime() const {
- return _shared->nextFrameDisplayTime();
- }
- crl::time VideoTrack::markFrameDisplayed(crl::time now) {
- const auto result = _shared->markFrameDisplayed(now);
- Ensures(result != kTimeUnknown);
- return result;
- }
- void VideoTrack::addTimelineDelay(crl::time delayed) {
- _shared->addTimelineDelay(delayed);
- //if (!delayed) {
- // return;
- //}
- //_wrapped.with([=](Implementation &unwrapped) mutable {
- // unwrapped.addTimelineDelay(delayed);
- //});
- }
- bool VideoTrack::markFrameShown() {
- if (!_shared->markFrameShown()) {
- return false;
- }
- _wrapped.with([](Implementation &unwrapped) {
- unwrapped.frameShown();
- });
- return true;
- }
- QImage VideoTrack::frame(
- const FrameRequest &request,
- const Instance *instance) {
- return frameImage(_shared->frameForPaint(), request, instance);
- }
- FrameWithInfo VideoTrack::frameWithInfo(
- const FrameRequest &request,
- const Instance *instance) {
- const auto data = _shared->frameForPaintWithIndex();
- return {
- .image = frameImage(data.frame, request, instance),
- .format = FrameFormat::ARGB32,
- .index = data.index,
- };
- }
- FrameWithInfo VideoTrack::frameWithInfo(const Instance *instance) {
- const auto data = _shared->frameForPaintWithIndex();
- const auto i = data.frame->prepared.find(instance);
- const auto none = (i == data.frame->prepared.end());
- if (none || i->second.request.requireARGB32) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.updateFrameRequest(
- instance,
- { .requireARGB32 = false });
- });
- }
- return {
- .image = data.frame->original,
- .yuv = &data.frame->yuv,
- .format = data.frame->format,
- .index = data.index,
- .alpha = data.frame->alpha,
- };
- }
- QImage VideoTrack::frameImage(
- not_null<Frame*> frame,
- const FrameRequest &request,
- const Instance *instance) {
- const auto i = frame->prepared.find(instance);
- const auto none = (i == frame->prepared.end());
- const auto preparedFor = frame->prepared.empty()
- ? FrameRequest::NonStrict()
- : (none ? frame->prepared.begin() : i)->second.request;
- const auto changed = !preparedFor.goodFor(request);
- const auto useRequest = changed ? request : preparedFor;
- if (changed) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.updateFrameRequest(instance, useRequest);
- });
- }
- if (frame->original.isNull()
- && (frame->format == FrameFormat::YUV420
- || frame->format == FrameFormat::NV12)) {
- frame->original = ConvertToARGB32(frame->format, frame->yuv);
- }
- if (GoodForRequest(
- frame->original,
- frame->alpha,
- _streamRotation,
- useRequest)) {
- return frame->original;
- } else if (changed || none || i->second.image.isNull()) {
- const auto j = none
- ? frame->prepared.emplace(instance, useRequest).first
- : i;
- if (changed && !none) {
- i->second.request = useRequest;
- }
- if (frame->prepared.size() > 1) {
- for (auto &[alreadyInstance, prepared] : frame->prepared) {
- if (alreadyInstance != instance
- && prepared.request == useRequest
- && !prepared.image.isNull()) {
- return prepared.image;
- }
- }
- }
- j->second.image = PrepareByRequest(
- frame->original,
- frame->alpha,
- _streamAspect,
- _streamRotation,
- useRequest,
- std::move(j->second.image));
- return j->second.image;
- }
- return i->second.image;
- }
- QImage VideoTrack::currentFrameImage() {
- const auto frame = _shared->frameForPaint();
- if (frame->original.isNull()
- && (frame->format == FrameFormat::YUV420
- || frame->format == FrameFormat::NV12)) {
- frame->original = ConvertToARGB32(frame->format, frame->yuv);
- }
- return frame->original;
- }
- void VideoTrack::unregisterInstance(not_null<const Instance*> instance) {
- _wrapped.with([=](Implementation &unwrapped) {
- unwrapped.removeFrameRequest(instance);
- });
- }
- void VideoTrack::PrepareFrameByRequests(
- not_null<Frame*> frame,
- const AVRational &aspect,
- int rotation) {
- Expects(frame->format != FrameFormat::ARGB32
- || !frame->original.isNull());
- if (frame->format != FrameFormat::ARGB32) {
- return;
- }
- const auto begin = frame->prepared.begin();
- const auto end = frame->prepared.end();
- for (auto i = begin; i != end; ++i) {
- auto &prepared = i->second;
- if (!GoodForRequest(
- frame->original,
- frame->alpha,
- rotation,
- prepared.request)) {
- auto j = begin;
- for (; j != i; ++j) {
- if (j->second.request == prepared.request) {
- prepared.image = QImage();
- break;
- }
- }
- if (j == i) {
- prepared.image = PrepareByRequest(
- frame->original,
- frame->alpha,
- aspect,
- rotation,
- prepared.request,
- std::move(prepared.image));
- }
- }
- }
- }
- bool VideoTrack::IsDecoded(not_null<const Frame*> frame) {
- return (frame->position != kTimeUnknown)
- && (frame->displayed == kTimeUnknown);
- }
- bool VideoTrack::IsRasterized(not_null<const Frame*> frame) {
- return IsDecoded(frame)
- && (!frame->original.isNull()
- || frame->format == FrameFormat::YUV420
- || frame->format == FrameFormat::NV12);
- }
- bool VideoTrack::IsStale(not_null<const Frame*> frame, crl::time trackTime) {
- Expects(IsDecoded(frame));
- return (frame->position < trackTime);
- }
- rpl::producer<> VideoTrack::checkNextFrame() const {
- return _wrapped.producer_on_main([](const Implementation &unwrapped) {
- return unwrapped.checkNextFrame();
- });
- }
- rpl::producer<> VideoTrack::waitingForData() const {
- return _wrapped.producer_on_main([](const Implementation &unwrapped) {
- return unwrapped.waitingForData();
- });
- }
- VideoTrack::~VideoTrack() {
- _wrapped.with([shared = std::move(_shared)](Implementation &unwrapped) {
- unwrapped.interrupt();
- });
- }
- } // namespace Streaming
- } // namespace Media
|