Added frame-by-frame stepping hotkeys to media view on paused video.

This commit is contained in:
23rd
2026-04-12 20:15:50 +03:00
parent 37d870fdd9
commit 418233fa2e
7 changed files with 41 additions and 0 deletions
@@ -61,6 +61,7 @@ struct VideoInformation {
QSize size;
QImage cover;
int rotation = 0;
float64 fps = 0.;
bool alpha = false;
};
@@ -179,6 +179,10 @@ Stream File::Context::initStream(
result.rotation = FFmpeg::ReadRotationFromMetadata(info);
result.aspect = FFmpeg::ValidateAspectRatio(
info->sample_aspect_ratio);
const auto guessed = av_guess_frame_rate(format, info, nullptr);
if (guessed.num > 0 && guessed.den > 0) {
result.fps = av_q2d(guessed);
}
} else if (type == AVMEDIA_TYPE_AUDIO) {
result.frequency = info->codecpar->sample_rate;
if (!result.frequency) {
@@ -64,6 +64,7 @@ void SaveValidVideoInformation(
to.size = from.size;
to.cover = std::move(from.cover);
to.rotation = from.rotation;
to.fps = from.fps;
to.alpha = from.alpha;
}
@@ -41,6 +41,7 @@ struct Stream {
// Video only.
int rotation = 0;
AVRational aspect = FFmpeg::kNormalAspect;
float64 fps = 0.;
FFmpeg::SwscalePointer swscale;
};
@@ -734,6 +734,7 @@ void VideoTrackObject::callReady() {
_stream.rotation),
.cover = frame->original,
.rotation = _stream.rotation,
.fps = _stream.fps,
.alpha = frame->alpha,
} });
}
@@ -146,6 +146,8 @@ constexpr auto kZoomToScreenLevel = 1024;
constexpr auto kOverlayLoaderPriority = 2;
constexpr auto kSeekTimeMs = 5 * crl::time(1000);
constexpr auto kSeekTimeMsLong = 10 * crl::time(1000);
constexpr auto kFrameStepFallbackFps = 30.;
constexpr auto kFrameStepThrottleMs = crl::time(150);
// macOS OpenGL renderer fails to render larger texture
// even though it reports that max texture size is 16384.
@@ -617,6 +619,10 @@ OverlayWidget::OverlayWidget()
startSpeedBoost();
});
_frameStepThrottle.setCallback([=] {
flushPendingFrameStep();
});
_docRectImage = QImage(
st::mediaviewFileSize * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
@@ -5071,6 +5077,21 @@ void OverlayWidget::playbackPauseResume() {
}
}
void OverlayWidget::flushPendingFrameStep() {
if (!_streamed || !_frameStepPending) {
_frameStepThrottle.cancel();
return;
}
const auto fps = _streamed->instance.info().video.fps;
const auto stepMs = 1000.
/ ((fps > 0.) ? fps : kFrameStepFallbackFps);
const auto shift = crl::time(std::round(_frameStepPending * stepMs));
_frameStepPending = 0;
_streamingStartPaused = true;
seekRelativeTime(shift);
_frameStepThrottle.callOnce(kFrameStepThrottleMs);
}
void OverlayWidget::seekRelativeTime(crl::time time) {
Expects(_streamed != nullptr);
@@ -6615,6 +6636,15 @@ void OverlayWidget::handleKeyPress(not_null<QKeyEvent*> e) {
activateControls();
seekRelativeTime(kSeekTimeMsLong);
return;
} else if ((key == Qt::Key_Period || key == Qt::Key_Comma)
&& _streamed->instance.player().paused()) {
activateControls();
_frameStepPending += (key == Qt::Key_Period) ? 1 : -1;
if (!_frameStepThrottle.isActive()) {
flushPendingFrameStep();
_frameStepThrottle.callOnce(kFrameStepThrottleMs);
}
return;
} else if (modifiers.testFlag(Qt::AltModifier)
&& (key == Qt::Key_Left || key == Qt::Key_Right)
&& _streamed->controls
@@ -419,6 +419,7 @@ private:
void seekRelativeTime(crl::time time);
void restartAtProgress(float64 progress);
void restartAtSeekPosition(crl::time position);
void flushPendingFrameStep();
void refreshClipControllerGeometry();
void refreshCaptionGeometry();
@@ -805,6 +806,8 @@ private:
QRect _speedBoostRect;
Ui::Animations::Simple _speedBoostAnimation;
base::Timer _speedBoostHoldTimer;
base::Timer _frameStepThrottle;
int _frameStepPending = 0;
Ui::Animations::Basic _speedBoostTicker;
float64 _speedBoostPhase = 0.;
crl::time _speedBoostLastFrame = 0;