air

AirでFLVプレイヤー作りました。

>>ダウンロードはコチラ

マイコミさんでやらせてもらってるセミナーで受講生からの希望があったんで、Flvプレイヤーを公開します。
一応ちょこっとだけ改造して、flv f4v mp4(H264のみ)に対応しています。

一応ソースコード(CS3形式)も添付してるんで、ご参考までに見てみてください。

一応コチラにもソースをぺとり

import fl.video.*;
import flash.events.*;
import flash.display.Sprite;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.desktop.*;

stage.quality = "best";
flle_txt.visible = false;

//ステージの拡大モードを設定
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

//widhtを基準とした、heightのアス比
var aspect:Number;

//コントロールを設定
player.playPauseButton = control_mc.playPauseButton_mc;
player.stopButton = control_mc.stopButton_mc;
player.seekBar = control_mc.seekBar_mc;
player.muteButton = control_mc.muteButton_mc;
player.volumeBar = control_mc.volumeBar_mc;
player.fullScreenButton = control_mc.fullScreenButton_mc;
//下マージン
var bottomMargin:int = 3;
//プレイヤーとコントロールのマージン
var ctrlMargin:int = 4;

//プレイヤーの初期設定
player.autoRewind = true;
player.x = 5;
player.y = 30;

//player.source = "test.flv";

//メタを取得できたら、コントロールをビデオの下に吸着
player.addEventListener(MetadataEvent.METADATA_RECEIVED , moveControl);
//吸着用のメソッド
function moveControl(event:MetadataEvent):void{
    aspect = player.metadata.height/player.metadata.width;
    windowFit();
}

//ステージのサイズに合わせてアス比固定で拡大縮小
stage.addEventListener(Event.RESIZE , videoResize);
function videoResize(event:Event):void{
    var playerWidth:Number = stage.stageWidth - 10;
    var playerHeight:Number = playerWidth * aspect;
    var ctrlsHeight:Number = control_mc.height + bottomMargin + player.y;
   
    if(playerHeight + ctrlsHeight < stage.stageHeight){
        player.setSize(playerWidth , playerHeight);
        //ドラッグターゲットのサイズ設定
        dragTarget_mc.width = playerWidth;
        dragTarget_mc.height = playerHeight;
        //BGのサイズ指定
        bg_mc.width = stage.width;
        bg_mc.height = stage.height;
    }else{
        playerHeight = stage.stageHeight - ctrlsHeight;
        playerWidth = playerHeight/aspect;
        player.setSize(playerWidth , playerHeight);
        //ドラッグターゲットのサイズ設定
        dragTarget_mc.width = playerWidth;
        dragTarget_mc.height = playerHeight;
        //BGのサイズ指定
        bg_mc.width = stage.width;
        bg_mc.height = stage.height;
    }
    ctrlMove();
}

//ウィンドウを操作するためのオブジェクト
var window:NativeWindow = this.stage.nativeWindow;

//ウインドウを初期サイズにフィットさせる
function windowFit(){
    window.height = 34 + player.y + player.metadata.height + ctrlMargin + control_mc.height + bottomMargin;
    window.width = 18 + player.metadata.width;
   
    flle_txt.text = window.height + " " + window.width;
   
    player.setSize(player.metadata.width , player.metadata.height);
   
    ctrlMove();
}

//コントロールを右下に吸着
function ctrlMove():void{
    control_mc.y = player.y + player.height + 5;
    close_btn.x = (player.width+5) - close_btn.width - 4;
   
    var mov:Number = control_mc.x = (player.width+5) - 318;
   
    if(mov >= 7){
        control_mc.x = mov;
    } else {
        control_mc.x = 7;
    }
}

//ボタン設定
open_btn.buttonMode = true;
fit_btn.buttonMode = true;
close_btn.buttonMode = true;
//フィットボタンでムービーサイズを元に戻す
fit_btn.addEventListener(MouseEvent.CLICK , fitVideo);
//ムービーリサイズ
function fitVideo(event:MouseEvent){
    windowFit();
}
//クローズボタン
close_btn.addEventListener(MouseEvent.CLICK , winClose);
function winClose(event:MouseEvent){
    window.close();
}

//オープンボタンでのファイル選択処理
open_btn.addEventListener(MouseEvent.CLICK , fileOpen);

//ファイルタイプをFLVのみに設定
//ファイルブラウズ処理
var fileLoader:File = new File();
fileLoader.addEventListener(Event.SELECT , fileSelected);
var filter:FileFilter = new FileFilter("Flvファイル", "*.flv; *f4v; *mp4");

function fileOpen(event:MouseEvent):void{
    fileLoader.browseForOpen("ファイルの選択", [filter]);
}
function fileSelected(event:Event){
    player.source = fileLoader.url;
    flle_txt.text = fileLoader.url;
}

//ドラッグ&ドロップ処理
dragTarget_mc.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
dragTarget_mc.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDrop);

//ドラッグ許可
function onDragIn(event:NativeDragEvent):void{
    flle_txt.text = "Drag";
    var dropData:Clipboard = event.clipboard;

    if (dropData.hasFormat(ClipboardFormats.FILE_LIST_FORMAT)) {
        NativeDragManager.acceptDragDrop(event.target);
    }
}

//ドロップ処理
function onDrop(event:NativeDragEvent):void{
   
    var droppedData:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT);
   
    var file:File = droppedData[droppedData.length -1];
    if(file.extension == "flv" || file.extension == "FLV" || file.extension == "F4V" || file.extension == "f4v" || file.extension == "mp4" || file.extension == "MP4"){
        player.source = file.url;
    }
}

そういえば、airに関して何も書いてないなぁってことで、基本となりそうなところから初めてみようかなと・・・。
てな訳で、一発目はディレクトリ操作ってことで。

airを使うと、当然ユーザーのディスク領域にアクセスすることが出来るんで、通常のアプリのように、ファイルを読み込んで編集して上書きして~・・・ってなことができたりします。

まぁその辺はおいおいという事で、今回は「アプリケーションがインストールされているディレクトリから、アプリケーション用のデータディレクトリにファイルをコピーして、そのファイルを読み込む」っていう処理を書いてみましょう。

>>Flaファイルをダウンロード

そもそもこのコードを書いたきっかけが、スキン対応のデスクトップツールを作ろうってことで、ユーザーが自由にスキンをインストールできるようにしなきゃならんが、インストール直後のデフォルトスキンが必要だよね。ってことで、最初は、アプリケーションがインストールされてる、カレントディレクトリに、「skins」とかのフォルダを作って、XMLのファイルリストに個々のスキンフォルダを対応させようって考えてたんだけれども・・・

applicationDirectory配下のファイルにアクセスする方法

上記の記事で、AIRアプリケーションのアンインストール時には、自分で作成したファイルは削除されないのでそのまま残ってしまうという欠点がある。
さらに悪いことに、もう一度同じAIRアプリケーションをインストールしようとした場合、インストール先のディレクトリが残ったままだとインストールに失敗してしまうので要注意。

うおぉ、こりゃいかん・・・

ってことで、もちょっと調べたら、「applicationStorageDirectory」なるものを発見。
どうやら、ここは、アンインストール時に削除されるようで、ならば、スキンリストはここに作成しようと決断

ならば、最初のデフォルトスキンも含んだ、スキンフォルダをapplicationStorageDirectoryに作成しなきゃねって事で、「アプリケーションがインストールされているディレクトリから、アプリケーション用のデータディレクトリにファイルをコピーして、そのファイルを読み込む」コードを書いたわけです。

まず下準備として、airのインストーラーの設定で

directory.JPG

こんな感じで、あらかじめ用意したdefaultSkins.swfというファイルを追加します。

ディレクトリの操作では、filesystemパッケージのfileクラスを使用します。
ではソースをば、

import flash.filesystem.*;
import flash.net.FileFilter;
/*
アプリケーションがインストールされてるフォルダ(アプリケーションディレクトリ)の"defaultSkins.swf"というファイルをアプリケーションのデータ用フォルダ(アプリケーションストレージディレクトリ)にコピーして利用します。

まずアプリケーションストレージディレクトリに"defaultSkins.swf"が存在するかどうかを調べ
無ければコピー、あればコピーせずに"defaultSkins.swf"を読み込み画面に表示します。
*/

//ファイル名
var skinFileName:String = "defaultSkins.swf";

//アプリケーションディレクトリのパスです。
var appDir:File = File.applicationDirectory;

//アプリケーションストレージディレクトリのパスです。
var appDb:File = File.applicationStorageDirectory;

//resolvePath()によって、新しいディレクトリパスを作ります。
//下記コードだと、「アプリケーションディレクトリ\defaultSkins.swf」となります。
var defaultSkin:File = appDir.resolvePath(skinFileName);

//下記コードだと「アプリケーションストレージディレクトリ\defaultSkins.swf」となります。
//ここでは、実際にそのファイルがあるかどうかは問題になりません。
var skinsPath:File = appDb.resolvePath(skinFileName);

file_txt.appendText("\n作成日時" + defaultSkin.creationDate);
file_txt.appendText("\n更新日時" + defaultSkin.modificationDate);

file_txt.appendText("\n" + skinsPath.nativePath);

//「アプリケーションストレージディレクトリ\defaultSkins.swf」が存在するかどうかを調べます。
//存在しなければ、copyToAsync()メソッドを利用して、ファイルをコピーします。
if(skinsPath.exists){
    file_txt.appendText("\ndefaultSkinsは存在します。");
    file_txt.appendText("\n作成日時" + skinsPath.creationDate);
    file_txt.appendText("\n更新日時" + skinsPath.modificationDate);
    setPanel();
}else{
    file_txt.appendText("\ndefaultSkinsは存在しません。\n作成します。");
    defaultSkin.copyToAsync(skinsPath , true);
}

defaultSkin.addEventListener(Event.COMPLETE , completedFunc);

function completedFunc(event:Event):void{
    file_txt.appendText("\nコピーしました。");
    var fileList:Array = new Array();
    fileList = appDb.getDirectoryListing();
   
    var count:int = fileList.length;
   
    for(var i:int = 0 ; i < count ; i++){
        file_txt.appendText("\n" + fileList[i].nativePath);
    }
    setPanel();
}

function setPanel():void{
    var skinAdress:URLRequest = new URLRequest(skinsPath.url);
    var loader:Loader = new Loader();
   
    loader.load(skinAdress);
    stage.addChild(loader);
   
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE , loadComp);
   
    function loadComp(event:Event):void{
        var tmp:MovieClip;
       
        tmp = event.target.content;
        tmp.gotoAndStop("昼");
    }
}

ポイントをいくつか
まず

var appDir:File = File.applicationDirectory;
var appDb:File = File.applicationStorageDirectory;

で「アプリケーションストレージディレクトリ」と「アプリケーションディレクトリ」の参照を取得します。
appDir.nativePath;
と記述すると、OSネイティブなディレクトリパスを取得できます。
例えば、Vistaの64Bitだと「C:\Program Files (x86)\dir」こうなるでしょう。
appDir.url;
と記述すると、「app:/」こうなります。ちなみに、ストレージディレクトリは「「app-storage:/」です。

つぎに
var defaultSkin:File = appDir.resolvePath(skinFileName);
resolvePathで、ディレクトリを移動しますので、defaultSkinのパスは「app:/defaultSkins.swf」となります。
nativePathは「C:\Program Files (x86)\dir\defaultSkins.swf」となるでしょうね。

ちなみにその下の
var skinsPath:File = appDb.resolvePath(skinFileName);
この行ですが、実際に「app-storage:/defaultSkins.swf」は、初回の起動時には存在しません。
でも、上記のコードはエラーになりません

resolvePathでは、実際にそのディレクトリやファイルが存在するかどうかは関係なありません。
そのファイルが存在するかどうかどうかは、Fileクラスのプロパティで取得できます。
その部分がこれです。
if(skinsPath.exists){
existsプロパティは、実際にそのファイル・ディレクトリが存在するかどうかを調べます。
ここでは、existsプロパティがtrue(ファイルが存在する)場合、直ちに読み込み処理をします。
無ければ、
defaultSkin.copyToAsync(skinsPath , true);
copyToAsyncメソッドで、defaultSkin(アプリケーションディレクトリのファイル)をskinsPathに(アプリケーションストレージディレクトリの、実際は存在しないファイル名)コピーし、終了したら読み込みます。

あとは、読み込み処理はloaderクラスを利用するんで、パスをURLRequestクラスのインスタンスにしなきゃいけなんで、Fileクラスのurlプロパティを使って、読み込みます。

てな訳で、アプリケーションの設定ファイルとか、後々追加更新するけれど、ユーザーに任意に操作させたくないなぁってファイルは、applicationStorageDirectoryを使って、このコードみたく、初回起動時にapplicationDirectoryからコピーするようにすると、なんだかアプリケーションみたいだね。

プロフィール

HN.NoBody

NoBody

市ヶ谷のとあるオフィスでFlashクリエイターとして労役中。
なんとなくチーフ。

twitterでつぶやき中