Contact Support | System Status
Page Contents

    Overview: Forensic Watermarking

    In this topic, you will get a high-level overview of Brightcove's Forensic Watermarking feature.

    Introduction

    Brightcove has partnered with NAGRA to provide forensic watermarking as a feature in the Video Cloud platform. This will help protect your premium content from piracy and unauthorized content sharing. This feature also helps to quickly identify the source of a content leak, so that action can be taken.

    Setup

    The following setup is needed to support Brightcove's Forensic Watermarking solution:

    1. Contact your account manager:
      1. Make sure your account is enabled for Dynamic Delivery.
      2. Enable your account for Forensic Watermarking; This is a paid add-on to Video Cloud.
    2. Get your License Key from NAGRA.
    3. Generate a public-private key pair which will be used by the Forensic Watermarking JWT and decrypted by the CDN. (see below)
    4. Use the script provided by NAGRA to generate a JSON web token (JWT) for forensic watermarking. Be sure to manage how the forensic watermark token and each viewer are linked. You will need it when you configure your players and in the case of doing a detection service to know which viewer leaked the content illegally.

    There are many ways to generate the public-private key pair. Here are some examples:

    Example bash script:

    Example script to generate the key pair:

    #!/bin/bash
    set -euo pipefail
    
    NAME=${1:-}
    test -z "${NAME:-}" && NAME="brightcove-forensic-watermarking-key-$(date +%s)"
    mkdir "$NAME"
    
    PRIVATE_PEM="./$NAME/private.pem"
    PUBLIC_PEM="./$NAME/public.pem"
    PUBLIC_TXT="./$NAME/public_key.txt"
    
    ssh-keygen -t rsa -b 2048 -m PEM -f "$PRIVATE_PEM" -q -N ""
    openssl rsa -in "$PRIVATE_PEM" -pubout -outform PEM -out "$PUBLIC_PEM" 2>/dev/null
    openssl rsa -in "$PRIVATE_PEM" -pubout -outform DER | base64 > "$PUBLIC_TXT"
    
    rm "$PRIVATE_PEM".pub
    
    echo "Public key to saved in $PUBLIC_TXT"
    

    Run the script:

    $ bash keygen.sh
    
    Example using Go

    Example using the Go programming language to generate the key pair:

    package main
      
      import (
        "crypto/rand"
        "crypto/rsa"
        "crypto/x509"
        "encoding/base64"
        "encoding/pem"
        "flag"
        "fmt"
        "io/ioutil"
        "os"
        "path"
        "strconv"
        "time"
      )
      
      func main() {
        var out string
      
        flag.StringVar(&out, "output-dir", "", "Output directory to write files into")
        flag.Parse()
      
        if out == "" {
          out = "rsa-key_" + strconv.FormatInt(time.Now().Unix(), 10)
        }
      
        if err := os.MkdirAll(out, os.ModePerm); err != nil {
          panic(err.Error())
        }
      
        priv, err := rsa.GenerateKey(rand.Reader, 2048)
        if err != nil {
          panic(err.Error())
        }
      
        privBytes := x509.MarshalPKCS1PrivateKey(priv)
      
        pubBytes, err := x509.MarshalPKIXPublicKey(priv.Public())
        if err != nil {
          panic(err.Error())
        }
      
        privOut, err := os.OpenFile(path.Join(out, "private.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
        if err != nil {
          panic(err.Error())
        }
      
        if err := pem.Encode(privOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes}); err != nil {
          panic(err.Error())
        }
      
        pubOut, err := os.OpenFile(path.Join(out, "public.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
        if err != nil {
          panic(err.Error())
        }
      
        if err := pem.Encode(pubOut, &pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes}); err != nil {
          panic(err.Error())
        }
      
        var pubEnc = base64.StdEncoding.EncodeToString(pubBytes)
      
        var pubEncOut = path.Join(out, "public_key.txt")
        if err := ioutil.WriteFile(pubEncOut, []byte(pubEnc+"\n"), 0600); err != nil {
          panic(err.Error())
        }
      
        fmt.Println("Public key saved in " + pubEncOut)
      }
      

    Example using node.js

    Example using node.js to generate the key pair:

    var crypto = require("crypto");
      var fs = require("fs");
      
      var now = Math.floor(new Date() / 1000);
      var dir = "rsa-key_" + now;
      fs.mkdirSync(dir);
      
      crypto.generateKeyPair(
        "rsa",
        {modulusLength: 2048},
        (err, publicKey, privateKey) => {
          fs.writeFile(
            dir + "/public.pem",
            publicKey.export({ type: "spki", format: "pem" }),
            err => {}
          );
          fs.writeFile(
            dir + "/public_key.txt",
            publicKey.export({ type: "spki", format: "der" }).toString("base64") +
              "\n",
            err => {}
          );
          fs.writeFile(
            dir + "/private.pem",
            privateKey.export({ type: "pkcs1", format: "pem" }),
            err => {}
          );
        }
      );
      
      console.log("Public key saved in " + dir + "/public_key.txt");

    Implementation

    You can implement Forensic Watermarking with either Video Cloud Studio, or you can do it manually using the Brightcove APIs. The methods are detailed in the following sections.

    Video Cloud Studio

    Follow these steps to use Forensic Watermarking with Video Cloud Studio:

    1. Forensic Watermarking is not linked to a specific ingest profile. You can use any Dynamic Delivery or Context Aware Encoding profile.

      1. Provide Brightcove with your NAGRA License Key. This License Key will be used during transcoding to generate the watermark.
      2. In the Upload module, select your ingest profile.
      3. Check the Enable Forensic Watermark option.

        Enable watermarking option
        Enable watermarking option
      4. Add the tag watermarked to the video, as this will make it much easier to identify the videos with forensic watermarking and to generate analytics reports on them:
        Add Tag
        Add Tag
      5. Drag or browse for you video to start ingestion.

    2. Configure your player.

      This is where you will pass the NAGRA watermark token and the Viewer ID to the player. You can use either the Brightcove web player or the Native SDK players:

    3. Play content that has been ingested for Forensic Watermarking.

    Analytics reports

    Analytics reports on viewers for forensic watermarked videos are available via the Analytics API only. Reports can be returned in JSON, CSV, or XLXS format

    In the Analytics data:

    1 Transaction = 1 unique Viewer per video in 24 hours

    Example 1: viewers for a video by date

    For this example, we will query the video, viewer and date dimensions and get the report back in CSV format.

    Sample request

    https://analytics.api.brightcove.com/v1/data?accounts=4800266849001&from=2021-06-23&to=2021-06-25&dimensions=date,viewer&limit=10&where=video==70702952720202&fields=video,viewer,video_view&format=csv

    Response

    "date","video","viewer","video_view"
    "2021-06-25","70702952720202","3f46037f932b0c5a","1"
    "2021-06-24","70702952720202","3f46037f932b0c5a","2"

    Note that the viewer is generated by the Brightcove player and set to a unique string based on user-agent and IP. Ideally you should set the viewer identifier yourself based on a sign-in id - see this topic for information on how to do that. Your identifier will be based on information that can be retrieved via JavaScript.

    Example 2: viewer count

    In this example, we will assume that all videos with forensic watermarking have the tag watermarked. We will again query the video, viewer and date dimensions and just return one item, as it is the summary we are interested in here:

    Sample request

    https://analytics.api.brightcove.com/v1/data?accounts=4800266849001&from=2021-06-23&to=2021-06-25&dimensions=date,viewer&limit=1&where=video.q==tags:watermarked

    Response

    {
      "item_count": 7,
      "items": [
        {
          "date": "2021-06-25",
          "viewer": "07B1489C-5786-400E-945B-ABB3559B3897",
          "video_view": 1
        }
      ],
      "summary": {
        "video_view": 25
      }
    }

    APIs

    Here are the APIs and fields related to Forensic Watermarking:

    Dynamic Ingest API

    Once an account is enabled for forensic watermarking, you can add forensic watermarking to a video by adding the forensic_watermarking field to your ingest request, and setting it to true. This can be done for the original ingestion, replacing, or retranscoding the video.

    Sample request body

    {
      "master": {
              "url": "http://solutions.brightcove.com/video/Walking_Dead_609.mp4",
              "audio_tracks": [
                  {
                      "language": "en",
                      "variant": "main"
                  }
              ]
          },
        "profile": "multi-platform-standard-static-with-mp4",
        "forensic_watermarking": true,
        "capture-images": true
    }

    There is also a forensic_watermarking_stub_mode flag which, when set to true produces visible forensic watermarks:

    Visible Forensic Watermarks
    Visible Forensic Watermarks

    forensic_watermarking must also be set to true to enable visible watermarks.

    Use visible watermarks on a video to test integrations (you should test with a video at least 10 minutes long). Once that you have verified that the forensic watermarks are present, you will want to remove them to have only invisible forensic watermarks. To do this, you will need to submit another Dynamic Ingest request to retranscode the video, this time setting forensic_watermarking_stub_mode to false.

    Sample request body for visible forensic watermarks

    {
      "master": {
              "url": "http://solutions.brightcove.com/video/Walking_Dead_609.mp4",
              "audio_tracks": [
                  {
                      "language": "en",
                      "variant": "main"
                  }
              ]
          },
        "profile": "multi-platform-standard-static-with-mp4",
        "forensic_watermarking": true,
        "forensic_watermarking_stub_mode": true,
        "capture-images": true
    }

    Sample request body to remove visible forensic watermarks

    {
        "profile": "multi-platform-standard-static-with-mp4",
        "forensic_watermarking": true,
        "forensic_watermarking_stub_mode": false,
        "capture-images": true
    }

    CMS API

    By default, forensic watermarked renditions will be delivered if forensic watermarking was requested during ingestion. The presence of forensic watermarks will be indicated by the read-only field forensic_watermarking. If the field has a value of ACTIVE, the forensic watermark was added to VOD. If the field has a value of UNAVAILABLE or null, the VOD doesn’t have the forensic watermark.

    Playback API

    The Playback API GET video responses will also contain a forensic_watermarking (if the account is enabled for forensic watermarking). If the value is ACTIVE, renditions with forensic watermarks will be delivered as sources to the player. Otherwise, non-watermarked renditions will be delivered.

    Zencoder

    To generate outputs with forensic watermarking in Zencoder, you need to specify 2 outputs per video, one with "forensic_watermark": "A" and the other with "forensic_watermark": "B" (these are the only two values the forensic_watermark field can have).

    Sample request (outputs only)

    {
      "outputs": [
          {
              "base_url": "s3://urlTest",
              "filename": "contextAwareEncoding1_A.m4f",
              "public": false,
              "format": "m4f",
              "label": "m4f-contextAwareEncoding1-A-b30e1",
              "generate_mp4_atom_map": true,
              "Mp4_atom_map_filename": "contextAwareEncod-A-atom_map.json",
              "skip_audio": true,
              "forensic_watermark": "A",
              "dynamic_profile_rendition": 1,
              "skip": {
                  "require_video": true
              },
              "fragment_duration": 2000,
              "segment_seconds": 2,
              "headers": {
                  "x-amz-server-side-encryption": "AES256"
              }
          },
          {         
              "base_url": "s3://urlTest",
              "filename": "contextAwareEncoding1_B.m4f",
              "public": false,
              "format": "m4f",
              "label": "m4f-contextAwareEncoding1-B-e348",
              "generate_mp4_atom_map": true,
              "mp4_atom_map_filename": "contextAwareEncod-B-atom_map.json",
              "skip_audio": true,
              "forensic_watermark": "B",
              "dynamic_profile_rendition": 1,
              "skip": {
                  "require_video": true
              },
              "fragment_duration": 2000,
              "segment_seconds": 2,
              "headers": {
                  "x-amz-server-side-encryption": "AES256"
              }
          }
      ]
    }

    Visible watermarks for testing

    For testing purposes, to verify that forensic watermarking is present, you can force visible forensic watermarking by adding "forensic_watermark_stub_mode":true to outputs as shown in the sample below:

    {
      "outputs": [
          {
              "base_url": "s3://urlTest",
              "filename": "contextAwareEncoding1_A.m4f",
              "public": false,
              "format": "m4f",
              "label": "m4f-contextAwareEncoding1-A-b30e1",
              "generate_mp4_atom_map": true,
              "Mp4_atom_map_filename": "contextAwareEncod-A-atom_map.json",
              "skip_audio": true,
              "forensic_watermark": "A",
              "forensic_watermark_stub_mode":true,
              "dynamic_profile_rendition": 1,
              "skip": {
                  "require_video": true
              },
              "fragment_duration": 2000,
              "segment_seconds": 2,
              "headers": {
                  "x-amz-server-side-encryption": "AES256"
              }
          },
          {         
              "base_url": "s3://urlTest",
              "filename": "contextAwareEncoding1_B.m4f",
              "public": false,
              "format": "m4f",
              "label": "m4f-contextAwareEncoding1-B-e348",
              "generate_mp4_atom_map": true,
              "mp4_atom_map_filename": "contextAwareEncod-B-atom_map.json",
              "skip_audio": true,
              "forensic_watermark": "B",
              "forensic_watermark_stub_mode":true,
              "dynamic_profile_rendition": 1,
              "skip": {
                  "require_video": true
              },
              "fragment_duration": 2000,
              "segment_seconds": 2,
              "headers": {
                  "x-amz-server-side-encryption": "AES256"
              }
          }
      ]
    }

    Supported features

    • DRM
    • Playback Restrictions
    • Re-transcoding
    • Source file replacement
    • CAE
    • Offline Download
    • Chromecast
    • Airplay

    Limitations

    • Only VOD is supported at this time. Forensic watermarking for Live streams is not supported.
    • Videos must be at least five minutes long to have forensic watermarking applied.
    • HLSv7 is not supported. HLSv7 is used to deliver HEVC, mixed codecs, and 5.1 audio - therefore HEVC, mixed-codec video, and 5.1 audio are currently not supported.
    • Encrypted HLS videos (HLSe) are not currently supported.
    • This feature requires a NAGRA License Key.
    • Players will require a forensic watermarking JWT that you will create using a script provided by NAGRA.
    • Delivery rules cannot be used with players used to deliver videos with forensic watermarking.
    • You must use Brightcove's CDN. BYO CDN is not supported.
    • Brightcove will not handle detection analysis. For this, contact NAGRA.

    Page last updated on 15 Jul 2021