HarmonyOS多媒体开发:音视频播放与录制全解析

发布于:2025-09-13 ⋅ 阅读:(23) ⋅ 点赞:(0)

本文将全面介绍HarmonyOS 5(API 12)中的多媒体开发能力,重点讲解音视频播放与录制的实现原理、最佳实践和性能优化技巧。

1. 多媒体核心架构

HarmonyOS多媒体子系统采用分层架构设计,提供统一的媒体服务接口。其核心包括媒体播放器(AVPlayer)、媒体录制器(AVRecorder)和屏幕录制(AVScreenCapture)三大模块,支持从简单的音频播放到复杂的屏幕录制等各种场景。

1.1 媒体服务优势

  • 轻量级引擎:占用较少系统资源(线程、内存),支持pipeline灵活拼装和插件化扩展
  • HDR视频支持:原生支持hdr vivid采集与播放,提供更炫彩的视觉体验
  • 音频池技术:针对短促音效播放场景,实现一次加载多次低时延播放

2. 音视频播放实现

2.1 AVPlayer基础使用

AVPlayer是HarmonyOS多媒体框架的核心播放管理类,负责管理和播放媒体资源,支持本地文件和网络流媒体。

import media from '@ohos.multimedia.media';
import common from '@ohos.app.ability.common';

@Entry
@Component
struct VideoPlayerDemo {
  // 播放器实例
  private avPlayer: media.AVPlayer | null = null;
  // 播放状态
  @State isPlaying: boolean = false;
  // 当前播放进度
  @State currentTime: number = 0;
  // 视频总时长
  @State duration: number = 0;

  // 组件初始化时创建播放器
  async aboutToAppear() {
    await this.initAVPlayer();
  }

  // 初始化AVPlayer
  private async initAVPlayer() {
    try {
      // 创建AVPlayer实例
      this.avPlayer = await media.createAVPlayer();
      
      // 设置视频源(支持网络和本地路径)
      await this.avPlayer.setSource({
        uri: 'https://example.com/sample.mp4', // 网络视频
        // uri: 'file:///data/media/local.mp4', // 本地视频
        mediaType: media.MediaType.VIDEO
      });

      // 准备播放器
      await this.avPlayer.prepare();
      
      // 获取视频总时长
      this.duration = await this.avPlayer.getDuration();
      
      // 设置时间更新监听
      this.avPlayer.on('timeUpdate', (time: number) => {
        this.currentTime = time;
      });
      
      // 设置播放完成监听
      this.avPlayer.on('end', () => {
        this.isPlaying = false;
        console.log('播放完成');
      });
      
    } catch (error) {
      console.error('AVPlayer初始化失败: ', JSON.stringify(error));
    }
  }

  // 播放/暂停切换
  private async togglePlay() {
    if (!this.avPlayer) return;
    
    try {
      if (this.isPlaying) {
        await this.avPlayer.pause();
      } else {
        await this.avPlayer.play();
      }
      this.isPlaying = !this.isPlaying;
    } catch (error) {
      console.error('播放控制失败: ', JSON.stringify(error));
    }
  }

  // 跳转到指定位置
  private async seekTo(position: number) {
    if (!this.avPlayer) return;
    
    try {
      await this.avPlayer.seek(position);
      this.currentTime = position;
    } catch (error) {
      console.error('跳转失败: ', JSON.stringify(error));
    }
  }

  // 释放资源
  private async releasePlayer() {
    if (this.avPlayer) {
      await this.avPlayer.release();
      this.avPlayer = null;
    }
  }

  build() {
    Column({ space: 10 }) {
      // 视频显示区域
      VideoComponent({ avPlayer: this.avPlayer })
        .width('100%')
        .height(300)
        .backgroundColor('#000000')

      // 播放控制区域
      Row({ space: 5 }) {
        Button(this.isPlaying ? '暂停' : '播放')
          .onClick(() => this.togglePlay())
          .width(80)
        
        Text(`${formatTime(this.currentTime)}/${formatTime(this.duration)}`)
          .fontSize(14)
          .textAlign(TextAlign.Center)
      }
      .margin(10)

      // 进度条组件
      Slider({ value: this.currentTime, min: 0, max: this.duration })
        .onChange((value: number) => this.seekTo(value))
        .width('90%')
    }
    .width('100%')
    .height('100%')
    .onDisappear(() => this.releasePlayer())
  }
}

// 时间格式化工具函数
function formatTime(milliseconds: number): string {
  const seconds = Math.floor(milliseconds / 1000);
  const minutes = Math.floor(seconds / 60);
  const remainingSeconds = seconds % 60;
  return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
}

2.2 支持的格式与协议

HarmonyOS AVPlayer支持多种主流媒体格式和协议:

类型 支持格式 说明
网络协议 HTTP/HTTPS, HLS, HTTP-FLV 支持直播和点播
音频格式 AAC, MP3, VORBIS, PCM, AMR 包含多种容器格式
视频格式 H.264, H.265, MP4, MKV, TS 支持4K分辨率

3. 音视频录制实现

3.1 AVRecorder音频录制

AVRecorder提供高质量的音频录制功能,支持多种音频格式和参数配置。

import media from '@ohos.multimedia.media';
import fileIo from '@ohos.file.fs';
import common from '@ohos.app.ability.common';

@Entry
@Component
struct AudioRecorderDemo {
  private avRecorder: media.AVRecorder | null = null;
  @State isRecording: boolean = false;
  @State recordTime: number = 0;
  private outputPath: string = '';
  private timer: number | null = null;

  // 初始化录制器
  async aboutToAppear() {
    await this.initRecorder();
  }

  private async initRecorder() {
    try {
      const context = getContext(this) as common.Context;
      
      // 创建输出目录
      this.outputPath = context.filesDir + '/recordings/';
      await fileIo.mkdir(this.outputPath, 0o755);
      
      // 创建AVRecorder实例
      this.avRecorder = await media.createAVRecorder();
      
      // 配置录制参数
      const config: media.AVRecorderConfig = {
        audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
        outputFormat: media.OutputFormat.FORMAT_AAC_ADTS,
        audioEncoder: media.AudioEncoder.AUDIO_ENCODER_AAC,
        audioSampleRate: 44100,
        audioChannels: 2,
        audioBitrate: 128000
      };
      
      await this.avRecorder.prepare(config);
      
    } catch (error) {
      console.error('录制器初始化失败: ', JSON.stringify(error));
    }
  }

  // 开始录制
  private async startRecording() {
    if (!this.avRecorder) return;
    
    try {
      // 生成唯一文件名
      const fileName = `recording_${Date.now()}.aac`;
      const fullPath = this.outputPath + fileName;
      
      await this.avRecorder.start(fullPath);
      this.isRecording = true;
      
      // 开始计时
      this.recordTime = 0;
      this.timer = setInterval(() => {
        this.recordTime += 1;
      }, 1000);
      
    } catch (error) {
      console.error('开始录制失败: ', JSON.stringify(error));
    }
  }

  // 停止录制
  private async stopRecording() {
    if (!this.avRecorder) return;
    
    try {
      await this.avRecorder.stop();
      this.isRecording = false;
      
      // 停止计时
      if (this.timer) {
        clearInterval(this.timer);
        this.timer = null;
      }
      
      console.log('录制完成,文件保存于: ' + this.outputPath);
      
    } catch (error) {
      console.error('停止录制失败: ', JSON.stringify(error));
    }
  }

  build() {
    Column({ space: 10 }) {
      Text('音频录制演示')
        .fontSize(20)
        .margin(10)
      
      Text(`录制时间: ${this.recordTime}秒`)
        .fontSize(16)
        .margin(5)
      
      Button(this.isRecording ? '停止录制' : '开始录制')
        .onClick(() => {
          if (this.isRecording) {
            this.stopRecording();
          } else {
            this.startRecording();
          }
        })
        .width(200)
        .margin(10)
    }
    .width('100%')
    .height('100%')
  }
}

3.2 屏幕录制(AVScreenCapture)

对于需要录制屏幕的场景,可以使用AVScreenCapture模块:

import { BusinessError } from '@ohos.base';

class ScreenRecorder {
  private capture: any = null;
  private isRecording: boolean = false;

  // 初始化屏幕录制
  async initialize() {
    try {
      // 创建屏幕录制实例
      this.capture = await media.createAVScreenCapture();
      
      // 配置录制参数
      const config: media.AVScreenCaptureConfig = {
        captureMode: media.CaptureMode.CAPTURE_HOME_SCREEN,
        dataType: media.DataType.ORIGINAL_STREAM,
        audioInfo: {
          micCapInfo: {
            audioSampleRate: 48000,
            audioChannels: 2,
            audioSource: media.AudioSource.MIC
          }
        },
        videoInfo: {
          videoCapInfo: {
            videoFrameWidth: 1280,
            videoFrameHeight: 720,
            videoSource: media.VideoSource.SURFACE_RGBA
          }
        }
      };
      
      await this.capture.init(config);
      await this.capture.setMicrophoneEnabled(true);
      
    } catch (error) {
      console.error('屏幕录制初始化失败: ', (error as BusinessError).message);
    }
  }

  // 开始屏幕录制
  async startRecording(outputPath: string) {
    if (!this.capture) return;
    
    try {
      await this.capture.start(outputPath);
      this.isRecording = true;
    } catch (error) {
      console.error('开始屏幕录制失败: ', (error as BusinessError).message);
    }
  }

  // 停止屏幕录制
  async stopRecording() {
    if (!this.capture) return;
    
    try {
      await this.capture.stop();
      this.isRecording = false;
    } catch (error) {
      console.error('停止屏幕录制失败: ', (error as BusinessError).message);
    }
  }
}

4. 权限管理

多媒体功能需要相应的权限声明和使用时动态申请。

4.1 权限声明

module.json5文件中声明所需权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "$string:microphone_permission_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.CAPTURE_SCREEN",
        "reason": "$string:screen_capture_permission_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      }
    ]
  }
}

4.2 动态权限申请

import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';
import { BusinessError } from '@ohos.base';

async function checkAndRequestPermission(permission: string, context: common.Context): Promise<boolean> {
  try {
    const atManager = abilityAccessCtrl.createAtManager();
    const bundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    const tokenId = bundleInfo.appInfo.accessTokenId;
    
    // 检查权限状态
    const grantStatus = await atManager.checkAccessToken(tokenId, permission);
    
    if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
      return true;
    }
    
    // 申请权限
    const requestResult = await atManager.requestPermissionsFromUser(context, [permission]);
    return requestResult.authResults[0] === 0;
    
  } catch (error) {
    console.error('权限检查失败: ', (error as BusinessError).message);
    return false;
  }
}

5. 性能优化与最佳实践

5.1 内存管理

  • 及时释放不再使用的播放器/录制器实例
  • 使用合适的视频分辨率和码率以减少内存占用
  • 实现资源预加载和缓存机制

5.2 能耗优化

  • 在后台时暂停不必要的媒体播放
  • 使用合适的编码参数平衡质量和能耗
  • 实现自适应码率调整以适应网络条件

5.3 错误处理

class MediaErrorHandler {
  static handleAVPlayerError(error: BusinessError): void {
    switch (error.code) {
      case 5400103:
        console.error('媒体资源格式不支持');
        break;
      case 5400104:
        console.error('网络连接失败');
        break;
      case 5400105:
        console.error('解码失败');
        break;
      default:
        console.error('未知错误: ', error.message);
    }
  }
  
  static handleRecorderError(error: BusinessError): void {
    // 录制错误处理逻辑
  }
}

6. 实战案例:简易视频播放器

以下是一个完整的视频播放器实现示例:

import media from '@ohos.multimedia.media';
import common from '@ohos.app.ability.common';

@Entry
@Component
struct SimpleVideoPlayer {
  private avPlayer: media.AVPlayer | null = null;
  @State isPlaying: boolean = false;
  @State currentTime: number = 0;
  @State duration: number = 0;
  @State showControls: boolean = true;
  private controlsTimeout: number | null = null;

  async aboutToAppear() {
    await this.initializePlayer();
  }

  private async initializePlayer() {
    try {
      this.avPlayer = await media.createAVPlayer();
      
      // 配置播放器
      await this.avPlayer.setSource({
        uri: 'https://example.com/sample.mp4',
        mediaType: media.MediaType.VIDEO
      });
      
      await this.avPlayer.prepare();
      this.duration = await this.avPlayer.getDuration();
      
      // 设置事件监听
      this.setupEventListeners();
      
    } catch (error) {
      console.error('播放器初始化失败: ', JSON.stringify(error));
    }
  }

  private setupEventListeners() {
    if (!this.avPlayer) return;
    
    // 时间更新监听
    this.avPlayer.on('timeUpdate', (time: number) => {
      this.currentTime = time;
    });
    
    // 播放完成监听
    this.avPlayer.on('end', () => {
      this.isPlaying = false;
      this.currentTime = 0;
    });
    
    // 错误监听
    this.avPlayer.on('error', (error: BusinessError) => {
      console.error('播放错误: ', error.message);
      this.isPlaying = false;
    });
  }

  private togglePlayPause() {
    if (!this.avPlayer) return;
    
    if (this.isPlaying) {
      this.avPlayer.pause();
    } else {
      this.avPlayer.play();
    }
    this.isPlaying = !this.isPlaying;
  }

  private async seekToPosition(position: number) {
    if (!this.avPlayer) return;
    
    const validPosition = Math.max(0, Math.min(position, this.duration));
    await this.avPlayer.seek(validPosition);
    this.currentTime = validPosition;
  }

  build() {
    Column() {
      // 视频显示区域
      Video({ avPlayer: this.avPlayer })
        .width('100%')
        .height(300)
        .onClick(() => {
          this.showControls = !this.showControls;
          this.resetControlsTimer();
        })
      
      // 控制界面
      if (this.showControls) {
        Column() {
          // 进度条
          Slider({
            value: this.currentTime,
            min: 0,
            max: this.duration,
            onChange: (value: number) => this.seekToPosition(value)
          })
          .width('90%')
          
          // 控制按钮
          Row({ space: 20 }) {
            Button(this.isPlaying ? '暂停' : '播放')
              .onClick(() => this.togglePlayPause())
            
            Text(`${formatTime(this.currentTime)} / ${formatTime(this.duration)}`)
              .fontSize(14)
          }
          .margin(10)
        }
        .backgroundColor('#CC000000')
        .padding(10)
      }
    }
    .width('100%')
    .height('100%')
  }

  private resetControlsTimer() {
    if (this.controlsTimeout) {
      clearTimeout(this.controlsTimeout);
    }
    this.controlsTimeout = setTimeout(() => {
      this.showControls = false;
    }, 3000);
  }
}

7. 总结

HarmonyOS 5的多媒体框架提供了强大而灵活的API集合,支持从简单的音频播放到复杂的屏幕录制等各种场景。通过合理使用AVPlayer、AVRecorder和AVScreenCapture等组件,并结合适当的权限管理和错误处理策略,开发者可以构建出高性能、用户体验良好的多媒体应用。

关键要点总结:

  • 使用统一的媒体服务接口简化开发流程
  • 合理配置编码参数以平衡质量和性能
  • 实现完善的错误处理和用户反馈机制
  • 注重权限管理和隐私保护
  • 优化内存使用和能耗表现

网站公告

今日签到

点亮在社区的每一天
去签到