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:短视频应用视频优化

需求分析

  • 首页需要快速加载和播放短视频
  • 视频需要自动播放和循环播放
  • 支持上下滑动切换视频
  • 需要保证视频播放的流畅性

实现方案

  1. 视频格式和编码

    • 使用 MP4 格式,确保广泛兼容性
    • 使用 H.264 编码,码率控制在 1-2Mbps
    • 分辨率设置为 720p 或 1080p,根据设备性能调整
    • 帧率设置为 30fps,保证流畅度
  2. 视频加载策略

    <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>
  3. 视频播放控制

    <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>
  4. 性能优化

    • 硬件加速:启用 GPU 加速视频解码
    • 内存管理:当视频不可见时,暂停播放并释放资源
    • 网络优化:使用 CDN 加速视频传输
    • 电池优化:在后台或非活动状态下暂停视频播放

案例 2:直播应用视频优化

需求分析

  • 需要支持实时直播流
  • 网络状况可能不稳定,需要自适应码率
  • 需要低延迟的播放体验
  • 支持多人同时观看

实现方案

  1. 直播协议选择

    • RTMP:低延迟,适合互动直播
    • HLS:自适应码率,适合网络波动较大的场景
    • FLV:延迟较低,适合网页端直播
  2. 直播流优化

    <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>
  3. 自适应码率

    • 使用 HLS 协议,根据网络状况自动调整视频质量
    • 配置多个码率的视频流,供播放器选择
    • 监控网络状态,手动切换合适的码率
  4. 低延迟优化

    • 减少关键帧间隔,降低解码延迟
    • 使用 RTMP 或 WebSocket 协议,减少传输延迟
    • 优化服务器配置,减少处理延迟
    • 客户端预加载策略,减少缓冲延迟

学习目标

  1. 掌握 uni-app 中视频优化的各种技术和方法
  2. 学会根据不同场景选择合适的视频格式和编码
  3. 掌握视频加载策略和播放控制的实现方法
  4. 了解视频性能优化的技巧和方法
  5. 能够处理跨平台视频播放的差异
  6. 提升应用中视频的播放体验

总结

视频优化是 uni-app 应用开发中的重要组成部分,通过合理选择视频格式、优化编码参数、使用合适的加载策略和播放控制,可以显著提升应用中视频的播放体验。在实际开发中,开发者应该根据具体场景选择合适的优化策略,平衡视频质量和加载速度,为用户提供流畅的视频播放体验。

同时,开发者还需要注意跨平台视频播放的差异,使用条件编译或平台特定的 API 来处理不同平台的视频播放问题,确保应用在所有平台上都能正常播放视频。对于直播等特殊场景,还需要考虑网络稳定性、延迟等因素,选择合适的直播协议和优化策略,提供最佳的直播体验。

« 上一篇 uni-app 图片优化 下一篇 » uni-app 音频处理