웹에서의 동영상은 계속해서 비약적으로 성장하고 있습니다. 성장하는 방법 중 하나는 WebRTC를 통해 API를 개별적으로 사용하는 것입니다. getUserMedia를 사용하여 사용자의 웹캠을 요청하고 이를 비디오 요소에 표시하는 간단한 예제를 만들었습니다. 한 단계 더 나아가 이 예제를 사용하여 브라우저에서 직접 콘텐츠를 저장한 다음 트랜스코딩해 보겠습니다.
getUserMedia 예제 만들기
더 자세히 알아보기 전에 간단한 초기 예제를 살펴봅시다. 여기서는 사용자의 비디오 스트림을 요청하고 페이지의 비디오 요소에 표시하기만 하면 됩니다. 좀 더 고급 예제에서는 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);
});
이제 '미디어 가져오기' 버튼과 동영상 요소만 있으면 모든 준비가 완료됩니다. 버튼을 클릭하고 브라우저에서 카메라에 대한 액세스를 허용하면 최종 결과는 다음과 같이 표시됩니다.
간단한 스크린샷
이 데모는 파이어폭스, 크롬 또는 오페라에서 작동합니다.
이제 브라우저를 통해 웹캠에 액세스할 수 있습니다. 이 예제는 재미있지만 다른 사람에게 자신을 보여주는 것밖에 할 수 없기 때문에 꽤 쓸모가 없습니다.
미디어 레코더 설정하기
참고: 2014년 현재 MediaRecorder API를 구현한 브라우저는 Firefox가 유일합니다. Chrome에서도 작동하도록 하려면 RecordRTC 및 MediaStreamRecorder와 같은 프로젝트가 있습니다.
이 예제에는 간단한 서버 측 컴포넌트가 필요하지만, 이 컴포넌트는 두 가지 작업만 수행하면 됩니다:
브라우저에서 직접 업로드할 수 있도록 유효한 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 base64Picy = new Buffer(JSON.stringify(policy)).toString('base64');
var signature = crypto.createHmac('sha1', AWS.config.credentials.secretAccessKey).update(base64Picy).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 인스턴스에서 데이터 사용 가능 이벤트를 기다릴 수 있습니다. 녹화가 완료되면 원래 사용자 미디어와 마찬가지로 재생할 수 있는 새 블롭이 포함됩니다.
// 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();
}
지금까지 이 예제를 따라 만들었으면 지금쯤 동영상을 다시 재생하려고 하다가 좌절하고 있을 것입니다. 안타깝게도 여기서 '제대로' 하는 것은 아무것도 작동하지 않습니다. 동영상 요소에 자동 재생을 사용하거나 play()를 호출하거나 종료된 이벤트에 currentTime을 설정해도 원하는 대로 작동하지 않습니다.
이는 단순히 이러한 블롭 재생과 관련된 Firefox 문제인 것 같습니다. 기능적인 해결 방법은 동영상이 반복되도록 하려면 종료된 이벤트의 소스를 간단히 교체하는 것입니다.
player.onended = function() {
video.pause();
video.src = blobUrl;
video.play();
}
이 블롭은 (대부분) 작동하는 WebM 동영상입니다. 이 블롭 URL을 소스로 앵커 태그를 생성하면 파일을 마우스 오른쪽 버튼으로 클릭하고 로컬에 저장할 수 있습니다. 하지만 로컬에서도 이 파일은 제대로 작동하지 않습니다(OS X에서는 이 파일을 HTML 파일로 인식하는 것 같습니다).
바로 이 부분에서 Zencoder가 잘 들어맞습니다. 처리하기 전에 파일을 온라인으로 가져와서 Zencoder가 액세스할 수 있도록 해야 합니다. 앞서 만든 API 엔드포인트 중 하나인 /upload를 사용하여 서명된 정책을 가져온 다음, 이를 사용하여 파일을 S3에 직접 게시합니다(이 예에서는 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('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);
});
}
}
업로드 비디오(비디오 블롭);
이제 S3 버킷에 동영상이 들어왔으니 실제로 처리하기만 하면 됩니다. 눈치채셨겠지만, 앞서 /process 엔드포인트에 이메일을 추가하여 작업이 완료되면 작업 알림(동영상 다운로드 링크 포함)을 직접 받을 수 있도록 했습니다.
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’);
});
};
프로세스('[email protected]', "myawesomerecording.webm");
몇 초 후, 브라우저에서 녹화된 새로운 동영상을 축하하는 이메일을 받게 될 것입니다. 포함된 링크는 임시 링크이므로 24시간 이내에 다운로드하거나 생성한 API 엔드포인트를 변경하여 결과물을 소유한 버킷에 업로드하세요.
이 기능을 보여드리기 위해 약간의 스타일링과 그리 화려하지는 않은 인터페이스를 포함한 데모를 만들었습니다. 비디오부스라는 이름의 이 데모는 프로젝트를 복제하여 자유롭게 실행해 보세요. Heroku에서 작동하는 데모로 플레이할 수도 있습니다.