HarmonyOS 鸿蒙Next中AVRecorder如何录制MP3格式音频?

HarmonyOS 鸿蒙Next中AVRecorder如何录制MP3格式音频? AVRecorder如何录制MP3格式音频?

3 回复

AVRecorder 支持以下音频编码格式:

  • ✅ MP3 (AUDIO_MP3)
  • AAC (AUDIO_AAC)
  • G711MU (AUDIO_G711MU)
  • AMR-NB (AUDIO_AMR_NB)
  • AMR-WB (AUDIO_AMR_WB)

MP3 录制完整示例

方案一:ArkTS 实现

import { common } from '@kit.AbilityKit';
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo as fs } from '@kit.CoreFileKit';

export class MP3Recorder {
  private avRecorder: media.AVRecorder | undefined = undefined;
  private audioFile: fs.File | undefined = undefined;

  /**
   * 初始化 MP3 录音器
   */
  async init(context: common.Context): Promise<boolean> {
    try {
      // 1. 创建 AVRecorder 实例
      this.avRecorder = await media.createAVRecorder();
      // 2. 注册状态变化回调
      this.avRecorder.on('stateChange', (state: media.AVRecorderState, reason: media.StateChangeReason) => {
        console.info(`AVRecorder state changed to ${state}, reason: ${reason}`);
      });
      // 3. 注册错误回调
      this.avRecorder.on('error', (error: BusinessError) => {
        console.error(`AVRecorder error: ${error.code}, ${error.message}`);
      });
      // 4. 创建 MP3 文件
      let path: string = context.filesDir + '/recording_' + Date.now() + '.mp3';
      this.audioFile = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
      console.info(`MP3 file created at: ${path}`);
      // 5. 配置录制参数 - 重点:MP3 格式配置
      let avProfile: media.AVRecorderProfile = {
        audioBitrate: 128000,           // 比特率 128kbps(常用:64k/128k/192k/320k)
        audioChannels: 2,               // 立体声(1=单声道,2=立体声)
        audioCodec: media.CodecMimeType.AUDIO_MP3,  // ⭐ MP3 编码
        audioSampleRate: 44100,         // 采样率 44.1kHz(常用:44100/48000)
        fileFormat: media.ContainerFormatType.CFT_MP3  // ⭐ MP3 容器格式
      };
      let avConfig: media.AVRecorderConfig = {
        audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,  // 麦克风输入
        profile: avProfile,
        url: 'fd://' + this.audioFile.fd
      };
      // 6. 准备录制
      if (this.avRecorder.state === 'idle' || this.avRecorder.state === 'stopped') {
        await this.avRecorder.prepare(avConfig);
        console.info('AVRecorder prepared for MP3 recording');
        return true;
      }
    } catch (error) {
      let err = error as BusinessError;
      console.error(`Failed to init MP3 recorder: ${err.code}, ${err.message}`);
      return false;
    }
    return false;
  }

  /**
   * 开始录制
   */
  async start(): Promise<boolean> {
    try {
      if (this.avRecorder?.state === 'prepared') {
        await this.avRecorder.start();
        console.info('MP3 recording started');
        return true;
      }
    } catch (error) {
      let err = error as BusinessError;
      console.error(`Failed to start recording: ${err.code}, ${err.message}`);
    }
    return false;
  }

  /**
   * 暂停录制
   */
  async pause(): Promise<boolean> {
    try {
      if (this.avRecorder?.state === 'started') {
        await this.avRecorder.pause();
        console.info('MP3 recording paused');
        return true;
      }
    } catch (error) {
      let err = error as BusinessError;
      console.error(`Failed to pause recording: ${err.code}, ${err.message}`);
    }
    return false;
  }

  /**
   * 恢复录制
   */
  async resume(): Promise<boolean> {
    try {
      if (this.avRecorder?.state === 'paused') {
        await this.avRecorder.resume();
        console.info('MP3 recording resumed');
        return true;
      }
    } catch (error) {
      let err = error as BusinessError;
      console.error(`Failed to resume recording: ${err.code}, ${err.message}`);
    }
    return false;
  }

  /**
   * 停止录制
   */
  async stop(): Promise<boolean> {
    try {
      if (this.avRecorder?.state === 'started' || this.avRecorder?.state === 'paused') {
        await this.avRecorder.stop();
        console.info('MP3 recording stopped');
        return true;
      }
    } catch (error) {
      let err = error as BusinessError;
      console.error(`Failed to stop recording: ${err.code}, ${err.message}`);
    }
    return false;
  }

  /**
   * 释放资源
   */
  async release(): Promise<void> {
    try {
      // 重置录制器
      if (this.avRecorder) {
        await this.avRecorder.reset();
        await this.avRecorder.release();
        this.avRecorder = undefined;
      }
      // 关闭文件
      if (this.audioFile) {
        await fs.close(this.audioFile.fd);
        this.audioFile = undefined;
      }
      console.info('MP3 recorder released');
    } catch (error) {
      let err = error as BusinessError;
      console.error(`Failed to release recorder: ${err.code}, ${err.message}`);
    }
  }

  /**
   * 获取当前状态
   */
  getState(): string {
    return this.avRecorder?.state || 'unknown';
  }
}

方案二:在 UI 组件中使用

import { MP3Recorder } from './MP3Recorder';

@Entry
@Component
struct RecordingPage {
  private recorder: MP3Recorder = new MP3Recorder();
  @State recordingState: string = 'idle';
  @State recordingTime: number = 0;
  private timer: number = -1;

  async aboutToAppear() {
    // 初始化录音器
    let context = getContext(this) as common.UIAbilityContext;
    await this.recorder.init(context);
    this.recordingState = this.recorder.getState();
  }

  aboutToDisappear() {
    // 释放资源
    this.recorder.release();
    if (this.timer !== -1) {
      clearInterval(this.timer);
    }
  }

  // 开始录音
  async startRecording() {
    let success = await this.recorder.start();
    if (success) {
      this.recordingState = 'recording';
      this.recordingTime = 0;
      // 启动计时器
      this.timer = setInterval(() => {
        this.recordingTime++;
      }, 1000);
    }
  }

  // 暂停录音
  async pauseRecording() {
    let success = await this.recorder.pause();
    if (success) {
      this.recordingState = 'paused';
      if (this.timer !== -1) {
        clearInterval(this.timer);
      }
    }
  }

  // 恢复录音
  async resumeRecording() {
    let success = await this.recorder.resume();
    if (success) {
      this.recordingState = 'recording';
      // 重启计时器
      this.timer = setInterval(() => {
        this.recordingTime++;
      }, 1000);
    }
  }

  // 停止录音
  async stopRecording() {
    let success = await this.recorder.stop();
    if (success) {
      this.recordingState = 'stopped';
      if (this.timer !== -1) {
        clearInterval(this.timer);
      }
    }
  }

  // 格式化时间显示
  formatTime(seconds: number): string {
    let min = Math.floor(seconds / 60);
    let sec = seconds % 60;
    return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
  }

  build() {
    Column() {
      Text('MP3 录音')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 20 })
      // 录音时长显示
      Text(this.formatTime(this.recordingTime))
        .fontSize(48)
        .fontWeight(FontWeight.Medium)
        .margin({ bottom: 20 })
      // 状态显示
      Text(`状态: ${this.recordingState}`)
        .fontSize(16)
        .margin({ bottom: 40 })
      // 控制按钮
      Row() {
        Button('开始')
          .onClick(() => this.startRecording())
          .enabled(this.recordingState === 'prepared')
          .margin({ right: 10 })
        Button('暂停')
          .onClick(() => this.pauseRecording())
          .enabled(this.recordingState === 'recording')
          .margin({ right: 10 })
        Button('继续')
          .onClick(() => this.resumeRecording())
          .enabled(this.recordingState === 'paused')
          .margin({ right: 10 })
        Button('停止')
          .onClick(() => this.stopRecording())
          .enabled(this.recordingState === 'recording' || this.recordingState === 'paused')
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

权限配置

不要忘记在 module.json5 中申请麦克风权限:

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

在使用前动态申请权限:

import { abilityAccessCtrl, common } from '@kit.AbilityKit';

async function requestMicrophonePermission(context: common.UIAbilityContext): Promise<boolean> {
  let atManager = abilityAccessCtrl.createAtManager();
  try {
    let data = await atManager.requestPermissionsFromUser(context, ['ohos.permission.MICROPHONE']);
    if (data.authResults[0] === 0) {
      console.info('Microphone permission granted');
      return true;
    } else {
      console.error('Microphone permission denied');
      return false;
    }
  } catch (error) {
    console.error(`Failed to request permission: ${error}`);
    return false;
  }
}

更多关于HarmonyOS 鸿蒙Next中AVRecorder如何录制MP3格式音频?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,AVRecorder目前不支持直接录制MP3格式音频。系统默认支持的音频编码格式为AAC。如需处理MP3文件,可通过MediaCodec进行音频格式转码,或使用第三方库实现MP3编码功能。开发者需在config中配置音频编码类型为AudioEncoder.AAC,录制完成后通过转换工具生成MP3文件。

在HarmonyOS Next中,目前AVRecorder暂不支持直接录制MP3格式音频。系统默认支持的音频编码格式为AAC(通过AudioEncoder.AAC_LC配置),这是移动设备上更通用的音频格式。

如果需要MP3格式输出,建议采用以下方案:

  1. 录制后转码:先使用AVRecorder录制AAC格式音频,然后通过第三方音频处理库(如LAME)将生成的AAC文件转换为MP3格式。

  2. 配置参数示例

let avRecorder: media.AVRecorder
let profile: media.AVRecorderProfile = {
  audioBitrate: 128000,
  audioChannels: 2,
  audioCodec: media.CodecMimeType.AUDIO_AAC,
  audioSampleRate: 44100,
  fileFormat: media.ContainerFormatType.CFT_MPEG_4
}
  1. 文件处理:录制完成后获取文件路径,使用转码工具处理:
// 获取录制文件
let file = fs.openSync(outputPath, fs.OpenMode.READ_WRITE)
// 调用转码接口转换为MP3

这种方案既保证了录制过程的稳定性,又能最终获得MP3格式文件。建议优先考虑AAC格式,其在保持音质的同时具有更好的设备兼容性。

回到顶部