Help us understand the problem. What is going on with this article?

NLE(Niconico Live Encoder)はどうやって配信用URLを取得しているのか?

More than 5 years have passed since last update.

NLEではセッションを消費せずに配信用のURLやキーを取得しています。この動きと同じことが実装できれば、配信ツールでセッションを一個消費ということも無くなるはずです。ということで、解析結果とサンプルコード(Ruby)になります。

全体的な動作

  1. アップデート確認
  2. ログイン
  3. ライセンス取得
  4. ログイン
  5. 配信情報確認
  6. 動画配信!

なお、アップデート確認とライセンス取得は不要です。

ログイン

Request

  • URL: https://account.nicovideo.jp/api/v1/login
  • Method: POST
  • User Agent: nicoliveenc/{NLEバージョン番号}
  • Content Type: application/x-www-form-urlencoded
  • Form Data:
    • site: nicolive_encoder
    • time: {UNIXタイム}
    • hash_key: {8桁16進数} # 省略可
    • mail: {メールアドレス}
    • password: {パスワード}

hash_keyの生成方法は不明です。なくても認証できます。

Response

  • Set Cookie:
    • Name: nicosid
    • Vlaue: {UNIXタイム}.{数字}
    • Path: /
    • Domain: .nicovideo.jp
  • Data: XML

    <nicovideo_user_response status="ok">
        <ticket>nicolive_encoder_{数字}</ticket>
    </nicovideo_user_response>
    

必要なのはticketのコンテンツのみです。nicosidは別に使わなくてもいいようです。もし、ログインに失敗している場合はstatusがfailになります。

バージョン確認

URL: http://live.nicovideo.jp/encoder/update.xml

NLEの最新バージョンを確認します。必須ではありません。詳細は省略。

ライセンス取得

URL: http://live.nicovideo.jp/encoder/getlicence

NLEのライセンスを取得します。必須ではありません。詳細は省略。

配信情報取得

Request

  • URL: http://live.nicovideo.jp/api/getpublishstatus
  • Method: POST
  • User Agent: nicoliveenc/{NLEバージョン番号}
  • Content Type: application/x-www-form-urlencoded
  • Cookie: nicosid={loginで取得した値} # 省略可
  • Form Data:
    • ticket: {loginで取得したticketの値}
    • nleserial: {getlicenceで取得したシリアル番号} # 省略可
    • accept-multi: {0/1}

Cookieは渡さなくてもいいようです。nlserialにはライセンス取得で取得したシリアル番号が必要ですが、なくても問題ありません。accept-multiは一つのみ取得(0)か複数取得(1)を指定できます。値によって結果のXMLが変わります。

Response

accept-multi=0の場合

  • Data: XML

    <getpublishstatus status="ok" time="1432436641" multi="true">
        <stream>{動画の情報 省略}</stream>
        <user>{ユーザーの情報 省略}</user>
        <rtmp is_fms="1">
            <url>rtmp://{ホスト}.live.nicovideo.jp:{ポート番号}/publicorigin/{数字}</url>
            <stream>lv{数字}</stream>
            <ticket>{ユーザID}:{ライブID}:{数字}:{数字}:{数字}:{数字}:{16進数}</ticket>
            <bitrate>{数字}</bitrate>
        </rtmp>
    </getpublishstatus>
    

accept-multi=1の場合

  • Data: XML

    <getpublishstatus status="ok" time="1432436641" multi="true">
        <list>
            <item>
                <stream>{動画の情報 省略}</stream>
                <rtmp is_fms="1">
                    <url>rtmp://{ホスト}.live.nicovideo.jp:{ポート番号}/publicorigin/{数字}</url>
                    <stream>lv{数字}</stream>
                    <ticket>{ユーザID}:{ライブID}:{数字}:{数字}:{数字}:{数字}:{16進数}</ticket>
                    <bitrate>{数字}</bitrate>
                </rtmp>
            </item>
        </list>
        <user>{ユーザーの情報 省略}</user>
    </getpublishstatus>
    

statusがokなら配信可能です。配信できないときはfailが入ります。accept-multi=0の場合は、単純にアクセスしたときと同じです。accept-multi=1の場合は、配信の情報だけ、list化されてitemに入れられます。実際の配信URLとストリームキーは下記になります。

  • 配信URL: {urlの値}?{tickeの値}
  • ストリームキー: {streamの値}

bitrateは現在配信可能なビットレートを表します。

サンプルコード

以上を踏まえて、配信URL、ストリームキー、ビットレートを取得するツールを作りました。煮るなり、焼くなり、Go言語製コメビュに移植するなり、ご自由に!

simnle.rb
# coding: utf-8

require "net/https"
require "uri"
require "nokogiri"

class SimNLE

  NICOURI = {
    login: URI("https://account.nicovideo.jp/api/v1/login"),
    getpub: URI("http://live.nicovideo.jp/api/getpublishstatus"),
  }

  USER_AGENT = "simnle.rb/0.0.1"

  def initialize(mail, password)
    @mail = mail
    @password = password
    @ticket = nil
    @nicosid = nil
  end

  def post_header
    header = {
      "User-Agent" => SimNLE::USER_AGENT,
      "Content-Type" => "application/x-www-form-urlencoded",
    }
    if @nicosid
      header["Cookie"] = "nicosid=#{URI.encode_www_form_component(@nicosid)}"
    end
    return header
  end

  def login
    https = Net::HTTP.new(SimNLE::NICOURI[:login].host, 443)
    https.use_ssl = true
    https.verify_mode = OpenSSL::SSL::VERIFY_NONE

    data = {
      "site" => "nicolive_encoder",
      "time" => Time.now.to_i.to_s,
      "mail" => @mail,
      "password" => @password,
    }

    response = nil
    https.start do
      response = https.post(SimNLE::NICOURI[:login].path,
          URI.encode_www_form(data), post_header)
    end
    response.value

    doc = Nokogiri::XML.parse(response.body)
    case doc.xpath("/nicovideo_user_response").attribute("status").to_s
    when "ok"
      @ticket = doc.xpath("/nicovideo_user_response/ticket").inner_text
      response.get_fields("set-cookie").each do |cookie_data|
        list = cookie_data.split(";")[0].strip.split("=")
        if list[0] == "nicosid"
          @nicosid = list[1]
        end
      end
      return @ticket
    when "fail"
      return nil
    else
      raise "Unknown status"
    end
  end

  def getpub
    unless @ticket || login
      raise "Cannot login"
    end

    http = Net::HTTP.new(SimNLE::NICOURI[:getpub].host, 80)

    data = {
      "ticket" => @ticket,
      "accept-multi" => 0,
    }

    response = nil
    http.start do
      response = http.post(SimNLE::NICOURI[:getpub].path,
          URI.encode_www_form(data), post_header)
    end
    response.value

    doc = Nokogiri::XML.parse(response.body)
    case doc.xpath("/getpublishstatus").attribute("status").to_s
    when "ok"
      url = doc.xpath("/getpublishstatus/rtmp/url").inner_text
      stream = doc.xpath("/getpublishstatus/rtmp/stream").inner_text
      ticket = doc.xpath("/getpublishstatus/rtmp/ticket").inner_text
      bitrate = doc.xpath("/getpublishstatus/rtmp/bitrate").inner_text
      rtmp_url = "#{url}?#{ticket}"
      rtmp_key = stream
      return rtmp_url, rtmp_key, bitrate
    when "fail"
      return nil
    else
      raise "Unknown status"
    end
  end
end

if __FILE__ == $0
  if ARGV.size < 2
    warn "Usage: ruby #{$0} mail password"
    exit 1
  end
  simnle = SimNLE.new(ARGV[0], ARGV[1])
  # puts simnle.login
  puts simnle.getpub
end
raccy
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした