Files
VibeCoding/webrtc_controller_flutter/lib/webrtc/self_codec_decoder.dart

119 lines
3.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
/// 自编码解码器(控制端)。
///
/// 接收来自 `video` DataChannel 的二进制分片(由被控端 [SelfCodecEncoder] 产生),
/// 通过原生 Android `MediaCodec` 硬解码,并渲染到 Flutter 纹理(`Texture`)。
///
/// 为保证与 `WebRTCControlled` 的编码器在二进制协议上完全一致,协议的
/// 分片重组、H.264 参数集解析与硬解、纹理渲染全部在原生侧完成;
/// 本类仅负责与原生侧通信。对应 Android 端 `SelfCodecDecoder`。
///
/// 注意:原生解码器目前仅在 Android 平台实现。其它平台Windows / Linux /
/// Web / iOS调用 [create] 会失败并回调 [onError],调用方应回退到
/// WebRTC 默认媒体流。
class SelfCodecDecoder {
static const MethodChannel _channel =
MethodChannel('com.ttstd.fluttercontroller/self_codec');
/// 串流模式常量(与 ControlMessage.stream_mode 对齐)。
static const int streamModeWebRtc = 0;
static const int streamModeSelfCodec = 1;
int? _textureId;
bool _disposed = false;
bool _handlerSet = false;
/// 解码输出尺寸变化(宽/高,单位像素)。
final void Function(int width, int height)? onSize;
/// 错误回调(如当前平台不支持硬解)。
final void Function(String error)? onError;
SelfCodecDecoder({this.onSize, this.onError}) {
_channel.setMethodCallHandler(_handleNativeCall);
_handlerSet = true;
}
Future<dynamic> _handleNativeCall(MethodCall call) async {
switch (call.method) {
case 'onSize':
final args = call.arguments;
final w = args is Map ? (args['width'] as int? ?? 0) : 0;
final h = args is Map ? (args['height'] as int? ?? 0) : 0;
if (w > 0 && h > 0) onSize?.call(w, h);
break;
case 'onError':
final msg = call.arguments is String ? call.arguments as String : '';
if (msg.isNotEmpty) onError?.call(msg);
break;
}
}
/// 创建解码纹理。成功返回纹理 id>=0失败返回 null。
Future<int?> create() async {
if (_disposed) return null;
try {
final id = await _channel.invokeMethod<int>('create');
if (id == null || id < 0) return null;
_textureId = id;
return _textureId;
} on PlatformException catch (e) {
onError?.call('创建自编码解码纹理失败: ${e.message}');
return null;
} catch (e) {
onError?.call('创建自编码解码纹理失败: $e');
return null;
}
}
/// 已创建的纹理 id未创建或已释放时为 null。
int? get textureId => _textureId;
/// 用于渲染解码画面的纹理控件。
Widget build(BuildContext context) {
final id = _textureId;
if (id == null) return const SizedBox.shrink();
return Texture(textureId: id);
}
/// 喂入来自 video DataChannel 的二进制分片。
Future<void> feed(Uint8List chunk) async {
if (_disposed || _textureId == null) return;
try {
await _channel.invokeMethod<void>('feed', chunk);
} catch (_) {
// 解码未就绪或被释放时忽略。
}
}
/// 启用/停用解码(仅自编码模式为 true
Future<void> setEnabled(bool enabled) async {
if (_disposed || _textureId == null) return;
try {
await _channel.invokeMethod<void>('setEnabled', enabled);
} catch (_) {
// 忽略。
}
}
/// 释放原生解码资源。重复调用安全。
Future<void> dispose() async {
if (_disposed) return;
_disposed = true;
if (_handlerSet) {
_channel.setMethodCallHandler(null);
_handlerSet = false;
}
try {
await _channel.invokeMethod<void>('dispose');
} catch (_) {
// 忽略。
}
_textureId = null;
}
}