前のエントリーの続き。クロムレスプレイヤーを試します。
クロムレスプレイヤーをロードするAPIは、プレイヤーがロードされた段階ではビデオは表示されないので、ロードされたらビデオを渡します。
VIDEO_IDで渡す
player.cueVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):Void
player.loadVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):Void |
player.cueVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):Void
player.loadVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):Void
URLで渡す
player.cueVideoByUrl(videoId:String, startSeconds:Number, suggestedQuality:String):Void
player.loadVideoByUrl(videoId:String, startSeconds:Number, suggestedQuality:String):Void |
player.cueVideoByUrl(videoId:String, startSeconds:Number, suggestedQuality:String):Void
player.loadVideoByUrl(videoId:String, startSeconds:Number, suggestedQuality:String):Void
このAPIの挙動が微妙に違っていてcueVideoById、cueVideoByUrlは自動的に再生しないがloadVideoById、loadVideoByUrlは自動的に再生される。
第2・第3引数は共通で、startSecondsは再生を開始する位置。suggestedQualityはビデオの解像度です。
ビデオを読み込み後は、FLVPlayerbackコンポーメントなんかと同様に、Player APIを使ってイベント処理や制御を組み込みます。
Player APIではプレイヤーの状態に応じて、
onStateChange
が発生します。
FLVPlaybackのように、再生ヘッドのインターバルだとか、バッファのインターバルのような、細かいイベントはないので、このonStateChangeを使って処理する形になりますが、肥大化しすぎのFLVPlaybackよりむしろ使いやすく感じました。
YouTube Chromeless Player
APIを組むのに補完が出ないのが面倒だったので、Proxyを作ってみた。
package video{
import flash.display.*;
import flash.utils.*;
public class YouTubePlayer extends Proxy{
public static const ON_READY:String = "onReady";
public static const ON_STATE_CHANGE:String = "onStateChange";
public static const ON_PLAYBACK_QUALITY_CHANGE:String = "onPlaybackQualityChange";
public static const ON_ERROR:String = "onError";
public function YouTubePlayer( object:DisplayObject ){
_object = object;
}
private var _object:DisplayObject;
override flash_proxy function callProperty(methodName:*, ... args):* {
var res:*;
res = _object[methodName].apply(_object, args);
return res;
}
override flash_proxy function getProperty(name:*):* {
return _object[name];
}
override flash_proxy function setProperty(name:*, value:*):void {
_object[name] = value;
}
public function cueVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "cueVideoById", videoId, startSeconds, suggestedQuality );
}
public function loadVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "loadVideoById", videoId, startSeconds, suggestedQuality );
}
public function cueVideoByUrl(mediaContentUrl:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "cueVideoByUrl", mediaContentUrl, startSeconds, suggestedQuality );
}
public function loadVideoByUrl(mediaContentUrl:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "loadVideoByUrl", mediaContentUrl, startSeconds, suggestedQuality );
}
public function playVideo():void{
flash_proxy::callProperty( "playVideo" );
}
public function pauseVideo():void{
flash_proxy::callProperty( "pauseVideo" );
}
public function stopVideo():void{
flash_proxy::callProperty( "stopVideo" );
}
public function seekTo(seconds:Number, allowSeekAhead:Boolean):void{
flash_proxy::callProperty( "seekTo", seconds, allowSeekAhead );
}
public function mute():void{
flash_proxy::callProperty( "mute" );
}
public function unMute():void{
flash_proxy::callProperty( "unMute" );
}
public function isMuted():Boolean{
return flash_proxy::callProperty( "isMuted" );
}
public function setVolume(volume:Number):void{
flash_proxy::callProperty( "setVolume", volume );
}
public function getVolume():Number{
return flash_proxy::callProperty( "getVolume" );
}
public function setSize(width:Number, height:Number):void{
flash_proxy::callProperty( "setSize", width, height );
}
public function getVideoBytesLoaded():Number{
return flash_proxy::callProperty( "getVideoBytesLoaded" );
}
public function getVideoBytesTotal():Number{
return flash_proxy::callProperty( "getVideoBytesTotal" );
}
public function getVideoStartBytes():Number{
return flash_proxy::callProperty( "getVideoStartBytes" );
}
public function getPlayerState():Number{
return flash_proxy::callProperty( "getPlayerState" );
}
public function getCurrentTime():Number{
return flash_proxy::callProperty( "getCurrentTime" );
}
public function getPlaybackQuality():String{
return flash_proxy::callProperty( "getPlaybackQuality" );
}
public function setPlaybackQuality(suggestedQuality:String):void{
flash_proxy::callProperty( "setPlaybackQuality", suggestedQuality );
}
public function getAvailableQualityLevels():Array{
return flash_proxy::callProperty( "getAvailableQualityLevels" );
}
public function getDuration():Number{
return flash_proxy::callProperty( "getDuration" );
}
public function getVideoUrl():String{
return flash_proxy::callProperty( "getVideoUrl" );
}
public function getVideoEmbedCode():String{
return flash_proxy::callProperty( "getVideoEmbedCode" );
}
public function addEventListener(event:String, listener:Function):void{
flash_proxy::callProperty( "addEventListener", event, listener );
}
public function destroy():void{
flash_proxy::callProperty( "destroy" );
}
}
} |
package video{
import flash.display.*;
import flash.utils.*;
public class YouTubePlayer extends Proxy{
public static const ON_READY:String = "onReady";
public static const ON_STATE_CHANGE:String = "onStateChange";
public static const ON_PLAYBACK_QUALITY_CHANGE:String = "onPlaybackQualityChange";
public static const ON_ERROR:String = "onError";
public function YouTubePlayer( object:DisplayObject ){
_object = object;
}
private var _object:DisplayObject;
override flash_proxy function callProperty(methodName:*, ... args):* {
var res:*;
res = _object[methodName].apply(_object, args);
return res;
}
override flash_proxy function getProperty(name:*):* {
return _object[name];
}
override flash_proxy function setProperty(name:*, value:*):void {
_object[name] = value;
}
public function cueVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "cueVideoById", videoId, startSeconds, suggestedQuality );
}
public function loadVideoById(videoId:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "loadVideoById", videoId, startSeconds, suggestedQuality );
}
public function cueVideoByUrl(mediaContentUrl:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "cueVideoByUrl", mediaContentUrl, startSeconds, suggestedQuality );
}
public function loadVideoByUrl(mediaContentUrl:String, startSeconds:Number, suggestedQuality:String):void{
flash_proxy::callProperty( "loadVideoByUrl", mediaContentUrl, startSeconds, suggestedQuality );
}
public function playVideo():void{
flash_proxy::callProperty( "playVideo" );
}
public function pauseVideo():void{
flash_proxy::callProperty( "pauseVideo" );
}
public function stopVideo():void{
flash_proxy::callProperty( "stopVideo" );
}
public function seekTo(seconds:Number, allowSeekAhead:Boolean):void{
flash_proxy::callProperty( "seekTo", seconds, allowSeekAhead );
}
public function mute():void{
flash_proxy::callProperty( "mute" );
}
public function unMute():void{
flash_proxy::callProperty( "unMute" );
}
public function isMuted():Boolean{
return flash_proxy::callProperty( "isMuted" );
}
public function setVolume(volume:Number):void{
flash_proxy::callProperty( "setVolume", volume );
}
public function getVolume():Number{
return flash_proxy::callProperty( "getVolume" );
}
public function setSize(width:Number, height:Number):void{
flash_proxy::callProperty( "setSize", width, height );
}
public function getVideoBytesLoaded():Number{
return flash_proxy::callProperty( "getVideoBytesLoaded" );
}
public function getVideoBytesTotal():Number{
return flash_proxy::callProperty( "getVideoBytesTotal" );
}
public function getVideoStartBytes():Number{
return flash_proxy::callProperty( "getVideoStartBytes" );
}
public function getPlayerState():Number{
return flash_proxy::callProperty( "getPlayerState" );
}
public function getCurrentTime():Number{
return flash_proxy::callProperty( "getCurrentTime" );
}
public function getPlaybackQuality():String{
return flash_proxy::callProperty( "getPlaybackQuality" );
}
public function setPlaybackQuality(suggestedQuality:String):void{
flash_proxy::callProperty( "setPlaybackQuality", suggestedQuality );
}
public function getAvailableQualityLevels():Array{
return flash_proxy::callProperty( "getAvailableQualityLevels" );
}
public function getDuration():Number{
return flash_proxy::callProperty( "getDuration" );
}
public function getVideoUrl():String{
return flash_proxy::callProperty( "getVideoUrl" );
}
public function getVideoEmbedCode():String{
return flash_proxy::callProperty( "getVideoEmbedCode" );
}
public function addEventListener(event:String, listener:Function):void{
flash_proxy::callProperty( "addEventListener", event, listener );
}
public function destroy():void{
flash_proxy::callProperty( "destroy" );
}
}
}
最低限の操作系を用意。Play・Pause、Stop、シークバー。
今回はAPIの確認なんで作り込まず、Flexのコンポーネントで定義。
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300" creationComplete="init()">
<s:layout>
<s:BasicLayout/>
</s:layout>
<fx:Script>
<![CDATA[
private function init():void{
alpha = 0.5;
seekBar.scaleY = 0.25;
seekBar.decrementButton.visible = false;
seekBar.incrementButton.visible = false;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
</fx:Declarations>
<s:Button x="55" y="0" label="Pause" id="playPause" width="50" fontSize="10"/>
<s:Button x="0" y="0" label="Stop" id="stop" width="50" fontSize="10"/>
<s:HScrollBar x="94" y="8" width="320" id="seekBar"/>
</s:Group> |
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300" creationComplete="init()">
<s:layout>
<s:BasicLayout/>
</s:layout>
<fx:Script>
<![CDATA[
private function init():void{
alpha = 0.5;
seekBar.scaleY = 0.25;
seekBar.decrementButton.visible = false;
seekBar.incrementButton.visible = false;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
</fx:Declarations>
<s:Button x="55" y="0" label="Pause" id="playPause" width="50" fontSize="10"/>
<s:Button x="0" y="0" label="Stop" id="stop" width="50" fontSize="10"/>
<s:HScrollBar x="94" y="8" width="320" id="seekBar"/>
</s:Group>
プレイヤーを格納する表示オブジェクト
package video{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import jp.progression.casts.*;
import jp.progression.casts.mx.*;
import jp.progression.commands.*;
import jp.progression.commands.display.*;
import jp.progression.commands.net.*;
import mx.events.*;
public class ChromelessPlayerContainer extends CastUIComponent{
public function ChromelessPlayerContainer(){
super();
_loader = new Loader();
_controller = new Controller();
_controller.y = 305;
//トラックを操作した時のアニメーション、マウス位置に移動させる
_controller.seekBar.setStyle( "smoothScrolling", false );
_controller.seekBar.setStyle( "repeatInterval", 0 );
_controller.seekBar.setStyle( "repeatDelay", 0 );
}
private var _loader:Loader; //ローダー
private var _player:YouTubePlayer; //プレイヤーオブジェクト
private var _controller:Controller; //コントローラー
protected override function atCastAdded():void{
addCommand(
new LoadURL( new URLRequest( "http://gdata.youtube.com/feeds/api/videos" ), {
onStart:function():void{
var vars:URLVariables = new URLVariables();
vars.vq = "teI8o6k67-k";
vars.format = 5;
this.request.data =vars;
}
} ),
new Func( function():void{
var _feed:XML = new XML( this.latestData );
//xmlが持つ名前空間を格納
var _media:Namespace = _feed.namespace( "media" ); //feedのyt名前空間
var _yt:Namespace = _feed.namespace( "yt" ); //feedのyt名前空間
_loader.contentLoaderInfo.addEventListener( Event.INIT, onInit );
_loader.load( new URLRequest( "http://www.youtube.com/apiplayer?version=3" ) );
function onInit( e:Event ):void{
e.target.removeEventListener( e.type, arguments.callee );
_player = new YouTubePlayer( _loader.content );
_player.addEventListener( YouTubePlayer.ON_STATE_CHANGE, onStateChange );
_player.addEventListener( "onReady", onReady );
}
function onReady( e:Event ):void{
e.target.removeEventListener( e.type, arguments.callee );
_player.setSize( 400, 300 );
//プレイヤーに映像を表示する
//表示するURLを取得
var q:QName = new QName( _media, "content" );
var media:XMLList = _feed.descendants( q );
//取得した要素のformatからswf形式を取得
for each( var node:XML in media ){
if( node.@_yt::format == 5 ){
break;
}
}
//読み込み
_player.loadVideoByUrl( node.@url, 0, "default" );
_loader.dispatchEvent( new Event( Event.COMPLETE ) );
}
}, null, _loader, Event.COMPLETE ),
new AddChild( this, _loader ),
new AddChild( this, _controller ),
new Func( function():void{
//停止ボタン
_controller.stop.addEventListener( FlexEvent.BUTTON_DOWN, onMouseDownStop );
//再生、一時停止ボタン
_controller.playPause.addEventListener( FlexEvent.BUTTON_DOWN, onMouseDownPlayPause );
//シーク操作
_controller.seekBar.addEventListener( FlexEvent.CHANGE_START, onChangeStart );
_controller.seekBar.addEventListener( FlexEvent.CHANGE_END, onChangeEnd );
})
)
}
//停止ボタンを押したら
private function onMouseDownStop( e:FlexEvent ):void{
//ビデオを停止
_player.stopVideo();
}
//再生・一時停止ボタンを押したら
private function onMouseDownPlayPause( e:FlexEvent ):void{
//ボタンにPlayが表示されていたら
if( _controller.playPause.label == "Play" ){
//一時停止
_player.playVideo();
}
//ボタンにPauseが表示されていたら
else{
//再生
_player.pauseVideo();
}
}
//プレイヤーの状態が変わったら
private function onStateChange( e:Event ):void{
var state:int = _player.getPlayerState();
//サムの進行を停止
_controller.removeEventListener( Event.ENTER_FRAME, onEnterFrame );
switch( state ){
case -1: //停止なら
_controller.playPause.label = "Play";
_controller.seekBar.value = 0;
break;
case 0 : //最後まで再生したら
break;
case 1 : //再生なら
_controller.playPause.label = "Pause";
//サムの進行開始
_controller.addEventListener( Event.ENTER_FRAME, onEnterFrame );
break;
case 2 : //一時停止なら
_controller.playPause.label = "Play";
break;
case 3 : //バッファ中なら
break;
case 5 : //キューされたら
//cueVideoById、cueVideoByUrlの時、発生
break;
}
}
//ユーザーがシーク操作を開始したら
private function onChangeStart( e:FlexEvent ):void{
_isSeek = true;
}
//ユーザーがシーク操作を完了したら
private function onChangeEnd( e:FlexEvent ):void{
_isSeek = false;
if( _player.getPlayerState() < 0 ){
_player.playVideo();
}
_player.seekTo( ( _controller.seekBar.value / _controller.seekBar.maximum ) * _player.getDuration(), true );
}
//コントローラ操作中はtrue
private var _isSeek:Boolean = false;
//サムを再生位置に進める
private function onEnterFrame( e:Event ):void{
//コントローラ捜査中は終了
if( _isSeek ) return;
//現在の再生時間
var time:Number = _player.getCurrentTime();
//ビデオの合計時間
var total:Number = _player.getDuration();
_controller.seekBar.value = ( time / total ) * 100;
}
}
} |
package video{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import jp.progression.casts.*;
import jp.progression.casts.mx.*;
import jp.progression.commands.*;
import jp.progression.commands.display.*;
import jp.progression.commands.net.*;
import mx.events.*;
public class ChromelessPlayerContainer extends CastUIComponent{
public function ChromelessPlayerContainer(){
super();
_loader = new Loader();
_controller = new Controller();
_controller.y = 305;
//トラックを操作した時のアニメーション、マウス位置に移動させる
_controller.seekBar.setStyle( "smoothScrolling", false );
_controller.seekBar.setStyle( "repeatInterval", 0 );
_controller.seekBar.setStyle( "repeatDelay", 0 );
}
private var _loader:Loader; //ローダー
private var _player:YouTubePlayer; //プレイヤーオブジェクト
private var _controller:Controller; //コントローラー
protected override function atCastAdded():void{
addCommand(
new LoadURL( new URLRequest( "http://gdata.youtube.com/feeds/api/videos" ), {
onStart:function():void{
var vars:URLVariables = new URLVariables();
vars.vq = "teI8o6k67-k";
vars.format = 5;
this.request.data =vars;
}
} ),
new Func( function():void{
var _feed:XML = new XML( this.latestData );
//xmlが持つ名前空間を格納
var _media:Namespace = _feed.namespace( "media" ); //feedのyt名前空間
var _yt:Namespace = _feed.namespace( "yt" ); //feedのyt名前空間
_loader.contentLoaderInfo.addEventListener( Event.INIT, onInit );
_loader.load( new URLRequest( "http://www.youtube.com/apiplayer?version=3" ) );
function onInit( e:Event ):void{
e.target.removeEventListener( e.type, arguments.callee );
_player = new YouTubePlayer( _loader.content );
_player.addEventListener( YouTubePlayer.ON_STATE_CHANGE, onStateChange );
_player.addEventListener( "onReady", onReady );
}
function onReady( e:Event ):void{
e.target.removeEventListener( e.type, arguments.callee );
_player.setSize( 400, 300 );
//プレイヤーに映像を表示する
//表示するURLを取得
var q:QName = new QName( _media, "content" );
var media:XMLList = _feed.descendants( q );
//取得した要素のformatからswf形式を取得
for each( var node:XML in media ){
if( node.@_yt::format == 5 ){
break;
}
}
//読み込み
_player.loadVideoByUrl( node.@url, 0, "default" );
_loader.dispatchEvent( new Event( Event.COMPLETE ) );
}
}, null, _loader, Event.COMPLETE ),
new AddChild( this, _loader ),
new AddChild( this, _controller ),
new Func( function():void{
//停止ボタン
_controller.stop.addEventListener( FlexEvent.BUTTON_DOWN, onMouseDownStop );
//再生、一時停止ボタン
_controller.playPause.addEventListener( FlexEvent.BUTTON_DOWN, onMouseDownPlayPause );
//シーク操作
_controller.seekBar.addEventListener( FlexEvent.CHANGE_START, onChangeStart );
_controller.seekBar.addEventListener( FlexEvent.CHANGE_END, onChangeEnd );
})
)
}
//停止ボタンを押したら
private function onMouseDownStop( e:FlexEvent ):void{
//ビデオを停止
_player.stopVideo();
}
//再生・一時停止ボタンを押したら
private function onMouseDownPlayPause( e:FlexEvent ):void{
//ボタンにPlayが表示されていたら
if( _controller.playPause.label == "Play" ){
//一時停止
_player.playVideo();
}
//ボタンにPauseが表示されていたら
else{
//再生
_player.pauseVideo();
}
}
//プレイヤーの状態が変わったら
private function onStateChange( e:Event ):void{
var state:int = _player.getPlayerState();
//サムの進行を停止
_controller.removeEventListener( Event.ENTER_FRAME, onEnterFrame );
switch( state ){
case -1: //停止なら
_controller.playPause.label = "Play";
_controller.seekBar.value = 0;
break;
case 0 : //最後まで再生したら
break;
case 1 : //再生なら
_controller.playPause.label = "Pause";
//サムの進行開始
_controller.addEventListener( Event.ENTER_FRAME, onEnterFrame );
break;
case 2 : //一時停止なら
_controller.playPause.label = "Play";
break;
case 3 : //バッファ中なら
break;
case 5 : //キューされたら
//cueVideoById、cueVideoByUrlの時、発生
break;
}
}
//ユーザーがシーク操作を開始したら
private function onChangeStart( e:FlexEvent ):void{
_isSeek = true;
}
//ユーザーがシーク操作を完了したら
private function onChangeEnd( e:FlexEvent ):void{
_isSeek = false;
if( _player.getPlayerState() < 0 ){
_player.playVideo();
}
_player.seekTo( ( _controller.seekBar.value / _controller.seekBar.maximum ) * _player.getDuration(), true );
}
//コントローラ操作中はtrue
private var _isSeek:Boolean = false;
//サムを再生位置に進める
private function onEnterFrame( e:Event ):void{
//コントローラ捜査中は終了
if( _isSeek ) return;
//現在の再生時間
var time:Number = _player.getCurrentTime();
//ビデオの合計時間
var total:Number = _player.getDuration();
_controller.seekBar.value = ( time / total ) * 100;
}
}
}