
A white flag post, or How I saved your video course from appearing on the tracker

Hello, Habr! Hmm, I feel like we've already met ... Ah, yes. Here is the post where we discussed lampically whether it is acceptable to monitor the environment, limit the user to the number of devices for viewing, provide executable files instead of paid videos and behave differently in every possible way when organizing the "protection" of video courses from piracy.
And everything would be fine, but it’s just impossible to criticize without proposing a solution in return. “Can you do better, perhaps ?!” , exclamations from the comments were heard. “It would be better to support a compatriot, help make their product better!” , I briefly recount some general thoughts. Fair. So, I really can do better. At the very least, my proposal will not require the end user to run crooked software instead of the expected video files.
Solution to all problems
And the solution is the most trivial, friends: watermarks. Yes, just watermarks. Instead of coming up with complex mechanisms for binding to a specific device, “sign” the video sequence. That's all.
What properties should a watermark have in order to perform a defensive function:
- The watermark should contain information that uniquely identifies the user who purchased the video course. This can be an activation key issued to the user, or the user’s login obtained upon registration on the video course purchase website, or time stamps corresponding to the time of purchase of the course (of course, if you can unambiguously correlate them with the identity of the buyer), or anything from this opera.
- The watermark should cover most of the frame so that it cannot be cut out without major losses for the video course.
- The watermark overlay scheme should be random for each copy of the course so that the villain does not write an automated machine for cutting out the same watermark.
If you make the watermark very transparent, its presence will not interfere with the user, but it is still worth mentioning this in the course description before payment .
Thus, in order to extract revealing information, a potential pirate would need to follow one of the scenarios described below:
- Cut out the entire watermark (remember that according to the 2nd property, the watermark should occupy the entire screen and continue to perform its protective functions even if it is partially erased), thereby invalidating the video clip (in my opinion, it is logical that in the case when there is not most video, there’s no value of the video).
- Edit each frame individually to get rid of the watermark without causing significant damage to the video. The complexity of performing such an action manually exceeds the creation of a video from scratch, and according to the 3rd property, the intruder does not have the ability to automate the process.
- (?) I guess you can ask a smart neural network to do this for you. Although not sure, not a specialist, you can correct me in the comments.
Proof of concept
In half an hour, a trivial script of 100 lines was compiled, demonstrating the simplicity and affordability of implementing such protection. I emphasize : not to show how smart I am, but quite the contrary, to note that a person who is very far from image processing was able to compose a fully working code (under the spoiler) in half an hour, this is how simple it is:
fckInfoprotectorV2.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Usage: python3 fckInfoprotectorV2.py
import os
from shutil import rmtree
import numpy as np
import cv2
class VideoSigner:
def __init__(self, video, watermark):
os.makedirs('original')
os.makedirs('watermarked')
self.vidin = cv2.VideoCapture(video)
self.fps = self.vidin.get(cv2.CAP_PROP_FPS)
self.frame_size = (
int(self.vidin.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(self.vidin.get(cv2.CAP_PROP_FRAME_HEIGHT))
)
self.watermark = cv2.imread(watermark, cv2.IMREAD_UNCHANGED)
self.wH, self.wW = self.watermark.shape[:2]
B, G, R, A = cv2.split(self.watermark)
B = cv2.bitwise_and(B, B, mask=A)
G = cv2.bitwise_and(G, G, mask=A)
R = cv2.bitwise_and(R, R, mask=A)
self.watermark = cv2.merge([B, G, R, A])
def __del__(self):
rmtree('original')
rmtree('watermarked')
def _split(self):
print('[*] Splitting video by frames... ', end='', flush=True)
(success, image), count = self.vidin.read(), 0
while success:
path = os.path.join('original', f'{count}.jpg')
cv2.imwrite(path, image)
success, image = self.vidin.read()
count += 1
print('Done')
def _watermark(self):
print('[*] Signing each frame... ', end='', flush=True)
for image_name in sorted(
os.listdir('original'),
key=lambda x: int(x.split('.')[0])
):
image_path = os.path.join('original', image_name)
image = cv2.imread(image_path)
h, w = image.shape[:2]
image = np.dstack([
image,
np.ones((h, w), dtype='uint8') * 255
])
overlay = np.zeros((h, w, 4), dtype='uint8')
half_h_diff = (h - self.wH) // 2
half_w_diff = (w - self.wW) // 2
overlay[half_h_diff:half_h_diff + self.wH, half_w_diff:half_w_diff + self.wW] = self.watermark
output = image.copy()
cv2.addWeighted(overlay, 0.25, output, 1.0, 0, output)
path = os.path.join('watermarked', image_name)
cv2.imwrite(path, output)
print('Done')
def _merge(self):
print('[*] Merging signed frames... ', end='', flush=True)
self.vidout = cv2.VideoWriter(
'signed.avi',
cv2.VideoWriter_fourcc(*'XVID'),
fps=self.fps,
frameSize=self.frame_size
)
for image_name in sorted(
os.listdir('watermarked'),
key=lambda x: int(x.split('.')[0])
):
image_path = os.path.join('watermarked', image_name)
image = cv2.imread(image_path)
self.vidout.write(image)
print('Done')
def sign(self):
self._split()
self._watermark()
self._merge()
if __name__ == '__main__':
signer = VideoSigner('SampleVideo_1280x720_1mb.mp4', 'watermark.png')
signer.sign()
The result of the script, on this example as an example:


Not for hype, but only for the common good.
I have the honor.