Gravez une vidéo en temps réel avec AS3

Bonjour, freaks de code! Ce tutoriel vous montrera comment scinder une vidéo en cours d'exécution en blocs comme si elle avait explosé. Et tout cela en utilisant seulement ActionScript. Pour ce tutoriel, nous utiliserons l'appareil photo en tant que source vidéo afin que vous puissiez voir les modifications en direct..


Aperçu du résultat final

Jetons un coup d'œil au résultat final sur lequel nous allons travailler:

Cliquez et faites glisser un bloc pour le déplacer sur l'écran! (Accès à la caméra requis.)


Étape 1: Configuration - IDE

Pour ce tutoriel, nous allons utiliser l'EDI FlashDevelop (bien que vous puissiez utiliser n'importe quel éditeur AS3). Au cas où vous ne l'avez pas et que vous voulez essayer, vous pouvez le récupérer à partir d'ici. Un tutoriel de base sur la configuration de FlashDevelop sur votre machine est disponible ici.

De plus, si Flash Professional est installé sur votre côté, cela fonctionnera également. Tout ce que vous avez à faire est de créer un fichier de classe externe comme indiqué ci-dessous et de le lier à votre projet Flash en tant que classe Document..

Cela met en place notre environnement de travail.


Étape 2: Configuration - Nouveau projet

Créer un nouveau projet AS3 dans FlashDevelop.

Quand ce sera fait, vous aurez un Principale classe créée dans le dossier src, comme indiqué dans le panneau de droite:


Étape 3: Configuration - La classe principale

Ensuite, nous devons faire la Main.as déposer un peu plus propre en éliminant du code. Initialement, lorsque vous ouvrez le Main.as fichier, il aurait du code quelque chose comme ça:

 package import flash.display.Sprite; import flash.events.Event; classe publique Main étend Sprite fonction publique Main (): void if (stage) init (); sinon addEventListener (Event.ADDED_TO_STAGE, init);  fonction privée init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // point d'accès   

Nous allons supprimer une partie du code pour le rendre plus propre. Donc, vous devriez avoir ceci:

 package import flash.display.Sprite; import flash.events.Event; classe publique Main étend Sprite fonction publique Main (): void 

Maintenant, toute la configuration est terminée et il est temps de plonger dans un code.


Étape 4: Déclaration des variables vidéo et caméra

Notre premier objectif est de dessiner la vidéo sur scène à l'aide de la caméra. pour cela, nous devons déclarer certaines variables. Mettez ces déclarations juste au-dessus de la Principale constructeur de classe.

 // variables vidéo private var camW: int = 300; var camH privé: int = 300; var vidéo privée: vidéo;

camW - La largeur de la caméra / vidéo.

camH - La hauteur de la caméra / vidéo.

vidéo - Notre Vidéo instance de classe.


Étape 5: préparer la caméra de périphérique

Comme mentionné précédemment, nous utiliserons la sortie de la caméra dans la vidéo. Nous devons donc commencer par préparer l'appareil photo de l'appareil. Le code suivant devrait aller dans le constructeur de la classe.

 var camera: Camera = Camera.getCamera ();

Ici, nous instancions un Caméra instance et obtenir la caméra de périphérique disponible en utilisant la méthode statique getCamera () du Caméra classe.

 camera.setMode (camW, camH, 30);

Nous fournissons quelques réglages de caméra: largeur, hauteur et fps.

Notez que nous n'avons pas rendu la variable de caméra globale car nous n'avons pas besoin d'y accéder ailleurs que dans cette fonction, comme vous le verrez ensuite. Dans l'étape suivante, nous initialisons le vidéo variable.


Étape 6: Créez réellement la vidéo!

 vidéo = nouvelle vidéo (camW, camH); video.attachCamera (caméra);

Nous avons maintenant instancié le vidéo variable et utilisé le attachCamera () méthode pour y attacher la caméra. Cela signifie que la vidéo utilise maintenant la sortie de la caméra comme source.

Tout est fait avec la vidéo et la caméra. N'oubliez pas que vous devez importer les classes appropriées dans votre code. Votre code de classe complet devrait ressembler à ceci pour le moment:

 package import flash.display.Sprite; import flash.events.Event; import flash.media.Camera; import flash.media.Video; classe publique Main étend Sprite // variables vidéo private var camW: int = 300; var camH privé: int = 300; var vidéo privée: vidéo; fonction publique Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); vidéo = nouvelle vidéo (camW, camH); video.attachCamera (caméra); 

Si vous exécutez actuellement le projet (F5 ou CTRL + Entrée), il s'agira d'une étape vide, mais vous obtiendrez probablement une demande d'accès à la caméra car l'application tente d'accéder à la caméra du périphérique. Permettez-ceci.

La raison pour laquelle vous ne voyez rien, c'est parce que nous ne voulons pas montrer la vidéo et nous ne l'avons donc pas ajoutée à la scène (liste d'affichage). Il sera simplement utilisé comme source pour nos blocs séparés. Si vous voulez vérifier que tout fonctionne correctement, ajoutez simplement la ligne suivante à la fin du Principale constructeur:

 addChild (vidéo); // supprime cette ligne après le test

Étape 7: Déclaration des variables de blocage

Maintenant, nous créons les blocs - les parties distinctes de la vidéo. Et la première étape consiste à déclarer certaines variables nécessaires. Alors allez-y et additionnez les déclarations de variable suivantes juste en dessous des variables vidéo:

 // variables de bloc private var rows: int = 3; vars privés: int = 3; private var blockW: int = camW / cols; private var blockH: int = camH / rows; private var pointCollection: Object = new Object ();

rangées - Nombre de lignes pour diviser la vidéo en.

cols - Oui. Vous avez ça. Nombre de colonnes dans lesquelles scinder la vidéo.

bloc - La largeur de chaque bloc. Il s’agit d’une variable dérivée, car elle est simplement calculée en divisant la largeur totale (camW) par nombre de colonnes (cols).

bloc - La hauteur de chaque bloc i.e. camH divisé par rangées.

pointCollection - Variable finale mais la plus importante. C'est un tableau associatif que nous allons utiliser pour stocker le point correspondant de chaque bloc. Par exemple, si un bloc a un nom bloc12, alors nous stockerions le point correspondant p12 comme ça :

 pointCollection ["block12"] = p12; // les points sont des instances de la classe Point ici

Étape 8: Commencez à faire les blocs

Maintenant que nous avons défini les variables requises, nous commençons réellement à créer les blocs. Nous allons garder tout le code de création de bloc dans une fonction appelée initBlocks (). Cette fonction sera appelée à partir du Principale constructeur après avoir réglé la vidéo.

Alors, déclarons d'abord une fonction appelée initBlocks () juste après Principale constructeur.

 fonction privée initBlocks (): void pour (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  // code to create each block   

Remarquez les deux pour les boucles que nous avons placées à l'intérieur, ce qui nous aidera à créer les blocs dans une grille 2D, en lignes. Et puis ajoutez un appel à cette fonction à la fin de Principale():

 fonction publique Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); vidéo = nouvelle vidéo (camW, camH); video.attachCamera (caméra); initBlocks (); 

Étape 9: Composants d'un bloc

Avant de créer les blocs, comprenons en quoi consiste un seul bloc. Chaque bloc est en réalité:

  • UNE Lutin
  • avec un Bitmap à l'intérieur
  • qui à son tour a besoin d'un BitmapData

Penser à Lutin comme conteneur le plus à l'extérieur. Complètement vierge.

Pour dessiner le bloc, il nous faut un Bitmap qui montrera la sortie vidéo correspondante.

Et enfin, chaque Bitmap a besoin de quelques données pour y puiser. Cela est donné sous la forme de BitmapData.


Étape 10: Créer la base du bloc - Sprite

Comme nous en avons discuté, le premier composant d'un bloc est un Sprite. Alors laisse en créer un. Tout le code que nous écrivons pour créer le bloc doit être écrit à l'intérieur du pour boucles.

 var newBlock: Sprite = new Sprite ();

Nous créons une nouvelle instance du Lutin classe.

 newBlock.name = "block" + r + c;

Ensuite, nous nommons le sprite pour pouvoir le référencer plus tard dans le code. La convention de nommage est simple: un bloc à la ligne r et à la colonne c est nommé bloc + r + c (+ signifie concaténation). Donc, un bloc à la ligne 2 et à la colonne 1 est nommé bloc21.


Étape 11: le positionner

Après l'avoir créé, nous devons le positionner sur la scène en fonction de ses rangées et de ses colonnes. Ajoutons donc le code suivant.

 var p: Point = nouveau Point (c * blocW, r * blocH);

Nous utilisons un Point objet de classe pour stocker les coordonnées de tout point ici. Et donc nous créons une nouvelle instance de Point et passe c * bloc comme la valeur x et r * blockH comme la valeur y. Maintenant, les coordonnées peuvent être consultées simplement comme p.x et p.y et est utilisé plus tard pour extraire la région rognée de chaque bloc d'une image vidéo complète. Rappelez-vous que le point de chaque bloc correspond aux coordonnées du point en haut à gauche de la grille..

Si vous avez un doute sur le calcul de la position ici, l’illustration suivante indique clairement.

 newBlock.x = c * (blockW + 1) + 20; newBlock.y = r * (blockH + 1) + 20;

Ensuite, nous positionnons le sprite. Les ordonnances sont plus ou moins les mêmes que ce à quoi nous nous attendons maintenant nous ajoutons 20 pour donner un décalage. Nous ajoutons aussi 1 à la bloc et bloc pour séparer les blocs d'un pixel, comme cela est visible dans la démo ci-dessus.

 pointCollection [newBlock.name] = p;

Enfin, nous sauvegardons le point que nous avions calculé plus tôt dans la pointCollection.


Étape 12: Ajout du bitmap au bloc

Passons maintenant aux 2ème et 3ème composants du bloc.

 var bmpd: BitmapData = new BitmapData (blockW, blockH);

Nous créons d'abord un BitmapData exemple et passez la largeur de bloc requise et la hauteur que nous avions stockées auparavant. Comme discuté plus tôt, un BitmapData instance est nécessaire pour créer un Bitmap instance qui est passée dans le constructeur.

 var bmp: Bitmap = new Bitmap (bmpd); bmp.name = "myBmp";

Maintenant, nous créons un Bitmap instance et passer le précédemment créé bmpd dans le constructeur. Aussi, nous nommons le bitmap myBmp afin que nous puissions le référencer plus tard.

 newBlock.addChild (bmp); addChild (newBlock);

À la fin nous ajoutons le bitmap bmp comme un enfant de newBlock et newBlock lui-même comme l'enfant de la scène.

Pour être sûr que vous êtes sur la bonne voie, votre Main.as le code devrait ressembler à ceci:

 package import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.geom.Point; import flash.media.Camera; import flash.media.Video; classe publique Main étend Sprite // variables vidéo private var camW: int = 300; var camH privé: int = 300; var vidéo privée: vidéo; // variables de bloc private var rows: int = 3; vars privés: int = 3; private var blockW: int = camW / cols; private var blockH: int = camH / rows; private var pointCollection: Array = new Array (); fonction publique Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); vidéo = nouvelle vidéo (camW, camH); video.attachCamera (caméra); initBlocks ();  fonction privée initBlocks (): void pour (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  var newBlock:Sprite = new Sprite(); newBlock.name = "block" + r + c; var p:Point = new Point(c * blockW, r * blockH); newBlock.x = c * (blockW + 1) + 20; newBlock.y = r * (blockH + 1) + 20; pointCollection[newBlock.name] = p; var bmpd:BitmapData = new BitmapData(blockW, blockH); var bmp:Bitmap = new Bitmap(bmpd); bmp.name = "myBmp"; newBlock.addChild(bmp); addChild(newBlock);     

Étape 13: Mise à jour des blocs - Concept

Même si les blocs sont placés aux bons emplacements, nous ne voyons toujours rien dans la gestion du projet. C'est parce que nous n'avons toujours rien dessiné à l'intérieur des bitmaps de bloc.

Notre prochaine étape consiste à exécuter une boucle à exécution constante qui effectue les opérations suivantes:

  1. Obtenir l'image vidéo actuelle.
  2. Boucle à travers tous les blocs.
  3. Récupère le point et l'enfant bitmap de chaque bloc.
  4. Dessine la partie correspondante de l'image vidéo sur le bitmap du bloc.

Alors? Faisons le!

Avant d’implémenter le code de la boucle, il nous faut une fonction LOOP (un peu comme une boucle de jeu). Ajoutez la déclaration de fonction suivante sous le initBlocks () une fonction :

 fonction privée updateBlocks (e: Event): void 

Comme cela est visible dans le paramètre de fonction, cela ressemble à un écouteur d’événement et oui, c’est vrai. Ceci est une fonction d'écoute que nous allons attacher à la ENTER_FRAME événement de la scène. Pour attacher l’auditeur, ajoutez cette ligne à la fin du texte. Principale() constructeur.

 fonction publique Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); vidéo = nouvelle vidéo (camW, camH); video.attachCamera (caméra); initBlocks (); addEventListener (Event.ENTER_FRAME, updateBlocks); 

Étape 14: Capture du cadre

C’est la première opération que nous effectuons dans notre boucle - la updateBlocks () fonction qui est appelée sur chaque image. Mettez le code suivant à l'intérieur updateBlocks () une fonction.

 var srcBmpd: BitmapData = new BitmapData (camW, camH);

Toutes les données bitmap dans Actionscript 3.0 doivent être contenues dans un BitmapData par exemple et nous en créons un. Nous allons peupler cette instance avec les données d'image vidéo actuelles.

 srcBmpd.draw (vidéo);

Ici nous avons utilisé le dessiner() fonction de la BitmapData classe. Il nécessite un objet de la classe any qui implémente IBitmapDrawable interface. Pour par exemple. Sprite, MovieClip, BitmapData, etc. Ce que cela fait est simplement prendre les données visuelles de l'objet passé et les stocke dans le BitmapData exemple.

Alors maintenant, nous avons l'image vidéo actuelle (ou, vous pourriez dire, une capture d'écran) dans la variable srcBmpd.

Étape 15: passons en boucle

Comme nous devons mettre à jour chaque bloc, nous créons un double pour-boucle, semblable à celle que nous avons écrite pour la création des blocs. Alors allez-y et ajoutez-le.

La fonction devrait ressembler à ceci maintenant:

 fonction privée updateBlocks (e: Event): void var srcBmpd: BitmapData = new BitmapData (camW, camH); srcBmpd.draw (vidéo); pour (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  // update code here   

Étape 16: Récupérer le bitmap et le point du bloc

N'oubliez pas que nous avons nommé les blocs d'une certaine manière lors de leur création afin de pouvoir référencer n'importe quel bloc en utilisant son numéro de ligne et de colonne. C'est ce que nous allons utiliser maintenant pour obtenir la référence de chaque bloc.

 var b_mc: Sprite = this.getChildByName ("bloc" + r + c) en tant que Sprite;

Nous utilisons le getChildByName fonction de la étape (ce) qui renvoient une référence à un objet dont le nom correspond à la chaîne transmise. Nous avons aussi transtypé à Lutin classe juste pour être sûr que l'objet retourné est un Lutin. Maintenant la référence du bloc est dans la variable b_mc.

 var bmp: Bitmap = b_mc.getChildByName ("myBmp") en tant que Bitmap;

De la même manière, nous récupérons la référence au bitmap ajouté en tant qu'enfant du sprite de bloc..

 var p: Point = collectionCollection [b_mc.name];

Enfin, nous obtenons le bloc actuel (b_mc) coordonnées du tableau dans lequel nous l'avons stocké précédemment en utilisant le nom du bloc.

Étape 17: dessinez-le!

Maintenant que nous avons toutes les informations requises sur quoi dessiner où, nous pouvons en fait dessiner. Notre motif ici est d’obtenir la région rectangulaire de la trame vidéo (i.e. srcBmpd) avec le point en haut à gauche comme point récupéré p, largeur comme bloc et hauteur comme bloc.

Pour cela, nous utilisons le copyPixels () méthode du BitmapData classe. Il copie en fait la région d'une autre source BitmapData spécifié en passant un Rectangle objet.

 bmp.bitmapData.copyPixels (srcBmpd, nouveau Rectangle (p.x, p.y, blockW, blockH), new Point ());

le dessiner() la fonction est appelée bmpde bitmapData propriété. Les paramètres qui y sont passés sont:

  1. La source BitmapData obeject. La capture d'écran de la vidéo dans ce cas (srcBmpd).
  2. UNE Rectangle objet qui spécifie le point en haut à gauche, la largeur et la colonne de la région dans la source à copier.
  3. Le point de la destination où la partie coupée doit être copiée. (0,0) dans ce cas. Nous passons donc simplement une nouvelle Point objet.

Terminé! Il est maintenant temps de lancer votre projet et de voir l'effet impressionnant.

Étape 18: Ajout de la fonctionnalité glisser-déposer

Pour ajouter la fonctionnalité glisser-déposer comme on le voit dans la démo, il suffit d’attacher deux écouteurs de souris à chaque bloc - un pour le SOURIS VERS LE BAS événement et un autre pour la MOUSE_UP un événement. Allez-y, définissez deux fonctions de gestionnaire de souris à la fin de la classe:

 fonction privée onMouseDown (e: MouseEvent): void Sprite (e.currentTarget) .startDrag ();  fonction privée onMouseUp (e: MouseEvent): void Sprite (e.currentTarget) .stopDrag (); 

Tout ce que nous faisons à l’intérieur de ces fonctions d’écoute est d’obtenir la référence au bloc de répartition des événements à l’aide de la touche currentTarget propriété du un événement objet, le transtyper en un Lutin (comme c’est ce que sont nos blocs) et appelez le startDrag () et stopDrag () gérer le glisser-déposer.

Ce n'est pas encore tout. Nous avons encore besoin d’attacher ces auditeurs aux événements correspondants. Alors ajoutez ces deux lignes à la initBlocks () une fonction.

 fonction privée initBlocks (): void pour (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  var newBlock:Sprite = new Sprite(); newBlock.name = "block" + r + c; var p:Point = new Point(c * blockW, r * blockH); newBlock.x = c * (blockW + 1) + 20; newBlock.y = r * (blockH + 1) + 20; pointCollection[newBlock.name] = p; var bmpd:BitmapData = new BitmapData(blockW, blockH); var bmp:Bitmap = new Bitmap(bmpd); bmp.name = "myBmp"; newBlock.addChild(bmp); addChild(newBlock); newBlock.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); newBlock.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);   

Étape 19: touche finale

Une dernière chose juste pour que cela paraisse plus interactif. Vous avez peut-être remarqué comment les blocs entrent et sortent en fondu lorsque vous appuyez dessus, puis les relâchez. C'est une manipulation alpha que nous faisons chez les auditeurs. Modifiez vos auditeurs à quelque chose comme:

 fonction privée onMouseDown (e: MouseEvent): void Sprite (e.currentTarget) .alpha = 0.4; Sprite (e.currentTarget) .startDrag ();  fonction privée onMouseUp (e: MouseEvent): void Sprite (e.currentTarget) .alpha = 1; Sprite (e.currentTarget) .stopDrag (); 

Et voilà l'effet de changement alpha.

Conclusion

L'effet a beaucoup de potentiel pour être utilisé dans diverses applications. J'ai récemment développé un jeu de puzzle.

En dehors de cela, il pourrait être utilisé pour créer des effets de transition pour les lecteurs vidéo, ou en conjonction avec la 3D pour texturer une surface avec une vidéo.

J'espère voir des trucs sympas que les gens trouveront avec cet effet!