2011年7月30日土曜日

red5をapplet化してみる。その5、メモリーによけいなものがのこった原因の考察

さて、前回ではClassLoaderで読み込んだデータが残る条件をしらべてみたわけですが、
GUI上で起動したRed5のプログラムを殺してもなぜデータがのこってしまったのでしょう?

というわけでその考察です。
JavaVisualVMのメモリーデータをDumpしてみるとslf4jのloggerの部分やapache-mina、springframework、それにRed5のプログラムがのこったままになっているようです。
いっぱいのこってます・・・orz
実際にどうなるかは、そのうちRed5を起動するJavaApplication公開するので、そのときにやってみてください。

どうやらThread的には、ほぼ壊滅状態みたいです。
net.sf.ehcache.CacheManager
NioSocketAcceptor-1
このあたりが待機スレッドになっているのと、
Jconsoleで接続したところ、Jconsoleに追加された管理用のObjectがしつこくのこっているくらいだろうか?

実際にRed5を実行したときに増えるスレッド一覧は次のとおり
NioSocketAcceptor-1
Red5_scheduler_QuartzSchedulerThread
Red5_scheduler_Worker-1 - 4
pool-2-thread-1
net.sf.ehcache.CacheManager
RMI TCP Connection
GC Daemon
RMI Reaper
RMI TCP Accept-0
RMI TCP Accept-9999
pool-1-thread-1-4
http-0.0.0.0-5080-Acceptor
ContainerBackgroundProcessor

先ほどのシャットダウンをやってみたときのと比べてみるとほぼ死んでいるような・・・
ついでにJMXに登録しているデータをリストアップしてみる。
起動時についてくるのはorg.red5.serverとred5Engineの2つ、後者はtomcatの動作からくるみたい。ということはJetty系のRed5の場合はそっちのデータがつくことも考慮しないとだめみたいですね。

シャットダウンを実行するとred5Engineの一部だけ残るみたいです。

今後とるべき方針はJMXに登録しているオブジェクトを削除。それでだめなら、該当スレッドをなんとか停止するようにする。

この2本だてでせめればよさそうですね。
ちなみに余談ですけど、再度起動した場合でもきちんとred5は動作するようです。古いデータが消えずにメモリーを圧迫するようなので、きちんとやらないとだめですけどね^^;

red5をapplet化してみる。その4、クラスの再読み込み時のメモリー状態について調査

この記事はRed5関係ないね・・・

Red5のShutdownコマンドによる、TomcatLoaderの自爆動作によってGUI動作のRed5に停止をかけてみたところ内部のクラスデータ等がきれいに消えなかった・・・
この問題に対処するため、なんとかデータがきれいに消えるようにしたいと思っています。

そこでJavaのClassLoaderの挙動について少々しらべてみました。

やったのは、GUI側のコマンドで別のJarデータを読み込ませて、その中にある。com.ttProject.entry.Launcherクラスのlaunchとunloadのメソッドを実行して、動作を確認するというものです。
用意したクラスはSingleton形式のクラス1つ。staticアクセスメンバをもつのが1つ。通常のクラスが1つ。
どのクラスもDataClassのオブジェクトを保持している。
とします。

JavaのJarライブラリ読み込みについておさらいしておくと・・・

  1. JDKやJREのあるディレクトリのシステムJarと同じ階層に設置しているJarデータは一番はじめに読み込まれる。(再読み込み不可)
  2. 実行するときのディレクトリにあるJarデータや実行時に指定したJarライブラリは次に読み込まれる。(再読み込み不可)
  3. まったく関係ない階層に設置しているJarデータをClassLoaderで動的に読み込む。(再読み込み可能)
  4. 1、2、3に同じパッケージクラスがあった場合、数字の若い方(先に読み込まれた方)が優先される。
というふうになっています。
一応、今回読み込みにつかったjarファイルを公開するために、JMXtestのリポジトリにプログラムを追加していますが、上記の仕様の都合上。JMXtest.jarの1つ上の階層にjartest.jarをおいていただくと動作確認できます。それ以外の場合はたぶんclassLoaderの読み込みのところで例外がでるはず。

プログラムの動作は
  1. 実行ボタンを押す
  2. Jarデータをロード
  3. unloadメソッドを実行(前の呼び出しによるstatic経由の変数にアクセスしてデータをDumpしようと試みる)
  4. launchメソッドを実行(クラスオブジェクトを設置して保持する。)
結果
まず3の前のデータをDumpの部分。staticアクセスなら同じ変数にアクセスできるのかなと思いましたが、再読み込みしてしまうとクラス空間が違う形になるため、アクセス不可。
よって、前回のClassLoaderのインスタンスがなくなり、前回のClassLoader経由で動作しているクラス(Thread等)がなくなると消滅するようです。
4回ほどクラスを実行したところ。
メモリー状態はこんな感じ、com.ttProject.entry系のクラスが1つもないです。
実行完了した瞬間にアクセスするためのデータが蒸発しているようです。
ClassLoaderのオブジェクトをstatic化して、ロードした後に消滅しないようにしてすると・・・
ちゃんとcom.ttProject.entry系のデータがある。
複数回読み込みを実行したのですが、最後の1回分だけデータが保持されています。
(クラスが重複していない。DataClassが3つあるのはSingleton static normalの3つのオブジェクトのせい)
最後に、Threadで無限ループをつくってLauncherクラスに設置したメンバー変数にnullをわりあてるようにしてみた。
無限ループをつくったので、ClassLoaderがなくなってもThreadがのこってしまうので、クラスのUnloadが走らないようです。
実行を繰り返すと、同じ名前の読み込みクラスがどんどん増えていきます。
これをみると、ClassLoaderもしくはスレッドがなくならない場合はクラスの読み込みデータはのこったままになるようですね。

さて、読み込まれたクラスの情報はだいたいわかりました。
 ClassLoaderそのものが存在しているもしくは、読み込まれたクラスに関連づけられているスレッドが実行中だと、読み込んだクラスがのこったままになるみたいです。
 上記の2つがなくなった場合はSingletonやstaticによる変数へのアクセスだとしてもきれいに掃除されてしまうようです。

前回のred5の停止命令の場合はよけいなデータがのこったわけですが、その原因を次回、少々さぐってみたいと思います。

JMXオブジェクトをつくってみる。その2

今回はもうちょっとJMXを掘り下げてみようと思います。

このブログにソースコードを貼付けるのはちょっとしんどかったのでgithubにリポジトリを公開しておきました。
よかったら利用してみてください。(一応実行可能jarファイルを登録していますが、jarの部分だけダウンロードすることはできないようです。落としても壊れたjarになってしまう。
git cloneコマンドかアーカイブとしてダウンロードしてください。)

さて、前回はコマンドラインベースでxxxMBeanの名前のインターフェイスをつけることでMBeanと判定させて登録していましたが、アノーテーションをつけてやるともっと簡単に登録できることがわかったので、やってみました。
githubにおいたデータならcom.ttProject.jmxtest.target以下のクラスがJMX登録用となっています。
今回はInterfaceの定義の前に@MXBeanをつけるだけという簡単なものになっています。

実行するとこんな感じ
ログの部分では、標準出力のデータが表示されます。
以下はJConsole側のスクリーンショット
登録ボタンを押すとTestObjectがMXBeanとして登録されます。
フィールドになにかいれて登録ボタンを押すと、名前付きで登録されます。

2011年7月27日水曜日

JMXオブジェクトをつくってみる。その1

大規模Javaプログラムを使う場合、JMXはもう必須といっていいほどものすごく便利な管理ツールです。
日本語の解説が少なすぎて切れそうですが、そんなJMXのさわりを書いてみます。

まずはプログラム
package com.ttProject.jmxtest;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class CuiEntry {
  private MBeanServer mbs;
  public static void main(String[] args) throws Exception {
    new CuiEntry();
  }
  public CuiEntry() throws Exception {
    mbs = ManagementFactory.getPlatformMBeanServer();
    mbs.registerMBean(new Test(), new ObjectName("com.ttProject.jmxtest:name=hoge"));
    System.out.println("start");
    while(true) {
      Thread.sleep(100);
    }
  }
  public interface TestMBean {
    public String getData();
    public String printData();
    public void doOutput();
  }
  public class Test implements TestMBean {
    private String data = "test";
    @Override
    public String getData() {
      return data;
    }
    @Override
    public String printData() {
      return data;
    }
    @Override
    public void doOutput() {
      System.out.println(data);
    }
  }
}
別にクラスをわけてもかまわないのですが、適当な名前のクラスとその名前にMBeanをつけたInterfaceを作成、それをregisterMBeanで登録してやると、JconsoleとかでJMX経由でサーバーの状態を参照したり、特定のプログラムを実行したりできます。

自分のオリジナルのクラスが追加されました。

2011年7月26日火曜日

red5をapplet化してみる。その3再起動した場合のメモリー状態

Red5をApplet化するために、とりあえずGUIで操作できるApplicationをつくってます。JavaSwingでつくっててこんな感じ
一応起動したあとには停止したり、別のRed5に移動して再起動させたりできるわけですが、red5-shutdown.shに記録されている方法で停止した場合に古いデータがきちんと削除されたりするのか調べてみました。

結論からいうと、停止してもきちんと全部のデータが削除されるわけではないっぽいです。
Java visualVMをつかって確認してみたところ、ログとかのインスタンスはのこったままになるようで、2重に読み込みされてました。このあたりのはRed5の場合プロセスが落ちるのでそのときにきちんとクリアされるのでしょう。
もしアプリケーションをjarファイルごと再読み込みさせるプログラムなんてものをつくろうとした場合、アンロードされないインスタンスがでてきて頭をなやませそうです・・・

一応記録です。2度Red5を起動しています。
スレッド数

クラスのロード

メモリー状態

アプレット化したとしても同じような感じになるかな・・・

2011年7月25日月曜日

動的にクラスを読み込んで実行する。(配列を呼び出す方法。)

Javaの場合ClassLoaderにjarファイルを設定してあとからプログラムを読み込みさせて、プログラムを実行するという便利な機能があります。

たとえばtest.jarにあるEntryクラスのObject getData(String str, Integer i);を呼び出すなら
// ファイルを読み込む
File jarFile = new File("test.jar");
// クラスローダーにセット
ClassLoader loader = URLClassLoader.newInstance(new URLS[]{jarFile.toURI().toURL()});
// クラス取得
Class clazz = loader.loadClass(com.ttProject.Entry);
// クラスオブジェクト作成
Object inst = clazz.newInstance();
// メソッド取り出し
Method method = clazz.getMethod("getData", String.class, Integer.class);
// 実行
Object obj = method.invoke(inst, "test", 15);


こんな感じになります。
詳細はGoogleあたりで調べてもらうとして・・・問題は単一Argumentでかつ渡す値が配列のとき・・・この方法では動作しません。
Method method = clazz.getMethod("getData", String[].class);
Object obj = method.invoke(inst, new String[]{"arg1", "arg2"});
これを実行するとargumentが一致しないというエラーがでます。
なぜかというと、本来invokeの第2引数は引数の配列を渡すのが本来の形だからです。
上記の場合StringはObjectを継承しています。
よってnew String[]{"arg1", "arg2"}はnew Object[]{"arg1", "arg2"}と等価です。
となると、Stringの配列ではなく2つの文字列を引数として渡していることになり、元々のメソッドの引数は配列の形で1つという条件と当てはまらなくなり例外になるというわけです。

Method method = clazz.getMethod("getData", String[].class);
Object obj = method.invoke(inst, new Object[]{new String[]{"arg1", "arg2"}});
としてやれば、JavaVMがただしく理解してきちんと動作します。
Object[]{}で囲っていやれば、よいというわけです。

余談ですが、getMethod側も実はただしい書き方があります。
Method method = clazz.getMethod("getData", new Class[]{String[].class});
と書くのが正解なようです。

このあたりの動作を正しく理解しておけば、いろいろなクラスが自由自在につかえるようになるでしょう。

さて、red5をGUIで実行できるようにする最後の追い込み、Bootstrapの拡張の作成するか・・・

2011年7月24日日曜日

JMX接続を自作する、その2

前回の記事の対象のプロセスにアクセスする方法ですがいくつかあります。

1:操作するプログラムが対象プロセス自身の場合
2:ローカルで動作しているプロセスでプロセスIDをつかってJMX接続する。
3:リモートで動作しているプロセスでJMXアドレスをつかって接続する。(IDやパスワードがあってもOK)
の3つでしょうか。

今回は1の場合について書いてみます。
1の場合はManagementFactoryからデータを引き出すことができます。

// MBeanServerConnectionを取得
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("java.lang:type=Memory");
MemoryMXBean proxy =
       JMX.newMXBeanProxy(mBeanServer, objectName, MemoryMXBean.class);
System.out.println(proxy.getHeapMemoryUsage());

結果は
init = 0(0K) used = 21876328(21363K) committed = 85000192(83008K) max = 129957888(126912K)

前回の記事で書いた
・java.langに設定されているManagementFactoryから取得できるメモリー状態、CPU状態等のシステムBean
このデータに関してはよけいなことしなくても簡単に引き出せます。
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
System.out.println(mbean.getHeapMemoryUsage());

結果は同じく
init = 0(0K) used = 21876328(21363K) committed = 85000192(83008K) max = 129957888(126912K)

とこんな感じです。

JMX接続を自作する、その1

さて、3つほどシステムにアクセスして状態を取得してどうのこうのというツールやコマンドの情報をかきましたが、これを自作できないか?と思う人がいると思います。

そして、自作できちゃいます。(さすがにコアな部分はNativeのプログラムにお任せすることになりますけど。)

では、やり方の概要
1:対象のプロセスにアクセスする。(いろんなやり方がある。)
2:MBeanServerConnectionを取得する。(MBeanServerでも良い。)
3:対象のBeanにアクセスして関数を実行したりいろいろする。

3で対象になるになるBeanとしては、次のものがあります。
・JMXにあらかじめ登録しているBean
・HotSpotDiagnostic(heapDumpを実行したりVMのオプションを書き換える)
・java.langに設定されているManagementFactoryから取得できるメモリー状態、CPU状態等のシステムBean
これらです。

例としてはこんな感じ
// JMXにつなぐ。
JMXServiceURL url =
                 new JMXServiceURL("service:jmx:rmi:///jnki/rmi://:9999/test");
JMXConnector connector = JMXConnectorFactory.connect(url);

// MBeanServerConnection取得
MBeanServerConnection mbeanserver = connector.getMBeanServerConnection();

// 一覧取得そして表示
Set<ObjectInstance> mbeans = mbeanserver.queryMBean(null, null);
for(ObjectInstance objInst : mbeans) {
    System.out.println(objInst);
}

CPU状態のダンプ

メモリーに関する記事を書いたので、次はCPUかな。
メモリー状態監視
メモリー状態ダンプ

CPUの状態となると、Thread状態のDumpでしょうか。

環境により、いろいろやり方がありますが今回はLinux用ということにしておきます。

1:SIGQUITをおくって標準出力に出す方法
$ kill -3 [プロセスID]
このコマンドを実行すると、ThreadDumpが取得できますが標準出力を殺すような動作させている場合にはつかえません。

2:jstackを利用する方法
$ jstack [プロセスID]なら関係なく取得することができます。コンソール上に出力する命令ですので、適当なファイルに出力するようにかえてやればOK
$ jstack [プロセスID] > /home/xxxxx/thread.dump
みたいな感じでどうぞ。

3:Java VisualVMを利用する方法
おなじみJavaVisualVM・・・
Threadの項目にThread Dumpというのがあるのでクリックする。

これを利用すると、デッドロックとかが検出できますね。
状態のDumpだけなので忙しいスレッドの取得等はできません。
そこででてくるのがこのコマンド
$ ps -efL | grep [プロセスID]
linuxのpsコマンドにLをいれるとプロセスごとではなくスレッドごとのデータを取得することができるので、上記のコマンドでどのスレッドのLoadAverageがあがっているのか確認することができます。
Threadの識別IDがわかったら、それをHexに直してJava側のDumpデータについているThreadの識別IDと比較してください。一致するThreadが問題のやつということになります。
これを利用すると、無限ループが発生している箇所の特定とかができますよ。

メモリー状態ダンプ

先の記事のメモリー状態監視でメモリーの使われ方を監視する方法を書きましたが、次にきになるのは実際のメモリーがどうなっているのか?ということです。

SunのJavaVMメモリーをDumpする方法がありますのでそれを利用します。
まず基本から
1:JDKについているjconsoleを起動します。
2:次に対象のプロセスにアクセスします。リモートプロセスの場合はservice:jmx:rmi://[]・・・という長いコマンドを入力する必要があります。
3:一番右のMBeanのタブをクリック
4:左のBeans一覧から[com.sun.management]-[HotSpotDiagnostic]-[操作]-[dumpHeap]を選択
5:右のパネルに関数の情報がでてくるので、
p0にファイルを指定[/tmp/message/memorydump.hprof]
p1にtrue/falseを指定[trueでいいと思います。このフラグはdumpするときにGC改修予定のオブジェクトを無視するかどうかのフラグです。]
6:dumpHeapのボタンをクリック
これで対象プロセスが動作しているサーバーにデータが出力されます。

dumpしたデータは作業用PCにダウンロードして、java visualvmを利用して確認します。
1:JDKについているJavaVisualVMを起動します。
2:読み込み→右下の拡張子設定をHeapDumpに設定
3:先ほどのデータを読み込む
これで読み込みが完了します。
(なおVisualVMはjconsoleの動作をかねてる部分もあるので、visualVMからdumpHeapを実行することもできます。(出力先は固定されますけど。))

クラスオブジェクトのもつフィールドデータやクラスオブジェクトを参照しているデータの情報がわかるので、必要のないはずのデータがなぜのこっているのか?どのクラスのデータがたまっているのか?といった情報がするみつけることができます。

ちなみに僕はこれを利用して、SpringのBeanの階層のrootを取得することができました。VisualVMめっちゃ便利です。

メモリー状態監視

Javaでサーバーを組み立てて、長期運用しているとOut of Memoryエラー等がでて、サーバーが飛んだりします。

こんな時はGCの挙動を確認してメモリーの利用量を監視してみましょう。
んで、やり方。

その1:GCのタイミングのときのみ記録する。
Javaを起動するときに特定のフラグをつけると、GCが発生したときにデータを記録してくれます。
-Xloggc:[ファイル名]
をいれるのが一般的かな。
-verbose:gc
というのもあります。

その2:メモリーの状態を記録する。
動作しているJavaのプロセスIDがわかるなら
jstatのコマンドを利用して記録することもできる。
$ jstat -gc [プロセスID] [記録間隔]
オプションは-gc以外にもいろいろあります。
こちらで調査した場合はgcviewerではグラフ化できないので、エクセルなりつかって、グラフ化するといいと思います。

その3:jconsoleやjava visualvmでメモリー利用量のグラフを眺める。jmx経由でjavaの状態を確認できるこれらのツールでプロセスにつなぐと状態をリアルタイムで確認することができます。
ただしGUIツールでデータの保存ができないのが難点(visualVMにはスナップショットの記録というのがあるけど)

ピークになる時間帯をあらかじめ目星つけておいてjstatで記録をとる。
リアルタイムにいろんな情報がとりたいときにはjconsoleやvisualVMで確認する。
この2つで基本せめることができますね。

2011年7月14日木曜日

red5をapplet化してみる。その2

applet化する上でGUIっぽく動作させる必要があるわけですが、そのためにRed5のプログラムを改造していました。

基本的にBootstrapでやってることをGUI経由で実行することで必要な動作ができる・・・みたいなものをつくれば十分なのですが、やってみたところどうもクラスの読み込みがおかしい・・・ClassLoaderがこわれてる?という状況に陥りました。

というわけでクラスローダーのhashCodeを取得する方法です。
現在のThreadのClassLoaderのHashCodeの取得の仕方
Thread.currentThread().getContextClassLoader().hashCode();

現在のクラスの読み込み時のClassLoaderのHashCodeの取得の仕方
this.getClass().getClassLoader().hashCode();

自力でClassLoaderを作成して読み込んだクラスを取得したいときに、ベースとなっているClassLoaderがおかしいと自力で読み込んだクラスのデータが見えなくなってしまい動作できなくなってしまうという現象がおこり、30分くらいはまっていました・・・

みなさんもClassNotFoundException等に悩まされた場合は、実行スレッドのClassLoaderといま利用しているクラスのClassLoaderが違ってない?というのを疑ってみてください。
もしかしたら解決できるかもしれませんよ。

youtubeにその場で録画みたいな機能ありますね

1:youtube.comにいく
2:ログインする。
3:右上のアップロードクリック
4:ウェブカメラから録画
5:録画開始したら・・・
6:コンソール(MSDOSとか)でnetstat -na | grep 1935を実行

するとこんな情報が取得できます。

xxxx-iMac:~ xxxxxxxxx$ netstat -na | grep 1935
tcp4       0    860  192.168.3.4.49393      64.233.183.192.1935    ESTABLISHED

youtubeの録画機能でつかってるrtmpサーバーは64.233.183.192みたいですね。
そしてここにhttpでアクセスする。
つまりブラウザで
http://64.233.183.192:1935/にアクセスする。
するとこんな文字列がでてきます。
「Wowza Media Server 1x Subscription for Google 1.7.2 build12227」

実はwowzaサーバーは1935ポートと8086ポートにhttpでアクセスするとサーバーの情報を取得したりできたりします。(ポートはデフォルト値なので、変更可能ですよ。)

というわけでyoutubeでは1x Subscription for Google 1.7.2というバージョンのwowzaサーバーつかってるっぽいですね。

2011年7月10日日曜日

red5をapplet化してみる。その1

さて、Red5のプログラム上から設定ファイル上のデータをある程度削除してみました。

やったこと

  • red5-core.xmlのrtmptの動作設定を削除
  • red5-common.xmlのWarDeployerを削除(warファイルのサポートしてもしかたないため)
  • red5-common.xmlのJMXAgentを削除JMXはあまり好きじゃないので削除する方向で考えたいところ
  • red5.xmlのJEEcontainerを削除してみた。Rtmp用のApplicationAdapterの読み込みがJEEに依存しているので、削除したらサーバーは起動するが、アプリケーションが読み込まれなくなった。プログラムの変更が必須のようです。(結局削除してない)
  • red5.xmlのplugin lancherを削除
  • red5-common.xmlとred5-core.xml上のplaceholderConfigを削除(${で指定しているデータも書き換え)
  • jee-container.xmlのservlet用のポートやアドレスも直書きに変更
  • red5.propertiesからsocket policy以外すべて削除
ここまで実行しました。動作は良好。

次は、bootstrapまわりからxml読み込みを抜いた動作するように調整していってみたいとおもいます。

red5をapplet化してみる。その0

red5をapplet化してみたいと思っています。

理由は

  1. 軽量red5を作成して、javascriptのbookmarkletでwonderFLのサイトにサーバーを任意で追加できるようにする。んで面白いプログラムをかいてみる。
  2. applet上でUDPホールパンチングを利用することで、P2Pを利用したデータのやりとりができるようにする予定なので、そのための準備

この2点をなんとかしてみたいですね。

今のところapplet動作に関してわかっていることは・・・

  • TCPのサーバーの作成は可能
  • ehcacheで利用するjava.io.tmpdirのデータの取得可能(ehcacheの動作はAMFデータのやりとりと癒着しているので、外せないと思う。)
これから知らないとだめだろうと思うことは
  • confの中の設定データやwebappの内部にあるアプリケーションの設定まわりのファイルはなるべくない方向にしたい。
  • rtmp以外のプロトコルはサポートする必要はないと思うので削除できるか?可能ならプログラムも軽量化する。
  • ライブラリの利用まわりの調整。
  • appletでのjmxってどうなる?
このあたりが懸念事項となっております。

2011年7月6日水曜日

RTMPClientの使い方その5、録画する。

録画の方法そのものは昔atwikiに書いたことがあって
http://www29.atwiki.jp/red5server/pages/29.html
ここにソースコードがあります(このソースコードもどこかのサイトから拾ってmina2.0に合わせただけのものです)。

そしてこのプログラムは間違っています。
どこが間違っているかというと、videoTS audioTSという変数を準備して、getTimestampで取得した時刻情報からパケットをおさめる時間を計算していますが、getTimestampがそもそもplay開始からの経過時間をもとにしているので、この計算が必要ないという・・・

    if (rtmpEvent instanceof VideoData) {
        videoTs += rtmpEvent.getTimestamp();
        tag.setTimestamp(videoTs);
    } else if (rtmpEvent instanceof AudioData) {
        audioTs += rtmpEvent.getTimestamp();
        tag.setTimestamp(audioTs);
    }

本当は
        tag.setTimestamp(rtmpEvent.getTimestamp());
だけでOK。いやむしろこうやらないとflvファイルがおかしくなります。

そして面白いのが、ここからです。
timestampの値を1/2 や 1/3にしてやると、通常の動画プレーヤーでみたらなんと倍速や3倍速になるという・・・
FMEでおくっているデータでも倍速になったりしてかなり楽しいことに・・・


イベントの処理時間をはやめているだけなんで、はやくなるかはプレーヤーのFLVの解釈のやりかた次第ですけどね。

2012/06/19追記
flvデータのタイムスタンプをいじって再生速度を変更させる件ですが、コーデックもろもろの条件により、ほぼ動作しません。
申し訳ありませんでした。

2011年7月1日金曜日

MacでAppletプログラムを組む(TCPサーバーを書く)

始めに注意。今回の記事には、自分で署名したappletをつけています。
悪さをするプログラムを書いていませんが、ご注意ください。


先日からやっているUDPホールパンチングによるNAT越えのP2Pですが、Javaアプリケーションで書いてもいいのですが、それだといまいち使いにくい・・・

ならJavaAppletで書いてしまってダウンロードしなくてもできるようにしてしまえ・・・
というわけでJavaAppletにとりいそぎサーバープログラムを書いてみたところ、つまりまくったので、MacのEclipseでAppletを組む人向けの記事をかいておこうとおもいます。

まず、1つ目、できあがったappletのブラウザ表示から・・・簡単なhtmlを書いて起動しても対象クラスがみつからないというエラーが大量にでました。
<applet archive="appletTest.jar" code="com.ttProject.appletTest.AppletEntry" width...> </applet>
こんなタグを書いているのにどうしても動作しない。
ブラウザのURLの部分をみてみたら、file://localhost/User/xxxxx
というアドレスになっていました。
これ実はfile:///User/xxxxxでないと動作しません。なぜlocalhostがはいったのかは不明。

続いて2つ目、サーバーとして動作させるため、サンドボックスから抜けないと別のところからの通信をうけとることができません。
そこで証明書をつくるのですが、Mac上で証明書をつくろうとすると文字化けして、どうしてもうまく動作させることができませんでした。
なので、サクラサーバーに借りているVPS上にいったんデータを移してからCentOS上で署名をするハメに・・・

最後に3つ目、署名もできたので、いざ実行させようとしてもどうしても動作しません。
原因はなんとeclipseのappletのビジュアルエディタでした。
このビジュアルエディタ、動作させるために、裏でappletを起動しているようです。今回はサーバープログラムを書いたのでポートをlistenするのですが、裏でうごいているappletが先にポートをとってしまうので動作しないというとんでもない事態になりました。
なので、それらのプログラムを殺してから実行しないと動作しないということがわかりました。
そしてつくったアプレットが以下
今回のアプレットはサーバーとして、8080ポートを監視します。
この動作はサンドボックス外の動作なので、署名付きのアプレットとなっております。
万一のことがおこりましても、このサイトでは責任を一切負いませんのでお気をつけ下さい。


アプレットが起動したら、適当なコンソールでnetstatで8080番をListenしていることと、telnet localhost 8080でこのアプレットに接続でき、適当な入力を送ると、アプレット上に表示されると思います。

これで一応サーバー動作はできることがわかったので、今度はUDP通信のテストを実行して、P2Pチャットでもつくろうかな。