会社のブログに記事を書きました。Brightcoveの動画組み込みに関連して、動画再生ができるのに、ボリューム調整などのUIパーツをクリックすることができないという問題が発生したので、その解決方法などについて触れています。
briccolog » Brightcoveの動画再生ができるけどUIが効かない?

会社のブログに記事を書きました。Brightcoveの動画組み込みに関連して、動画再生ができるのに、ボリューム調整などのUIパーツをクリックすることができないという問題が発生したので、その解決方法などについて触れています。
briccolog » Brightcoveの動画再生ができるけどUIが効かない?
会社のブログに記事を書きました。Brightcoveさんの動画配信プラットフォームを使って、Flashコンテンツの中に指定の動画を読み込み・表示する方法についての記事です。
briccolog » Brightcoveの動画をFlashコンテンツに読み込む
縦書きの文字列を表示する必要が出てきたので、TextLayoutFormat (TLF)について勉強してみます。
まだ制作用のPCにFlash CS5が入っていない(というか、CS4も入っていない。使ってるのはCS3です)ので、このままだとTLFが使えない。CS5はそのうち導入する予定だけど、取り急ぎFlashDevelop + Flex4SDKの組み合わせでなんとかならんか試してみます。
ああ・・・。IEよ、またお前なのね。コトのきっかけになったのはクライアントからの
swfを埋め込んだhtmlページについて、サーバにアップしてIEでアクセスする分には問題ないが、ローカル環境でIEで開くとオブジェクトの位置が大幅にずれる。
・・・というご指摘。調べてみると確かにそうなっていることを確認。さらに調べてみると、サーバにアップしたものについても
・・・という現象を発見(汗)。「ななな、なんじゃこりゃあああああ!」と思ってさらにさらに調べてみると、下記の記事を発見。
・・・みなさん、苦労されてきたんですね。とりあえず、上記のみなさんの記事と今回の実際のファイルから、自分なりに発生条件や不具合の内容から解決方法までまとめてみました。
とある案件でちょっとハマったので備忘録がわりにメモ。まず今回のファイル構成は以下のとおり。
処理の流れとしては、
(1)main.swfからSSLサーバのcrossdomain.xmlを読み込み。
(2)main.swfからdata_output.phpにアクセスし、読み込む画像のURL一覧を記載したXMLデータを取得。
(3)(2)で取得したXMLデータをもとに、個別の画像を読み込み。
・・・という、割とよくあると言えばよくある構成です。ポイントはやっぱりSSL領域とのクロスドメイン通信があること。ドメイン名が同じでも、非SSLのサーバとSSLサーバとでは別ドメインと判断されるんですよね。
で、一通り作ってサーバにアップしたところ、FirefoxやChromeやSafariでは動くのになぜかIEだけ画像が表示されない!crossdomain.xmlはきちんと記述できてるし、XMLデータを非SSL側のサーバに設置すればIEでも動くので、どこで止まってるんだろうと調べたところ、(2)のXMLデータ取得のところでポシャってることが判明。で、Google先生に尋ねて出てきたのが以下の記事:
つまりこれはIEに起因する問題で、非SSL領域のSWF(に限らず、Ajax通信などでも同じでしょう)からSSL領域のデータをリクエストした際、サーバからのレスポンスヘッダにPragma: no-cacheが含まれているとIE側で正しく処理ができないんだそうです。なんとご無体な・・・。
FirefoxでHttpFox使ってdata_output.phpのレスポンスヘッダを調べてみたところ、あったあったありましたよPragma:no-cacheが。ということで、php側に対応を依頼しました。
しかしこれ随分と前から存在する問題のようですけど、IE側では修正(変更?)する予定は無いんでしょうか・・・。

製作中のとあるフルFlashサイト。ブラウザで普通に確認してる分には問題なく動作していたため全く気がつかなかったが、Firefoxのエラーコンソールを開いてみると「Error calling method on NPObject」というJavaScriptエラーが頻発!「なんじゃこりゃ」ということで早速調べてみたところ、原因がわかりました。
まず、今回のサイトは以下のような条件が揃っていました。
そしてSWFWheelのサイトを見ていたところ、以下のような注意書きが:
その他
外部ドメインからの読み込み
SWFWheelを使ったswfファイルを、外部ドメインから読み込む場合、HTML側とActionScript側の両方でExternalInterfaceの動作を明示的に許可する必要があります。
- HTML側にて、paramタグ及びembedタグでallowScriptAccessの値にalwaysを指定する。
- ActionScript側にて、Security.allowDomain等で許可するドメインを指定する。
ビンゴですね。1.の方は対応済みだったんですが、2.の方が未対応でした。ところで今回のサイト、サブドメインの数が一定でなく、運用後に随時追加される可能性があります。ので、ワイルドカードを使って以下のようなコードを追加。
Security.allowDomain("*.hogehoge.home");
結果は・・・?ダメでした。まだエラーが出ます。うーん、もしかしてワイルドカードでサブドメインを指定するのは無理なのか?ということで、今度は以下のコードで実験。
Security.allowDomain("*");
成功!エラーがでなくなりました。全ドメインを対象にしてしまうのは危険といえば危険ですが、現時点ではこれが最善の策でしょうか。
HTTPトンネリングの件に関連して、FCS関連でプロキシサーバを経由した場合の接続方法についての下記の記事も簡単に和訳しました。
例によって文書自体が古い上に意訳・適当な訳文ですが、HTTPトンネリングと同じく何かの参考までに。
※注:HTTPトンネリングの記事同様、こちらもリンク先が無くなってしまいました。アドビサイトにどこかに原文残ってないのかな・・・。
内部のネットワークとインターネットとの中継役としてプロキシサーバを利用することで、組織のネットワーク環境をより安全にすることができる。WEBプロキシサーバというのは通常、ネットワークの通信料を削減するためのキャッシングサーバとして利用される。とはいえ、プロキシサーバの使い道は他にもある。
ネットワークレイヤーのファイヤーウォールとプロキシサーバを組み合わせることで、WEBへのアクセス自体は確保した上で、組織のネットワーク内にあるワークステーションから外部のネットワークに対して直接アクセスする必要がなくなる。ブラウザはリモートのサーバに対してWEBページのデータを直接要求するのではなく、プロキシサーバに対して要求しなければならない。ページのデータがキャッシュされていなかった場合には、プロキシサーバがリモートのサーバに対してリクエストを行ない、返ってきたデータをブラウザに戻すことになる。プロキシサーバが中継役として存在する場合、ファイヤーウォールは基本的にネットワーク内部から外部に対して直接送出されるリクエストは全てブロックするように設定されていることが多い―――たとえそれが80番ポートに対するものであったとしても。プロキシサーバだけが80番ポートを通じて外側の世界との接続を許されているのだ(図8を参照)。
HTTPトンネリングを使わない場合、プロキシサーバの内側にいて、インターネットへの直接接続が許可されていない環境にあるユーザーはコミュニケーションサーバへの接続ができない。HTTPトンネリングを使えばできるようになることがしばしばある。プロキシサーバからしてみれば、RTMPTを使った接続要求というのは通常のHTTPリクエストと同様に捉えられる。コミュニケーションサーバからのレスポンスもHTTPレスポンスと同様に判断される。このため、プロキシサーバはRTMPTリクエストの送出もコミュニケーションサーバからのデータ受信についても対応できるはずである。ところが、HTTPトンネリングがうまくいくかどうかは保証されていないのだ。プロキシサーバにはWEBページをキャッシュする中継役以上の機能がある。それは、外部のネットワークリソースに対するアクセスの制御ということだ。例えば、プロキシの管理者はアクセス禁止サイトのリストを作成し、プロキシサーバ内の全ユーザーに対して、それらのサイトへのアクセスを拒否することができる。プロキシサーバの中には、さらに詳細にデータの調査やフィルタリングが可能なものもある。例えば、特定のコンテンツや、text/thmlのようなMIMEタイプのみを許可することもできる。WEBサーバがWEBページのデータをブラウザに対して返す際、HTTPヘッダにはContentTypeというデータが含まれる。WEBサーバがWEBページのデータを送出する場合のHTTPヘッダの書式は以下のようになる:
HTTP/1.1 200 OK Server: Netscape-Enterprise/6.0 Date: Sat, 24 May 2003 01:09:43 GMT Content-type: text/html Etag: "c96066c3-1-0-1e8" Last-modified: Tue, 25 Jun 1996 19:11:18 GMT Content-length: 488 Accept-ranges: bytes
また、画像データを送出する場合のHTTPヘッダの書式は以下のようになる:
HTTP/1.1 200 OK Server: Netscape-Enterprise/6.0 Date: Sat, 24 May 2003 01:51:17 GMT Content-type: image/gif Etag: "782311ca-126-0-f1ae" Last-modified: Mon, 06 May 2002 21:10:38 GMT Content-length: 61870 Accept-ranges: bytes
Macromedia Flash Communication Serverが返すデータはテキストデータではないが、HTTPヘッダにはContent-typeの記述が含まれない。そして、Content-typeが記述されていない場合にはtext/htmlと判断されてしまう。このため、RTMPTのトラフィックであっても、全てのプロキシサーバを通過できるという保証は無いのである。プロキシサーバとアプリケーションレイヤのファイヤーウォールというものが、システムアドミニストレータの設定した幅広いバリエーションのルールに従ってコンテンツのフィルタリングを行うように作られているということを考慮すれば、これはおかしなことではない。ITセキュリティの部署が正規のHTML文書といくつかの画像フォーマット(image/GIFなど)のみを許可すると決定したならば、それを実現するための精巧なツールが開発されるということだ。もしRTMPT接続さえも通さないプロキシサーバの場合は、組織のファイヤーウォール担当の管理者に連絡を取り、プロキシサーバの例外設定が可能かどうか確認するといい。
図8.プロキシサーバとファイヤーウォール
注:各端末(Workstation)はインターネットに直接接続することはできず、必ずプロキシサーバを通してリソース要求を行うことになる。プロキシサーバはインターネットからリソースを取得し、それを各端末に転送する。各端末がファイヤーウォールを越えて外部のシステムにアクセスすることは決してない。
Macromediaは先日、Flash Communication Server バージョン1.5のリリースとともに、Flash Communication Serverコンポーネントのアップデート版をリリースした。このアップデートの目玉のひとつが、Simple Connectコンポーネント内部に新規に追加されたコードである。このアップデートによって、デフォルトのRTMPポート(1935)経由で到達できないサーバへの接続時間を短縮するための機能が追加された。Simple Connectコンポーネントのアプリケーションディレクトリのパラメーターに「rtmp」が指定されていて、かつポート番号が指定されていない場合、Simple Connectコンポーネントは次のように動作する:
厳密に言えばこのようなやり方は必要ない。FlashPlayerはデフォルトでまず1935番ポートを使ったRTMP接続を試み、その後443番ポート、80番ポートを試みる。その後、80番ポートを使ったRTMPT接続を試みるからだ。ただしこれらの試みは時間がかかる。はじめのRTMP接続が失敗した場合、250ミリ秒後に80番ポートを使ったRMTPT接続を試みることで、接続プロセスにかかる時間は劇的に短縮されるのだ。このコンポーネントの作りを理解するため、FCSimpleConnectClassのactualConnect()メソッドを見てみよう。すべての人がSimple Connectコンポーネントを使うわけではないので、以下に示すコードは本来のコードを少々編集したものになっている。ただし行っている処理はほぼ同じだ。筆者はこのコードをメインのタイムラインに記述したが、よりオブジェクト指向的なアプローチで書き換えることも可能である。
//接続に成功するとonConnectファンクションが実行される
function onConnect(nc) {
_global.main_nc = nc;// グローバル変数に接続成功済みのNetConnectionオブジェクトを記録
// 確認のためURIをtraceし、使用中のプロトコルを確認
trace("onConnect> "+nc.uri);
// 接続に成功したNetConnectionオブジェクトのためのonStatusハンドラを生成
main_nc.onStatus = function(info) {
//ここに各種のイベントをハンドルするためのコードを記述
//以下のコードはinfoオブジェクトの内容を出力するもの
trace("----main_nc.onStatus----");
for (var p in info) {
trace(p+": "+info[p]);
}
};
//あとは他のコンポーネントに接続したりプログラムを初期化するなど・・・
}
//接続に失敗するとonConnectFailedファンクションが実行される
function onConnectFailed(info) {
//infoオブジェクトを検証し、接続失敗の原因についてレポートする
//以下のコードは単にinfoオブジェクトの内容をtraceしているだけなので、
//各自でエラーハンドリングコードを書き換えること
trace("----onConnectFailed----");
//infoオブジェクトの全プロパティの分ループ処理
for (var p in info) {
trace(p+": "+info[p]);
// If the application rejects the connection and passes back
// and application object loop through all its properties too.
if (p == "application") {
var appObj = info.application;
for (var q in appObj) {
trace(" "+q+": "+appObj[q]);
}
}
}
}
//RTMP接続用のNetConnectionオブジェクトを生成
rtmp_nc = new NetConnection();
//こちらの接続が先に成功した場合に、rtmpt_ncオブジェクトを破棄するための
//onStatusハンドラを生成
rtmp_nc.onStatus = function(info) {
this.pending = false;
if (info.code == "NetConnection.Connect.Success") {
if (rtmpt_nc.pending) {
rtmpt_nc.onStatus = null;
rtmpt_nc.close();
rtmpt_nc = null;
clearInterval(connectionID);
}
onConnect(this);
} else if (!rtmpt_nc.pending) {
onConnectFailed(info);
}
};
//RTMPT接続用のNetConnectionオブジェクトを生成
rtmpt_nc = new NetConnection();
//こちらの接続が先に成功した場合に、rtmp_ncオブジェクトを破棄するための
//onStatusハンドラを生成
rtmpt_nc.onStatus = function(info) {
this.pending = false;
if (info.code == "NetConnection.Connect.Success") {
if (rtmp_nc.pending) {
rtmp_nc.onStatus = null;
rtmp_nc.close();
rtmp_nc = null;
}
onConnect(this);
} else if (!rtmp_nc.pending) {
onConnectFailed(info);
}
};
//どちらのNetConnectionオブジェクトについてもペンディングフラグを立てておく
rtmp_nc.pending = true;
rtmpt_nc.pending = true;
//rtmp接続を試みる
rtmp_nc.connect("rtmp://host.domain.com/myApp/myInstance",userName,password);
//rtmpt接続を400ミリ秒後に行うように設定
connectionID = setInterval(connectRTMPT, 400);
//このファンクションが実行されたらインターバルを破棄してRTMPT接続を試みる
function connectRTMPT() {
clearInterval(connectionID);
rtmpt_nc.connect("rtmpt://host.domain.com/myApp/myInstance",userName,password);
}
//80番ポートが使えず、8080番ポートを使う場合などのconnectRTMPTメソッドの記述例は以下のとおり
function connectRTMPT() {
clearInterval(connectionID);
rtmpt_nc.connect("rtmpt://myHost.myDomain.com:8080/myApp/myInstance",userName,password);
}
諸事情からFlashCommunicationServerへのHTTPトンネリング接続の方法について調べる機会があり、下記のドキュメントを簡単に和訳してみました。
※注:リンク先が無くなってしまいました・・・。げ、原文まだどっかに残ってないかな・・・。
旧Macromedia時代の文書で、例によってかなり意訳・適当な訳文ですが、何かの参考までに。
FlashPlayerはFlashCommunicationServerと通信する際、デフォルトではRTMPプロトコルを使い、1935番ポートに接続する。 失敗したら443番ポートと80番ポートでの接続を試みる。 ファイヤーウォールが設定されている場合、非標準のポートを経由したTCP/IP通信が許可されていないことがあるため。 これらの手法で、およそ96%のユーザーをカバーすることができるはず。
100%に近いユーザーをカバーするためには(もしあれば)プロクシを通すか、ファイヤーウォールのためにHTTP通信しかできない場合などはHTTPトンネリングを使ってHTTPプロトコル越しにRTMPパケットを送信することになる。
以前のFlashPlayerでは、サーバへの通信要求は常にRTMPプロトコルで行われ、オプションとしてポート番号を指定することが可能であった。以下のコードでは、RTMPを利用し、1935番、443番、80番の3つのポートを使った計3回の接続を試みる。
nc.connect("rtmp://mysite.com/myapp");
以下のようにしてポート番号を指定した場合には、指定されたポート番号を使った1回の接続のみを試みる。
nc.connect("rtmp://mysite.com:PORT/myapp");
現在のFlashPlayerでは、以下の3つのうちどれか一つのプロトコルを使った接続がサポートされるようになった。
コードの書式は以下のようになる。
nc.connect("rtmpt://flashteam.macromedia.com/myapp");
nc.connect("rtmps://flashteam.macromedia.com/myapp");
上記の場合、各プロトコルのデフォルトポートに対しての計1回の接続のみを試みる。
ポート番号を指定する場合の書式は以下の通り。
nc.connect("rtmp://flashteam.macromedia.com:PORT/myapp");
nc.connect("rtmpt://flashteam.macromedia.com:PORT/myapp");
nc.connect("rtmps://flashteam.macromedia.com:PORT/myapp");
上記の場合は、指定されたポートに対しての計1回の接続を試みる。
また、以下のような特殊な接続方法もある。
nc.connect("rtmp://flashteam.macromedia.com/myapp");
この場合、RTMP:1935を試みたあとにRTMPT:80(昔のHTTP:80に代わるもの)を試みる。
新しいFlashPlayerでは、これは「万が一の場合の代替手法」として機能している。もしデフォルトの方法が失敗した場合、FlashPlayerは80番ポートでの接続を試みる。おそらくこれがHTTPトンネリングのための最も一般的な手法であろう。
メモ:HTTPトンネリングはリアルタイムな音声・映像配信のパフォーマンスに影響をおよぼすことがある。
・・・結論としては、以下の方法がベスト、っていう認識でいいのかな。
nc.connect("rtmp://flashteam.macromedia.com/myapp");
SWFから動的にPDFファイルを生成するためのライブラリであるAlivePDFをちょっと使ってみました。
alivepdf – Project Hosting on Google Code
AIRなら単体でのPDF出力も可能なようですが、SWFファイルからだと単体では無理で、サーバー側にファイル出力用(?)のphpプログラムを設置する必要があるようです。ただ、そのphpプログラムも提供されていたので、とりあえず単純なプログラムを組んで試してみました。
(続きを読む…)
AS3から実装された、マウスホイールを検知するMouseEvent.MOUSE_WHEELイベントはMacでは動作しない、というのは知っていて、その問題を解消するためのライブラリがあるという話も聞いていたのですが、実際に使ってみる機会に遭遇したので、備忘録もかねて導入までのフローをメモしてみました。ちなみに参考にしたのはfeb19さんの以下の記事:
導入したライブラリはSWFWheelです。実際の導入フローは以下の通り:
(続きを読む…)