onMetaData の使い方

NetStream から FLV などを扱うときに、全体の再生時間が知りたい事は良くあります。しかし NetStream から総バイト数は取得できても、直接秒数を取得できるプロパティは存在しません。
この場合 onMetaData というイベントを使って FLV 自体からメタデータを取得する事になるのですが、使い方が分かりにくかったのでメモ。
先に書いておくと、これは通常のイベントのように addEventListener を使って登録するものではありません。

イベントの受け取り先指定

onMetaData は登録しなくとも送出されるイベントです。なので、記述の際にはイベントを受け取るオブジェクトを指定する事になります。
NetStream の場合は client プロパティで受け取り先を指定し、デフォルトでは 自分自身(this)が指定されています。これを変更する事で、イベントを受け取る事ができるようになります。

サンプルソース

下のサンプルでは client にカスタムクラス Screen 自身を指定しています。totalTime というプロパティに、メタデータから再生時間を取得するサンプルです。
stream.client = this; という行に注目してください。

package jp.ne.sakura.hl2
{
	import flash.events.*;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	
	import mx.containers.*;
	import mx.core.*;
	
	public class Screen extends UIComponent
	{
		private var _path:String;
		private var con:NetConnection;
		private var stream:NetStream;
		private var video:Video;
		private var totalTime:Number;
		
		public function Screen(path:String)
		{
			_path = path;
			
			con = new NetConnection();
			con.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			con.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			con.connect(null);
		}

		private function netStatusHandler(e:NetStatusEvent):void {
				switch (e.info.code) {
				case "NetConnection.Connect.Success":
					connectStream();
					break;
				case "NetStream.Play.StreamNotFound":
					trace("Unable to locate video: " + _path);
					break;
				}
		}
		
		private function connectStream():void {
			stream = new NetStream(con);
			// メタデータの取得用
			stream.client = this;
			
			// ストリームの状態をイベントで監視
			stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
			
			// ビデオを定義し、ストリームをアタッチ
			video = new Video();
			video.attachNetStream(stream);
			stream.play(_path);
			addChild(video);
		}
		
		private function securityErrorHandler(e:SecurityErrorEvent):void {
			trace("securityErrorHandler: " + e);
		}
        
		private function asyncErrorHandler(e:AsyncErrorEvent):void {
			// ignore AsyncErrorEvent events.
		}
		
		public function onMetaData(param:Object):void {
			totalTime = param.duration;
		}
	}
}

ここで問題になるのは onMetaData メソッドの呼び出し時に NetStream 側から Screen.onMetaData にアクセスしようとする点。そのため onMetaData メソッドが private になっていると、正しくメソッドを実行する事ができません。
したがって、 client に this を指定する場合は onMetaData メソッドを public にする必要があります。

その他の実装

オブジェクトを作成してそこにメソッドをアタッチする方法も存在します。先ほどのサンプルの以下の部分を変更します。

stream.client = this;

変更後はこちら。

var obj:Object = new Object();
obj.onMetaData = this.onMetaData;
stream.client = obj;

この場合 Screen クラスに定義される onMetaData は private でも構いません。


最後に、カスタムクラスを定義してそれを client に指定する方法も存在します。
やはり変更するのはこの部分。

stream.client = this;

カスタムクラスへの参照に変更します。

stream.client = new CustomClient();

そして CustomClient クラスを定義します。この場合 Screen 側には onMetaData メソッドはそもそも必要ありません。

class CustomClient {
	public function onMetaData(param:Object):void {
		totalTime = param.duration;
	}
}