Video Redaction

WebViewer Video uses ffmpeg in the background to allow for redaction of video frames. In order to utilize ffmpeg, a server must be setup to allow for it. We have created a sample server for you to use and deploy. We recommend hosting the server on AWS. You can also try this now with our Video Redaction Demo.

Before you begin, make sure your development environment includes Node.js, npm and ffmpeg.

Sample redaction page

The WebViewer-Video github sample has a redaction sample ready to go. It will initialize and run the client and server components. We recommend running that sample directly. To do so, first clone repository and then run the following commands:

sh

1# command line
2npm i
3npm run start-samples

The steps below will recreate the sample. Use them to integrate redaction into you application.

Integration

WebViewer Video uses ffmpeg in the background to allow for redaction of video frames. In order to utilize ffmpeg, a server must be setup on the user side to allow for it. Find a sample server on Github. Please follow installation steps there before continuing. The example code below shows how to integrate the server within your application:

Initial setup

Before you begin, make sure your development environment includes Node.js, npm and ffmpeg.

How to use

Below is an example of how to integrate the server with WebViewer Video. In this example, we used update element to overload the click event of the Redact All button. This click event will now make a call to our custom server where the redaction of the video will take place with ffmpeg.

Apryse Docs Image

JavaScript (v8.0+)

1import WebViewer from '@pdftron/webviewer';
2import { initializeVideoViewer } from '@pdftron/webviewer-video';
3WebViewer({
4 path: '/webviewer/lib',
5 enableRedaction: true,
6 },
7 viewer.current,
8).then(async instance => {
9 // Extends WebViewer to allow loading HTML5 videos (.mp4, ogg, webm).
10 const {
11 UI,
12 loadVideo,
13 } = await initializeVideoViewer(
14 instance,
15 {
16 license: '---- Insert commercial license key here after purchase ----',
17 enableRedaction: true,
18 }
19 );
20 // Load a video at a specific url.
21 // Can be a local or public link
22 const videoUrl = 'https://pdftron.s3.amazonaws.com/downloads/pl/video/bunny-short.mp4';
23 loadVideo(videoUrl);
24
25 // Overloading the onclick function of redacting video button with custom code
26 // Function must be async and must return data when function is finished to remove loading spinner.
27 // enableRedaction must be set to true when initalizing WebViewer Video:
28 // https://docs.apryse.com/api/video/module-@pdftron_webviewer-video.html#.initializeVideoViewer__anchor
29 UI.updateElement('redactApplyButton', {
30 onClick: async redactAnnotations => {
31 const response = await fetch('http://localhost:3001/video/redact', {
32 method: 'POST',
33 body: JSON.stringify({
34 intervals: redactAnnotations.map(annotation => ({
35 start: annotation.getStartTime(),
36 end: annotation.getEndTime(),
37 shouldRedactAudio: annotation.shouldRedactAudio || annotation.redactionType === 'audioRedaction',
38 shouldRedactVideo: annotation.redactionType !== 'audioRedaction',
39 })),
40 url: videoUrl,
41 }),
42 headers: {
43 'Accept': 'application/json',
44 'Content-Type': 'application/json'
45 },
46 });
47
48 const videoBuffer = await response.arrayBuffer();
49
50 const newVideoBlob = new Blob([videoBuffer], { type: 'video/mp4' });
51 loadVideo(URL.createObjectURL(newVideoBlob));
52 return videoBuffer;
53 }
54 });
55});

How to persist changes on your server to client

Currently our server returns the video file by sending back a buffer to the client. You will find that code, in this file.

JavaScript (v8.0+)

1fs.readFile(`./tmp/${uuid}.mp4`, (err, data) => {
2 resolve(data);
3});

The issue here is that when redactions are applied to this buffer, we cannot send that local file back to the server so it must be persisted on the server through a variable or by uploading to the cloud in order to have further redactions applied to a previously edited video.

Here is an example of replacing the server code above with an upload to s3:

New server code:

JavaScript (v8.0+)

1return new Promise((resolve) => {
2 fs.readFile(`./tmp/${uuid}.mp4`, async (err, data) => {
3 await s3.upload({
4 Bucket: 'pdftron-media-demo-files',
5 Key: `${uuid}.mp4`,
6 Body: data
7 });
8
9 let s3URL = await s3.getSignedUrl({
10 Bucket: 'pdftron-media-demo-files',
11 Key: `${uuid}.mp4`,
12 });
13
14 resolve(s3URL);
15 });
16});

New client code:

JavaScript (v8.0+)

1import WebViewer from '@pdftron/webviewer';
2import { initializeVideoViewer } from '@pdftron/webviewer-video';
3WebViewer({
4 path: '/webviewer/lib',
5 enableRedaction: true,
6 },
7 viewer.current,
8).then(async instance => {
9 // Extends WebViewer to allow loading HTML5 videos (.mp4, ogg, webm).
10 const {
11 UI,
12 loadVideo,
13 } = await initializeVideoViewer(
14 instance,
15 {
16 license: '---- Insert commercial license key here after purchase ----',
17 enableRedaction: true,
18 }
19 );
20 // Load a video at a specific url.
21 // Can be a local or public link
22 let currentVideoUrl = 'https://pdftron.s3.amazonaws.com/downloads/pl/video/bunny-short.mp4';
23 loadVideo(currentVideoUrl);
24 // Overloading the onclick function of redacting video button with custom code
25 // Function must be async and must return data when function is finished to remove loading spinner.
26 // enableRedaction must be set to true when initalizing WebViewer Video:
27 // https://docs.apryse.com/api/video/module-@pdftron_webviewer-video.html#.initializeVideoViewer__anchor
28 UI.updateElement('redactApplyButton', {
29 onClick: async redactAnnotations => {
30 const response = await fetch('http://localhost:3001/video/redact', {
31 method: 'POST',
32 body: JSON.stringify({
33 intervals: redactAnnotations.map(annotation => ({
34 start: annotation.getStartTime(),
35 end: annotation.getEndTime(),
36 shouldRedactAudio: annotation.shouldRedactAudio || annotation.redactionType === 'audioRedaction',
37 shouldRedactVideo: annotation.redactionType !== 'audioRedaction',
38 })),
39 url: currentVideoUrl,
40 }),
41 headers: {
42 'Accept': 'application/json',
43 'Content-Type': 'application/json'
44 },
45 });
46 currentVideoUrl = await response.text();
47 loadVideo(currentVideoUrl);
48 return currentVideoUrl;
49 }
50 });
51});

Now when using the endpoint /video/redact, your redacted video will be uploaded to s3 and changes will be persisted on the client.

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales