HTML5视频捕获和保存

25

我正在建立一个可以进行卡拉OK的网站,用户可以录制自己跟唱mp3的视频。我已经实现了访问相机和显示实时流的功能,但是如何保存视频以便用户可以下载并保留它呢?

我的代码:

<!DOCTYPE html>
<head>
<link href="css/bootstrap.css" rel="stylesheet"">
<style>
#container {
margin: 0px auto;
width: 500px;
height: 375px;
border: 10px #333 solid;
}
#videoElement {
width: 500px;
height: 375px;
background-color: #666;
}
</style>
</head>
<body>

<button class="btn" onclick="show();">Record!</button>


<div id="record" style="display:none; text-align:center;">
<div id="container">
<video autoplay="false" id="videoElement">
</video>
</div>
<button id="play" class="btn" onclick="play()">Start Recording!</button>
<audio id="song" style="hidden">
<source src="love.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</div>



<script src="http://code.jquery.com/jquery.js"></script>
<script src="js/bootstrap.js"></script>
<script>

var video = document.querySelector("#videoElement");

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||    navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;

if (navigator.getUserMedia) {       
navigator.getUserMedia({video: true, audio: true}, handleVideo, videoError);
}

function handleVideo(stream) {
video.src = window.URL.createObjectURL(stream);
document.getElementById("videoElement").pause();
}

function videoError(e) {
alert("There was an error with the video stream.\nCheck that your webcam is connected.");
}

function play()
{
var video = document.getElementById("videoElement");
var music = document.getElementById("song");
   var button = document.getElementById("play");
   if (video.paused) {
      video.play();
      music.play();
      button.textContent = "Stop Recording";
   } else {
      video.pause();
      music.pause();
      button.textContent = "Continue Recording";
   }
}

function show()
{
document.getElementById("record").style.display="block";
}
</script>
</body>

在 handleVideo 中,我能否以流的形式保存它或者采用其他方式保存?


我认为你可以通过jQuery来实现这个。这里有一个演示:http://www.scriptcam.com/demo_2.cfm,如果需要进一步的帮助,还需要源代码。你可能想要检查一下它。 - surhidamatya
你尝试使用这个解决方案了吗? - Thomas Ayoub
1
你应该试一下这个。它是一个非常好的教程/手册页面,有代码片段和演示。希望能对你有所帮助 :) - user2936450
一个新的API MediaRecorder 已经发布,目前只在FF中支持。 - user1693593
这个网站听起来很棒!!!你能提供一个链接吗? - Cameron
3个回答

20

更新于2014年12月 提醒一下,有一个新的API即将推出,名为MediaRecorder。目前仅在Firefox中受支持,并处于早期阶段,但是需要注意。

媒体流和本地存储

在纯本地环境中,您不能获得非常好的结果。您可以使用canvas元素将帧绘制到上面并将jpeg图像与音频(必须分别保存)一起从视频流中保存到本地存储中,然后在发布时使用库创建例如MJPEG文件(据我所知,目前没有任何支持音频的库)。

然而,您将遇到几个问题:使用JavaScript处理所有这些信息需要很长时间 - 仅将帧保存为jpeg,将其转换为blob并将其保存到文件系统或索引数据库中将消耗您可用于单个帧的大部分(或更多)时间预算。

你将无法正确同步视频帧和音频 - 你可以保存时间戳并使用它来“修正”帧,但你的FPS很可能会变化,从而创建一个不流畅的视频。即使你在时间上有些同步,你也可能会遇到延迟问题,其中音频和视频不匹配,因为它们最初是两个单独的流。
但视频很少超过30 FPS(美国)或25 FPS(欧洲),所以你不需要浏览器可能提供的完整60 FPS速率。这为US(NTSC)系统每帧约33毫秒提供了更好的时间预算,如果你在使用PAL系统的国家,则会更多一些。使用更低的帧速率没有问题,但在某个点(<12-15 FPS)你会开始注意到严重的不流畅。
然而,许多因素将影响此操作,例如CPU、磁盘系统、帧尺寸等。JavaScript是单线程的,canvas API是同步的,因此12核CPU在这方面帮助不大,Web Workers的实用性目前基本上仅限于更长时间的任务。如果您有大量可用的内存,可以将帧缓存在内存中,这是可行的,并且可以在后期进行所有处理,这也需要一些时间。以720P @ 30 FPS录制的流每秒最少消耗105 MB(这只是原始数据,不包括浏览器对缓冲区的内部处理,这可能会使其加倍甚至三倍)。
WebRTC
更好的解决方案可能是使用WebRTC并连接到服务器(外部或本地),在那里处理流。该流将包含同步的音频和视频,您可以将流临时存储到磁盘上,而无需受到浏览器沙箱存储区的限制。这里的缺点将是(对于外部连接)带宽,因为这可能会降低质量,以及服务器的能力。
这打开了使用Node.js、.Net或PHP等第三方组件进行实际处理的可能性(或者更底层的方法,例如使用编译的C/C++和CGI/piping,如果您喜欢的话)。
你可以查看这个开源项目,它支持WebRTC流的重新编码:http://lynckia.com/licode/ Licode项目提供了一个NodeJS客户端API,用于WebRTC,因此您可以在服务器端使用它,请参阅文档 这基本上是当前HTML5技术所能达到的程度。
闪存
然后有一种选择,安装Flash并使用它 - 你 仍需要一个服务器端解决方案(Red5,Wowza或AMS)。
这可能会给你带来更少的痛苦体验,但你需要在浏览器中安装Flash(显然),而且在许多情况下,由于许可证问题,成本因素较高(请参见Red5作为开源的替代方案)。
如果您愿意支付商业解决方案,那么有这样的解决方案:
http://nimbb.com/

2
现在Chrome支持MediaRecorder了。 - Hp93

9

下面是完整可用的代码,可以捕获视频并保存到本地:

它需要获取文件、相机和麦克风的权限:

<html>
    <div class="left">
        <div id="startButton" class="button">
        Start
        </div>
        <h2>Preview</h2>
        <video id="preview" width="160" height="120" autoplay muted></video>
    </div>

    <div class="right">
        <div id="stopButton" class="button">
        Stop
        </div>
        <h2>Recording</h2>
        <video id="recording" width="160" height="120" controls></video>
        <a id="downloadButton" class="button">
        Download
        </a>
    </div>

    <script>

    let preview = document.getElementById("preview");
    let recording = document.getElementById("recording");
    let startButton = document.getElementById("startButton");
    let stopButton = document.getElementById("stopButton");
    let downloadButton = document.getElementById("downloadButton");
    let logElement = document.getElementById("log");

    let recordingTimeMS = 5000;


    function log(msg) {
        //logElement.innerHTML += msg + "\n";
    }

    function wait(delayInMS) {
        return new Promise(resolve => setTimeout(resolve, delayInMS));
    }

    function startRecording(stream, lengthInMS) {
        let recorder = new MediaRecorder(stream);
        let data = [];

        recorder.ondataavailable = event => data.push(event.data);
        recorder.start();
        log(recorder.state + " for " + (lengthInMS/1000) + " seconds...");

        let stopped = new Promise((resolve, reject) => {
        recorder.onstop = resolve;
        recorder.onerror = event => reject(event.name);
        });

        let recorded = wait(lengthInMS).then(
        () => recorder.state == "recording" && recorder.stop()
        );

        return Promise.all([
            stopped,
            recorded
        ])
        .then(() => data);
    }

    function stop(stream) {
        stream.getTracks().forEach(track => track.stop());
    }

    startButton.addEventListener("click", function() {
        navigator.mediaDevices.getUserMedia({
            video: true,
            audio: false
        }).then(stream => {
                preview.srcObject = stream;
                downloadButton.href = stream;
                preview.captureStream = preview.captureStream || preview.mozCaptureStream;
                return new Promise(resolve => preview.onplaying = resolve);
              }).then(() => startRecording(preview.captureStream(), recordingTimeMS))
              .then (recordedChunks => {
                let recordedBlob = new Blob(recordedChunks, { type: "video/webm" });
                recording.src = URL.createObjectURL(recordedBlob);  
                downloadButton.href = recording.src;
                downloadButton.download = "RecordedVideo.webm";

                log("Successfully recorded " + recordedBlob.size + " bytes of " +
                    recordedBlob.type + " media.");
              })
              .catch(log);
        }, false);


        stopButton.addEventListener("click", function() {
        stop(preview.srcObject);
        }, false);

    </script>
</html>

参考资料:录制媒体元素


1

流在此处创建。

function handleVideo(stream) {
 video.src = window.URL.createObjectURL(stream);
 document.getElementById("videoElement").pause();
}

您的数据是流或window.URL.createObjectURL(stream)。

但是,您不能简单地将流或window.URL.createObjectURL(stream)写入localstorage(2mb..太小)或webkitRequestFileSystem(允许您使用GB)...您需要读取输出到视频标记的数据并将其转换为画布作为单个帧将其保存到webkitfilesystem中。

由于文件系统最近已更改,因此我搜索了新代码,并为您找到了这个完美的示例。https://gist.github.com/piatra/2549734

在该示例中,他使用

setTimeout(function(){ draw(v, bc, w, h); }, 200);

每200毫秒写入一帧的代码。

如果您想要自定义帧速率,请将200毫秒更改为1000/25..(25fps)。

或者使用requestanimationframe,如果您的CPU支持,则应该获得大约60fps。

现在您没有像mp4这样的真正流媒体格式...但有很多帧可以使用另一个函数显示...再次需要非常快的CPU。

在此示例中,音频不起作用。

要记录WAV音频(无法记录mp3或aac)...我找到了这个。

http://typedarray.org/from-microphone-to-wav-with-getusermedia-and-web-audio/

所以最终你可以做到这一点...但仅仅几分钟就需要大量的空间,并且需要非常快的CPU来处理所有内容。

如何将录制的视频保存到项目文件夹中,而无需用户下载到他的系统中? - Thanveer Shah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接