2010-10 << 2010-11 >> 2010-12

2010-11-06 (土)

*[ニコニコ動画] 動画をjpegで取得する

iPhoneとかが使ってるインターフェイスにアクセスしてみる.

iPhone/iPod touchアプリ “ニコニコ動画” をパケット解析する(2)

iPhoneとか持ってないので,この記事がとても参考になりました.

最初はデータ生成するからちょっと待て(?)といわれるので,数秒置いて同じリクエストを送るとデータが帰ってくるようです.

間違ってるかもしれませんが,chat_get=0で情報を取得したあと,chat_get=1とかにして動画データを取得できるようです.

取得したデータを見たら,動画情報,コメント,音声,jpeg,jpeg,音声,jpeg,jpeg,音声…という風に格納されていました.データの先頭にサイズが入っているのでパースするのは簡単そう.

音声部分は,生のPCMではないようなので,ニコモバの資料にある通りADPCMでしょうか.ADPCMと一言で言ってもフォーマットがいくつかあるので困りますが.

詳細な説明を書きたいところですが,なんかダメな気もするので我慢.

サンプル

必要なモジュールは適当に入れてください.

#!/usr/local/bin/ruby -Ke
# -*- coding: ascii-8bit -*-
require "rubygems"
require 'httpclient'
require 'kconv'

account = { 'mail'=>'hoge@exsample.com', 'password'=>'*******' }
vid = 'sm9'

client = HTTPClient.new

res = client.post('https://secure.nicovideo.jp/secure/login?site=nicoiphone', account)
if res.content !~ /status="ok"/i || res.content !~ /<ticket>([^<]+)<\//
    print "TICKET ERROR\n"
    exit
end
ticket = $1
print "ticket: #{ticket}\n"

res = client.get_content('http://i.nicovideo.jp/v2/login?ticket='+ticket).toutf8
if res !~ /status="ok"/ || res !~ /<session_id>([^<]+)<\//
    print "LOGIN ERROR\n"
    exit
end

sid = $1
print "sid: #{sid}\n";

res = client.get_content("http://fence.i.nicovideo.jp/v2/videostatus?video_id=#{vid}").toutf8
print "status: #{res}\n"

res = client.get_content('http://fence.i.nicovideo.jp/v2/gate?video_id='+vid+'&sid='+sid,nil,{'User-Agent' => 'iPad'}).force_encoding('ascii-8bit')
if res !~ /tid\x00.([^\x00]+)\x00.*psvr\x00.([^\x00]+)/m
    print "GATE ERROR\n"
    exit
end

tid = $1
psvr = $2
time = 0

begin
    res = client.get_content("http://fence.i.nicovideo.jp/v2/play?transmission_speed=0&sound_quality=16&frame_rate=8&chat_get=1&video_id=#{vid}&sid=#{sid}&network=wifi&thread_id=#{tid}&date_time=#{time}&play_server=#{psvr}&initial=1").force_encoding('ascii-8bit')
    p a = res.unpack('a4NnnN')
    if a[0] == "DNOP" && a[2] == 2
        print "wait...\n"
        sleep 1
    elsif a[0] != "DWNG"
        print "header: #{a[0]}\n"
        exit
    end
end while a[0] == "DNOP" && a[2] == 2

pos = 0x14 + a[5]
frame = 0
while pos+6<res.length do
    block_info = res[pos,6].unpack('nN')
    p pos
    p block_info
    if block_info[0] == 0xCB
      foo = File.open("#{vid}_#{sprintf("%04d_%02d",time,frame)}.adpcm",'wb')
      foo.write res[pos+6,block_info[1]]
      foo.close
    end
    if block_info[0] == 0xC8
      foo = File.open("#{vid}_#{sprintf("%04d_%02d",time,frame)}.jpg",'wb')
      foo.write res[pos+6,block_info[1]]
      foo.close
      frame+=1
    end
    pos+= 6 + block_info[1]
end