just_audio自定义StreamAudioSource请求不足致音频播放短暂问题求助
gRPC流式音频播放问题解决思路
问题背景
采用gRPC协议做音乐字节流式传输,无法使用player.setUrl方法,将字节数据存入List<int>类型的musicBytes后,自定义了StreamAudioSource子类MyCustomSource,但request函数仅被调用两次,音频只能短暂播放,平台为Win10。
问题原因
当前实现存在两个核心问题:
sourceLength写死固定值,播放器无法获取准确的总音频长度,导致分段请求逻辑异常;- 一次性返回请求区间内的所有字节,没有实现播放器需要的分块流式返回逻辑,播放器仅能获取前两次请求的数据,后续没有持续的音频块供应。
解决方案
方案1:基于已缓存的完整字节列表实现分块播放
如果musicBytes已经缓存了完整的音频字节,修改MyCustomSource实现分块返回数据,让播放器能根据播放进度持续请求后续音频块:
import 'dart:math' as math; class MyCustomSource extends StreamAudioSource { final MusicService _musicService; // 构造函数注入服务,避免每次请求重复获取实例 MyCustomSource(this._musicService); @override Future<StreamAudioResponse> request([int? start, int? end]) async { start ??= 0; final totalLength = _musicService.musicBytes.length; end ??= totalLength; // 设置单块音频大小(示例为128KB,可根据实际调整) const chunkSize = 128 * 1024; // 计算实际返回的结束位置,避免超出总长度或请求的end值 final actualEnd = math.min(start + chunkSize, end); return StreamAudioResponse( sourceLength: totalLength, contentLength: actualEnd - start, offset: start, stream: Stream.value(_musicService.musicBytes.sublist(start, actualEnd)), contentType: 'audio/mpeg', ); } }
方案2:适配gRPC实时流式传输(未缓存完整数据)
如果gRPC是实时推送音频字节,musicBytes处于持续接收状态,需要直接对接gRPC的流,并处理可能的seek需求:
class MyCustomSource extends StreamAudioSource { final Stream<List<int>> _grpcAudioStream; final int? _totalLength; // gRPC能提前返回总长度则传入,否则为null final List<int> _cachedBytes = []; MyCustomSource(this._grpcAudioStream, this._totalLength) { // 订阅gRPC流,缓存已接收的字节 _grpcAudioStream.listen((bytes) { _cachedBytes.addAll(bytes); }); } @override Future<StreamAudioResponse> request([int? start, int? end]) async { start ??= 0; end ??= _totalLength ?? _cachedBytes.length; // 如果请求的起始位置超过已缓存长度,等待数据接收 while (start >= _cachedBytes.length) { await Future.delayed(const Duration(milliseconds: 100)); } const chunkSize = 128 * 1024; final actualEnd = math.min(start + chunkSize, end); return StreamAudioResponse( sourceLength: _totalLength, contentLength: actualEnd - start, offset: start, stream: Stream.value(_cachedBytes.sublist(start, actualEnd)), contentType: 'audio/mpeg', ); } }
关键说明
- 分块返回是核心:播放器会根据当前播放进度,持续调用
request请求下一块音频数据,确保播放不中断; - 准确的总长度:
sourceLength必须设置为真实的音频总字节数,不能写死固定值,否则播放器无法判断播放进度和剩余数据; - 实时流适配:如果是实时接收gRPC数据,需要缓存已接收的字节,并在请求时等待数据可用,保证seek功能正常。
内容的提问来源于stack exchange,提问作者Yusuf Muharrem Daşkaya
相关产品推荐
相关产品推荐

