Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add screenshot button to video annotator #1004

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions resources/assets/js/annotations/annotatorContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,25 @@ export default {
crossOriginError: false,
};
},
provide() {
const appData = {}

let imageInfo = {};
imageInfo['currentId'] = biigle.$require('annotations.imageId');
imageInfo['ids'] = biigle.$require('annotations.imagesIds');
imageInfo['filenames'] = biigle.$require('annotations.imagesFilenames');
imageInfo['type'] = 'image';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do you use type?

imageInfo['fileChangedEvent'] = 'images.change';
imageInfo['mapChangedEvent'] = 'annotations.map.init';

// Need defineProperty to maintain reactivity.
// See https://stackoverflow.com/questions/65718651/how-do-i-make-vue-2-provide-inject-api-reactive
Object.defineProperty(appData, "info", {
get: () => imageInfo,
})

return { 'files': appData };
},
computed: {
canAdd() {
return this.isEditor && (this.image !== null);
Expand Down
12 changes: 7 additions & 5 deletions resources/assets/js/annotations/components/screenshotButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default {
currentId: null,
};
},
inject: ['files'],
computed: {
filename() {
if (this.currentId) {
Expand Down Expand Up @@ -150,15 +151,16 @@ export default {
},
},
created() {
let ids = biigle.$require('annotations.imagesIds');
let filenames = {};
biigle.$require('annotations.imagesFilenames').forEach((filename, index) => {
let ids = this.files.info.ids;
this.files.info.filenames.forEach((filename, index) => {
filenames[ids[index]] = filename;
});
this.filenames = filenames;
this.currentId = biigle.$require('annotations.imageId');
Events.$on('images.change', this.updateCurrentId);
Events.$on('annotations.map.init', this.setMap);
this.currentId = this.files.info.currentId
Events.$on(this.files.info.fileChangedEvent, this.updateCurrentId);
Events.$on(this.files.info.mapChangedEvent, this.setMap);

Keyboard.on('p', this.capture);
},
};
Expand Down
6 changes: 6 additions & 0 deletions resources/assets/js/videos/components/settingsTab.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
<script>
import PowerToggle from '../../core/components/powerToggle';
import Settings from '../stores/settings';
import ScreenshotButton from '../../annotations/components/screenshotButton.vue';

export default {
components: {
powerToggle: PowerToggle,
screenshotButton: ScreenshotButton
},
props: {
supportsJumpByFrame: {
type: Boolean,
default: false,
},
crossOriginError: {
type: Boolean,
default: false,
}
},
data() {
return {
Expand Down
39 changes: 38 additions & 1 deletion resources/assets/js/videos/videoContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,28 @@ export default {
swappingLabel: false,
disableJobTracking: false,
supportsJumpByFrame: false,
hasCrossOriginError: false,
};
},
provide() {
const appData = {}

let videoInfo = {};
videoInfo['currentId'] = biigle.$require('videos.id');
videoInfo['ids'] = biigle.$require('videos.videoIds');
videoInfo['filenames'] = biigle.$require('videos.videoFilenames');
videoInfo['type'] = 'video';
videoInfo['fileChangedEvent'] = 'video.change';
videoInfo['mapChangedEvent'] = 'videos.map.init';

// Need defineProperty to maintain reactivity.
// See https://stackoverflow.com/questions/65718651/how-do-i-make-vue-2-provide-inject-api-reactive
Object.defineProperty(appData, "info", {
get: () => videoInfo,
})

return { 'files': appData };
},
computed: {
selectedAnnotations() {
return this.filteredAnnotations.filter((a) => a.isSelected);
Expand Down Expand Up @@ -560,7 +580,19 @@ export default {
.then(this.maybeFocusInitialAnnotation)
.then(this.maybeInitCurrentTime);

this.video.src = this.videoFileUri.replace(':id', video.id);
let self = this;
fetch(this.videoFileUri.replace(':id', video.id))
.then((res) => {
res.blob().then(function (blob) {
let urlCreator = window.URL || window.webkitURL;
self.video.src = urlCreator.createObjectURL(blob);
});
})
.catch((e) => {
// Access on non-CORS enabled file causes TypeError
this.hasCrossOriginError = e instanceof TypeError;
self.video.src = self.videoFileUri.replace(':id', video.id);
});
Comment on lines +584 to +595
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tries to download the whole video. Usually the video is fetched dynamically through a series of HTTP range requests, which is crucial here.

You need another method to detect CORS. Maybe create a small canvas element and try to read some data from the video into it?


return promise;
},
Expand Down Expand Up @@ -692,6 +724,9 @@ export default {
UrlParams.set(params);
},
},
videoId() {
Events.$emit('video.change', this.videoId, this.video);
}
},
created() {
let shapes = biigle.$require('videos.shapes');
Expand Down Expand Up @@ -752,6 +787,8 @@ export default {
if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1){
Messages.danger('Current versions of the Firefox browser may not show the correct video frame for a given time. Annotations may be placed incorrectly. Please consider using Chrome until the issue is fixed in Firefox. Learn more on https://github.com/biigle/core/issues/391.');
}
Events.$emit('videos.map.init', this.$refs.videoScreen.map);

},
};
</script>
7 changes: 7 additions & 0 deletions resources/views/videos/show/sidebar-settings.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
<settings-tab inline-template
v-on:update="handleUpdatedSettings"
:supports-jump-by-frame="supportsJumpByFrame"
:cross-origin-error="hasCrossOriginError"
>
<div class="annotator-tab settings-tab">
<div class="sidebar-tab__section">
<button v-if="crossOriginError" class="btn btn-default" title="Screenshots are not available for remote videos without cross-origin resource sharing" disabled="disabled" ><span class="fa fa-camera" aria-hidden="true"></span> Capture screenshot</button>
<screenshot-button v-else inline-template>
<button class="btn btn-default" title="Get a screenshot of the visible area 𝗣" v-on:click="capture"><span class="fa fa-camera" aria-hidden="true"></span> Capture screenshot</button>
</screenshot-button>
</div>

<div class="sidebar-tab__section">
<h5 title="Set the opacity of annotations">Annotation Opacity (<span v-text="annotationOpacity"></span>)</h5>
Expand Down
Loading