uni-app 视频优化
核心知识点
1. 视频格式选择
- MP4:最广泛支持的视频格式,适合大多数场景
- WebM:开源视频格式,压缩率高,适合网页端
- HLS:自适应码率流媒体协议,适合网络波动较大的场景
- FLV:Flash 视频格式,适合直播场景
- AVI:传统视频格式,文件较大,不推荐使用
2. 视频编码优化
- H.264/AVC:广泛使用的编码标准,兼容性好
- H.265/HEVC:新一代编码标准,压缩率更高
- VP9:开源编码标准,适合 Web 端
- AV1:最新编码标准,压缩率最高,但解码要求较高
- 编码参数:码率、分辨率、帧率、关键帧间隔等
3. 视频加载策略
- 预加载:提前加载视频内容
- 懒加载:按需加载视频
- 分段加载:将视频分成多个片段加载
- 自适应码率:根据网络状况自动调整视频质量
- 缓存策略:本地缓存视频内容
4. 视频播放控制
- 播放器选择:使用原生播放器或第三方播放器
- 播放控制:播放、暂停、快进、快退等
- 音量控制:系统音量和播放器音量
- 全屏控制:横屏和竖屏切换
- 播放状态监听:加载中、播放中、暂停、结束等
5. 视频性能优化
- 硬件加速:使用 GPU 加速视频解码
- 内存管理:及时释放视频资源
- 渲染优化:减少视频渲染对 UI 的影响
- 电池优化:减少视频播放对电池的消耗
- 网络优化:减少视频传输的数据量
6. 跨平台视频处理
- 平台差异:不同平台对视频格式和编码的支持
- 条件编译:为不同平台提供不同的视频处理方案
- 原生能力:使用平台原生的视频播放 API
- 第三方库:使用跨平台视频处理库
实用案例
案例 1:短视频应用视频优化
需求分析
- 首页需要快速加载和播放短视频
- 视频需要自动播放和循环播放
- 支持上下滑动切换视频
- 需要保证视频播放的流畅性
实现方案
视频格式和编码
- 使用 MP4 格式,确保广泛兼容性
- 使用 H.264 编码,码率控制在 1-2Mbps
- 分辨率设置为 720p 或 1080p,根据设备性能调整
- 帧率设置为 30fps,保证流畅度
视频加载策略
<template> <view class="video-container" @touchstart="touchStart" @touchend="touchEnd"> <video v-for="(video, index) in videos" :key="index" :src="video.url" :id="`video-${index}`" :class="{ active: currentIndex === index }" :style="{ transform: `translateY(${index * 100 - currentIndex * 100}%)` }" :autoplay="currentIndex === index" :loop="true" :muted="false" objectFit="cover" @ended="videoEnded(index)" @error="videoError(index)" class="video-player" /> </view> </template> <script> export default { data() { return { videos: [ { url: 'https://example.com/video1.mp4' }, { url: 'https://example.com/video2.mp4' }, { url: 'https://example.com/video3.mp4' } ], currentIndex: 0, startY: 0 } }, methods: { touchStart(e) { this.startY = e.touches[0].clientY }, touchEnd(e) { const endY = e.changedTouches[0].clientY const diff = endY - this.startY if (diff > 50 && this.currentIndex > 0) { // 向上滑动,播放上一个视频 this.currentIndex-- this.preloadVideo(this.currentIndex - 1) } else if (diff < -50 && this.currentIndex < this.videos.length - 1) { // 向下滑动,播放下一个视频 this.currentIndex++ this.preloadVideo(this.currentIndex + 1) } }, preloadVideo(index) { // 预加载相邻的视频 if (index >= 0 && index < this.videos.length) { const video = uni.createVideoContext(`video-${index}`) if (video) { video.load() } } }, videoEnded(index) { // 视频播放结束,重新开始 const video = uni.createVideoContext(`video-${index}`) if (video) { video.play() } }, videoError(index) { console.error('视频加载失败:', index) // 可以显示错误提示或切换到备用视频 } }, onLoad() { // 预加载第一个视频 this.preloadVideo(0) // 预加载第二个视频 this.preloadVideo(1) } } </script> <style scoped> .video-container { position: relative; width: 100%; height: 100vh; overflow: hidden; } .video-player { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transition: transform 0.3s ease; } </style>视频播放控制
<template> <view class="video-player-wrapper"> <video id="video-player" :src="videoUrl" :autoplay="autoplay" :controls="showControls" :loop="loop" :muted="muted" objectFit="contain" @play="onPlay" @pause="onPause" @ended="onEnded" @timeupdate="onTimeUpdate" @error="onError" class="video-player" /> <view class="custom-controls" v-if="showCustomControls"> <button @click="togglePlay" class="control-btn">{{ isPlaying ? '暂停' : '播放' }}</button> <button @click="toggleFullScreen" class="control-btn">全屏</button> <view class="progress-bar"> <view class="progress-track" @click="seek"></view> <view class="progress-fill" :style="{ width: `${progress}%` }"></view> </view> <text class="time-display">{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</text> </view> </view> </template> <script> export default { data() { return { videoUrl: 'https://example.com/video.mp4', autoplay: false, showControls: false, showCustomControls: true, loop: false, muted: false, isPlaying: false, currentTime: 0, duration: 0, progress: 0 } }, mounted() { this.videoContext = uni.createVideoContext('video-player', this) }, methods: { togglePlay() { if (this.isPlaying) { this.videoContext.pause() } else { this.videoContext.play() } }, toggleFullScreen() { this.videoContext.requestFullScreen({ direction: 0 }) }, seek(e) { const { clientX, target } = e const rect = target.getBoundingClientRect() const percentage = (clientX - rect.left) / rect.width const seekTime = percentage * this.duration this.videoContext.seek(seekTime) }, formatTime(seconds) { const minutes = Math.floor(seconds / 60) const remainingSeconds = Math.floor(seconds % 60) return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}` }, onPlay() { this.isPlaying = true }, onPause() { this.isPlaying = false }, onEnded() { this.isPlaying = false this.currentTime = 0 this.progress = 0 }, onTimeUpdate(e) { this.currentTime = e.detail.currentTime this.duration = e.detail.duration || this.duration this.progress = (this.currentTime / this.duration) * 100 }, onError(e) { console.error('视频错误:', e) } } } </script>性能优化
- 硬件加速:启用 GPU 加速视频解码
- 内存管理:当视频不可见时,暂停播放并释放资源
- 网络优化:使用 CDN 加速视频传输
- 电池优化:在后台或非活动状态下暂停视频播放
案例 2:直播应用视频优化
需求分析
- 需要支持实时直播流
- 网络状况可能不稳定,需要自适应码率
- 需要低延迟的播放体验
- 支持多人同时观看
实现方案
直播协议选择
- RTMP:低延迟,适合互动直播
- HLS:自适应码率,适合网络波动较大的场景
- FLV:延迟较低,适合网页端直播
直播流优化
<template> <view class="live-container"> <video id="live-player" :src="liveUrl" autoplay controls objectFit="cover" @statechange="stateChange" @error="liveError" class="live-player" /> <view class="live-info"> <text class="live-title">{{ liveTitle }}</text> <text class="viewer-count">观看人数: {{ viewerCount }}</text> </view> </view> </template> <script> export default { data() { return { liveUrl: 'rtmp://example.com/live/stream', liveTitle: '直播标题', viewerCount: 0, playerState: 'loading' } }, mounted() { this.liveContext = uni.createVideoContext('live-player', this) this.updateViewerCount() }, methods: { stateChange(e) { console.log('播放器状态变化:', e.detail.code) switch (e.detail.code) { case 0: this.playerState = 'loading' break case 1: this.playerState = 'playing' break case 2: this.playerState = 'paused' break case 3: this.playerState = 'ended' break case -1: this.playerState = 'error' break } }, liveError(e) { console.error('直播错误:', e) // 切换到备用直播源 this.switchToBackupStream() }, switchToBackupStream() { this.liveUrl = 'rtmp://backup.example.com/live/stream' // 重新加载视频 this.$nextTick(() => { this.liveContext.play() }) }, updateViewerCount() { // 模拟更新观看人数 setInterval(() => { this.viewerCount = Math.floor(Math.random() * 1000) + 100 }, 5000) } } </script>自适应码率
- 使用 HLS 协议,根据网络状况自动调整视频质量
- 配置多个码率的视频流,供播放器选择
- 监控网络状态,手动切换合适的码率
低延迟优化
- 减少关键帧间隔,降低解码延迟
- 使用 RTMP 或 WebSocket 协议,减少传输延迟
- 优化服务器配置,减少处理延迟
- 客户端预加载策略,减少缓冲延迟
学习目标
- 掌握 uni-app 中视频优化的各种技术和方法
- 学会根据不同场景选择合适的视频格式和编码
- 掌握视频加载策略和播放控制的实现方法
- 了解视频性能优化的技巧和方法
- 能够处理跨平台视频播放的差异
- 提升应用中视频的播放体验
总结
视频优化是 uni-app 应用开发中的重要组成部分,通过合理选择视频格式、优化编码参数、使用合适的加载策略和播放控制,可以显著提升应用中视频的播放体验。在实际开发中,开发者应该根据具体场景选择合适的优化策略,平衡视频质量和加载速度,为用户提供流畅的视频播放体验。
同时,开发者还需要注意跨平台视频播放的差异,使用条件编译或平台特定的 API 来处理不同平台的视频播放问题,确保应用在所有平台上都能正常播放视频。对于直播等特殊场景,还需要考虑网络稳定性、延迟等因素,选择合适的直播协议和优化策略,提供最佳的直播体验。