生配信チェッカーサイトを作りたいブログ

生配信チェッカーサイトを作りたい人がメモ書きをするブログ

配信ラグについての考察(メモ)

原因
視聴者側の原因
・契約回線の通信速度が遅い
 →光回線にかえましょう
・プロバイダの通信速度が遅い
 →プロバイダをかえましょう
http://yasashikunet.com/provider/
wifiの通信速度が遅い
 →無線機器を見直すか、有線接続にしましょう
・PCのスペック不足
・キャッシュがどうたらこうたら

サーバー側の問題
・物理的な距離が遠い(海外等)
・回線が弱く、大量のリクエストをさばききれていない

通信プロトコルの問題
https://www.cavelis.net/howto/#!faq_node
p2p通信のゲスト
 ラグが生じやすい
TCP vs UDP
 UDPの方が有利
RTMP
RTMFP
WebRTC
HLS

書きかけ

beam.proから情報を取得する

beam.proは新鋭のゲームストリーミング配信サイト。
最近マイクロソフトに買収されたらしいのでXBOX系のゲームとのコラボが強化されるのかもしれない。
ゲーム配信に特化した配信サイトといえばhitboxがあげられるが、beam.proはよりラグが少ないとのうわさもある。

Rest APIのマニュアル
https://dev.beam.pro/rest.html#

エンドポイント
https://beam.pro/api/v1/

全チャンネル取得(配信中のみ)
https://beam.pro/api/v1/channels

個別のチャンネル情報取得
https://beam.pro/api/v1/channels/hogehoge

<?php

$info = beam_info();
var_dump($info);

function beam_info(){
$url_base = "https://beam.pro/api/v1/channels/";
$beam_id = "test";
$url = $url_base . $beam_id ;

$json = file_get_contents($url);
$contents = json_decode($json, true);

//var_dump($contents);
$info['isLive'] = $contents['online'];
$info['viewers'] = $contents['viewersCurrent'];
$info['title'] = $contents['protected'];
$info['created_at'] = $contents['updatedAt'];
$info['id'] = $contents['id'];
$info['channel_name'] = $contents['name'];
$info['user_name'] = $contents['user']['username'];
$info['url'] = "https://beam.pro/" . $contents['token'];
$info['archive_url'] = "";
$info['snapshot'] = $contents['cover']['url'];
$info['lastDate'] = "";
return $info;
}


ところでこのブログのアクセス解析を見ると、最近cavetubeからの訪問者がいらっしゃったようだ。
どこかの配信でとりあげられたのだろうか。自分の用の備忘録過疎ブログではあるが、人が来るのは素直にうれしい。

生配信サイトを作る場合のメモ3

今回は最近かなり勢いのあるWebRTCについて。
ブラウザを用いてp2p通信ができ、RTMFPと双璧をなす存在だと考えています。
chromefirefox等のブラウザが対応しており、デフォルトでWebカメラやマイク等のデバイスから情報を取り込むことが可能です。

スクリーンキャプチャ
http://tips.hecomi.com/entry/20140101/1388593695
chromeだと chrome://flags から設定可能
 タブのキャプチャ可能なchromeエクステンション
https://www.webrtc-experiment.com/screen-broadcast/
WebRTC動画の保存
https://www.webrtc-experiment.com/RecordRTC/AudioVideo-on-Firefox.html
キャプチャボードの画面を取り込めるかどうかはブラウザによる?
取り込んだ画面の解像度やフレームレートはどのように設定すれば良いか?

ライブラリ
・PeerJS
 http://peerjs.com/
・EasyRTC
・XSockets
・simpleWebRTC
・Holla
・webRTC.io

・SkyWay
NTTが開発するPeerJS互換のJavaScriptライブラリ、仲介サーバ機能、WebAPI、日本語の開発ドキュメントを提供するサービス。
http://nttcom.github.io/skyway/

サーバ
・peerserver
 http://peerjs.com/peerserver
 PeerJSが提供するWebRTCに関するサーバ



参考
http://blog.livedoor.jp/tak_tak0/archives/52134277.html

ここでとりあえず試せる。キャプチャーボードの画面は移るが、解像度などの設定方法不明。
https://opentokrtc.com
SkyWayチャットでキャプチャーボードの画面が映らなかった。
https://chat.skyway.io/

生配信サイトを作る場合のメモ2

http://checker-site.hatenablog.com/entry/2016/06/12/190007
の続き。

特にRTMPS(Real Time Media Flow Protocol)について。
UDPp2pプロトコルなのでサーバーを介するRTMP(Real Time Messaging Protocol)と比べてサーバーの負担を減らすことができる。

商用ソフトとしては
Adobe Media Server(AMS)
・Wowza Streaming Engine

フリーでオープンソースなRTMPSサーバーソフトは
・MonaServer
http://www.monaserver.ovh/
・Cumulus
https://github.com/OpenRTMFP/Cumulus
 (どうもManaserverに置き換えられ、開発は停止されているらしい。
 https://groups.google.com/forum/#!topic/monaserver/3I-VsRGWP6c
)
・ArcusNode
https://github.com/OpenRTMFP/ArcusNode
の3つ。
MonaServerはバイナリが用意されているので簡単に試すことができる。
OBSとMonaServerを使ってRTMFP通信をするテスト。
https://obsproject.com/forum/resources/how-to-set-up-your-own-private-rtmfp-server-using-monaserver.153/

MonaServerをサーバーサイドで起動して、OBSで配信をつなぐ。
OBSの設定は例えばこんな感じ(プレイパスをtestに設定)。
f:id:checker_site:20160619194751p:plain

配信を見るには例えば、ブラウザからこのサービスを使用する。
http://raspi.monaserver.ovh/MonaClients/VideoPlayer/?fit&url=rtmfp://127.0.0.1/test

疑問
・OBSでの接続ではRTMPでサーバーにつないでいる?
flashのみで配信設定できればここの負担を減らすことができる?ウェブカメやマイクとつなぐのは容易だけど、ウインドウ画面取り込み等をするのが難しい?
・cavetube(とkukululive?)は配信中にブラウザで配信画面を移しているのは、配信者のflash playerを利用してFTMFP通信をしているためか?

参考
http://forums.whirlpool.net.au/archive/2505029
http://raspi.monaserver.ovh/MonaClients/VideoPlayer/?fit&url=rtmfp://127.0.0.1/test
https://helping-squad.com/monaserver-setup-and-configuration/

HLSについても調べたい。

生配信サイトを作る場合のメモ

チェッカーサイトではなく生配信サイトを作るにはどうしたらよいのか調べたメモ。
参考になるのはLiveTube, CaveTubeなど。

プロトコルRTMP(Real Time Messaging Protocol)のサーバーソフトとしては

Adobe Media Server(AMS)
 Adobe純正でお高い。60万くらい?
・Wowza Streaming Engine
 Adobeよりは安い
Red5 Media Server
 オープンソースで無償。言語はJAVA。いまのところRTMFPは実装していない。
C++ RTMP Server(crtmpserver)
 オープンソースで無償。言語はC++
(crtmpserver関係
http://server-setting.info/centos/crtmpserver-streaming.html)
・nginxにモジュールをインストールして使う
 https://obsproject.com/forum/resources/how-to-set-up-your-own-private-rtmp-server-using-nginx.50/
 
等がある。無償で使うなら下2つ。
またクライアントソフトとしてはflowplayer・JW Player等がある。
http://flowplayer.org/
https://www.jwplayer.com/


Red5+Flowplayerでストリーミングサイトは構築できそう。


RTMPp2p版?のRTMFP(Real Time Media Flow Protocol)ではサーバーの負担を減らすことができるらしい。
AMSが対応している。Red5は対応していないが、改造すればイケる?
CaveTubeはRed5Adobe Cirrusを組み合わせているのか?
http://hakusailove.hatenablog.com/entry/2013/07/07/142716

Adobe Cirrus(旧Stratus)
http://labs.adobe.com/technologies/cirrus/
P2P接続確立を補助してくれる無料のサービス。実験的な位置づけなのか無料で使用できる。
RTMFPサーバーを使用させてサービスといったところか?中身はAMS?


もしCirrusが利用できなくなったら自前でRTMFPサーバーをたてる必要がある?
RTMFPサーバーソフトとしては無料の
・Cumulus
https://github.com/OpenRTMFP/Cumulus

・ArcusNode
https://github.com/OpenRTMFP/ArcusNode

等がある。

気になるのはSkyWay等のWebRTC。
ブラウザとp2pの組み合わせで最小限のサーバー構成で生配信サイトは作れないのか。

参考
https://wpaso.wordpress.com/category/flash/
http://www29.atwiki.jp/red5server/pages/43.html
http://makisuke.seesaa.net/article/214998778.html
http://poepoemix.blogspot.jp/2011/10/rtmfp_29.html
http://foonyan.sakura.ne.jp/wisteriahill/website/p2p/cirrus/index.html
http://flashdevelop.jp/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8

youtubeliveから情報を取得する

生配信中かどうかを判定するには、StackOverFlowのこの記事の内容を参考にする。
http://stackoverflow.com/questions/32454238/how-to-check-if-youtube-channel-is-streaming-live

GETで
https://www.googleapis.com/youtube/v3/search?part=snippet&channelId={channelId}&type=video&eventType=live&key={APPKEY}
からjsonをダウンロードできる。liveBroadcastContentがliveであれば生配信中。videoIDを読み取り、配信URL等を取得できる。

<?php

$search_api_base = "https://www.googleapis.com/youtube/v3/search?part=snippet";
$channelId = "channelId";
$key = "key";
$search_api = $search_api_base . "&channelId=" . $channelId . "&type=video&eventType=live" . "&key=" . $key ;
$search_contents = file_get_contents($search_api);
$search_json = json_decode($search_contents,true);
$isLive = (string) $search_json["items"][0]["snippet"]["liveBroadcastContent"];
$videoId = (string) $search_json["items"][0]["id"]["videoId"];
if($isLive == "live"){
	//視聴者数取得
	$viewers_url = "https://www.youtube.com/live_stats?v=" . $videoId;
	$viewers = file_get_contents($viewers_url);

	$video_url_base = "https://www.googleapis.com/youtube/v3/videos?part=snippet";
	$video_url = $video_url_base . "&id=" . $videoId . "&key=" . $key;
	$video_contents = file_get_contents($video_url);
	$video_json = json_decode($video_contents,true);

	//配信タイトル(トピック)取得
	$title = $video_json["items"][0]["snippet"]["title"];
	//配信開始時刻取得
	$created_at = $video_json["items"][0]["snippet"]["publishedAt"];
	//チャンネル名取得
	$channel_name = $video_json["items"][0]["snippet"]["channelTitle"];
	//スナップショット取得, サイズは default,medium,high,standard,maxresの5種類
	//それぞれ 120x90, 320x180, 480x360, 640x480, 1280x720
	$snapshot = $video_json["items"][0]["snippet"]["thumbnails"]["standard"]["url"];	
	$snapshot_width = $video_json["items"][0]["snippet"]["thumbnails"]["standard"]["width"];	
	$snapshot_height = $video_json["items"][0]["snippet"]["thumbnails"]["standard"]["height"];	
	
	//説明文取得
	$description = $video_json["items"][0]["snippet"]["description"];	
//	var_dump($video_json);

	echo "isLive:" . $isLive . PHP_EOL ;
	echo "viewers:" . $viewers . PHP_EOL ;
	echo "title:" . $title . PHP_EOL ;
	echo "created_at:" . $created_at . PHP_EOL ;
	echo "id:" . $videoId . PHP_EOL ;
	echo "snapshot:" . $snapshot . PHP_EOL ;
	echo "channel_name:" . $channel_name . PHP_EOL ;
	echo "description:" . $description . PHP_EOL ;
	
}


channelIdはUC~で始まる個々のチャンネルに与えられるIDで「UC**」。チャンネルのURLの後半部分。
https://www.youtube.com/channel/UC** ←ここ

マニュアルはここ↓
https://developers.google.com/youtube/v3/docs/search?hl=ja

本当はsearchではなく、channelでこんな感じにやってChannel IDを使って確認したいところだけど、どうもできないようだ。
https://www.googleapis.com/youtube/v3/channels?id={channelId}&key={APPKEY}&part=id,snippet,brandingSettings,contentDetails,invideoPromotion,statistics,topicDetails

追記

searchでは以下の問題あり
・maxResultsが最大50。少なすぎる。
・channelIDを複数指定することが不可。
 http://stackoverflow.com/questions/21667899/how-to-search-content-across-multiple-channel-in-youtube-api
 つまりchannelIDを一つずつ指定して、一回一回APIを叩くか、channelIDを指定せずにeventType=liveのものを50ずつ取得するか(次のページに進むにはnextPageTokenを使う必要がある)。
 http://qiita.com/yuji_saito/items/8f472dcd785c1fadf666
 いずれにしてもAPIをたくさん叩くということ。仮にregionCode=JPなんてしても、かなり多くのライブが引っかかってしまう、さすがyoutube

channelsはそもそもlive情報を取得することができない。

なのでいまのところsearchで複数回リクエストするしかなさそうだ。

テーブルを作成(mysql)

忘れてしまうので記録。
ここのjsonデータを出力できるようなテーブルを作成する構文をphpで出力する。
http://checker-site.hatenablog.com/entry/2015/12/14/235144

<?php
$json_data = array();
//idにauto_incrementを設定
echo "create table test(id int(11) auto_increment,name varchar(20),gender varchar(2),twitter_name varchar(20),twitter_id varchar(20),status  tinyint(1), checker_id int(11),";

$all = array(
		"total",
		"broadcast",
		"chat");

$total = array(
		"viewers",
		"isLive",
		"lastDate");

$service = array("ustream", 
				"twitch",
				"niconico",
				"livetube",
				"youtubelive",
				"twicas",
				"fc2",
				"afreecacom",
				"cavetube",
				"hitbox");

$chat_service = array("ustirc", 
				"juschat");				

$chat = array("id", 
			"channel_name");				

				
$item = array("isLive",
			"viewers",
			"title", 
			"description", 
			"created_at",
			"id",
			"channel_name",
			"user_name",
			"url",
			"archive_url",
			"snapshot",
			"lastDate");

foreach($total as $i){
	$json_data["total"][$i] = true;
		if($i == "viewers"){
			echo "total_" . $i . " int(11),";
		}elseif($i == "isLive"){
			echo "total_" . $i . " tinyint(1),";			
		}elseif($i=="lastDate"){
			echo "total_" . $i . " TIMESTAMP,";			
		}elseif($i=="twitter_name"){
			echo "total_" . $i . " varchar(20),";			
		}elseif($i=="twitter_id"){
			echo "total_" . $i . " int(11),";			
		}
}

foreach($service as $i){
	foreach($item as $j){
		$json_data["broadcast"][$i][$j] = true;
		if($j == "isLive"){
			echo $i . "_" . $j . " tinyint(1),";	
		}elseif($j == "viewers"){
			echo $i . "_" . $j . " int(11),";
		}elseif($j == "title"){
			echo $i . "_" . $j . " varchar(40),";
		}elseif($j == "description"){
			echo $i . "_" . $j . " varchar(100),";
		}elseif($j=="created_at"){
			echo $i . "_" . $j . " TIMESTAMP,";			
		}elseif($j=="id"){
			echo $i . "_" . $j . " int(11),";			
		}elseif($j=="channel_name"){
			echo $i . "_" . $j . " varchar(20),";	
		}elseif($j=="user_name"){
			echo $i . "_" . $j . " varchar(20),";	
		}elseif($j=="url"){
			echo $i . "_" . $j . " varchar(100),";		
		}elseif($j=="archive_url"){
			echo $i . "_" . $j . " varchar(100),";		
		}elseif($j=="snapshot"){
			echo $i . "_" . $j . " varchar(100),";	
		}elseif($j=="lastDate"){
			echo $i . "_" . $j . " TIMESTAMP,";				
		}
	}
}

foreach($chat_service as $i){
	foreach($chat as $j){
		$json_data["chat"][$i][$j] = true;
		if($j == "isLive"){
			echo $i . "_" . $j . " tinyint(1),";	
		}elseif($j == "viewers"){
			echo $i . "_" . $j . " int(11),";
		}elseif($j == "title"){
			echo $i . "_" . $j . " varchar(40),";
		}elseif($j=="created_at"){
			echo $i . "_" . $j . " TIMESTAMP,";			
		}elseif($j=="id"){
			echo $i . "_" . $j . " int(11),";			
		}elseif($j=="channel_name"){
			echo $i . "_" . $j . " varchar(20),";	
		}elseif($j=="user_name"){
			echo $i . "_" . $j . " varchar(20),";	
		}elseif($j=="url"){
			echo $i . "_" . $j . " varchar(100),";		
		}elseif($j=="archive_url"){
			echo $i . "_" . $j . " varchar(100),";		
		}elseif($j=="snapshot"){
			echo $i . "_" . $j . " varchar(100),";	
		}elseif($j=="lastDate"){
			echo $i . "_" . $j . " TIMESTAMP,";				
		}
	}
}

$json = json_encode($json_data);
echo "PRIMARY KEY (id));";//idにauto_incrementを設定
echo PHP_EOL . PHP_EOL;

出力結果↓
create table test(id int(11) auto_increment,name varchar(20),gender varchar(2),twitter_name varchar(20),twitter_id varchar(20),status tinyint(1), checker_id int(11),total_viewers int(11),total_isLive tinyint(1),total_lastDate date,ustream_isLive tinyint(1),ustream_viewers int(11),ustream_title varchar(40),ustream_description varchar(100),ustream_created_at date,ustream_id int(11),ustream_channel_name varchar(20),ustream_user_name varchar(20),ustream_url varchar(100),ustream_archive_url varchar(100),ustream_snapshot varchar(100),ustream_lastDate date,twitch_isLive tinyint(1),twitch_viewers int(11),twitch_title varchar(40),twitch_description varchar(100),twitch_created_at date,twitch_id int(11),twitch_channel_name varchar(20),twitch_user_name varchar(20),twitch_url varchar(100),twitch_archive_url varchar(100),twitch_snapshot varchar(100),twitch_lastDate date,niconico_isLive tinyint(1),niconico_viewers int(11),niconico_title varchar(40),niconico_description varchar(100),niconico_created_at date,niconico_id int(11),niconico_channel_name varchar(20),niconico_user_name varchar(20),niconico_url varchar(100),niconico_archive_url varchar(100),niconico_snapshot varchar(100),niconico_lastDate date,livetube_isLive tinyint(1),livetube_viewers int(11),livetube_title varchar(40),livetube_description varchar(100),livetube_created_at date,livetube_id int(11),livetube_channel_name varchar(20),livetube_user_name varchar(20),livetube_url varchar(100),livetube_archive_url varchar(100),livetube_snapshot varchar(100),livetube_lastDate date,youtubelive_isLive tinyint(1),youtubelive_viewers int(11),youtubelive_title varchar(40),youtubelive_description varchar(100),youtubelive_created_at date,youtubelive_id int(11),youtubelive_channel_name varchar(20),youtubelive_user_name varchar(20),youtubelive_url varchar(100),youtubelive_archive_url varchar(100),youtubelive_snapshot varchar(100),youtubelive_lastDate date,twicas_isLive tinyint(1),twicas_viewers int(11),twicas_title varchar(40),twicas_description varchar(100),twicas_created_at date,twicas_id int(11),twicas_channel_name varchar(20),twicas_user_name varchar(20),twicas_url varchar(100),twicas_archive_url varchar(100),twicas_snapshot varchar(100),twicas_lastDate date,fc2_isLive tinyint(1),fc2_viewers int(11),fc2_title varchar(40),fc2_description varchar(100),fc2_created_at date,fc2_id int(11),fc2_channel_name varchar(20),fc2_user_name varchar(20),fc2_url varchar(100),fc2_archive_url varchar(100),fc2_snapshot varchar(100),fc2_lastDate date,afreecacom_isLive tinyint(1),afreecacom_viewers int(11),afreecacom_title varchar(40),afreecacom_description varchar(100),afreecacom_created_at date,afreecacom_id int(11),afreecacom_channel_name varchar(20),afreecacom_user_name varchar(20),afreecacom_url varchar(100),afreecacom_archive_url varchar(100),afreecacom_snapshot varchar(100),afreecacom_lastDate date,cavetube_isLive tinyint(1),cavetube_viewers int(11),cavetube_title varchar(40),cavetube_description varchar(100),cavetube_created_at date,cavetube_id int(11),cavetube_channel_name varchar(20),cavetube_user_name varchar(20),cavetube_url varchar(100),cavetube_archive_url varchar(100),cavetube_snapshot varchar(100),cavetube_lastDate date,hitbox_isLive tinyint(1),hitbox_viewers int(11),hitbox_title varchar(40),hitbox_description varchar(100),hitbox_created_at date,hitbox_id int(11),hitbox_channel_name varchar(20),hitbox_user_name varchar(20),hitbox_url varchar(100),hitbox_archive_url varchar(100),hitbox_snapshot varchar(100),hitbox_lastDate date,ustirc_id int(11),ustirc_channel_name varchar(20),juschat_id int(11),juschat_channel_name varchar(20),PRIMARY KEY (id));