前回UV4Lを使った自前のHPを作りました。今回はRaspberry PIにPythonのWebserverを立ち上げてUV4Lと互いに通信出来るようにしたいと思います。
先ずは、現状の再確認
現在の状況をまとめると以下の様になっています。

- NoteBook側
- ホームディレクトリに、”intercom.html”(実際にはその他のファイルもあるが)を保存
- そのファイルを、ブラウザ(FireFox)で実行。
- Raspberry PI側
- 起動時と共に、”UV4L”が起動。UV4L内には
- Web_Server(http://raspberrypi.local:8090でアクセス出来るサーバー)
- Socket Server(P2P通信を行う前に互いの情報を交換するサーバー)
- その他、Stream Server, WebRTC Server等が有る
- 起動時と共に、”UV4L”が起動。UV4L内には
- 互いの通信
- 互いの通信は、P2Pなので間にサーバーを通していない。
- HPを通して、Raspberry からの動画と音声、NoteBookからの音声をStreaming出来る。
- NoteBook上でRaspberryからの動画と音声の録音が可能。
操作はWebブラウザで行い録画等も可能ですが、例えば
- 暗くなったのでライトを付けたい
- 留守中の訪問者の録画を行いたい
と思ってもこの状態では出来ません。そこで、”Mjpg-Stremer & FFmpegを使う”の時と同じ様に、UV4LをMjpg-Streamerに見立てて、構成を以下の様に変更してみました。

- Raspberry PI側
- PythonのWebserverを立ち上げる:ポートを8010とする。
- WebServerのHPとして、”intercom.html”を使用する。
- PythonのWebserverとUV4L間で通信する。
- ライト点灯や留守録は、PythonのWebserverにコードを追加して行う。
- NoteBook側
- HPをRaspberry側に移動したので、PythonのWebserver(ポート8010)にアクセスしHPを表示させる。
この構成、実際にコーディングしてみたら思わぬ自体になりました。
navigator.mediaDevices.getUserMedia()には使用制限が有った
コード自体は簡単で作成出来、Raspberry PIからの映像と音声のStramingは出来たのですが、NoteBook側からRaspberry PIに音声を送ろうとした時にエラーが出ました。エラーの内容は、このブラウザでは、navigator.mediaDevices.getUserMedia()が使えないというものでした。navigator.mediaDevices.getUserMedia()には使用制限が有り、プロトコルが以下の時に使用できる様です
- HPPTS:/
- file:///
- または、
localhost
からLoadされたもの
今回はPython WebServerのプロトコルがHTTPでHPPTSでは無いのが原因の様です。これでは、”Mjpg-Stremer & FFmpegを使う”と同様NoteBook側から音声が送れないのでHPのコード ”intercom.html” をNoteBook側に戻す事にしました。
今度はPython WebServerからのデータが受けれない
今回の構成は、”intercom.html”をNoteBook側に戻し、Python WebServerは残しています。

この状態下FirFoxで、”intercom.html”を実行。Raspberry PIからの音声と動画、NoteBookからの音声のStreamingは問題なく出来るようになりました。またHPからPython Web Serverに命令を送ってLED等を点灯が出来る様になったのでが、Python Web Serverからのデータを受ける事が出来ません。表示されるエラーメッセージは、’Access-Control-Allow-Origin’ missingです。オリジンが違うと言う事みたいですが、オリジンって何?
オリジンとは
オリジンとは、プロトコル、ドメイン、ポートをまとめて呼ぶようです。今回のケースですが、NoteBookで”intercom.html”を実行していますのでオリジンは、file:///home//intercom/intercom.html。このコード以下を実行しています。この場合のオリジンは、”http://raspberrypi.local:8010″。確かに互いのオリジンは全く違います。
var xhr = new XMLHttpRequest();
xhr.open('GET', "http://raspberrypi.local:8010/?1=0");
xhr.send();
そもそもオリジンが違うと何でデータをダウンロード出来ないのか。それは、Web セキュリティ上の問題の様です。クライアントにデータを提供しているサーバー側からすれば、自分(オリジン)とは違うサーバーからデータをダウンロードされそれは結果自分の管理外の現象が起こる可能性が有り、それは避ける必要が有るからです。とはいえ今回の様に違うオリジンからデータをダウンロードしたい時はあるはずで、そうゆう時はどうすれば良いのか。
オリジン違いで値を返したい時
結論から言うとサーバーが Access-Control-Allow-Origin
ヘッダーを返信すれば値を返せる様です。
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: <origin>
オリジンとして”*”を指定すれば全てのクライアントに、特定のオリジンのみ許可する時は<origin>にクライアントを指定します。
ここで疑問なんですが、例えばサーバが全てのリクエストに対してヘッダーとして、Access-Control-Allow-Origin: *を返せばクライアントはオリジンが違ってもデータをダウンロード出来ることになります。これでWeb セキュリティ上の問題が解決出来るんでしょうか。
ともあれ上記の様にPython WebServerにヘッダーを追加すればデータをダウンロード出来るようになりました。
確認アプリ
ここまでの動作を確認する為に簡単なアプリを書いてみました。アプリは、
- Raspberry PI側
- 電源を入れる。レディーになった時点でUV4Lは起動している。
- その後 Python WebServerを起動する。このサーバーのポートは8010。
- Notebook側
- ”intercom.html”をFireFoxで上げる
- このHPは 前回作成したHP画面に、”test”ボタンを追加したもの
- この”test”ボタンをクリックすると、xhr.open(‘GET’, “http://raspberrypi.local:8010/?1=0”)が実行されRaspberry PIにGetリクエストが送られる。
- リクエストを受けたRaspberry PI
- リクエストを受けてモニターに、”Get Request”と表示。
- クライアントに、”Data Back!”とデータを返す。
- データを受けるNoteBook
- データを受けて、コンソールに、”Data Back!”と表示
Raspberry PI 側にPython WebServerを追加しています。ファイル名は intercom.py です。
intercom.py
from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse,parse_qs
class MyHandler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
a = next(iter(params))
if a == "1":
print('Get Request')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Content-type', "text/plain")
self.end_headers()
self.wfile.write("Data Back!".encode())
host = ''
port = 8010
httpd = HTTPServer((host, port), MyHandler)
print('serving at port', port)
httpd.serve_forever()
- 6行:Python WebServerでは、Getリクエストは全てここで処理されます。
- 8から10行: リクエストからクエリの取り出し
- 12行: クエリのパラメータが、”1”で有ることを判断
- 13行: モニターに”Get Request”と表示
- 15行: ヘッダーに、Access-Control-Allow-Origin: * を指定
- 18行: ”Data Back!”をクライアントに送信
- 21行: 使用ポートを8010に指定。
NoteBook側は、前回作成したソフトを変更しています。変更したソフトは、”intercom.html”と”interfom.js”です。
“intercom.html”でHPに”Test”ボタンを追加。33行でHPにボタンを追加しています。ボタンをクリックすると、”test()”関数が実行されます。
intercom.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel='stylesheet' type='text/css' href='./intercom.css' >
<title>UV4L WebRTC</title>
</head>
<body>
<div class='b_frame'>
<div class='t_font'><u>UV4L WebRTC 1.0</u></div>
<div class='p_frame'>
<video id="remote-video" autoplay="" width="640" height="480"> </video>
<video id="play-video" controls style='display:none'>Your browser does not support the video tag.</video>
</div>
<div class="_menu">
<div class="input-group">
<label>Stream</label>
<button type='button' id='start' onclick='start();'>Start</button>
<button type='button' id="pause" onclick="pause();" title="pause or resume">Pause</button>
<button type='button' id="mute" onclick="mute();" title="mute or unmute ">Mute</button>
<button type='button' id='stop' onclick="stop();">Stop</button><br>
<label>Take & Play</label>
<button type='button' id='photo' onclick="take_photo();" title="take phato">Photo</button>
<button type='button' id="record" onclick="start_stop_record();" title="start or stop recording">Video</button>
<button type='button' id="play" onclick="play_video();" title="play video">Play</button><br>
<label>Download</label>
<button type='button' id='dn_photo' onclick="dn_photo();" title="save photo">Photo</button>
<button type='button' id='dn_video' onclick="download();" title="save video">Video</button><br>
<label>Test</label>
<button type='button' id='test' onclick="test();" title="save video">test</button>
<canvas style='display:none'></canvas>
</div>
</div>
</div>
<script src='intercom.js'></script>
</body>
</html>
次は、”interfom.js”。”test()”関数を実行してGetリクエストを行います。下記は追加された関数、”test()”部分をのみを示しています。
intercom.js
function test() {
var xhr = new XMLHttpRequest();
xhr.open('GET', "http://raspberrypi.local:8010/?1=0");
xhr.send();
xhr.onreadystatechange = function()
{
if(xhr.readyState === 4 && xhr.status === 200)
console.log( xhr.responseText );
}
}
//====================================================================================================================
var ws = null;
var pc;
var audio_video_stream;
var recorder = null;
var aa_streams = [];
var mediaConstraints = {
optional: [],
- 3行: ここでGetリクエストを行っています。
- 5行: ここで、Python Serverからの返答を待っています。
- 8行: コンソールに受け取ってデータを表示。
各ファイルの保存場所ですが、以下の様にしています。
- Raspberry PI側の”intercom.py”
- Raspberry PIのホームディレクトリの下に”intercom”というフォルダーを作りそこに保存。
- NoteBook側の、”intercom/html”と”intercom.js”
- NoteBookのホームディレクトリの下に”intercom”というフォルダーを作りそこに保存。
- NoteBook側のファイルをここに保存します。ー>intercomダウンロード
実行
- Raspberry PI側
- 電源を入れてレディーになるのを待つ
- intercom フォルダーに移動。
- このフォルダーには、intercom.pyのみが保存されています。
- ここで、python3 intercom.py とサーバーを立ち上げる。
- NoteBook側
- FireFoxを使って”intercom.html”を立ち上げる。
- FireFoxの開発ツールを使ってHPの表示を変更。コンソールタブを選択。
- HPの画面に有る”test”ボタンを押すと、”Data Back!”とサーバーからの値が表示されます。
- この時Raspberry PI側のモニターには、クライアントからのGetリクエストを受けて”Get Request”と表示されます。
まとめ
ややこしい事になって来ましたが、NoteBookで挙げたHPからオリジンが違うRaspberry PIのPythonサーバーとデータのやり取りが出来る様になりました。次は、Pythonサーバーで写真、動画の留守録が出来る様にしたいと思います。