Je vais aborder dans cet article une problématique assez récurrente en matière de gestion video en Flash. Les classes concernées sont les classes as3 natives NetStream et Video. Je profiterai de l’ occasion pour vous montrer comment gérer simplement la Vidéo avec la classe VideoDisplay de LowRA (dans son implémentation actuelle).
Fermer un flux NetStream et arrêter le téléchargement
Ce paragraphe ne traite pas d’un bug mais s’ attache à vous exposer comment bien utiliser la classe NetStream. 2 cas de figure peuvent se présenter:
1) J’ appel un média et décide de fermer le flux presque immédiatement. Horreur, Je constate que le média continue de se télécharger !
2) Je souhaite réutiliser une instance NetStream et appeler un nouveau média sur celui-ci. Horreur, Je constate que le premier média continue parfois de se télécharger (a priori plateforme Windows uniquement).
Gérer la vidéo avec une instance NetStream unique peut être un choix intéréssant si vous êtes en recherche de performance optimale. En effet, la consommation mémoire s’avère être au plus bas et stable dans des conditions de stress tests.
Ceci étant le coût d’ utilisation d’une instance NetStream dédiée (une instance pour chaque nouvel appel vers un média) reste minime dans des conditions d’utilisation classique. Il s’agit de l’implémentation retenue le plus couramment:
- Akamai Multi Player‘s VideoController class
[as]
// Handles a successful connection
private function connectedHandler():void {
if (!_ak || !_ak.netConnection) {
return;
}
_successfulPort = _ak.actualPort;
_successfulProtocol = _ak.actualProtocol;
_model.debug(“Connected to ” + _ak.netConnection.uri);
_model.isLive = _ak.isLive;
_ns = new AkamaiDynamicNetStream(_ak);
[/as]
- LowRA’s VideoDisplay class
[as]
protected function _load() : void
{
if( DEBUG ) PixlibDebug.DEBUG( this + “._load” );
if ( !getURL() )
{
PixlibDebug.ERROR( this + ” can’t play without any valid url property, loading fails.” );
} else {
_bLoadProblem = false;
_connection = new NetConnection();
_connection.connect( null );
_stream = new NetStream( _connection );
[/as]
Les exemples qui suivent se baseront sur le choix d’une attribution NetStream systématique (un nouveau flux pour chaque appel média), non parce que le choix d’ une instance NetStream unique soit moins fiable mais parce qu’ il impose une contrainte ergonomique (interdire toute nouvelle ouverture de flux tant que le média en cours de lecture n’est pas assuré d’ être correctement fermé).
S’assurer de la fermeture d’un flux video
C’est assez simple en réalité. Pour bien contrôler un objet NetStream il faut respecter le modèle évenementielle. On ecoutera pour ce faire l’évenement NetStatusEvent, en attente du code Play.Start:
[as]
_stream.addEventListener(NetStatusEvent.NET_STATUS, _onNetStatus);
_stream.play( “http://www.deja-vue.net/test/mp4/juno.mp4″ );
private function _onNetStatus ( e : NetStatusEvent ) : void
{
switch ( e.info.code )
{
case ‘NetStream.Play.Start’ :
_stream.close();
break;
}
}[/as]
On comprendra dès lors que la gestion de la vidéo devient vite compliquée en passant par la manipulation de l’api native puisqu’il faut prendre en charge des callbacks sur chaque nouvelle instance NetStream.
D’ où l’intérêt de passer par un gestionnaire comme la classe VideoDisplay de LowRA précisemment dédiée à cette tache. Son utilisation est assez simple:
[as]
import com.bourre.media.video.VideoDisplay;
import com.bourre.media.video.VideoDisplayEvent;
var _video : VideoDisplay = new VideoDisplay();
addChild(_video.getVideo());
_video.addEventListener(VideoDisplayEvent.onStartStreamEVENT, _onStartStream);
_video.load(new URLRequest(“http://www.deja-vue.net/test/mp4/juno.mp4″));
public function _onStartStream (e : VideoDisplayEvent):void {}
[/as]
A l’ heure ou je publie cet article la classe VideoDisplay ne résout toutefois pas encore la problématique précedemment exposée.
Nul doute que l’ équipe se chargera d’ améliorer l’ implémentation au plus tôt. En attendant je vous propose une solution articulée autour de l’ héritage permettant:
- d’ appeler un nouveau média avec fermeture automatique du flux existant et du téléchargement en cours.
- de fermer un flux et d’ arrêter le téléchargement en cours.
En voici l’ utilisation :
[as]
import com.bourre.commands.CommandManagerMS;
import com.bourre.commands.Delegate;
import com.bourre.media.video.VideoDisplayEvent;
var vd : VideoDisplayExtended = new VideoDisplayExtended(this/*DisplayObjectContainer*/);
vd.addVideo();
vd.addEventListener(VideoDisplayEvent.onStartStreamEVENT, _onStartStream);
// tests
vd.load( new URLRequest(_sURL1) );
vd.load( new URLRequest(_sURL2) );
CommandManagerMS.getInstance().delay(new Delegate(vd.load, new URLRequest(_sURL1)), 3000);
CommandManagerMS.getInstance().delay(new Delegate(vd.closeStream), 6000);
[/as]
Voici un exemple avec les sources
VideoDisplayExtended.as (226)
VideoDisplayTest.zip (251)
Une autre problématique qui revient assez souvent, et pour cause il s’ agit cette fois-ci d’ un problème qui à la peau dure , le vidage d’ un objet Video.
Vidage d’ un objet Video avec Video.clear()
Quand je parle d’ un problème qui à la peau dure, je ne plaisante malheureusement pas comme certains d’entre vous le savent sans doute déjà car sa notification (ASC-3115, FP-539, FP-178) remonte maintenant à environ un an et reste toujours d’ actualité. D’après les ingénieurs en charge de sa résolution, un fix serait prévue seulement pour la prochaine mise à jour majeure du player !
Nous en sommes à la version 10.0.22.87 et rien de nouveau sous le capot…
Mais attachons nous plutôt à l’origine du problème. Video.clear() nous indique un comportement anormal mais n’en est pas la source. Video.smoothing est le véritable levier introduisant le bug.
Dès lors que la propriété smoothing voit sa valeur de départ changé (valeur booléenne positive) la méthode Video.clear() devient inefficace. Et non seulement Video.clear() échoue mais NetStream.seek() devient lui aussi inopérant sous ces mêmes conditions (vous constaterez en effet qu’un appel seek(0) ne parvient pas à nettoyer l’objet video).
Les deux tests suivants présentent des résultats assez surprenant:
[as]
var _video : Video = new Video();
_video.smoothing = true;
trace(“_video.smoothing:” + _video.smoothing);//true
_video = new Video();
trace(“_video.smoothing:” + _video.smoothing);//true ???[/as]
Pas mal non ?
Et que penser de ceci ?
[as]
var _video1 : Video = new Video();
var _video2 : Video = new Video();
_video1.smoothing = true;
trace(“_video2.smoothing:” + _video2.smoothing);//true ???
[/as]
Workarounds
Il a été suggéré de restaurer la propriété smoothing sur sa valeur initiale (FP-178):
Peut-être ceci a t-il pu fonctionner sous certaines versions antérieures du player mais cela ne marche à priori pas avec les players les plus récents (10.0.12.36 et 10.0.22.87). Si vous êtes plus chanceux vous êtes plus qu’ invité à vous manifester ☺ !
Il nous reste heureusement une alternative, un peu radicale, certes (mais la seule) consistant à détruire et recréer un nouvel objet Video. Voici l’implémentation de cette technique extrait de la classe VideoDisplayExtended précédemment évoquée.
[as]
// VideoDisplayExtended.as
public function removeVideo():void
{
if( _target.contains(_video) ) _target.removeChild(_video);
}
public function addVideo():void
{
if( !_target.contains(_video) ) _target.addChild(_video);
}
public function setVideo(v:Video):void
{
if(_video) _video.attachNetStream(null);
_video = null;
_video = v;
_video.attachNetStream(_stream);
}
[/as]
[as]
// basicplayer.fla
function _onStopStream(e : VideoDisplayEvent) : void
{
// …
if(vd.getVideo().smoothing) {
vd.removeVideo()
vd.setVideo(new Video());
// update position
_onMetaData(null);
vd.addVideo()
} else {
vd.getVideo().clear();
}
}
[/as]
Si vous souhaitez conservez la dimension vous devez passer par les propriétés width et height du nouvel objet Video. Le constructor de l’ objet Video ne fonctionne qu’ une seule fois…
[as]
var _video1 : Video = new Video(500, 400);
var _video2 : Video = new Video(200, 200);
trace(“_video2.width:” + _video2.width);// 500 !
trace(“_video2.height:” + _video2.height);// 400 !
[/as]
[as]
if(_video.getVideo().smoothing) {
_video.removeVideo();
// new Video( _video.getVideo().width, _video.getVideo().height )// not working
var v : Video = new Video();
v.width = _video.getVideo().width;
v.height = _video.getVideo().height;
_video.setVideo(v);
_video.addVideo();
} else {
_video.getVideo().clear();
}
[/as]
Bien, j’espère que vous aurez apprécié ces quelques clarifications et apprécierez, pour ceux d’ entre vous qui ne la connaissaient pas encore, l’ utilisation de la classe VideoDisplay de LowRA. Vos remarques sont attendues !
VideoDisplayExtended.as (226)
VideoDisplayTest.zip (251)
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.
Comments
5 Responses to “Le point sur NetStream.close(), le bug Video.smoothing/ Video.clear() et la classe VideoDisplay de LowRA”
Leave Comment
Sympa ce petit billet.
Un grand merci pour ces eclaircissements avancés et bien commentés
Je m’arrachais les cheveux avec ce bug jusqu’à ce que je tombe sur ce post! Merci d’avoir partager un workaround fonctionnel.
Super article!
Je bosse en Flex uniquement, j’ai eu le malheur d’utiliser VideoDisplay ;-(
J’ai remarqué que parfois il bug complêt et se met dans un état “unresponsive”, la seule solution est de virer le component et d’en remettre un nouveau!
Merci infiniement pour ce partage.
Je travaille depuis quelques jours sur un chat en AS3 avec Red5 v0.7 et je n’arrivais pas à switcher le contenu de mon netStream sur mon objet Video…
J’ai dû essayer des milliers de combinaison , en suppriamant tel ou tel objet, en le recréant à tel ou tel event, etc… Et même des choses encore plus inavouables !!!
( un singe a un jour écrit du Shaekspear dit-on, alors pourquoi pas moi ? )
…Mais en vain !
Il a certes encore fallu que je procède à quelques arrangements mineurs pour adapter le code de lowRA que vous proposez ici, mais, honnêtement, je ne suis même pas encore sûr de pourquoi ça marche…
( J’ai un peu la tête comme un ballon dans l’immédiat )
…Mais ça marche ! Encore mille mercis et hourrah pour vous Mr. Michael Barbero !