mirror of https://github.com/procxx/kepka.git
Prepare frames for all instances with requests.
This commit is contained in:
parent
dbdd9aa481
commit
8211c94a74
|
@ -127,19 +127,23 @@ struct FrameRequest {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const {
|
[[nodiscard]] bool empty() const {
|
||||||
return resize.isEmpty();
|
return resize.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const FrameRequest &other) const {
|
[[nodiscard]] bool operator==(const FrameRequest &other) const {
|
||||||
return (resize == other.resize)
|
return (resize == other.resize)
|
||||||
&& (outer == other.outer)
|
&& (outer == other.outer)
|
||||||
&& (radius == other.radius)
|
&& (radius == other.radius)
|
||||||
&& (corners == other.corners);
|
&& (corners == other.corners);
|
||||||
}
|
}
|
||||||
bool operator!=(const FrameRequest &other) const {
|
[[nodiscard]] bool operator!=(const FrameRequest &other) const {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool goodFor(const FrameRequest &other) const {
|
||||||
|
return (*this == other) || (strict && !other.strict);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Streaming
|
} // namespace Streaming
|
||||||
|
|
|
@ -84,6 +84,7 @@ void Document::registerInstance(not_null<Instance*> instance) {
|
||||||
|
|
||||||
void Document::unregisterInstance(not_null<Instance*> instance) {
|
void Document::unregisterInstance(not_null<Instance*> instance) {
|
||||||
_instances.remove(instance);
|
_instances.remove(instance);
|
||||||
|
_player.unregisterInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Document::waitingShown() const {
|
bool Document::waitingShown() const {
|
||||||
|
|
|
@ -137,7 +137,7 @@ void Instance::callWaitingCallback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage Instance::frame(const FrameRequest &request) const {
|
QImage Instance::frame(const FrameRequest &request) const {
|
||||||
return player().frame(request);
|
return player().frame(request, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instance::markFrameShown() {
|
bool Instance::markFrameShown() {
|
||||||
|
|
|
@ -809,10 +809,18 @@ QSize Player::videoSize() const {
|
||||||
return _information.video.size;
|
return _information.video.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage Player::frame(const FrameRequest &request) const {
|
QImage Player::frame(
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance) const {
|
||||||
Expects(_video != nullptr);
|
Expects(_video != nullptr);
|
||||||
|
|
||||||
return _video->frame(request);
|
return _video->frame(request, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::unregisterInstance(not_null<const Instance*> instance) {
|
||||||
|
if (_video) {
|
||||||
|
_video->unregisterInstance(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Media::Player::TrackState Player::prepareLegacyState() const {
|
Media::Player::TrackState Player::prepareLegacyState() const {
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Reader;
|
||||||
class File;
|
class File;
|
||||||
class AudioTrack;
|
class AudioTrack;
|
||||||
class VideoTrack;
|
class VideoTrack;
|
||||||
|
class Instance;
|
||||||
|
|
||||||
class Player final : private FileDelegate {
|
class Player final : private FileDelegate {
|
||||||
public:
|
public:
|
||||||
|
@ -60,7 +61,10 @@ public:
|
||||||
[[nodiscard]] rpl::producer<bool> fullInCache() const;
|
[[nodiscard]] rpl::producer<bool> fullInCache() const;
|
||||||
|
|
||||||
[[nodiscard]] QSize videoSize() const;
|
[[nodiscard]] QSize videoSize() const;
|
||||||
[[nodiscard]] QImage frame(const FrameRequest &request) const;
|
[[nodiscard]] QImage frame(
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance = nullptr) const;
|
||||||
|
void unregisterInstance(not_null<const Instance*> instance);
|
||||||
bool markFrameShown();
|
bool markFrameShown();
|
||||||
|
|
||||||
[[nodiscard]] Media::Player::TrackState prepareLegacyState() const;
|
[[nodiscard]] Media::Player::TrackState prepareLegacyState() const;
|
||||||
|
|
|
@ -45,7 +45,10 @@ public:
|
||||||
void interrupt();
|
void interrupt();
|
||||||
void frameShown();
|
void frameShown();
|
||||||
void addTimelineDelay(crl::time delayed);
|
void addTimelineDelay(crl::time delayed);
|
||||||
void updateFrameRequest(const FrameRequest &request);
|
void updateFrameRequest(
|
||||||
|
const Instance *instance,
|
||||||
|
const FrameRequest &request);
|
||||||
|
void removeFrameRequest(const Instance *instance);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class FrameResult {
|
enum class FrameResult {
|
||||||
|
@ -68,6 +71,8 @@ private:
|
||||||
void readFrames();
|
void readFrames();
|
||||||
[[nodiscard]] ReadEnoughState readEnoughFrames(crl::time trackTime);
|
[[nodiscard]] ReadEnoughState readEnoughFrames(crl::time trackTime);
|
||||||
[[nodiscard]] FrameResult readFrame(not_null<Frame*> frame);
|
[[nodiscard]] FrameResult readFrame(not_null<Frame*> frame);
|
||||||
|
void fillRequests(not_null<Frame*> frame) const;
|
||||||
|
[[nodiscard]] QSize chooseOriginalResize() const;
|
||||||
void presentFrameIfNeeded();
|
void presentFrameIfNeeded();
|
||||||
void callReady();
|
void callReady();
|
||||||
[[nodiscard]] bool loopAround();
|
[[nodiscard]] bool loopAround();
|
||||||
|
@ -98,7 +103,7 @@ private:
|
||||||
crl::time _loopingShift = 0;
|
crl::time _loopingShift = 0;
|
||||||
rpl::event_stream<> _checkNextFrame;
|
rpl::event_stream<> _checkNextFrame;
|
||||||
rpl::event_stream<> _waitingForData;
|
rpl::event_stream<> _waitingForData;
|
||||||
FrameRequest _request = FrameRequest::NonStrict();
|
base::flat_map<const Instance*, FrameRequest> _requests;
|
||||||
|
|
||||||
bool _queued = false;
|
bool _queued = false;
|
||||||
base::ConcurrentTimer _readFramesTimer;
|
base::ConcurrentTimer _readFramesTimer;
|
||||||
|
@ -297,6 +302,36 @@ auto VideoTrackObject::readFrame(not_null<Frame*> frame) -> FrameResult {
|
||||||
return FrameResult::Done;
|
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() const {
|
||||||
|
auto chosen = QSize();
|
||||||
|
for (const auto &[_, request] : _requests) {
|
||||||
|
const auto byWidth = (request.resize.width() >= chosen.width());
|
||||||
|
const auto byHeight = (request.resize.height() >= chosen.height());
|
||||||
|
if (byWidth && byHeight) {
|
||||||
|
chosen = request.resize;
|
||||||
|
} else if (byWidth || byHeight) {
|
||||||
|
return QSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chosen;
|
||||||
|
}
|
||||||
|
|
||||||
void VideoTrackObject::presentFrameIfNeeded() {
|
void VideoTrackObject::presentFrameIfNeeded() {
|
||||||
if (_pausedTime != kTimeUnknown || _resumedTime == kTimeUnknown) {
|
if (_pausedTime != kTimeUnknown || _resumedTime == kTimeUnknown) {
|
||||||
return;
|
return;
|
||||||
|
@ -304,19 +339,19 @@ void VideoTrackObject::presentFrameIfNeeded() {
|
||||||
const auto rasterize = [&](not_null<Frame*> frame) {
|
const auto rasterize = [&](not_null<Frame*> frame) {
|
||||||
Expects(frame->position != kFinishedPosition);
|
Expects(frame->position != kFinishedPosition);
|
||||||
|
|
||||||
frame->request = _request;
|
fillRequests(frame);
|
||||||
frame->original = ConvertFrame(
|
frame->original = ConvertFrame(
|
||||||
_stream,
|
_stream,
|
||||||
frame->decoded.get(),
|
frame->decoded.get(),
|
||||||
frame->request.resize,
|
chooseOriginalResize(),
|
||||||
std::move(frame->original));
|
std::move(frame->original));
|
||||||
if (frame->original.isNull()) {
|
if (frame->original.isNull()) {
|
||||||
frame->prepared = QImage();
|
frame->prepared.clear();
|
||||||
fail(Error::InvalidData);
|
fail(Error::InvalidData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoTrack::PrepareFrameByRequest(frame);
|
VideoTrack::PrepareFrameByRequests(frame);
|
||||||
|
|
||||||
Ensures(VideoTrack::IsRasterized(frame));
|
Ensures(VideoTrack::IsRasterized(frame));
|
||||||
};
|
};
|
||||||
|
@ -405,8 +440,14 @@ void VideoTrackObject::addTimelineDelay(crl::time delayed) {
|
||||||
_syncTimePoint.worldTime += delayed;
|
_syncTimePoint.worldTime += delayed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoTrackObject::updateFrameRequest(const FrameRequest &request) {
|
void VideoTrackObject::updateFrameRequest(
|
||||||
_request = request;
|
const Instance *instance,
|
||||||
|
const FrameRequest &request) {
|
||||||
|
_requests.emplace(instance, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoTrackObject::removeFrameRequest(const Instance *instance) {
|
||||||
|
_requests.remove(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoTrackObject::tryReadFirstFrame(FFmpeg::Packet &&packet) {
|
bool VideoTrackObject::tryReadFirstFrame(FFmpeg::Packet &&packet) {
|
||||||
|
@ -898,33 +939,75 @@ bool VideoTrack::markFrameShown() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage VideoTrack::frame(const FrameRequest &request) {
|
QImage VideoTrack::frame(
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance) {
|
||||||
const auto frame = _shared->frameForPaint();
|
const auto frame = _shared->frameForPaint();
|
||||||
const auto changed = (frame->request != request)
|
const auto i = frame->prepared.find(instance);
|
||||||
&& (request.strict || !frame->request.strict);
|
const auto none = (i == frame->prepared.end());
|
||||||
|
const auto preparedFor = none
|
||||||
|
? FrameRequest::NonStrict()
|
||||||
|
: i->second.request;
|
||||||
|
const auto changed = !preparedFor.goodFor(request);
|
||||||
|
const auto useRequest = changed ? request : preparedFor;
|
||||||
if (changed) {
|
if (changed) {
|
||||||
frame->request = request;
|
|
||||||
_wrapped.with([=](Implementation &unwrapped) {
|
_wrapped.with([=](Implementation &unwrapped) {
|
||||||
unwrapped.updateFrameRequest(request);
|
unwrapped.updateFrameRequest(instance, request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return PrepareFrameByRequest(frame, !changed);
|
if (GoodForRequest(frame->original, useRequest)) {
|
||||||
|
return frame->original;
|
||||||
|
} else if (changed || none || i->second.image.isNull()) {
|
||||||
|
const auto j = none
|
||||||
|
? frame->prepared.emplace(instance, useRequest).first
|
||||||
|
: i;
|
||||||
|
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,
|
||||||
|
useRequest,
|
||||||
|
std::move(j->second.image));
|
||||||
|
return j->second.image;
|
||||||
|
}
|
||||||
|
return i->second.image;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage VideoTrack::PrepareFrameByRequest(
|
void VideoTrack::unregisterInstance(not_null<const Instance*> instance) {
|
||||||
not_null<Frame*> frame,
|
_wrapped.with([=](Implementation &unwrapped) {
|
||||||
bool useExistingPrepared) {
|
unwrapped.removeFrameRequest(instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoTrack::PrepareFrameByRequests(not_null<Frame*> frame) {
|
||||||
Expects(!frame->original.isNull());
|
Expects(!frame->original.isNull());
|
||||||
|
|
||||||
if (GoodForRequest(frame->original, frame->request)) {
|
const auto begin = frame->prepared.begin();
|
||||||
return frame->original;
|
const auto end = frame->prepared.end();
|
||||||
} else if (frame->prepared.isNull() || !useExistingPrepared) {
|
for (auto i = begin; i != end; ++i) {
|
||||||
frame->prepared = PrepareByRequest(
|
auto &prepared = i->second;
|
||||||
frame->original,
|
if (!GoodForRequest(frame->original, prepared.request)) {
|
||||||
frame->request,
|
auto j = begin;
|
||||||
std::move(frame->prepared));
|
for (; j != i; ++j) {
|
||||||
|
if (j->second.request == prepared.request) {
|
||||||
|
prepared.image = QImage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (j == i) {
|
||||||
|
prepared.image = PrepareByRequest(
|
||||||
|
frame->original,
|
||||||
|
prepared.request,
|
||||||
|
std::move(prepared.image));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return frame->prepared;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoTrack::IsDecoded(not_null<const Frame*> frame) {
|
bool VideoTrack::IsDecoded(not_null<const Frame*> frame) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ constexpr auto kFrameDisplayTimeAlreadyDone
|
||||||
= std::numeric_limits<crl::time>::max();
|
= std::numeric_limits<crl::time>::max();
|
||||||
|
|
||||||
class VideoTrackObject;
|
class VideoTrackObject;
|
||||||
|
class Instance;
|
||||||
|
|
||||||
class VideoTrack final {
|
class VideoTrack final {
|
||||||
public:
|
public:
|
||||||
|
@ -53,7 +54,10 @@ public:
|
||||||
void addTimelineDelay(crl::time delayed);
|
void addTimelineDelay(crl::time delayed);
|
||||||
bool markFrameShown();
|
bool markFrameShown();
|
||||||
[[nodiscard]] crl::time nextFrameDisplayTime() const;
|
[[nodiscard]] crl::time nextFrameDisplayTime() const;
|
||||||
[[nodiscard]] QImage frame(const FrameRequest &request);
|
[[nodiscard]] QImage frame(
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance);
|
||||||
|
void unregisterInstance(not_null<const Instance*> instance);
|
||||||
[[nodiscard]] rpl::producer<> checkNextFrame() const;
|
[[nodiscard]] rpl::producer<> checkNextFrame() const;
|
||||||
[[nodiscard]] rpl::producer<> waitingForData() const;
|
[[nodiscard]] rpl::producer<> waitingForData() const;
|
||||||
|
|
||||||
|
@ -63,6 +67,13 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class VideoTrackObject;
|
friend class VideoTrackObject;
|
||||||
|
|
||||||
|
struct Prepared {
|
||||||
|
Prepared(const FrameRequest &request) : request(request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameRequest request = FrameRequest::NonStrict();
|
||||||
|
QImage image;
|
||||||
|
};
|
||||||
struct Frame {
|
struct Frame {
|
||||||
FFmpeg::FramePointer decoded = FFmpeg::MakeFramePointer();
|
FFmpeg::FramePointer decoded = FFmpeg::MakeFramePointer();
|
||||||
QImage original;
|
QImage original;
|
||||||
|
@ -70,8 +81,7 @@ private:
|
||||||
crl::time displayed = kTimeUnknown;
|
crl::time displayed = kTimeUnknown;
|
||||||
crl::time display = kTimeUnknown;
|
crl::time display = kTimeUnknown;
|
||||||
|
|
||||||
FrameRequest request = FrameRequest::NonStrict();
|
base::flat_map<const Instance*, Prepared> prepared;
|
||||||
QImage prepared;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Shared {
|
class Shared {
|
||||||
|
@ -129,9 +139,7 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static QImage PrepareFrameByRequest(
|
static void PrepareFrameByRequests(not_null<Frame*> frame);
|
||||||
not_null<Frame*> frame,
|
|
||||||
bool useExistingPrepared = false);
|
|
||||||
[[nodiscard]] static bool IsDecoded(not_null<const Frame*> frame);
|
[[nodiscard]] static bool IsDecoded(not_null<const Frame*> frame);
|
||||||
[[nodiscard]] static bool IsRasterized(not_null<const Frame*> frame);
|
[[nodiscard]] static bool IsRasterized(not_null<const Frame*> frame);
|
||||||
[[nodiscard]] static bool IsStale(
|
[[nodiscard]] static bool IsStale(
|
||||||
|
|
|
@ -377,7 +377,7 @@ QImage OverlayWidget::videoFrame() const {
|
||||||
// ? ImageRoundRadius::Ellipse
|
// ? ImageRoundRadius::Ellipse
|
||||||
// : ImageRoundRadius::None;
|
// : ImageRoundRadius::None;
|
||||||
return _streamed->instance.player().ready()
|
return _streamed->instance.player().ready()
|
||||||
? _streamed->instance.player().frame(request)
|
? _streamed->instance.frame(request)
|
||||||
: _streamed->instance.info().video.cover;
|
: _streamed->instance.info().video.cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue