ウェブ上の動画は飛躍的に成長し続けています。WebRTCとAPIを個別に使用することで、その成長の一端を示すことができます。getUserMedia を使用してユーザーのウェブカメラを要求し、video要素に表示する簡単な例を作成しました。これをさらに一歩進めるために、その例を使ってコンテンツを保存し、ブラウザから直接トランスコードしてみましょう。
getUserMediaの作成例:
さらに先に進む前に、最初の簡単な例を見てみましょう。ここで行うのは、ユーザーの動画ストリームをリクエストし、それをページ上のvideo要素に表示することだけです。より高度な例ではjQuery を使用します。
// Do the vendor prefix dance
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
// Set up an error handler on the callback
var errCallback = function(e) {
console.log(‘Did you just reject me?!’, e);
};
// Request the user’s media
function requestMedia(e) {
e.preventDefault();
// Use the vendor prefixed getUserMedia we set up above and request just video
navigator.getUserMedia({video: true, audio: false}, showMedia, errCallback);
}
// Actually show the media
function showMedia(stream) {
var video = document.getElementById(‘user-media’);
video.src = window.URL.createObjectURL(stream);
video.onloadedmetadata = function(e) {
console.log(‘Locked and loaded.’);
};
}
// Set up a click handler to kick off the process
$(function() {
$(‘#get-user-media’).click(requestMedia);
});
あとは「Get Media」ボタンとvideo要素があれば準備完了です。ボタンをクリックし、ブラウザがカメラにアクセスできるようにすると、最終的にはこのようになります。
簡単なスクリーンショット
このデモはFirefox、Chrome、Operaで動作するはずです。
これでブラウザからウェブカメラにアクセスできるようになりました。これは楽しい例だと思いますが、誰かに自分自身を見せることしかできないので、かなり役に立ちません。
Media Recorderのセットアップ:
注:2014年現在、MediaRecorder APIを実装しているブラウザはFirefoxだけです。Chromeでも動作させたい場合は、RecordRTCやMediaStreamRecorderなどのプロジェクトがあります。
この例ではシンプルなサーバーサイド・コンポーネントが必要になりますが、必要なことは2つだけです:
- 有効なAWSポリシーを返し、ブラウザから直接アップロードできるようにする
- Zencoderにエンコーディングジョブを送信する
このような例ではNode用のExpressフレームワークを使いたいところですが、Sinatraのような他のものを使う方が快適な場合は、この例を無視して好きなものを使ってて構いません。クライアントサイドのコードの方が重要なので、サーバーサイドの実装は掘り下げません。
var S3_BUCKET = 'YOUR-S3-BUCKET-NAME';
<p>var express = require(‘express’);
var path = require(‘path’);
var logger = require(‘morgan’);
var bodyParser = require(‘body-parser’);
var crypto = require(‘crypto’);
var moment = require(‘moment’);
var AWS = require(‘aws-sdk’);
var s3 = new AWS.S3({ params: { Bucket: S3_BUCKET }});
var zencoder = require(‘zencoder’)();
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(express.static(path.join(__dirname, 'public')));
app.post(‘/process’, function(req, res) {
// Build up the S3 URL based on the specified S3 Bucket and filename included
// in the POST request body.
var input = ‘https://’+S3_BUCKET+’.s3.amazonaws.com/’+req.body.filename;
createJob(input, req.body.email, function(err, data) {
if (err) { return res.send(500, err); }
res.send(200, data);
});
});
app.post(‘/upload’, function(req, res) {
var cors = createS3Policy();
res.send(201, { url: ‘https://’+S3_BUCKET+’.s3.amazonaws.com/’, cors: cors });
});
function createS3Policy() {
var policy = {
“expiration”: moment().utc().add(‘days’, 1).toISOString(),
“conditions”: [
{ “bucket”: S3_BUCKET },
{ “acl”:”private” },
[ “starts-with”, “$key”, “” ],
[ “starts-with”, “$Content-Type”, “” ],
[ “content-length-range”, 0, 5368709120 ]
]
};
var base64Policy = new Buffer(JSON.stringify(policy)).toString('base64');
var signature = crypto.createHmac('sha1', AWS.config.credentials.secretAccessKey).update(base64Policy).digest('base64');
return {
key: AWS.config.credentials.accessKeyId,
policy: base64Policy,
signature: signature
};
}
function createJob(input, email, cb) {
var watermark = {
url: ‘https://s3.amazonaws.com/zencoder-demo/blog-posts/videobooth.png’,
x: ‘-0’,
y: ‘-0’,
width: ‘30%’
};
zencoder.Job.create({
input: input,
notifications: [ email ],
outputs: [
{ format: ‘mp4’, watermarks: [watermark] },
{ format: ‘webm’, watermarks: [watermark] }
]
}, cb);
}
var server = app.listen(app.get(‘port’), function() {
console.log(‘Express server listening on port ‘ + server.address().port);
});
このサンプルはほとんどそのまま動作するはずですが、AWSの設定とZENCODER_API_KEY環境変数が必要です。また、使用するバケットでCORS 設定する必要があります。以下は動作が確認されているCORS設定の例です:
<?xml version=”1.0″ encoding=”UTF-8″?> <CORSConfiguration xmlns=”http://s3.amazonaws.com/doc/2006-03-01/”> <CORSRule> <AllowedOrigin>\*</AllowedOrigin> <AllowedMethod>POST</AllowedMethod> <AllowedHeader>\*</AllowedHeader> </CORSRule> </CORSConfiguration>
ユーザーメディアの記録:
上記の単純な例では、getUserMedia APIを使ってユーザーのメディアをリクエストしています。
幸運なことに、MediaRecorderというAPIがあります。現在これをサポートしているブラウザはFirefoxだけですが(バージョン25現在)、Whammyのように他のブラウザの擬似シムとして動作するプロジェクトもあります。
APIはシンプルです。前の例で再生に使ったのと同じストリームを取り出し、それを使ってMediaRecorderの新しいインスタンスを作成するだけです。新しいレコーダーができたら、あとはstart()を呼んで録画を開始し、stop()を呼んで停止するだけです。
var recorder = new MediaRecorder(this.stream);
recorder.start(); // 録画中!
// ...数秒後...
recorder.stop();
録画メディアの取得:
ウェブカメラの録画を開始・停止しました。さて、どうやってこの録画を見るのでしょうか?
録画するために作成したMediaRecorderのインスタンスのondataavailableイベントをリッスンできます。それが完了すると、新しいBlobが含まれ、元のユーザー・メディアと同じように再生できます。
// We’ll keep using the same recorder
recorder.ondataavailable = function(e) {
var videoBlob = new Blob([e.data], { type: e.data.type });
var player = document.getElementById(‘playback-video-el’);
var blobUrl = URL.createObjectURL(videoBlob);
player.src = blobUrl;
player.play();
}
もし、あなたがこれらの例に従って練習を積み重ねてきたのなら、今頃はビデオを再生しようとしてイライラしている頃でしょう。悲しいことに、ここで「正しい」ことをしてもうまくいきません。video要素で自動再生を使用することも、play() を呼び出すことも、ended イベントで currentTime を設定することも、あなたが望むことはできません。
これは単に、これらのblobを再生する際のFirefoxの問題のようだ。機能的な回避策は、動画をループさせたい場合は、終了したイベントのソースを単純に置き換えることです。
player.onended = function() {
video.pause();
video.src = blobUrl;
video.play();
}
あなたが持っているこのブロブは、(ほとんど)機能的なWebM動画です。このブロブのURLをソースとしてアンカータグを作成すれば、右クリックしてファイルをローカルに保存できます。しかし、ローカルに保存しても、このファイルは正しく動作しません(OS XはこれをHTMLファイルだと思っているようです)。
そこでZencoderが活躍します。処理する前に、Zencoderがアクセスできるようにファイルをオンラインにする必要があります。先ほど作成したAPIエンドポイントの1つ、/uploadを使って署名されたポリシーを取得し、それを使ってファイルを直接S3にPOSTします(この例ではjQueryを使用)。
function uploadVideo(video) {
$.post(‘/upload’, { key: “myawesomerecording.webm” }).done(function(data) {
// The API endpoint we created returns a URL, plus a cors object with a key, policy, and signature.
formUpload(data.url, data.cors.key, data.cors.policy, data.cors.signature, filename, recording);
});
function formUpload(url, accessKey, policy, signature, filename, video) {
var fd = new FormData();</p>
fd.append('key', filename);
fd.append('AWSAccessKeyId', accessKey);
fd.append('acl', 'private');
fd.append('policy', policy);
fd.append('signature', signature);
fd.append('Content-Type', "video/webm");
fd.append("file", video);
$.ajax({
type: ‘POST’,
url: url,
data: fd,
processData: false,
contentType: false
}).done(function(data) {
cb(null);
}).fail(function(jqxhr, status, err) {
cb(err);
});
}
}
uploadVideo(videoBlob);
これでS3バケットに動画が保存されたので、あとは実際に処理するだけです。先ほど/processエンドポイントにemailを追加したので、処理が完了したらジョブの通知(動画のダウンロードリンクを含む)を直接受け取ることができます。
function process(email, filename) {
$.post(‘/process’, {
filename: filename,
email: email
}).done(function(data) {
console.log(‘All done! you should get an email soon.’);
}).fail(function(jqXHR, error, data) {
console.log(‘Awww…sad…something went wrong’);
});
};
process('[email protected]', "myawesomerecording.webm");
数秒後、ブラウザで録画した真新しい動画を祝福するメールが届くはずです。含まれるリンクは一時的なものなので、24時間以内にダウンロードするか、私たちが作成したAPIエンドポイントを変更して、あなたが所有するバケットに出力をアップロードしてください。
当社はこの機能を紹介するために、ちょっとしたスタイリングと派手ではないインターフェイスを含むデモを作成しました。VideoBoothという名前ですが、プロジェクトをクローンして自由に動かしてみてください。また、Heroku上で動作するデモで遊ぶこともできます。