You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.8 KiB
128 lines
3.8 KiB
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// Opus 音频格式的应用类型
|
|
class OpusApplicationType {
|
|
/// 通用音频编码 (OPUS_APPLICATION_AUDIO)
|
|
static const int audio = 2048;
|
|
|
|
/// 语音编码 (OPUS_APPLICATION_VOIP)
|
|
static const int voip = 2048;
|
|
|
|
/// 低延迟编码 (OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
|
static const int restrictedLowDelay = 2051;
|
|
}
|
|
|
|
/// Opus 编解码插件,提供对 Opus 音频格式的编码和解码功能
|
|
class Opus {
|
|
static const MethodChannel _channel = MethodChannel('com.yunqiinnovation.opus');
|
|
|
|
/// 创建 Opus 编码器实例
|
|
///
|
|
/// [sampleRate] 采样率 (Hz),常用值:8000, 16000, 24000, 48000
|
|
/// [channels] 声道数,1=单声道,2=立体声
|
|
/// [application] 应用类型,默认为音频编码 [OpusApplicationType.audio]
|
|
/// [complexity] 复杂度 (0-10),数值越高质量越好,但CPU占用也更高
|
|
/// [frameSize] 编码帧大小,常用值:120, 240, 480, 960, 2880
|
|
///
|
|
/// 返回创建是否成功
|
|
static Future<bool> createEncoder({
|
|
int sampleRate = 48000,
|
|
int channels = 2,
|
|
int application = OpusApplicationType.audio,
|
|
int complexity = 10,
|
|
int frameSize = 960,
|
|
}) async {
|
|
try {
|
|
final bool? success = await _channel.invokeMethod<bool>('createEncoder', {
|
|
'sampleRate': sampleRate,
|
|
'channels': channels,
|
|
'application': application,
|
|
'complexity': complexity,
|
|
'frameSize': frameSize,
|
|
});
|
|
return success ?? false;
|
|
} on PlatformException catch (e) {
|
|
print('Failed to create Opus encoder: ${e.message}');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 创建 Opus 解码器实例
|
|
///
|
|
/// [sampleRate] 采样率 (Hz),常用值:8000, 16000, 24000, 48000
|
|
/// [channels] 声道数,1=单声道,2=立体声
|
|
///
|
|
/// 返回创建是否成功
|
|
static Future<bool> createDecoder({
|
|
int sampleRate = 48000,
|
|
int channels = 2,
|
|
}) async {
|
|
try {
|
|
final bool? success = await _channel.invokeMethod<bool>('createDecoder', {
|
|
'sampleRate': sampleRate,
|
|
'channels': channels,
|
|
});
|
|
return success ?? false;
|
|
} on PlatformException catch (e) {
|
|
print('Failed to create Opus decoder: ${e.message}');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 编码 PCM 数据为 Opus 格式
|
|
///
|
|
/// [pcmData] 16位 PCM 音频数据
|
|
///
|
|
/// 返回编码后的 Opus 数据,如果失败返回 null
|
|
static Future<Uint8List?> encodeData(Uint8List pcmData) async {
|
|
try {
|
|
final result = await _channel.invokeMethod<Uint8List>('encodeData', {
|
|
'input': pcmData,
|
|
});
|
|
return result;
|
|
} on PlatformException catch (e) {
|
|
print('Failed to encode data: ${e.message}');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 解码 Opus 数据为 PCM 格式
|
|
///
|
|
/// [opusData] Opus 编码数据
|
|
/// [frameSize] 解码帧大小,需要与编码时使用的帧大小匹配
|
|
///
|
|
/// 返回解码后的 PCM 数据,如果失败返回 null
|
|
static Future<Uint8List?> decodeData(Uint8List opusData, {int frameSize = 960}) async {
|
|
try {
|
|
final result = await _channel.invokeMethod<Uint8List>('decodeData', {
|
|
'input': opusData,
|
|
'frameSize': frameSize,
|
|
});
|
|
return result;
|
|
} on PlatformException catch (e) {
|
|
print('Failed to decode data: ${e.message}');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 释放编码器资源
|
|
static Future<void> disposeEncoder() async {
|
|
try {
|
|
await _channel.invokeMethod<void>('disposeEncoder');
|
|
} on PlatformException catch (e) {
|
|
print('Failed to dispose encoder: ${e.message}');
|
|
}
|
|
}
|
|
|
|
/// 释放解码器资源
|
|
static Future<void> disposeDecoder() async {
|
|
try {
|
|
await _channel.invokeMethod<void>('disposeDecoder');
|
|
} on PlatformException catch (e) {
|
|
print('Failed to dispose decoder: ${e.message}');
|
|
}
|
|
}
|
|
}
|