Flutter just_audio无法播放流式音频问题求助
问题分析与修复方案
你的分块加载音频无法播放,核心问题出在MyCustomSource的流处理逻辑和参数设置上,以下是具体问题和修复方案:
主要问题点
- 初始数据未传入流:
request方法生成的byteStream没有包含第一块已加载的initialBytes,播放器启动时无可用数据初始化解码器。 - 错误设置固定源长度:分块加载时音频总长度未知,
sourceLength设为当前bytes.length会导致播放器误判音频已结束。 - 流转换逻辑错误:
handleData中没有正确传递后续音频块,而是重复截取已保存的bytes列表,导致数据流异常。
修复后的MyCustomSource代码
class MyCustomSource extends StreamAudioSource { final StreamController<List<int>> _controller = StreamController<List<int>>.broadcast(); List<int> bytes = []; bool isClosed = false; MyCustomSource() { _controller.stream.listen((chunk) { bytes.addAll(chunk); }, onDone: () { isClosed = true; print("Stream closed"); }); } void addBytes(List<int> chunk) { if (!isClosed) { _controller.add(chunk); print("Bytes added, total length: ${bytes.length}"); } } void complete() { if (!isClosed) { _controller.close(); } } @override Future<StreamAudioResponse> request([int? start, int? end]) async { start ??= 0; end ??= int.maxValue; // 用最大值表示直到流结束 // 处理初始已加载字节中符合范围的部分 final initialChunk = bytes.sublist( start, bytes.length > end ? end : bytes.length, ); // 构建包含初始数据和后续流的组合流 final byteStream = Stream.value(initialChunk) .where((chunk) => chunk.isNotEmpty) // 过滤空块 .asyncExpand((_) => _controller.stream) .transform(StreamTransformer.fromHandlers( handleData: (chunk, sink) { // 计算当前chunk中需要发送的片段 int chunkStart = 0; int chunkEnd = chunk.length; if (start >= bytes.length - chunk.length) { chunkStart = start - (bytes.length - chunk.length); chunkStart = chunkStart < 0 ? 0 : chunkStart; } if (end < bytes.length) { chunkEnd = end - (bytes.length - chunk.length); chunkEnd = chunkEnd > chunk.length ? chunk.length : chunkEnd; } if (chunkStart < chunkEnd) { sink.add(chunk.sublist(chunkStart, chunkEnd)); } start = bytes.length; // 达到指定范围则关闭流 if (start >= end) { sink.close(); } }, handleDone: (sink) => sink.close(), )); return StreamAudioResponse( sourceLength: null, // 分块加载时总长度未知,设为null contentLength: end - start, offset: start, stream: byteStream, contentType: 'audio/mpeg', ); } }
控制器代码调整
确保设置音频源后触发播放,同时优化逻辑避免重复创建资源:
// 提前初始化customSource customSource = MyCustomSource(); bool isFirstChunk = true; await for (var chunk in res!) { customSource.addBytes(chunk); audioData.addAll(chunk); if (isFirstChunk) { await player.setAudioSource(customSource); await player.play(); // 触发播放 isFirstChunk = false; } } // 所有块加载完成后关闭流 customSource.complete();
额外注意事项
- 确保第一块音频数据足够让解码器识别格式(比如MP3需包含ID3头和第一个有效帧),若第一块太小,可等待加载前2-3块再初始化播放器。
- 检查
res的分块是否正确,确保每个chunk都是有效的音频字节片段,无关键数据截断。
内容的提问来源于stack exchange,提问作者dev90
相关产品推荐
相关产品推荐

