From 61f9fa917a18ab218b0aaadf1341c98a22ea362e Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 11 Apr 2024 15:18:19 +0200 Subject: [PATCH 01/18] Add "show previous frame" and "show next frame" buttons in video navigation Add "show previous frame" and "show next frame" buttons in video navigation Add "show previous frame" and "show next frame" buttons in video navigation --- .../js/videos/components/videoScreen.vue | 30 +++++++++---- .../components/videoScreen/videoPlayback.vue | 44 +++++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 1acb99237..8e839febb 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -11,13 +11,20 @@ :position="mousePosition" >
-
+
+
+
+ +
+
+
@@ -550,8 +564,8 @@ export default { this.map.on('moveend', this.emitMoveend); Keyboard.on('Escape', this.resetInteractionMode, 0, this.listenerSet); - Keyboard.on('ArrowRight', this.emitNext, 0, this.listenerSet); - Keyboard.on('ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.on('ArrowRight', this.showNextFrame, 0, this.listenerSet); + Keyboard.on('ArrowLeft', this.showPreviousFrame, 0, this.listenerSet); }, mounted() { this.map.setTarget(this.$el); diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index 121bbc524..8e6cd7d6e 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -153,6 +153,50 @@ export default { handleSeeked() { this.renderVideo(true); }, + // 3 next methods are a workaround to get previous and next frames, adapted from here: https://github.com/angrycoding/requestVideoFrameCallback-prev-next/tree/main + frameInfoCallback() { + let promise = new Vue.Promise((resolve, reject) => { + if ("requestVideoFrameCallback" in HTMLVideoElement.prototype) { + this.video.requestVideoFrameCallback((now, metadata) => { + resolve(metadata); + }) + } + else { + reject("Your browser does not support requestVideoFrameCallback()."); + } + }) + return promise; + }, + async showPreviousFrame() { + // force rerender + this.video.currentTime += 1; + this.video.currentTime -= 1; + + // get current frame time + const firstMetadata = await this.frameInfoCallback(); + + for (;;) { + // now adjust video's current time until actual frame time changes + this.video.currentTime -= 0.01; + let metadata = await this.frameInfoCallback(); + if (metadata.mediaTime !== firstMetadata.mediaTime) break; + } + }, + async showNextFrame() { + // force rerender + this.video.currentTime += 1; + this.video.currentTime -= 1; + + // get current frame time + const firstMetadata = await this.frameInfoCallback(); + + for (;;) { + // now adjust video's current time until actual frame time changes + this.video.currentTime += 0.01; + let metadata = await this.frameInfoCallback(); + if (metadata.mediaTime !== firstMetadata.mediaTime) break; + } + }, }, watch: { seeking(seeking) { From b97823185c68dc337cdcce5293a9f3dc784dabe7 Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 16 May 2024 14:28:44 +0200 Subject: [PATCH 02/18] Add a supportsJumpByFrame attribute which checks for browser compatibility and add a setting button to enable jumpByFrame feature --- .../js/videos/components/settingsTab.vue | 23 +++++++++++++++++++ .../js/videos/components/videoScreen.vue | 8 ++++++- .../components/videoScreen/videoPlayback.vue | 11 +++------ resources/assets/js/videos/stores/settings.js | 1 + resources/assets/js/videos/videoContainer.vue | 8 ++++++- resources/views/videos/show/content.blade.php | 1 + .../videos/show/sidebar-settings.blade.php | 5 ++++ 7 files changed, 47 insertions(+), 10 deletions(-) diff --git a/resources/assets/js/videos/components/settingsTab.vue b/resources/assets/js/videos/components/settingsTab.vue index 3932af765..4eb7ae2ac 100644 --- a/resources/assets/js/videos/components/settingsTab.vue +++ b/resources/assets/js/videos/components/settingsTab.vue @@ -6,6 +6,12 @@ export default { components: { powerToggle: PowerToggle, }, + props: { + supportsJumpByFrame: { + type: Boolean, + default: false, + }, + }, data() { return { restoreKeys: [ @@ -15,6 +21,7 @@ export default { 'showLabelTooltip', 'showMousePosition', 'showProgressIndicator', + 'enableJumpByFrame', ], annotationOpacity: 1, showMinimap: true, @@ -23,8 +30,14 @@ export default { showMousePosition: false, playbackRate: 1.0, showProgressIndicator: true, + enableJumpByFrame: false, }; }, + computed: { + jumpByFrameNotSupported() { + return !this.supportsJumpByFrame; + }, + }, methods: { handleShowMinimap() { this.showMinimap = true; @@ -50,6 +63,12 @@ export default { handleHideProgressIndicator() { this.showProgressIndicator = false; }, + handleEnableJumpByFrame() { + this.enableJumpByFrame = true; + }, + handleDisableJumpByFrame() { + this.enableJumpByFrame = false; + }, }, watch: { annotationOpacity(value) { @@ -86,6 +105,10 @@ export default { this.$emit('update', 'showProgressIndicator', show); Settings.set('showProgressIndicator', show); }, + enableJumpByFrame(show) { + this.$emit('update', 'enableJumpByFrame', show); + Settings.set('enableJumpByFrame', show); + } }, created() { this.restoreKeys.forEach((key) => { diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 8e839febb..be156baf7 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -21,6 +21,7 @@
{ - if ("requestVideoFrameCallback" in HTMLVideoElement.prototype) { - this.video.requestVideoFrameCallback((now, metadata) => { - resolve(metadata); - }) - } - else { - reject("Your browser does not support requestVideoFrameCallback()."); - } + this.video.requestVideoFrameCallback((now, metadata) => { + resolve(metadata); + }) }) return promise; }, diff --git a/resources/assets/js/videos/stores/settings.js b/resources/assets/js/videos/stores/settings.js index 5a88bd2c4..e4381187e 100644 --- a/resources/assets/js/videos/stores/settings.js +++ b/resources/assets/js/videos/stores/settings.js @@ -7,6 +7,7 @@ let defaults = { showLabelTooltip: false, showMousePosition: false, showProgressIndicator: true, + enableJumpByFrame: false, }; export default new Settings({ diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index f20248b00..3b8124aa2 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -68,6 +68,7 @@ export default { showMousePosition: false, playbackRate: 1.0, showProgressIndicator: true, + enableJumpByFrame: false, }, openTab: '', urlParams: { @@ -93,7 +94,8 @@ export default { attachingLabel: false, swappingLabel: false, disableJobTracking: false, - }; + supportsJumpByFrame: false, + }; }, computed: { selectedAnnotations() { @@ -720,6 +722,10 @@ export default { if (this.canEdit) { this.initializeEcho(); } + + if ("requestVideoFrameCallback" in HTMLVideoElement.prototype) { + this.supportsJumpByFrame = true; + } }, mounted() { // Wait for the sub-components to register their event listeners before diff --git a/resources/views/videos/show/content.blade.php b/resources/views/videos/show/content.blade.php index eed4e7908..243aa50e5 100644 --- a/resources/views/videos/show/content.blade.php +++ b/resources/views/videos/show/content.blade.php @@ -38,6 +38,7 @@ :show-label-tooltip="settings.showLabelTooltip" :show-minimap="settings.showMinimap" :show-mouse-position="settings.showMousePosition" + :enable-jump-by-frame="settings.enableJumpByFrame" :video="video" :height-offset="screenHeightOffset" :show-prev-next="hasSiblingVideos" diff --git a/resources/views/videos/show/sidebar-settings.blade.php b/resources/views/videos/show/sidebar-settings.blade.php index f77eb6e4c..ce9a52790 100644 --- a/resources/views/videos/show/sidebar-settings.blade.php +++ b/resources/views/videos/show/sidebar-settings.blade.php @@ -1,6 +1,7 @@
@@ -35,6 +36,10 @@ Mouse Position
+ +
From beeeae417e4b05f759e493fb4f36b8ac1e27de7f Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 16 May 2024 15:17:08 +0200 Subject: [PATCH 03/18] Fix coding style issue --- .../assets/js/videos/components/videoScreen/videoPlayback.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index b69dad567..201db4f66 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -155,7 +155,7 @@ export default { }, // 3 next methods are a workaround to get previous and next frames, adapted from here: https://github.com/angrycoding/requestVideoFrameCallback-prev-next/tree/main frameInfoCallback() { - let promise = new Vue.Promise((resolve, reject) => { + let promise = new Vue.Promise((resolve) => { this.video.requestVideoFrameCallback((now, metadata) => { resolve(metadata); }) From b8a297412c8aa9c6edabc1bcafd9a9083666d8a5 Mon Sep 17 00:00:00 2001 From: anouk Date: Tue, 21 May 2024 12:26:22 +0200 Subject: [PATCH 04/18] Adapt shortcuts and icons depending on enableJumpByFrame feature --- .../js/videos/components/videoScreen.vue | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index be156baf7..e682948ed 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -11,18 +11,16 @@ :position="mousePosition" >
-
- + -
-
@@ -42,16 +40,14 @@ > -
-
-
@@ -408,6 +404,9 @@ export default { disableJobTracking() { return this.reachedTrackedAnnotationLimit; }, + jumpByFrameEnabled() { + return this.enableJumpByFrame; + } }, methods: { createMap() { @@ -560,6 +559,24 @@ export default { heightOffset() { this.updateSize(); }, + jumpByFrameEnabled(enabled) { + if(enabled) { + Keyboard.off('ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.off('ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.on('Shift+ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.on('Shift+ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.on('ArrowRight', this.showNextFrame, 0, this.listenerSet); + Keyboard.on('ArrowLeft', this.showPreviousFrame, 0, this.listenerSet); + } + else { + Keyboard.off('Shift+ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.off('Shift+ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.off('ArrowRight', this.showNextFrame, 0, this.listenerSet); + Keyboard.off('ArrowLeft', this.showPreviousFrame, 0, this.listenerSet); + Keyboard.on('ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.on('ArrowLeft', this.emitPrevious, 0, this.listenerSet); + } + }, }, created() { this.$once('map-ready', this.initLayersAndInteractions); @@ -570,8 +587,8 @@ export default { this.map.on('moveend', this.emitMoveend); Keyboard.on('Escape', this.resetInteractionMode, 0, this.listenerSet); - Keyboard.on('ArrowRight', this.showNextFrame, 0, this.listenerSet); - Keyboard.on('ArrowLeft', this.showPreviousFrame, 0, this.listenerSet); + Keyboard.on('ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.on('ArrowLeft', this.emitPrevious, 0, this.listenerSet); }, mounted() { this.map.setTarget(this.$el); From 9efd6e4ece680234beba5cb8ca033382b0480537 Mon Sep 17 00:00:00 2001 From: anouk Date: Tue, 21 May 2024 16:09:09 +0200 Subject: [PATCH 05/18] Update manual for jump by frame feature --- .../tutorials/videos/shortcuts.blade.php | 32 ++++++++++++++++++- .../manual/tutorials/videos/sidebar.blade.php | 6 ++++ .../videos/show/sidebar-settings.blade.php | 3 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/resources/views/manual/tutorials/videos/shortcuts.blade.php b/resources/views/manual/tutorials/videos/shortcuts.blade.php index d9442e5d6..edcbc363b 100644 --- a/resources/views/manual/tutorials/videos/shortcuts.blade.php +++ b/resources/views/manual/tutorials/videos/shortcuts.blade.php @@ -14,7 +14,12 @@ Key Function - + + + + If Jump by frame is disabled + + Arrow left @@ -24,6 +29,31 @@ Arrow right Next video + + + + If Jump by frame is enabled + + + + + Arrow left + Previous frame + + + Arrow right + Next frame + + + Shift+Arrow left + Previous video + + + Shift+Arrow right + Next video + + + + Zoom the video to the original resolution diff --git a/resources/views/manual/tutorials/videos/sidebar.blade.php b/resources/views/manual/tutorials/videos/sidebar.blade.php index a2f628dc4..262ea16e3 100644 --- a/resources/views/manual/tutorials/videos/sidebar.blade.php +++ b/resources/views/manual/tutorials/videos/sidebar.blade.php @@ -71,5 +71,11 @@

The mouse position switch controls the display of an additional map overlay that shows the current position of the cursor on the video in pixels.

+ +

+ The jump by frame switch allows you to navigate frame by frame (forward and backward) in the video. Note that this is an experimental feature as it is only available in Chrome and may not always give the right frame, so please use it with caution. +
When the feature is enabled, new buttons will appear in the navigation bar and keyboard shortcuts are modified: use 𝗮𝗿𝗿𝗼𝘄𝘀 to switch frames and 𝗦𝗵𝗶𝗳𝘁+𝗮𝗿𝗿𝗼𝘄𝘀 to switch videos. When the feature is disabled, use old shortcuts to switch videos. +

+
@endsection diff --git a/resources/views/videos/show/sidebar-settings.blade.php b/resources/views/videos/show/sidebar-settings.blade.php index ce9a52790..94694c455 100644 --- a/resources/views/videos/show/sidebar-settings.blade.php +++ b/resources/views/videos/show/sidebar-settings.blade.php @@ -37,7 +37,8 @@
From cee1f467125a338d795b3af0b778f27b2d72e673 Mon Sep 17 00:00:00 2001 From: anouk Date: Wed, 22 May 2024 11:04:15 +0200 Subject: [PATCH 06/18] Prevent double calls to prev/next frame by calling seek method --- .../js/videos/components/videoScreen.vue | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index e682948ed..c8b554c43 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -20,9 +20,10 @@ > Date: Thu, 23 May 2024 10:28:05 +0200 Subject: [PATCH 07/18] Fix coding issues --- resources/assets/js/videos/components/videoScreen.vue | 2 +- .../assets/js/videos/components/videoScreen/videoPlayback.vue | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 735c20be5..6f0dabbe9 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -426,7 +426,7 @@ export default { }, jumpByFrameEnabled() { return this.enableJumpByFrame; - } + }, jumpBackwardMessage() { return `Rewind video by ${this.jumpStep} s 𝗖𝘁𝗿𝗹+𝗟𝗲𝗳𝘁 𝗮𝗿𝗿𝗼𝘄`; }, diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index ef0381170..928d3cc30 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -190,6 +190,8 @@ export default { this.video.currentTime += 0.01; let metadata = await this.frameInfoCallback(); if (metadata.mediaTime !== firstMetadata.mediaTime) break; + } + }, // Methods to jump back and forward in video. Step is given by parameter jumpStep. jumpBackward() { if (this.video.currentTime > 0 && this.jumpStep > 0) { From ddbdb83d4031cbe59f2bd383788e2b5f6c9ddbf1 Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 23 May 2024 10:47:17 +0200 Subject: [PATCH 08/18] Move prev/next video buttons to separate button group --- .../js/videos/components/videoScreen.vue | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 6f0dabbe9..91a9d4ffd 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -11,13 +11,19 @@ :position="mousePosition" >
-
+
+ +
+
-
Date: Thu, 23 May 2024 11:08:27 +0200 Subject: [PATCH 09/18] Move keyboard shortcuts on/off to a separate method --- .../js/videos/components/videoScreen.vue | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 91a9d4ffd..7fb18b327 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -424,9 +424,6 @@ export default { disableJobTracking() { return this.reachedTrackedAnnotationLimit; }, - jumpByFrameEnabled() { - return this.enableJumpByFrame; - }, jumpBackwardMessage() { return `Rewind video by ${this.jumpStep} s 𝗖𝘁𝗿𝗹+𝗟𝗲𝗳𝘁 𝗮𝗿𝗿𝗼𝘄`; }, @@ -568,6 +565,24 @@ export default { if(!this.seeking) this.showNextFrame(); }, + adaptKeyboardShortcuts() { + if(this.enableJumpByFrame) { + Keyboard.off('ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.off('ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.on('Shift+ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.on('Shift+ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.on('ArrowRight', this.emitNextFrame, 0, this.listenerSet); + Keyboard.on('ArrowLeft', this.emitPreviousFrame, 0, this.listenerSet); + } + else { + Keyboard.off('Shift+ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.off('Shift+ArrowLeft', this.emitPrevious, 0, this.listenerSet); + Keyboard.off('ArrowRight', this.emitNextFrame, 0, this.listenerSet); + Keyboard.off('ArrowLeft', this.emitPreviousFrame, 0, this.listenerSet); + Keyboard.on('ArrowRight', this.emitNext, 0, this.listenerSet); + Keyboard.on('ArrowLeft', this.emitPrevious, 0, this.listenerSet); + } + } }, watch: { selectedAnnotations(annotations) { @@ -595,23 +610,8 @@ export default { heightOffset() { this.updateSize(); }, - jumpByFrameEnabled(enabled) { - if(enabled) { - Keyboard.off('ArrowRight', this.emitNext, 0, this.listenerSet); - Keyboard.off('ArrowLeft', this.emitPrevious, 0, this.listenerSet); - Keyboard.on('Shift+ArrowRight', this.emitNext, 0, this.listenerSet); - Keyboard.on('Shift+ArrowLeft', this.emitPrevious, 0, this.listenerSet); - Keyboard.on('ArrowRight', this.emitNextFrame, 0, this.listenerSet); - Keyboard.on('ArrowLeft', this.emitPreviousFrame, 0, this.listenerSet); - } - else { - Keyboard.off('Shift+ArrowRight', this.emitNext, 0, this.listenerSet); - Keyboard.off('Shift+ArrowLeft', this.emitPrevious, 0, this.listenerSet); - Keyboard.off('ArrowRight', this.emitNextFrame, 0, this.listenerSet); - Keyboard.off('ArrowLeft', this.emitPreviousFrame, 0, this.listenerSet); - Keyboard.on('ArrowRight', this.emitNext, 0, this.listenerSet); - Keyboard.on('ArrowLeft', this.emitPrevious, 0, this.listenerSet); - } + enableJumpByFrame() { + this.adaptKeyboardShortcuts(); }, }, created() { @@ -622,9 +622,8 @@ export default { this.map.on('pointermove', this.updateMousePosition); this.map.on('moveend', this.emitMoveend); + this.adaptKeyboardShortcuts(); Keyboard.on('Escape', this.resetInteractionMode, 0, this.listenerSet); - Keyboard.on('ArrowRight', this.emitNext, 0, this.listenerSet); - Keyboard.on('ArrowLeft', this.emitPrevious, 0, this.listenerSet); Keyboard.on('Control+ArrowRight', this.jumpForward, 0, this.listenerSet); Keyboard.on('Control+ArrowLeft', this.jumpBackward, 0, this.listenerSet); }, From a31095e2dc4e675f073ae3da60700d00e0bfbd5d Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 23 May 2024 11:23:53 +0200 Subject: [PATCH 10/18] Remove unnecessary spaces --- .../assets/js/videos/components/videoScreen/videoPlayback.vue | 4 ++-- resources/assets/js/videos/videoContainer.vue | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index 928d3cc30..530ee5c14 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -173,7 +173,7 @@ export default { for (;;) { // now adjust video's current time until actual frame time changes this.video.currentTime -= 0.01; - let metadata = await this.frameInfoCallback(); + let metadata = await this.frameInfoCallback(); if (metadata.mediaTime !== firstMetadata.mediaTime) break; } }, @@ -188,7 +188,7 @@ export default { for (;;) { // now adjust video's current time until actual frame time changes this.video.currentTime += 0.01; - let metadata = await this.frameInfoCallback(); + let metadata = await this.frameInfoCallback(); if (metadata.mediaTime !== firstMetadata.mediaTime) break; } }, diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 48de09a30..89a0f01c5 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -96,7 +96,7 @@ export default { swappingLabel: false, disableJobTracking: false, supportsJumpByFrame: false, - }; + }; }, computed: { selectedAnnotations() { From 5324956f1e5831bcbda709dad8f460af681c2a08 Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 23 May 2024 17:37:42 +0200 Subject: [PATCH 11/18] Add arrows icons to jump by frame section in manual --- resources/views/manual/tutorials/videos/sidebar.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/manual/tutorials/videos/sidebar.blade.php b/resources/views/manual/tutorials/videos/sidebar.blade.php index b5cab73c4..c61793127 100644 --- a/resources/views/manual/tutorials/videos/sidebar.blade.php +++ b/resources/views/manual/tutorials/videos/sidebar.blade.php @@ -78,7 +78,7 @@

The jump by frame switch allows you to navigate frame by frame (forward and backward) in the video. Note that this is an experimental feature as it is only available in Chrome and may not always give the right frame, so please use it with caution. -
When the feature is enabled, new buttons will appear in the navigation bar and keyboard shortcuts are modified: use 𝗮𝗿𝗿𝗼𝘄𝘀 to switch frames and 𝗦𝗵𝗶𝗳𝘁+𝗮𝗿𝗿𝗼𝘄𝘀 to switch videos. When the feature is disabled, use old shortcuts to switch videos. +
When the feature is enabled, new buttons will appear in the navigation bar and keyboard shortcuts are modified: use / to switch frames and 𝗦𝗵𝗶𝗳𝘁 + / to switch videos. When the feature is disabled, use old shortcuts to switch videos.

From f9c6020f4e71c5eac11ee8cd7b2b9df3818fb935 Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 6 Jun 2024 16:48:03 +0200 Subject: [PATCH 12/18] move jump frame button to the right --- .../assets/js/videos/components/videoScreen.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 7fb18b327..465ba9567 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -24,13 +24,6 @@ >
- + Date: Thu, 6 Jun 2024 17:20:00 +0200 Subject: [PATCH 13/18] Add small changes in manual --- resources/views/manual/tutorials/videos/shortcuts.blade.php | 4 ++-- resources/views/manual/tutorials/videos/sidebar.blade.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/manual/tutorials/videos/shortcuts.blade.php b/resources/views/manual/tutorials/videos/shortcuts.blade.php index 665ae8972..6b02899cf 100644 --- a/resources/views/manual/tutorials/videos/shortcuts.blade.php +++ b/resources/views/manual/tutorials/videos/shortcuts.blade.php @@ -17,7 +17,7 @@ - If Jump by frame is disabled + If Jump by frame is disabled @@ -32,7 +32,7 @@ - If Jump by frame is enabled + If Jump by frame is enabled diff --git a/resources/views/manual/tutorials/videos/sidebar.blade.php b/resources/views/manual/tutorials/videos/sidebar.blade.php index c61793127..0392933f5 100644 --- a/resources/views/manual/tutorials/videos/sidebar.blade.php +++ b/resources/views/manual/tutorials/videos/sidebar.blade.php @@ -78,7 +78,7 @@

The jump by frame switch allows you to navigate frame by frame (forward and backward) in the video. Note that this is an experimental feature as it is only available in Chrome and may not always give the right frame, so please use it with caution. -
When the feature is enabled, new buttons will appear in the navigation bar and keyboard shortcuts are modified: use / to switch frames and 𝗦𝗵𝗶𝗳𝘁 + / to switch videos. When the feature is disabled, use old shortcuts to switch videos. +
When the feature is enabled, new buttons will appear in the navigation bar and keyboard shortcuts are modified: use Arrow left / Arrow right to switch frames and Shift + Arrow left / Arrow right to switch videos. When the feature is disabled, use old shortcuts to switch videos.

From fc49c77c33e1b4b9e598a7a6ac558d28e9ade7aa Mon Sep 17 00:00:00 2001 From: anouk Date: Thu, 13 Jun 2024 16:47:18 +0200 Subject: [PATCH 14/18] Fix bug on video edge conditions and infinite seeking state --- .../js/videos/components/videoScreen.vue | 10 --- .../components/videoScreen/videoPlayback.vue | 80 +++++++++++++------ resources/assets/js/videos/videoContainer.vue | 22 +++++ resources/views/videos/show/content.blade.php | 2 + 4 files changed, 79 insertions(+), 35 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen.vue b/resources/assets/js/videos/components/videoScreen.vue index 465ba9567..ca856b794 100644 --- a/resources/assets/js/videos/components/videoScreen.vue +++ b/resources/assets/js/videos/components/videoScreen.vue @@ -555,16 +555,6 @@ export default { this.setPaused(true); this.resetInteractionMode(); }, - emitPreviousFrame() { - this.$emit('seek', this.video.currentTime, true); - if(!this.seeking) - this.showPreviousFrame(); - }, - emitNextFrame() { - this.$emit('seek', this.video.currentTime, true); - if(!this.seeking) - this.showNextFrame(); - }, adaptKeyboardShortcuts() { if(this.enableJumpByFrame) { Keyboard.off('ArrowRight', this.emitNext, 0, this.listenerSet); diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index 530ee5c14..08b86695b 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -24,6 +24,8 @@ export default { // Allow a maximum of 100x magnification. More cannot be represented in the // URL parameters. minResolution: 0.01, + // parameter tracking seeking state specific for frame jump, needed because looking for seeking directly leads to error + seekingFrame: this.seeking, }; }, methods: { @@ -153,7 +155,23 @@ export default { handleSeeked() { this.renderVideo(true); }, - // 3 next methods are a workaround to get previous and next frames, adapted from here: https://github.com/angrycoding/requestVideoFrameCallback-prev-next/tree/main + // 5 next methods are a workaround to get previous and next frames, adapted from here: https://github.com/angrycoding/requestVideoFrameCallback-prev-next/tree/main + async emitPreviousFrame() { + if(this.video.currentTime == 0 || this.seekingFrame) return; + this.$emit('start-seeking'); + this.seekingFrame = true; + await this.showPreviousFrame(); + this.$emit('stop-seeking'); + this.seekingFrame = false; + }, + async emitNextFrame() { + if(this.video.duration - this.video.currentTime == 0 || this.seekingFrame) return; + this.$emit('start-seeking'); + this.seekingFrame = true; + await this.showNextFrame(); + this.$emit('stop-seeking'); + this.seekingFrame = false; + }, frameInfoCallback() { let promise = new Vue.Promise((resolve) => { this.video.requestVideoFrameCallback((now, metadata) => { @@ -163,34 +181,46 @@ export default { return promise; }, async showPreviousFrame() { - // force rerender - this.video.currentTime += 1; - this.video.currentTime -= 1; + try { + // force rerender adapting step on begining or end of video + let step = 1; + if(this.video.currentTime < 1) {step = this.video.currentTime;} + if(this.video.duration - this.video.currentTime < 1) {step = this.video.duration - this.video.currentTime;} + await (this.video.currentTime += step); + await (this.video.currentTime -= step); - // get current frame time - const firstMetadata = await this.frameInfoCallback(); - - for (;;) { - // now adjust video's current time until actual frame time changes - this.video.currentTime -= 0.01; - let metadata = await this.frameInfoCallback(); - if (metadata.mediaTime !== firstMetadata.mediaTime) break; - } + // get current frame time + const firstMetadata = await this.frameInfoCallback(); + for (;;) { + // now adjust video's current time until actual frame time changes + this.video.currentTime -= 0.01; + // check that we are not at first frame, otherwise we'll end up in infinte loop + if (this.video.currentTime == 0) break; + const metadata = await this.frameInfoCallback(); + if (metadata.mediaTime !== firstMetadata.mediaTime) break; + } + } catch(handleErrorResponse) {} }, async showNextFrame() { - // force rerender - this.video.currentTime += 1; - this.video.currentTime -= 1; - - // get current frame time - const firstMetadata = await this.frameInfoCallback(); + try { + // force rerender adapting step on begining or end of video + let step = 1; + if(this.video.currentTime < 1) {step = this.video.currentTime;} + if(this.video.duration - this.video.currentTime < 1) {step = this.video.duration - this.video.currentTime;} + await (this.video.currentTime += step); + await (this.video.currentTime -= step); - for (;;) { - // now adjust video's current time until actual frame time changes - this.video.currentTime += 0.01; - let metadata = await this.frameInfoCallback(); - if (metadata.mediaTime !== firstMetadata.mediaTime) break; - } + // get current frame time + const firstMetadata = await this.frameInfoCallback(); + for (;;) { + // now adjust video's current time until actual frame time changes + this.video.currentTime += 0.01; + // check that we are not at last frame, otherwise we'll end up in infinte loop + if (this.video.duration - this.video.currentTime == 0) break; + const metadata = await this.frameInfoCallback(); + if (metadata.mediaTime !== firstMetadata.mediaTime) break; + } + } catch(handleErrorResponse) {} }, // Methods to jump back and forward in video. Step is given by parameter jumpStep. jumpBackward() { diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 89a0f01c5..8897de70a 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -204,6 +204,28 @@ export default { return promise; }, + startSeeking() { + if (this.seeking) { + return Vue.Promise.resolve(); + } + let promise = new Vue.Promise((resolve, reject) => { + this.video.addEventListener('seeking', resolve); + this.video.addEventListener('error', reject); + }); + this.seeking = true; + return promise; + }, + stopSeeking() { + if (!this.seeking) { + return Vue.Promise.resolve(); + } + let promise = new Vue.Promise((resolve, reject) => { + this.video.addEventListener('seeked', resolve); + this.video.addEventListener('error', reject); + }); + this.seeking = false; + return promise; + }, selectAnnotation(annotation, time, shift) { if (this.attachingLabel) { this.attachAnnotationLabel(annotation); diff --git a/resources/views/videos/show/content.blade.php b/resources/views/videos/show/content.blade.php index c17318776..bb1510718 100644 --- a/resources/views/videos/show/content.blade.php +++ b/resources/views/videos/show/content.blade.php @@ -63,6 +63,8 @@ v-on:attaching-active="handleAttachingLabelActive" v-on:swapping-active="handleSwappingLabelActive" v-on:seek="seek" + v-on:start-seeking="startSeeking" + v-on:stop-seeking="stopSeeking" v-on:is-invalid-shape="handleInvalidShape" > Date: Thu, 13 Jun 2024 16:56:42 +0200 Subject: [PATCH 15/18] Fix handling error --- .../assets/js/videos/components/videoScreen/videoPlayback.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index 08b86695b..a3b92731a 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -199,7 +199,7 @@ export default { const metadata = await this.frameInfoCallback(); if (metadata.mediaTime !== firstMetadata.mediaTime) break; } - } catch(handleErrorResponse) {} + } catch(e) {console.error(e)} }, async showNextFrame() { try { @@ -220,7 +220,7 @@ export default { const metadata = await this.frameInfoCallback(); if (metadata.mediaTime !== firstMetadata.mediaTime) break; } - } catch(handleErrorResponse) {} + } catch(e) {console.error(e)} }, // Methods to jump back and forward in video. Step is given by parameter jumpStep. jumpBackward() { From e0e215815cfc837d80da98b42971970abc2eb04e Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 14 Jun 2024 10:37:52 +0200 Subject: [PATCH 16/18] Update video jump by frame manual articles --- .../tutorials/videos/shortcuts.blade.php | 62 +++++++++---------- .../manual/tutorials/videos/sidebar.blade.php | 3 +- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/resources/views/manual/tutorials/videos/shortcuts.blade.php b/resources/views/manual/tutorials/videos/shortcuts.blade.php index 6b02899cf..632ee1d43 100644 --- a/resources/views/manual/tutorials/videos/shortcuts.blade.php +++ b/resources/views/manual/tutorials/videos/shortcuts.blade.php @@ -15,12 +15,6 @@ Function - - - If Jump by frame is disabled - - - Arrow left Previous video @@ -29,31 +23,6 @@ Arrow right Next video - - - - If Jump by frame is enabled - - - - - Arrow left - Previous frame - - - Arrow right - Next frame - - - Shift+Arrow left - Previous video - - - Shift+Arrow right - Next video - - - Ctrl+Arrow left Jump backward in video by a time defined in jump step parameter @@ -149,6 +118,37 @@ +

+ When jump by frame is enabled: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyFunction
Arrow leftPrevious frame
Arrow rightNext frame
Shift+Arrow leftPrevious video
Shift+Arrow rightNext video
+

When the video labels tab is open:

diff --git a/resources/views/manual/tutorials/videos/sidebar.blade.php b/resources/views/manual/tutorials/videos/sidebar.blade.php index 0392933f5..65b76a58c 100644 --- a/resources/views/manual/tutorials/videos/sidebar.blade.php +++ b/resources/views/manual/tutorials/videos/sidebar.blade.php @@ -77,8 +77,7 @@

- The jump by frame switch allows you to navigate frame by frame (forward and backward) in the video. Note that this is an experimental feature as it is only available in Chrome and may not always give the right frame, so please use it with caution. -
When the feature is enabled, new buttons will appear in the navigation bar and keyboard shortcuts are modified: use Arrow left / Arrow right to switch frames and Shift + Arrow left / Arrow right to switch videos. When the feature is disabled, use old shortcuts to switch videos. + The jump by frame switch allows you to navigate frame by frame (forward and backward) in the video. Note that this is an experimental feature as it is only available in Chrome and may not always give the right frame, so please use it with caution. When the feature is enabled, the and control buttons will appear in the tool bar at the bottom of the video. Use these to jump to the previous/next frame. Also, the keyboard shortcuts are updated.

From ef0298e43d6548aa0dcc9b988ecb1ac5400807fa Mon Sep 17 00:00:00 2001 From: anouk Date: Tue, 18 Jun 2024 12:14:30 +0200 Subject: [PATCH 17/18] Clean code and remove useless stopSeeking --- .../components/videoScreen/videoPlayback.vue | 26 ++++++++++++------- resources/assets/js/videos/videoContainer.vue | 19 -------------- resources/views/videos/show/content.blade.php | 1 - 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue index a3b92731a..9cf5babc1 100644 --- a/resources/assets/js/videos/components/videoScreen/videoPlayback.vue +++ b/resources/assets/js/videos/components/videoScreen/videoPlayback.vue @@ -161,7 +161,6 @@ export default { this.$emit('start-seeking'); this.seekingFrame = true; await this.showPreviousFrame(); - this.$emit('stop-seeking'); this.seekingFrame = false; }, async emitNextFrame() { @@ -169,7 +168,6 @@ export default { this.$emit('start-seeking'); this.seekingFrame = true; await this.showNextFrame(); - this.$emit('stop-seeking'); this.seekingFrame = false; }, frameInfoCallback() { @@ -184,10 +182,14 @@ export default { try { // force rerender adapting step on begining or end of video let step = 1; - if(this.video.currentTime < 1) {step = this.video.currentTime;} - if(this.video.duration - this.video.currentTime < 1) {step = this.video.duration - this.video.currentTime;} - await (this.video.currentTime += step); - await (this.video.currentTime -= step); + if (this.video.currentTime < 1) { + step = this.video.currentTime; + } + if (this.video.duration - this.video.currentTime < 1) { + step = this.video.duration - this.video.currentTime; + } + this.video.currentTime += step; + this.video.currentTime -= step; // get current frame time const firstMetadata = await this.frameInfoCallback(); @@ -205,10 +207,14 @@ export default { try { // force rerender adapting step on begining or end of video let step = 1; - if(this.video.currentTime < 1) {step = this.video.currentTime;} - if(this.video.duration - this.video.currentTime < 1) {step = this.video.duration - this.video.currentTime;} - await (this.video.currentTime += step); - await (this.video.currentTime -= step); + if (this.video.currentTime < 1) { + step = this.video.currentTime; + } + if (this.video.duration - this.video.currentTime < 1) { + step = this.video.duration - this.video.currentTime; + } + this.video.currentTime += step; + this.video.currentTime -= step; // get current frame time const firstMetadata = await this.frameInfoCallback(); diff --git a/resources/assets/js/videos/videoContainer.vue b/resources/assets/js/videos/videoContainer.vue index 8897de70a..ed2b73633 100644 --- a/resources/assets/js/videos/videoContainer.vue +++ b/resources/assets/js/videos/videoContainer.vue @@ -205,26 +205,7 @@ export default { return promise; }, startSeeking() { - if (this.seeking) { - return Vue.Promise.resolve(); - } - let promise = new Vue.Promise((resolve, reject) => { - this.video.addEventListener('seeking', resolve); - this.video.addEventListener('error', reject); - }); this.seeking = true; - return promise; - }, - stopSeeking() { - if (!this.seeking) { - return Vue.Promise.resolve(); - } - let promise = new Vue.Promise((resolve, reject) => { - this.video.addEventListener('seeked', resolve); - this.video.addEventListener('error', reject); - }); - this.seeking = false; - return promise; }, selectAnnotation(annotation, time, shift) { if (this.attachingLabel) { diff --git a/resources/views/videos/show/content.blade.php b/resources/views/videos/show/content.blade.php index bb1510718..2a75a4c39 100644 --- a/resources/views/videos/show/content.blade.php +++ b/resources/views/videos/show/content.blade.php @@ -64,7 +64,6 @@ v-on:swapping-active="handleSwappingLabelActive" v-on:seek="seek" v-on:start-seeking="startSeeking" - v-on:stop-seeking="stopSeeking" v-on:is-invalid-shape="handleInvalidShape" > Date: Tue, 18 Jun 2024 12:53:35 +0200 Subject: [PATCH 18/18] Fix merging issues --- resources/assets/js/videos/components/settingsTab.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/assets/js/videos/components/settingsTab.vue b/resources/assets/js/videos/components/settingsTab.vue index fba7a05e0..33510d854 100644 --- a/resources/assets/js/videos/components/settingsTab.vue +++ b/resources/assets/js/videos/components/settingsTab.vue @@ -72,6 +72,7 @@ export default { }, handleDisableJumpByFrame() { this.enableJumpByFrame = false; + }, handleMuteVideo() { this.muteVideo = true; }, @@ -122,7 +123,7 @@ export default { enableJumpByFrame(show) { this.$emit('update', 'enableJumpByFrame', show); Settings.set('enableJumpByFrame', show); - } + }, muteVideo(show) { this.$emit('update', 'muteVideo', show); Settings.set('muteVideo', show);