The Nexguard library forensic watermarking tool Quickmark embeds a unique, invisible serial number onto video/audio content.

The minimum Android API for using the watermarking feature is 21 (Lollipop 5.0).
Watermark requires Android System Webview Ver 42 or higher to be installed on the device.
Having any UI element rendered over the OTVVideoView may prevent the watermark from working properly.

Prerequisites

A working watermark requires the following parameters to work properly:

  • A Context to call OTVWatermark constructor
  • A valid OTVVideoView to bind the watermark to
  • A valid customer/system generated token of type String
  • A valid URL of type String
  • A valid Tenant name of type String

The last optional value is to set the ApiKey which is also of type String, but this parameter is not mandatory. If these values available, a working QuickMarkView can be created.
The core class of the watermarking function is Watermark. Its purpose is to take the above parameters passed by the user to create a new object of the QuickMarkView class and bind it to the provided OTVVideoView before starting the watermark playback. The class is also responsible for handling error message events and start/stopping the QuickMarkView whenever video playback is play/paused.

Dependencies

The Quickmark library (current version 1.1.5) is embedded within the SDK .aar library, there is no need to include or import additional libraries to your project.

Example code

Attaching a watermark is quite simple if you have the five parameters above. Since OTVVideoView is required to bind/unbind the QuickMarkView, it is recommended to tie the watermark’s lifecycle to the OTVVideoView’s.
Below is a simple example of implementing the watermark feature:

Create/bind watermark

 ...
import nagra.otv.sdk.OTVLog;
import nagra.otv.sdk.OTVSDK;
import nagra.otv.sdk.OTVVideoView;
import nagra.otv.sdk.Watermarking.Watermark;
import nagra.otv.sdk.Watermarking.WatermarkErrorId;
import nagra.otv.sdk.Watermarking.WatermarkErrorListener;
import nagra.otv.sdk.Watermarking.WatermarkMessageListener;
...
    private static String TAG = "Watermarking";

    private static String TOKEN      = "TOKEN";
    private static String API_KEY    = "API_KEY";
    private static String TENANT_ID  = "TENANT_ID";
    private static String SERVER_URL = "SERVER_URL";

    private OTVideoView mVideoView = null;
    private Watermark mWatermark


    // your function that creates/initialises your video view.
    // Once the video view is setup you can call watermark to bind it to the player
    private void initVideoView() {
        // The OTVSDK.load() method must be called before the SDK can be used
        // You may wish to do this in an application class
        // OTVSDK expects to find the player licence as an opy_licence in the res/raw directory
        OTVSDK.load(getActivity());
        ...
        mVideoView = (OTVVideoView) xView.findViewById(R.id.otvVideoView);
        //some video view initialising code
        ...
        startWatermark();
    }

    private void startWatermark() {
        mWatermark = new Watermark(getContext());
        mWatermark.addErrorListener(mErrorListener);
        mWatermark.addMessageListener(mMessageListener);
        mWatermark.setToken(TOKEN);
        mWatermark.setApiKey(API_KEY);
        mWatermark.setTenant(TENANT_ID);
        mWatermark.setUrl(SERVER_URL);
        mWatermark.bind(mVideoView);
    }

    /*
    *Create a watermark error listener to react to server watermark responses
    */
    private WatermarkErrorListener mErrorListener = new WatermarkErrorListener() {
        @Override
        public void onError(WatermarkErrorId watermarkErrorId, String s) {
            switch (watermarkErrorId) {
            case INVALID_TOKEN:
            case INVALID_URL:
            case INVALID_SERVER_ANSWER:
            case CANNOT_CONTACT_SERVER:
                yourErrorHandlingFunction(s);
                break;
            case NO_ERROR:
            default:
                noErrorFunction();
            }
        }
    };

    private WatermarkMessageListener mMessageListener = new WatermarkMessageListener() {
        @Override
        public void onMessage(String s) {
            OTVLog.d(TAG, "watermark message: " + s);
            yourMessageHandler(s);
        }
    };

 

All mWatermark configuration must be set prior to the binding call.

Destroy/unbind watermark

Assuming that the watermark has been constructed as referenced above then the code below should be run when the OTVVideoView is being destroyed to keep the watermark lifecycle matching with the video lifecycle.

     private void destroyVideoView() {
      //some video view destruction code
      ...
      stopWatermark();
      mWatermark = null;
    }

    /*
    * stop the watermark view and remove all listeners then unbind the OTVVideoView.
    */
    private void stopWatermark(){
      if(mWatermark != null && mWatermark.boundPlayer() == mVideoView) {
        mWatermark.removeErrorListener(mErrorListener);
        mWatermark.removeMessageListener(mMessageListener);
        mWatermark.unbind(mVideoView);
      }
    }

 

Limitations

Currently, the only cases that trigger an error are:

  • Null tenant value
  • Null token value
  • Null server URL value
  • No server response
  • A URL shorter than four characters

In particular, the following errors do not get thrown (errors are silently ignored):

  • Invalid URL string
  • Incorrect token value

Future versions of the NexGuard implementation are expected to address some of these issues.