Un message de drapeau blanc, ou Comment j'ai sauvé votre cours vidéo d'apparaître sur le tracker

captain_barbossa.jpg

Bonjour, Habr! Hmm, j'ai l'impression que nous nous sommes déjà rencontrés ... Ah, oui. Voici le post où nous avons discuté de manière ironique s'il est acceptable de surveiller l'environnement, de limiter l'utilisateur au nombre d'appareils à visualiser, de fournir des fichiers exécutables au lieu de vidéos payantes et de se comporter différemment de toutes les manières possibles lors de l'organisation de la "protection" des cours vidéo contre le piratage.

Et tout irait bien, mais il est tout simplement impossible de critiquer sans proposer une solution en retour. "Pouvez-vous faire mieux?" - des exclamations des commentaires ont été entendues. "Il vaudrait mieux soutenir un compatriote, aider à améliorer son produit!" - Je revois brièvement quelques réflexions générales. Juste. Donc, je peux vraiment faire mieux . À tout le moins, ma proposition n'exigera pas que l'utilisateur final exécute un logiciel tordu au lieu des fichiers vidéo attendus.

Solution à tous les problèmes


Et la solution est la plus banale, les amis: les filigranes. Oui, juste des filigranes. Au lieu de proposer des mécanismes complexes de liaison à un appareil spécifique, «signez» la séquence vidéo. C'est tout.

Quelles propriétés un filigrane doit-il avoir pour remplir une fonction défensive:

  1. Le filigrane doit contenir des informations qui identifient de manière unique l'utilisateur qui a acheté le cours vidéo. Cela peut être une clé d'activation délivrée à l'utilisateur, ou la connexion de l'utilisateur obtenue lors de l'inscription sur le site Web d'achat de cours vidéo, ou des horodatages correspondant au moment de l'achat du cours (bien sûr, si vous pouvez les corréler sans ambiguïté avec l'identité de l'acheteur), ou quoi que ce soit de cet opéra.
  2. Le filigrane doit couvrir la majeure partie de l'image afin qu'il ne puisse pas être découpé sans pertes importantes pour le cours vidéo.
  3. Le schéma de superposition de filigrane doit être aléatoire pour chaque copie du cours afin que le méchant n'écrive pas une machine automatisée pour découper le même filigrane.

Si vous rendez le filigrane très transparent, sa présence n'interférera pas avec l'utilisateur, mais cela vaut la peine de le mentionner dans la description du cours avant le paiement .

Ainsi, afin d'extraire des informations révélatrices, un pirate potentiel devrait suivre l'un des scénarios décrits ci-dessous:

  1. Découpez l' intégralité du filigrane (rappelez-vous que selon la 2ème propriété, le filigrane devrait occuper tout l'écran et continuer à remplir ses fonctions de protection même s'il est partiellement effacé), invalidant ainsi le clip vidéo (à mon avis, il est logique que dans le cas où il n'y a pas le plus vidéo, la vidéo n'a aucune valeur).
  2. Modifiez chaque image individuellement pour vous débarrasser du filigrane sans endommager considérablement la vidéo. La complexité de l'exécution manuelle d'une telle action dépasse la création d'une vidéo à partir de zéro, et selon la troisième propriété, l'intrus n'a aucun moyen d'automatiser le processus.
  3. (?) Je suppose que vous pouvez demander à un réseau neuronal intelligent de le faire pour vous. Bien que pas sûr, pas un spécialiste, vous pouvez me corriger dans les commentaires.

Preuve de concept


En une demi-heure, un script trivial de 100 lignes a été compilé, démontrant la simplicité et l'accessibilité de la mise en œuvre d'une telle protection. J'insiste : pour ne pas montrer à quel point je suis intelligent, mais même au contraire, pour noter qu'une personne très éloignée du traitement d'image a pu composer un code pleinement fonctionnel (sous le spoiler) en une demi-heure, c'est aussi simple que cela:

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() 


Le résultat du script, sur cet exemple comme exemple:

sample_original.gif

sample_signed.gif


Pas pour le battage médiatique, mais seulement pour le bien commun.

J'ai l'honneur.

Source: https://habr.com/ru/post/fr450344/


All Articles