前端实现视频录制功能

本文介绍了一种在Web端实现视频录制的方法,利用navigator.mediaDevices.getUserMedia获取摄像头流,并通过MediaRecorder API完成录制过程。文章提供了完整的HTML和JavaScript代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

web端实现视频录制

技术参考地址 github源项目地址 ,需要注意的是:

浏览器内置 navigator对象
需要https或者localhost才能访问

获取用户摄像头,并且用canvas显示

html 代码(我使用的是jade模板,现在已经更新为pug)

  h1 Hello
  p Welcome to bridge+ 视频面试
  <div style='width: 80%; margin: 0 auto;'>
  <button id="begin">开始</button>
  <button id="record" disabled>录制</button>
  <button id="play" disabled>播放</button>
  <button id="download" disabled>下载</button>
  <button id="reStart" disabled>重新开始</button>
  <button id="upload">上传</button>
  </div>
  div(style='width: 80%; margin: 0 auto;')
    video(id="gum", autoplay, muted, width="0", height='0', style='opacity: 0;')
    br
    div(style='width: 402px;height: 302px; border-radius: 5px; border:1px solid grey;')
      canvas(id='myCanvas')
    p(id='status', style='text-align: center;width: 402px;')
  script(src="/javascripts/video/adapter-latest.js")
  script(src="/javascripts/jquery-1.11.3.js")
  script(src="/javascripts/video/main.js")

javascript代码:

        var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
var mediaRecorder;
var recordedBlobs;
var sourceBuffer;
var littleVideo = false;

var gumVideo = document.querySelector('video#gum');
var statusV = document.getElementById('status');
//var recordedVideo = document.querySelector('video#recorded');
statusV.innerText = '准备阶段';

var beginButton = document.querySelector('button#begin');
var recordButton = document.querySelector('button#record');
var playButton = document.querySelector('button#play');
var downloadButton = document.querySelector('button#download');
var reStartButton = document.querySelector('button#reStart');
var uploadBtn = document.querySelector('button#upload');
beginButton.onclick= function(){
  littleVideo = true;
    statusV.innerText = '读题阶段';
};

recordButton.onclick = toggleRecording;
playButton.onclick = play;
downloadButton.onclick = download;
reStartButton.onclick = restartRecord;
uploadBtn.onclick = upload;

// window.isSecureContext could be used for Chrome
var isSecureOrigin = location.protocol === 'https:' ||
    location.hostname === 'localhost';
if (!isSecureOrigin) {
    alert('getUserMedia() must be run from a secure origin: HTTPS or localhost.' +
        '\n\nChanging protocol to HTTPS');
    location.protocol = 'HTTPS';
}

var constraints = {
    audio: true,
    video: true
};

function drawLittleVideo(c){
    var ctx=c.getContext('2d');
    //清除画布内容,重新画满
    ctx.clearRect(0,0,c.width,c.height);
    ctx.drawImage(gumVideo,320,0,80,60);
    //设置字体填充颜色
    ctx.font = "15px Courier New";
    ctx.strokeStyle  = "black";
    ctx.fillText('请做一个简单的自我介绍',100,150);
}

function drawBigVideo(c){
    var ctx=c.getContext('2d');
    //清除画布内容,重新画满
    ctx.clearRect(0,0,c.width,c.height);
    ctx.drawImage(gumVideo,0,0,c.width,c.height);
}

function handleSuccess(stream) {
    recordButton.disabled = false;
    console.log('getUserMedia() got stream: ', stream);
    window.stream = stream;
    if (window.URL) {
        //gumVideo.src = window.URL.createObjectURL(stream);
        gumVideo.srcObject = stream;
    } else {
        gumVideo.src = stream;
    }

    var c=document.getElementById("myCanvas");
    c.width = 400;
    c.height = 300;
    var i;
    gumVideo.addEventListener('play',function() {
        var p = document.createElement('p');
        p.innerText = 'hello';
        i=window.setInterval(function() {
            if(littleVideo){
                drawLittleVideo(c);
            }else{
                drawBigVideo(c);
            }
        },20);
    },false);
    gumVideo.addEventListener('pause',function() {if(i){window.clearInterval(i);}},false);
    gumVideo.addEventListener('ended',function() {if(i){clearInterval(i);}},false);
}

function handleError(error) {
    console.log('navigator.getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).
then(handleSuccess).catch(handleError);

function handleSourceOpen(event) {
    console.log('MediaSource opened');
    sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
    console.log('Source buffer: ', sourceBuffer);
}

// recordedVideo.addEventListener('error', function(ev) {
//     console.error('MediaRecording.recordedMedia.error()');
//     alert('Your browser can not play\n\n' + recordedVideo.src
//         + '\n\n media clip. event: ' + JSON.stringify(ev));
// }, true);

function handleDataAvailable(event) {
    if (event.data && event.data.size > 0) {
        recordedBlobs.push(event.data);
    }
}

function handleStop(event) {
    console.log('Recorder stopped: ', event);
}

function toggleRecording() {
    if (recordButton.textContent === '录制' || recordButton.textContent === '重录') {
        littleVideo = false;
        startRecording();
    } else {
        stopRecording();
        recordButton.textContent = '重录';
        playButton.disabled = false;
        downloadButton.disabled = false;
        reStartButton.disabled = false;
    }
}

function startRecording() {
    statusV.innerText = '答题阶段';
    //todo:显示录制中 提示
    recordedBlobs = [];
    var options = {mimeType: 'video/webm;codecs=vp9'};
    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
        console.log(options.mimeType + ' is not Supported');
        options = {mimeType: 'video/webm;codecs=vp8'};
        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
            console.log(options.mimeType + ' is not Supported');
            options = {mimeType: 'video/webm'};
            if (!MediaRecorder.isTypeSupported(options.mimeType)) {
                console.log(options.mimeType + ' is not Supported');
                options = {mimeType: ''};
            }
        }
    }
    try {
        mediaRecorder = new MediaRecorder(window.stream, options);
    } catch (e) {
        console.error('Exception while creating MediaRecorder: ' + e);
        alert('Exception while creating MediaRecorder: '
            + e + '. mimeType: ' + options.mimeType);
        return;
    }
    console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
    recordButton.textContent = '停止';
    playButton.disabled = true;
    downloadButton.disabled = true;
    reStartButton.disabled = true;
    mediaRecorder.onstop = handleStop;
    mediaRecorder.ondataavailable = handleDataAvailable;
    mediaRecorder.start(10); // collect 10ms of data
    console.log('MediaRecorder started', mediaRecorder);
}

function stopRecording() {
    statusV.innerText = '答题结束,确认阶段';
    //影藏录制中提
    mediaRecorder.stop();
    console.log('Recorded Blobs: ', recordedBlobs);
    //recordedVideo.controls = true;
}

function play() {
    var superBuffer = new Blob(recordedBlobs, {type: 'video/mp4'});
    //recordedVideo.src = window.URL.createObjectURL(superBuffer);
    gumVideo.src = window.URL.createObjectURL(superBuffer);
}

function download() {
    var blob = new Blob(recordedBlobs, {type: 'video/mp4'});
    var url = window.URL.createObjectURL(blob);
    var a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    //文件名 通过方法传进来 检测是否合法?
    a.download = 'bridge-plus.mp4';
    document.body.appendChild(a);
    a.click();
    setTimeout(function() {
        document.body.removeChild(a);
        window.URL.revokeObjectURL(url);
    }, 100);
}


function upload(){
    //保存在本地,通过post请求
    //还可以用append方法添加一些附加信息参数为(name,value),如下面的代码:
    //formData.append('nickName','Jack');//-->后台用req.body.nickName将值'Jack'取出
    var blob = new Blob(recordedBlobs, {type: 'video/mp4'});
    var data = new FormData();
    data.append('video', blob);
    data.append('qw', 123);
    data.append('question', 1);
    // var formData = new FormData();
    //
    // formData.append("username", "Groucho");
    // formData.append("accountnum", 123456); // 数字 123456 会被立即转换成字符串 "123456"
    //
    // // JavaScript file-like 对象
    // var content = '<a id="a"><b id="b">hey!</b></a>';
    // var blob = new Blob([content], { type: "text/xml"});
    //
    // formData.append("webmasterfile", blob);
    //
    // console.log(formData);

    $.ajax({
        type: "POST",
        url: "/form/data",
        data: data,
        processData:false,   //  告诉jquery不要处理发送的数据
        contentType:false,    // 告诉jquery不要设置content-Type请求头
        success:function(msg){
            console.log(msg);
        }
    });
}

function restartRecord(){
    //清楚存留 按钮功能重置
    recordButton.textContent = '录制';
    playButton.disabled = true;
    downloadButton.disabled = true;
    reStartButton.disabled = true;
    //handleSuccess();
    navigator.mediaDevices.getUserMedia(constraints).
    then(handleSuccess).catch(handleError);
    statusV.innerText = '准备阶段';
}

注:学习过程中,记录重要部分,自己备用 之后有时间会进行修改。

<think>好的,用户想在鸿蒙系统的前端实现视频录制功能,需要了解实现方法、相关API、文档和示例代码。我需要根据提供的引用资料来整理答案。 首先,根据引用[3],鸿蒙当前仅支持使用AVRecorder进行视频录制,它集成了音频捕获、编码和封装功能,适合直接生成视频文件的场景。这说明用户应该使用AVRecorder来实现视频录制。 接下来,权限部分。引用[1]提到需要系统相册、麦克风和摄像头权限。所以需要指导用户如何配置这些权限,可能是在config.json中添加ohos.permission.MICROPHONE、ohos.permission.CAMERA和ohos.permission.READ_MEDIA。 然后是API部分。引用[3]和[4]提到了AVRecorder和相关库的链接,比如libnative_media_codecbase.so等。但用户需要的是前端实现,可能更关注JS API。不过现有引用中没有提到JS API的具体类,可能需要根据系统文档补充,比如@ohos.multimedia.media中的AVRecorder类。 开发步骤方面,应该包括初始化AVRecorder、设置参数、开始录制、停止录制等步骤。需要检查是否有示例代码,如引用[3]提到的开发步骤,可能需要结合代码示例来说明。 另外,引用[2]提到AudioCapturer用于音频录制,但用户的问题是关于视频录制,所以可能不需要这部分,除非需要同时处理音频,但AVRecorder已经集成音频,所以可能不需要单独处理。 关于动态库的链接,引用[4]中的CMake部分可能属于Native开发,而用户问的是前端,所以可能不需要这部分,或者需要说明在JS开发中是否需要配置这些库,但可能在前端框架中已经封装好了。 最后,确保示例代码使用JavaScript或ArkTS,并符合鸿蒙前端开发的规范。可能需要参考官方文档中的AVRecorder示例,配置参数如视频源、音频源、输出格式、文件路径等。 总结步骤:配置权限、导入模块、创建实例、设置监听、准备参数、开始/停止录制,并提供示例代码。同时,需要提醒用户注意事项,如权限申请、参数兼容性等。</think>在HarmonyOS前端实现视频录制功能需要按照以下步骤操作,涉及权限配置、API调用及参数设置: ### 一、权限配置 在`config.json`中添加三项权限: ```json "reqPermissions": [ {"name": "ohos.permission.MICROPHONE"}, {"name": "ohos.permission.CAMERA"}, {"name": "ohos.permission.READ_MEDIA"} ] ``` 权限说明: - 摄像头权限:获取视频输入源[^1] - 麦克风权限:录制音频流[^2] - 媒体读取权限:保存视频文件到相册[^1] ### 二、核心API说明 使用`@ohos.multimedia.media`模块的AVRecorder类: ```typescript import media from '@ohos.multimedia.media'; // 创建实例 let avRecorder: media.AVRecorder; media.createAVRecorder().then((recorder) => { avRecorder = recorder; }); ``` ### 三、实现步骤 1. **初始化配置** ```typescript let profile: media.AVRecorderProfile = { audioBitrate: 100000, // 音频码率 audioChannels: 2, // 立体声 audioCodec: media.CodecMimeType.AUDIO_AAC, audioSampleRate: 48000, fileFormat: media.ContainerFormatType.CFT_MPEG_4, videoBitrate: 2000000, // 视频码率 videoCodec: media.CodecMimeType.VIDEO_MPEG4, videoFrameWidth: 640, // 分辨率 videoFrameHeight: 480, videoFrameRate: 30 // 帧率 }; let config: media.AVRecorderConfig = { audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, profile: profile, url: 'file://com.example/video.mp4', // 存储路径 rotation: 0 // 旋转角度 }; ``` 2. **事件监听** ```typescript avRecorder.on('stateChange', (state) => { console.log('Current state: ' + state); }); avRecorder.on('error', (err) => { console.error('Error occurred: ' + err.message); }); ``` 3. **录制控制** ```typescript // 开始录制 avRecorder.prepare(config).then(() => { return avRecorder.start(); }); // 停止录制 avRecorder.stop().then(() => { avRecorder.release(); }); ``` ### 四、完整示例代码(ArkTS) ```typescript import media from '@ohos.multimedia.media'; @Entry @Component struct VideoRecorder { private avRecorder: media.AVRecorder | null = null; build() { Column() { Button('开始录制') .onClick(() => this.startRecording()) Button('停止录制') .onClick(() => this.stopRecording()) } } async startRecording() { try { this.avRecorder = await media.createAVRecorder(); // 配置参数(参考上文) await this.avRecorder.prepare(config); await this.avRecorder.start(); } catch (err) { console.error('Start failed: ' + err.message); } } async stopRecording() { if (this.avRecorder) { await this.avRecorder.stop(); this.avRecorder.release(); } } } ``` ### 五、注意事项 1. 需要动态申请运行时权限[^1] 2. 视频编码参数需与设备支持的规格匹配[^3] 3. 文件存储路径需使用应用沙箱目录[^3] 4. 录制过程中需保持屏幕常亮
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值