<?xml version="1.0" encoding="utf-8"?>

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:cc="http://web.resource.org/cc/"
  xmlns="http://purl.org/rss/1.0/">

  <channel rdf:about="http://unknownplace.org/memo/">
    <title>unknownplace.org - Tags: ustream</title>
    <description>powered by CLON 0.2</description>
    <link>http://unknownplace.org/memo/</link>
    <dc:language>ja</dc:language>
    <dc:creator>typester</dc:creator>    <dc:date>2007-10-03T22:14:00+09:00</dc:date>
    <admin:generatorAgent rdf:resource="http://unknownplace.org/apps/clon?v=0.2" />

    <items>
      <rdf:Seq>
	<rdf:li rdf:resource="http://unknownplace.org/memo/2007/10/03#e002" />
 	<rdf:li rdf:resource="http://unknownplace.org/memo/2007/10/03#e001" />
       </rdf:Seq>
    </items>
  </channel>

  <item rdf:about="http://unknownplace.org/memo/2007/10/03#e002">
    <title>22:14  ustreamのflvの音声変換</title>
    <description />
    <link>http://unknownplace.org/memo/2007/10/03#e002</link>
    <content:encoded><![CDATA[<p><a href="http://code.google.com/p/nelly2pcm/">nelly2pcm</a> + <a href="http://sox.sf.net/">sox</a> でいけました！</p>

<pre><code>nelly2pcm test.flv | sox -t raw -c 1 -2 -r 11000 -s - test.wav
</code></pre>

<p>#plagger-ja++</p>
]]></content:encoded>
    <dc:subject>ustream</dc:subject>
    <dc:creator>typester</dc:creator>
    <dc:date>2007-10-03T22:14:00+09:00</dc:date>
  </item>
  <item rdf:about="http://unknownplace.org/memo/2007/10/03#e001">
    <title>19:18  ustreamの録画flvデータのURLを得るスクリプト</title>
    <description />
    <link>http://unknownplace.org/memo/2007/10/03#e001</link>
    <content:encoded><![CDATA[<blockquote>
  <p><a href="http://clouder.jp/yoshiki/mt/archives/000996.html">Clouder::Blogger: ustreamの録画した動画のflvをダウンロードする方法</a></p>
  
  <p>これは想像ですが、swf内でhttp://gw.ustream.tv/gateway.phpをPOSTしているのはわかっているので、そこで取得しているのか、もしくはJavaScriptでAjaxをつかってどこかから取得しているのか、といったところだと思いますが、いずれにしてももう少し解析が必要です。</p>
</blockquote>

<p>とあるのをみて、gateway.php を調べてみました。</p>

<p>これはFlashのAMFのRPCサーバーで、録画されたものを再生するときには <code>client.watch_video</code> という関数を呼んでいるようです。</p>

<p>as3で書くと</p>

<pre><code>package {
    import flash.display.*;
    import flash.net.*;

    public class gw extends Sprite {
        public function gw() {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;

            var cid:String = stage.loaderInfo.parameters["cid"];
            if (cid) {
                var nc:NetConnection = new NetConnection();
                nc.objectEncoding = ObjectEncoding.AMF0;
                nc.connect("http://gw.ustream.tv/gateway.php");
                nc.call("client.watch_video", new Responder(gatewayHandler), { cid: cid });
            }
        }

        private function gatewayHandler(res:Object):void {
            for (var i:String in res) {
                log(i+": "+res[i]);
            }
        }
    }
}
</code></pre>

<p>こんな感じになります。cid は再生URL <code>http://ustream.tv/recorded/RLdKCmCouWOpkkEznZH4QA</code> とかの <code>RLdKCmCouWOpkkEznZH4QA</code> 部分です。</p>

<p>これをperlで呼ぶにはどうしたらいいか！ ということでがんばってみました。</p>

<p>CPANにこのAPIのサーバー側の実装 (AMF::Perl) があったのでそれを適当に読んだり、実際API呼んでるときのパケットと見比べたりしつつして以下のようにすればできました。</p>

<pre><code>#!/usr/bin/env perl

use strict;
use warnings;

use AMF::Perl;
use LWP::UserAgent;

my $cid = shift || '5Fq8cQp9eHsEEknbQ22OOZniqS2xUiuz';

my $amf = AMF::Perl::Util::Object-&gt;new;
$amf-&gt;addBody('client.watch_video', '/1', { cid =&gt; $cid });

my $outputstream = AMF::Perl::IO::OutputStream-&gt;new;
my $serializer = AMF::Perl::IO::Serializer-&gt;new($outputstream, 'utf-8');
$serializer-&gt;serialize($amf);

my $amfdata = $outputstream-&gt;flush;

$amfdata =~ s/\xff\xff\xff\xff/\0\0\0\x31\x0a\0\0\0\x01/;

my $ua = LWP::UserAgent-&gt;new;

my $req = HTTP::Request-&gt;new( POST =&gt; 'http://gw.ustream.tv/gateway.php' );
$req-&gt;content( $output );
$req-&gt;content_type('application/x-amf');
$req-&gt;content_length( length $amfdata );

my $res = $ua-&gt;request($req);

my $deserializer = AMF::Perl::IO::Deserializer-&gt;new(AMF::Perl::IO::InputStream-&gt;new($res-&gt;content), 'utf-8');

use YAML;
print Dump $deserializer-&gt;getObject-&gt;getBodyAt(0)-&gt;{value};
</code></pre>

<p>これで</p>

<pre><code>---
err_desc: ok
server_id: 9
success: 1
user_id: 32983
video_length: 366.207
video_name: broadcast/35957/1191239239348
video_type: broadcast
</code></pre>

<p>とかいう出力がでます。これから</p>

<pre><code>http://flash{server_id}.ustream.tv:18881/{video_name}.flv
</code></pre>

<p>というflvのURLが作れます。</p>

<p>perlソースの途中の謎の置換 <code>s/\xff\xff\xff\xff/\0\0\0\x31\x0a\0\0\0\x01/;</code> してて、これはなにかよくわかってません。ただ実際のパケットと見比べて違う部分置換してみたらいけたという代物です。</p>

<p>あとこれで得られるflvデータの音声部分が謎のcodecで変換できない！ だれかこれをmp3とかにする方法ご存知のかたいましたらぜひともお知らせください！</p>
]]></content:encoded>
    <dc:subject>perl, ustream</dc:subject>
    <dc:creator>typester</dc:creator>
    <dc:date>2007-10-03T19:18:00+09:00</dc:date>
  </item>
</rdf:RDF>
