Annotate videos

WebViewer Video allows you to create annotations in a video file and attach them to a specific timeframe. Try it out in our Video Annotation Demo now. It leverages some of the familiar WebViewer annotation tools like Note, Free Text, Rectangle and Free Hand. Video annotations comes out of the box with zero configuration.

Apryse Docs Image

It is also possible to control the amount of time said annotation will be displayed on video playback. After the annotation is created, click on the desired annotation on top of the timeline reel and use the directional arrows to expand or contract the amount of time it will be displayed.

Apryse Docs Image

Annotations can also have comments attached to it, initially ordered by timeframe (bottom being the earliest to show on video). Those are searchable and can be further sorted by position, time, status, author, type and timecode.

Apryse Docs Image

A common use case is to save and retrieve the annotations. Just like on WebViewer, this data is stored in the format of a XFDF string that can be exported using the AnnotationsManager's exportAnnotations API.

One example is to have a custom save button added to the WebViewer's header using the setHeaderItems API that triggers a call to exportAnnotations and sending a POST request to your server to store it.

Assuming that there is a previously setup server running and a POST endpoint that accepts a body containing the XFDF string, you can achieve with the following code snippet:

JavaScript

1// setHeaderItems is a function that can be extracted from a WebViewer instance
2setHeaderItems((header) => {
3 header.push({
4 type: "actionButton",
5 img: "<svg>...</svg>",
6 onClick: async () => {
7 // Save annotations when button is clicked
8 // widgets and links will remain in the document without changing so it isn't necessary to export them
9
10 // Make a POST request with XFDF string
11 const saveXfdfString = (documentId, xfdfString) => {
12 return new Promise((resolve) => {
13 fetch(`/server/annotationHandler.js?documentId=${documentId}`, {
14 method: "POST",
15 body: xfdfString,
16 }).then((response) => {
17 if (response.status === 200) {
18 resolve();
19 }
20 });
21 });
22 };
23
24 // Step 1: Get a list of annotations
25 const annotations = docViewer.getAnnotationManager().getAnnotationsList();
26
27 // Step 2: Export to a string
28 var xfdfString = await annotManager.exportAnnotations({
29 links: false,
30 widgets: false,
31 annotList: annotations,
32 });
33
34 // Step 3: Call your endpoint and save it
35 await saveXfdfString(DOCUMENT_ID, xfdfString);
36 alert("Annotations saved successfully.");
37 },
38 });
39});

To load the annotations, the process is quite similar. It involves making a GET request to your server to retrieve the XFDF string, use the AnnotationManager's importAnnotations and finally call video.updateAnnotationsToTime(0) to apply the annotations to the specific timeframes that they were created.

These calls need to be done after the video has been loaded. You can safely do these steps inside docViewer's videoElementLoaded event like the following code snippet example:

1docViewer.addEventListener("videoElementLoaded", () => {
2 const video = docViewer.getDocument().getVideo();
3
4 // Make a GET request to get XFDF string
5 const loadXfdfString = (documentId) => {
6 return new Promise((resolve) => {
7 fetch(`/server/annotationHandler.js?documentId=${documentId}`, {
8 method: "GET",
9 }).then((response) => {
10 if (response.status === 200) {
11 response.text().then((xfdfString) => {
12 resolve(xfdfString);
13 });
14 }
15 });
16 });
17 };
18
19 // Step 1: Make a request to your server to get the XFDF string
20 loadXfdfString(DOCUMENT_ID)
21 .then((xfdfString) => {
22 // Step 2: Call importAnnotations to save annotations to AnnotationManager
23 const annotManager = docViewer.getAnnotationManager();
24 return annotManager.importAnnotations(xfdfString);
25 })
26 .then(() => {
27 // Step 3: Apply the annotations to the video timeframes
28 video.updateAnnotationsToTime(0);
29 });
30});

Programmatically adding Annotations

Annotations can also be added programmatically.

JavaScript (v8.0+)

1docViewer.on("videoElementLoaded", () => {
2 const annot = new instance.Core.Annotations.RectangleAnnotation({
3 Author: 'Test Author',
4 Subject: 'Rectangle',
5 ToolName: 'AnnotationCreateRectangle',
6 Width: 231.5,
7 Height: 182.5,
8 X: 321,
9 Y: 40,
10 Listable: true,
11 Color: new instance.Core.Annotations.Color(78, 125, 233, 1),
12 StrokeColor: new instance.Core.Annotations.Color(78, 125, 233, 1),
13 StrokeThickness: 5,
14 Opacity: 1,
15 });
16
17 // Annot time is in seconds
18 annot.setStartTime(0);
19 annot.setEndTime(100);
20
21 const video = getVideo();
22 annotManager.addAnnotations([ annot ]);
23
24 // Apply the annotations to the video timeframes
25 video.updateAnnotationsToTime(0);
26});

For a complete working sample, you can check out the official WebViewer Video Sample.

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales