微信小程序录制视频功能实现

article/2025/10/29 15:41:11

 背景:

之前项目中有个需求,需要在pc端的web页面做一套业务的开户流程,其中在开户流程中需要法人上传本人的开户视频(需要支持扫二维码在手机上录制视频上传)。pc端上传的功能比较好实现,但是扫二维码在手机上录制视频并且同步到pc页面的开户流程中就不太好做了。

一开始有想到两种方案:

a: 做一个h5的视频录制页面,然后生成链接的二维码,手机扫二维码进到h5页面录制视频。

  • 优点:视频录制的时长可以控制
  • 缺点:无法自定义视频拍摄的界面,只能使用相机原生的界面拍摄,可能还要适配各种机型

b: 在小程序的页面里录制视频,而且有现成的组件和api比较好实现

  • 优点:视频拍摄的界面可以调整,比如在界面上添加展示文案,不需要再花精力去适配机型
  • 缺点:录制时长比较短,目前有5分钟的时长限制

考虑到这个开户视频需要在录制界面上展示一段朗读的文字,h5可能暂时无法实现,刚好公司目前有一个微信小程序已经上线,所以最终选择了在小程序上开发一个录制视频的功能页面。

实现原理

要在微信小程序实现录制视频的功能要用到两个媒体组件camera和video,其中camera组件用来拍摄视频,video组件用来预览拍摄的视频。

camera

camera组件可以调用系统相机进行拍照或者视频录制,其中onCameraFrame 接口可以根据 属性frame-size 返回不同尺寸的原始帧数据,这个原始帧数据就是我们要拍摄的视频数据。

用法

html
<camera device-position="front" flash="off" binderror="error"></camera>js// 创建 camera 上下文 CameraContext 对象
const context = wx.createCameraContext() 
// context.onCameraFrame()返回视频图像的监听器
const listener = context.onCameraFrame((frame) => {// frame.data 就是视频数据 格式是ArrayBufferconsole.log(frame.data instanceof ArrayBuffer, frame.width, frame.height)
})
listener.start() // 开始监听帧数据
setTimeou(() => {listener.start() // 停止监听帧数据
}, 10)

video

通过camera组件的api接口可以拿到实时的视频数据了,但是ArrayBuffer格式的数据是无法预览的。那要预览视频可以用video媒体组件,video组件中可以指定视频的src路径,即播放视频的资源地址,但是只支持网络路径、本地临时路径、云文件ID这些选项。

所以要播放录制的视频还需要拿到视频的本地临时路径,那怎么拿到呢,需要结合下面两个api

开始录像CameraContext.startRecord,可以设置视频的时长

结束录像CameraContext.stopRecord,通过成功的回调函数拿到视频的临时路径

官方给出的使用例子

<view class="page-body"><view class="page-body-wrapper"><camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;"></camera><view class="btn-area"><button type="primary" bindtap="startRecord">开始录像</button></view><view class="btn-area"><button type="primary" bindtap="stopRecord">结束录像</button></view><view class="preview-tips">预览</view><video wx:if="{{videoSrc}}" class="video" src="{{videoSrc}}"></video></view>
</view>
Page({onLoad() {this.ctx = wx.createCameraContext()},startRecord() {this.ctx.startRecord({success: (res) => {console.log('startRecord')}})},stopRecord() {this.ctx.stopRecord({success: (res) => {this.setData({src: res.tempThumbPath,videoSrc: res.tempVideoPath})}})},error(e) {console.log(e.detail)}
})

代码实现

  1. 首先创建 camera 上下文 CameraContext 对象ctx
  2. 通过ctx.onCameraFrame注册一个listener用于获取 Camera 实时帧数据
  3. 调用ctx.startRecord开始录像,调用成功后触发listener.start()开始监听获取实时的视频数据
  4. 调用ctx.stopRecord结束录像,在成功回调中获取视频的临时路径预览视频,并且触发listener.stop()结束视频侦听保存完整的视频数据
<template><view><view class="page-index" :style="{height: windowHeight+'px', position: 'relative'}"><!-- 录制视频区域 --><camera v-if="!videoSrc.length" device-position="front" flash="off" binderror="error":style="{width:cameraWidth+'px',height: windowHeight+'px'}"><p style="color:red; font-size: 20px;">【本人为我司法定代表人,现声明:我司已充分知晓。。。。】</p><view class="time-clycle" v-if="showTimer">{{timeStamp===0?"开始":timeStamp}}</view><!-- 录制视频时间显示 --><view class="video-time"><span>00:<span v-if="videoTime<10">0</span><span>{{videoTime}}</span></span></view></camera><!-- 查看录制视频 --><video :style="{width:cameraWidth+'px',height: windowHeight+'px'}" v-else :src="videoSrc" controls></video></view><button :disabled="disStartBtn" class="video-operate" v-if="showStartBtn" @click="handleStartCamera()">开始录制</button><button class="video-operate" v-if="showStopBtn" @click="handleStopCamera()">结束录制</button><!-- 上传 --><view v-if="videoSrc.length" class="video-result"><u-button :custom-style="firstButtonStyle" style="width: 35%" type="primary" hover-class="active"@click="removeVedio()">重新录制</u-button><u-button :custom-style="secondButtonStyle" style="width: 65%" type="primary"@click="uploadVedio()">确定上传</u-button></view></view></template><script>import {saveVideoFile} from '@/api/upload-video';import {getUrlParams} from '@/common/utils'export default {components: {},data() {return {}},methods: {/*** 上传视频 */uploadVedio() {this.handleUploadFile(this.videoFile);},/*** 保存录制的视频到后台* @param {Object} params*/saveVideoFileInfo(params) {saveVideoFile(params).then(res => {uni.showModal({title: '提示',content: '录制视频已上传成功,请前往PC端开户流程页面查看视频',showCancel: false,confirmText: '知道了',success: function(res) {if (res.confirm) {// 退出小程序wx.exitMiniProgram();}}})})},/*** 上传文件 */async handleUploadFile(file) {const res = await this.$wxApi.uploadVideo({url: '/dragon/file_extend/upload',name: 'file_data',filePath: this.videoSrc,file,formData: {appId: this.appId,fileName: 'corpIdVideo.mp4'}});this.saveVideoFileInfo({appId: this.appId,fileInfo: JSON.stringify(res)})},/*** 获取系统信息 设置相机的大小适应屏幕*/setCameraSize() {//获取设备信息const res = wx.getSystemInfoSync();//获取屏幕的可使用宽高,设置给相机this.cameraWidth = res.windowWidth;this.windowHeight = res.windowHeight - this.iphoneHeight;},/*** 录制视频计时器*/videoTimeInterval() {this.videoTimer =setInterval(() => {++this.videoTime;}, 1000)},/*** 倒计时录像*/startVideoTimer() {this.timeId = setInterval(() => {if (this.timeStamp > this.timeStampEnd) {this.timeStamp--;} else if (this.timeStamp === this.timeStampEnd) {this.showTimer = false;this.clearTimer()// 开始录像this.startShootVideo();} else {this.clearTimer()}}, 1000)},/*** 倒计时重置*/clearTimer() {this.showTimer = false;clearInterval(this.timeId);this.timeId = null;this.timeStamp = this.timeStampStart;},/*** 重新录制*/removeVedio() {this.videoSrc = '';this.showStartBtn = true;this.disStartBtn = false;this.showStopBtn = false;// 倒计时重置clearInterval(this.timeId);this.timeId = null;this.timeStamp = this.timeStampStart;// 录制时间重置clearInterval(this.videoTimer);this.videoTimer = null;this.videoTime = this.timeStampEnd;this.listener.stop();},/*** 开始录像的方法*/startShootVideo() {this.startTime = new Date().getTime();this.showStopBtn = true;this.listener.start();this.videoSrc = ''let that = this;this.ctx.startRecord({success: (res) => {that.videoTimeInterval()},fail(err) {wx.showToast({title: `开始录像失败${err.errMsg},请重新扫码进入`,icon: 'none',duration: 4000});that.removeVedio();}})},/*** 结束录像的方法*/stopShootVideo() {let that = this;clearInterval(that.videoTimer);this.ctx.stopRecord({compressed: false, //压缩视频success: (res) => {that.videoSrc = res.tempVideoPath;},fail(err) {wx.showToast({title: `结束录像失败${err.errMsg},请重新扫码进入`,icon: 'none',duration: 4000});that.removeVedio();}});},/*** 获取麦克风权限*/getSetting() {return new Promise((resolve, reject) => {wx.getSetting({success(res) {if (!res.authSetting['scope.record']) {wx.authorize({scope: 'scope.record',success() {resolve(true)// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问},fail(err) {resolve(false)}})} else {resolve(true)}},fail(err) {resolve(false)}})})},/*** 开始录制*/async handleStartCamera() {this.disStartBtn = true;// 判断是否授权了麦克风const authorizePass = await this.getSetting();if (!authorizePass) {wx.showToast({title: `录像失败,麦克风未授权`,icon: 'none',duration: 4000});this.removeVedio();return}this.showTimer = true;this.timeStamp = this.timeStampStart;// 倒计时3s后,调用开始录像方法this.startVideoTimer();},/*** 结束录制*/handleStopCamera() {this.endTime = new Date().getTime();if (this.endTime - this.startTime < this.minRecordTime) {wx.showToast({title: `录制时间太短,请重新录制`,icon: 'none',duration: 3000});// 停止录像this.ctx.stopRecord();this.removeVedio();} else if (this.endTime - this.startTime > this.maxRecordTime) {wx.showToast({title: `录制时间超过30s,请重新录制`,icon: 'none',duration: 3000});this.removeVedio();} else {this.showStartBtn = false;this.showStopBtn = false;this.stopShootVideo();}},/*** 获取url参数* @param {Object} options*/getCodeQuery(options) {let queryAll = decodeURIComponent(options.q);let appId = getUrlParams('appId', queryAll);this.appId = appId;}},onLoad(options) {if (options.q) {this.getCodeQuery(options);}this.setCameraSize();this.ctx = wx.createCameraContext();// 获取 Camera 实时帧数据this.listener = this.ctx.onCameraFrame((frame) => {this.videoFile = frame.data;})}}
</script><style lang="scss" scoped>.page-index {position: relative;.time-clycle {position: absolute;display: block;top: 50%;left: 50%;transform: translateX(-50%);color: #fff;font-size: 80rpx;text-align: center;text-shadow: 5rpx 5rpx 5rpx #333;}.video-time {position: absolute;color: #fff;right: 0;bottom: 0;widows: 100rpx;heigh: 60rpx;background-color: red;}}.video-operate {position: fixed;text-align: center;left: 0;right: 0;padding-bottom: 68rpx;height: 120rpx;line-height: 120rpx;}.video-result {position: fixed;display: flex;text-align: center;left: 0;right: 0;padding-bottom: 68rpx;height: 120rpx;line-height: 120rpx;}
</style>

看看功能效果


http://chatgpt.dhexx.cn/article/LWaQQMGV.shtml

相关文章

微信小程序学习——视频播放小程序

一、视图设计 &#xff08;一&#xff09;导航栏设计 # app.json——导航栏设置 {"pages":["pages/index/index"],"window":{"backgroundTextStyle":"light","navigationBarBackgroundColor": "#000080&qu…

小程序视频(video)播放不了

1、原因&#xff1a;URl加密了 解决&#xff1a; 2、CSS层级遮挡&#xff0c;导致无法点击问题解决 因层级覆盖、定位等&#xff0c;导致某些地方点击不生效&#xff0c;给遮挡元素设置pointer-events: none即可 若遮挡元素这层有点击事件等&#xff0c;需要另给操作区域设置…

教你下载微信小程序里的视频

目录 利用 Fiddler (http抓包工具)IDM 油猴脚本下载 利用 Fiddler (http抓包工具) 在电脑上打开了小程序的视频&#xff0c;但是我们现在还是不能获取到视频的url地址&#xff0c;这里就需要用到我们的fiddler工具了&#xff08;Fiddler是一个http协议调试代理工具&#xff0c…

小程序中的视频下载

一个朋友问我他看到小程序中有一个视频&#xff0c;但是他不知道如何下载下来&#xff0c;我在手机上面打开想直接下载发现还真没有可以直接下载的按钮&#xff0c;然后研究得出大概思路如下&#xff0c;在电脑中打开小程序&#xff0c;然后使用wireshark转包工具抓取数据分析获…

微信小程序实现视频功能(一):视频上传

微信小程序开发实现视频的上传&#xff08;官方提供了API&#xff0c;基本直接调用就可以了&#xff09; 一、效果图&#xff1a; 二、代码实现 1. video.wxml <!--pages/video/video.wxml--> <view class"main"><view class"playerInfo"…

微信小程序视频+微信视频号视频下载教程

之前「随书光盘」那期文章说了要出一期文章来教下「微信小程序视频」怎么下载 阿虚小小研究了一下&#xff0c;今天就来分享一下教程 同时这篇文章也会顺便教下微信「视频号」的视频怎么下载 ▍1 微信小程序视频怎么下载&#xff1f; 以下要说的是真限制在微信小程序内观看的…

下载微信小程序中的视频

工具准备&#xff1a; Fiddler 我这里用的5.0的版本。&#xff08;这个用来抓取视频下载地址&#xff09; Internet Download Manager&#xff08;idm&#xff09;版本6.37&#xff08;这个用来下载视频&#xff09; 步骤&#xff1a; 打开Fiddler如下图配置后抓包 2.登录PC版微…

微信小程序——video视频播放

1、功能介绍 可以实现视频的播放暂停&#xff0c;发送弹幕&#xff0c;获取视频所处位置等。 2、video组件 2.1、用处 video组件常用于视频的播放&#xff0c;默认宽度为300px&#xff0c;高度为225px。 2.2、属性 属性说明src视频的资源地址loop是否循环播放&#xff0c;…

视频播放微信小程序

2022年夏季《移动软件开发》实验报告 一、实验目标 1、掌握视频列表的切换方法&#xff1b;2、掌握视频自动播放方法&#xff1b;3、掌握视频随机颜色弹幕效果。 二、实验步骤 列出实验的关键步骤、代码解析、截图。 1、创建项目 填好项目名称、目录、AppID等信息后&…

微信小程序视频基本操作

微信小程序视频基本操作 1、视频1.1 wx.createVideoContext(string id,Object this)接口1.1.2 VideoContext对象常用函数1.1.3 小案例 1.2 wx.chooseVideo()接口object.success 回调函数参数Object res 示例代码 1.2.1 小案例 1.3 wx.saveVideoToPhotosAlbum(Object object)接口…

【微信小程序】视频播放小程序

课程&#xff1f;中国海洋大学22夏《移动软件开发》实验名称实验3&#xff1a;视频播放小程序 一、实验目标 1、掌握视频列表的切换方法&#xff1b; 2、掌握视频自动播放方法&#xff1b; 3、掌握视频随机颜色弹幕效果。 二、实验步骤 一、项目创建 二、页面配置 创建页面…

微信小程序如何使用视频组件

小程序中可以使用视频组件来播放自己想要展示的视频&#xff0c;具体步骤如下 一、添加视频组件 1、点击“店铺”——“装修店铺”——“编辑”&#xff0c;进入小程序页面编辑。 2、点击“组件库”——“视频”&#xff0c;页面会添加视频组件&#xff0c;点击视频组件的“编…

PHP实现页面静态化、纯静态化及伪静态化

概念 php静态化分为&#xff1a;纯静态化 和 伪静态化&#xff1b; 纯静态化又分为&#xff1a;局部静态化 和 完全静态化 纯静态化&#xff1a;是把PHP生成的动态页面保存成静态的html文件&#xff0c;用户访问该静态页面&#xff0c;而不是用户每一次访问都重新生成一张相…

SpringBoot整合Thymleaf实现页面静态化

文章整理题材来源于传智播客乐优商城项目实战&#xff01; 1. 问题需求分析 在做乐优商城时&#xff0c;页面是通过Thymeleaf模板引擎渲染后返回到客户端。当商品详情页数据渲染时&#xff0c;在后台需要大量的数据查询&#xff0c;而后渲染得到HTML页面。在用户访问量大的情况…

网站页面静态化(二)thymeleaf生成

今年是农历大年初三&#xff0c;在这里首先给各位朋友拜个年&#xff0c;祝大家新年快乐&#xff0c;虎年大吉大利&#xff0c;事业蒸蒸日上。过年无事&#xff0c;把页面静态化技术整理整理。本文将以thymeleaf为例子&#xff0c;说明在springboot当中&#xff0c;如何基于thy…

HTML页面静态化技术

随着网站的内容的增多和用户访问量的增多&#xff0c;无可避免的是网站加载会越来越慢&#xff0c;受限于带宽和服务器同一时间的请求次数的限制&#xff0c;我们往往需要在此时对我们的网站进行代码优化和服务器配置的优化。 此文已Django的电商网站为例&#xff08;处理高并…

Thymeleaf动态页面静态化

Thymeleaf 目标 Thymeleaf的介绍Thymeleaf的入门Thymeleaf的语法及标签搜索页面渲染商品详情页静态化功能实现 1.Thymeleaf介绍 1动态页面&#xff1a; 通过执行asp、php、jsp和.net等程序生成客户端网页代码的网页。通常可以通过网站后台管理系统对网站的内容进行更新管理…

Java使用Freemarker页面静态化生成实现

Java使用Freemarker页面静态化生成实现 页面静态化其实就是将原来的动态网页(例如通过ajax请求动态获取数据库中的数据并展示的网页)改为通过静态化技术生成的静态网页&#xff0c;这样用户在访问网页时&#xff0c;服务器直接给用户响应静态html页面&#xff0c;没有了动态查询…

CMS-页面静态化技术

页面静态化技术 页面静态化技术是什么&#xff1f; 页面静态化主要运用在一些大型网站&#xff0c;有大量信息需要与数据库交互。比如大型电商网站京东&#xff0c;淘宝 &#xff0c;天猫等。电商网站最大的特点是什么&#xff1f;数据庞大&#xff0c;页面众多。那么我们都知…

java页面静态化

1.概念 页面静态化&#xff0c;其实就是将动态生成的jsp页面&#xff0c;变成静态的HTML页面&#xff0c;让用户直接访问。 2.优点 &#xff08;1&#xff09;加快页面打开浏览速度&#xff0c;静态页面无需连接数据库&#xff0c;打开速度较动态页面有明显提高。 &#xff…