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.
221 lines
5.0 KiB
221 lines
5.0 KiB
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// OPUS解码参数选项
|
|
class OpusOption {
|
|
/// 是否具有协议头
|
|
final bool hasHeader;
|
|
|
|
/// 通道数。取值范围:[1, 2]
|
|
final int channel;
|
|
|
|
/// 采样率。参考值:{8000, 16000, 24000, 32000, 44100, 48000}
|
|
final int sampleRate;
|
|
|
|
/// 数据包长度。仅hasHeader = false时生效。
|
|
final int packetSize;
|
|
|
|
/// 构造函数
|
|
OpusOption({
|
|
this.hasHeader = false,
|
|
this.channel = 1,
|
|
this.sampleRate = 16000,
|
|
this.packetSize = 40,
|
|
});
|
|
|
|
/// 转换为参数Map
|
|
Map<String, dynamic> toMap() {
|
|
return {
|
|
'hasHeader': hasHeader,
|
|
'channel': channel,
|
|
'sampleRate': sampleRate,
|
|
'packetSize': packetSize,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// 杰理OPUS编解码器事件
|
|
class OpusEvent {
|
|
/// 事件类型
|
|
final String event;
|
|
|
|
/// 数据类型 (file或stream)
|
|
final String? type;
|
|
|
|
/// 解码后的数据 (仅在流解码时有效)
|
|
final Uint8List? data;
|
|
|
|
/// 输出文件路径 (仅在文件解码完成时有效)
|
|
final String? filePath;
|
|
|
|
/// 错误码 (仅在发生错误时有效)
|
|
final int? errorCode;
|
|
|
|
/// 错误信息 (仅在发生错误时有效)
|
|
final String? errorMessage;
|
|
|
|
/// 构造函数
|
|
OpusEvent({
|
|
required this.event,
|
|
this.type,
|
|
this.data,
|
|
this.filePath,
|
|
this.errorCode,
|
|
this.errorMessage,
|
|
});
|
|
|
|
/// 从Map构造对象
|
|
factory OpusEvent.fromMap(Map<dynamic, dynamic> map) {
|
|
return OpusEvent(
|
|
event: map['event'],
|
|
type: map['type'],
|
|
data: map['data'],
|
|
filePath: map['filePath'],
|
|
errorCode: map['code'],
|
|
errorMessage: map['message'],
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 杰理OPUS编解码插件
|
|
class JlOpus {
|
|
static const MethodChannel _methodChannel =
|
|
MethodChannel('com.yunqiinnovation.jl_opus');
|
|
static const EventChannel _eventChannel =
|
|
EventChannel('com.yunqiinnovation.jl_opus/events');
|
|
static const EventChannel _eventChannel1 =
|
|
EventChannel('com.yunqiinnovation.jl_opus/events1');
|
|
|
|
/// 事件流
|
|
Stream<OpusEvent>? _eventStream;
|
|
|
|
/// 获取事件流
|
|
Stream<OpusEvent> get eventStream {
|
|
_eventStream ??= _eventChannel.receiveBroadcastStream().map((event) {
|
|
if (event is Map) {
|
|
return OpusEvent.fromMap(event);
|
|
}
|
|
throw PlatformException(
|
|
code: 'INVALID_EVENT',
|
|
message: '无效的事件数据',
|
|
);
|
|
});
|
|
return _eventStream!;
|
|
}
|
|
|
|
/// 解码后的数据流
|
|
final StreamController<Uint8List> _decodedDataController =
|
|
StreamController<Uint8List>.broadcast();
|
|
|
|
/// 获取解码数据流
|
|
Stream<Uint8List> get decodedDataStream => _decodedDataController.stream;
|
|
|
|
/// 构造函数,设置事件监听
|
|
JlOpus() {
|
|
_setupEventListener();
|
|
}
|
|
|
|
/// 设置事件监听
|
|
void _setupEventListener() {
|
|
eventStream.listen((event) {
|
|
if (event.event == 'onDecodeStream' && event.data != null) {
|
|
_decodedDataController.add(event.data!);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 初始化OPUS解码器
|
|
Future<bool> initOpusDecoder() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('initOpusDecoder');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 解码OPUS文件
|
|
///
|
|
/// [inPath] 输入文件路径
|
|
/// [outPath] 输出文件路径
|
|
/// [option] 解码参数
|
|
Future<String?> decodeOpusFile(
|
|
String inPath, String outPath, OpusOption option) async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<String>(
|
|
'decodeOpusFile',
|
|
{
|
|
'inPath': inPath,
|
|
'outPath': outPath,
|
|
...option.toMap(),
|
|
},
|
|
);
|
|
return result;
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 开始解码OPUS数据流
|
|
///
|
|
/// [option] 解码参数
|
|
Future<bool> startDecodeStream(OpusOption option) async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>(
|
|
'startDecodeStream',
|
|
option.toMap(),
|
|
);
|
|
return result ?? false;
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 停止解码OPUS数据流
|
|
Future<bool> stopDecodeStream() async {
|
|
try {
|
|
final result =
|
|
await _methodChannel.invokeMethod<bool>('stopDecodeStream');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 写入OPUS音频数据进行解码
|
|
///
|
|
/// [data] OPUS音频数据
|
|
Future<bool> writeAudioStream(Uint8List data) async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>(
|
|
'writeAudioStream',
|
|
{'data': data},
|
|
);
|
|
return result ?? false;
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 判断是否正在解码
|
|
Future<bool> isDecoding() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('isDecoding');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 释放资源
|
|
Future<bool> dispose() async {
|
|
try {
|
|
_decodedDataController.close();
|
|
final result = await _methodChannel.invokeMethod<bool>('dispose');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|