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;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
[[nodiscard]] bool empty() const {
|
||||
return resize.isEmpty();
|
||||
}
|
||||
|
||||
bool operator==(const FrameRequest &other) const {
|
||||
[[nodiscard]] bool operator==(const FrameRequest &other) const {
|
||||
return (resize == other.resize)
|
||||
&& (outer == other.outer)
|
||||
&& (radius == other.radius)
|
||||
&& (corners == other.corners);
|
||||
}
|
||||
bool operator!=(const FrameRequest &other) const {
|
||||
[[nodiscard]] bool operator!=(const FrameRequest &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool goodFor(const FrameRequest &other) const {
|
||||
return (*this == other) || (strict && !other.strict);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Streaming
|
||||
|
|
|
@ -84,6 +84,7 @@ void Document::registerInstance(not_null<Instance*> instance) {
|
|||
|
||||
void Document::unregisterInstance(not_null<Instance*> instance) {
|
||||
_instances.remove(instance);
|
||||
_player.unregisterInstance(instance);
|
||||
}
|
||||
|
||||
bool Document::waitingShown() const {
|
||||
|
|
|
@ -137,7 +137,7 @@ void Instance::callWaitingCallback() {
|
|||
}
|
||||
|
||||
QImage Instance::frame(const FrameRequest &request) const {
|
||||
return player().frame(request);
|
||||
return player().frame(request, this);
|
||||
}
|
||||
|
||||
bool Instance::markFrameShown() {
|
||||
|
|
|
@ -809,10 +809,18 @@ QSize Player::videoSize() const {
|
|||
return _information.video.size;
|
||||
}
|
||||
|
||||
QImage Player::frame(const FrameRequest &request) const {
|
||||
QImage Player::frame(
|
||||
const FrameRequest &request,
|
||||
const Instance *instance) const {
|
||||
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 {
|
||||
|
|
|
@ -29,6 +29,7 @@ class Reader;
|
|||
class File;
|
||||
class AudioTrack;
|
||||
class VideoTrack;
|
||||
class Instance;
|
||||
|
||||
class Player final : private FileDelegate {
|
||||
public:
|
||||
|
@ -60,7 +61,10 @@ public:
|
|||
[[nodiscard]] rpl::producer<bool> fullInCache() 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();
|
||||
|
||||
[[nodiscard]] Media::Player::TrackState prepareLegacyState() const;
|
||||
|
|
|
@ -45,7 +45,10 @@ public:
|
|||
void interrupt();
|
||||
void frameShown();
|
||||
void addTimelineDelay(crl::time delayed);
|
||||
void updateFrameRequest(const FrameRequest &request);
|
||||
void updateFrameRequest(
|
||||
const Instance *instance,
|
||||
const FrameRequest &request);
|
||||
void removeFrameRequest(const Instance *instance);
|
||||
|
||||
private:
|
||||
enum class FrameResult {
|
||||
|
@ -68,6 +71,8 @@ private:
|
|||
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() const;
|
||||
void presentFrameIfNeeded();
|
||||
void callReady();
|
||||
[[nodiscard]] bool loopAround();
|
||||
|
@ -98,7 +103,7 @@ private:
|
|||
crl::time _loopingShift = 0;
|
||||
rpl::event_stream<> _checkNextFrame;
|
||||
rpl::event_stream<> _waitingForData;
|
||||
FrameRequest _request = FrameRequest::NonStrict();
|
||||
base::flat_map<const Instance*, FrameRequest> _requests;
|
||||
|
||||
bool _queued = false;
|
||||
base::ConcurrentTimer _readFramesTimer;
|
||||
|
@ -297,6 +302,36 @@ auto VideoTrackObject::readFrame(not_null<Frame*> frame) -> FrameResult {
|
|||
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() {
|
||||
if (_pausedTime != kTimeUnknown || _resumedTime == kTimeUnknown) {
|
||||
return;
|
||||
|
@ -304,19 +339,19 @@ void VideoTrackObject::presentFrameIfNeeded() {
|
|||
const auto rasterize = [&](not_null<Frame*> frame) {
|
||||
Expects(frame->position != kFinishedPosition);
|
||||
|
||||
frame->request = _request;
|
||||
fillRequests(frame);
|
||||
frame->original = ConvertFrame(
|
||||
_stream,
|
||||
frame->decoded.get(),
|
||||
frame->request.resize,
|
||||
chooseOriginalResize(),
|
||||
std::move(frame->original));
|
||||
if (frame->original.isNull()) {
|
||||
frame->prepared = QImage();
|
||||
frame->prepared.clear();
|
||||
fail(Error::InvalidData);
|
||||
return;
|
||||
}
|
||||
|
||||
VideoTrack::PrepareFrameByRequest(frame);
|
||||
VideoTrack::PrepareFrameByRequests(frame);
|
||||
|
||||
Ensures(VideoTrack::IsRasterized(frame));
|
||||
};
|
||||
|
@ -405,8 +440,14 @@ void VideoTrackObject::addTimelineDelay(crl::time delayed) {
|
|||
_syncTimePoint.worldTime += delayed;
|
||||
}
|
||||
|
||||
void VideoTrackObject::updateFrameRequest(const FrameRequest &request) {
|
||||
_request = request;
|
||||
void VideoTrackObject::updateFrameRequest(
|
||||
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) {
|
||||
|
@ -898,33 +939,75 @@ bool VideoTrack::markFrameShown() {
|
|||
return true;
|
||||
}
|
||||
|
||||
QImage VideoTrack::frame(const FrameRequest &request) {
|
||||
QImage VideoTrack::frame(
|
||||
const FrameRequest &request,
|
||||
const Instance *instance) {
|
||||
const auto frame = _shared->frameForPaint();
|
||||
const auto changed = (frame->request != request)
|
||||
&& (request.strict || !frame->request.strict);
|
||||
const auto i = frame->prepared.find(instance);
|
||||
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) {
|
||||
frame->request = request;
|
||||
_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(
|
||||
not_null<Frame*> frame,
|
||||
bool useExistingPrepared) {
|
||||
void VideoTrack::unregisterInstance(not_null<const Instance*> instance) {
|
||||
_wrapped.with([=](Implementation &unwrapped) {
|
||||
unwrapped.removeFrameRequest(instance);
|
||||
});
|
||||
}
|
||||
|
||||
void VideoTrack::PrepareFrameByRequests(not_null<Frame*> frame) {
|
||||
Expects(!frame->original.isNull());
|
||||
|
||||
if (GoodForRequest(frame->original, frame->request)) {
|
||||
return frame->original;
|
||||
} else if (frame->prepared.isNull() || !useExistingPrepared) {
|
||||
frame->prepared = PrepareByRequest(
|
||||
frame->original,
|
||||
frame->request,
|
||||
std::move(frame->prepared));
|
||||
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, 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,
|
||||
prepared.request,
|
||||
std::move(prepared.image));
|
||||
}
|
||||
}
|
||||
}
|
||||
return frame->prepared;
|
||||
}
|
||||
|
||||
bool VideoTrack::IsDecoded(not_null<const Frame*> frame) {
|
||||
|
|
|
@ -18,6 +18,7 @@ constexpr auto kFrameDisplayTimeAlreadyDone
|
|||
= std::numeric_limits<crl::time>::max();
|
||||
|
||||
class VideoTrackObject;
|
||||
class Instance;
|
||||
|
||||
class VideoTrack final {
|
||||
public:
|
||||
|
@ -53,7 +54,10 @@ public:
|
|||
void addTimelineDelay(crl::time delayed);
|
||||
bool markFrameShown();
|
||||
[[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<> waitingForData() const;
|
||||
|
||||
|
@ -63,6 +67,13 @@ public:
|
|||
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();
|
||||
QImage original;
|
||||
|
@ -70,8 +81,7 @@ private:
|
|||
crl::time displayed = kTimeUnknown;
|
||||
crl::time display = kTimeUnknown;
|
||||
|
||||
FrameRequest request = FrameRequest::NonStrict();
|
||||
QImage prepared;
|
||||
base::flat_map<const Instance*, Prepared> prepared;
|
||||
};
|
||||
|
||||
class Shared {
|
||||
|
@ -129,9 +139,7 @@ private:
|
|||
|
||||
};
|
||||
|
||||
static QImage PrepareFrameByRequest(
|
||||
not_null<Frame*> frame,
|
||||
bool useExistingPrepared = false);
|
||||
static void PrepareFrameByRequests(not_null<Frame*> frame);
|
||||
[[nodiscard]] static bool IsDecoded(not_null<const Frame*> frame);
|
||||
[[nodiscard]] static bool IsRasterized(not_null<const Frame*> frame);
|
||||
[[nodiscard]] static bool IsStale(
|
||||
|
|
|
@ -377,7 +377,7 @@ QImage OverlayWidget::videoFrame() const {
|
|||
// ? ImageRoundRadius::Ellipse
|
||||
// : ImageRoundRadius::None;
|
||||
return _streamed->instance.player().ready()
|
||||
? _streamed->instance.player().frame(request)
|
||||
? _streamed->instance.frame(request)
|
||||
: _streamed->instance.info().video.cover;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue