3

我正在使用 websockets 在 Dart/Flutter 中构建一个 Transcribe Streaming 应用程序。当我流式传输测试音频(从单声道、16kHz、16 位签名的小端 WAV 文件中提取)时,我得到...

BadRequestException:无法解码您提供的音频流。检查音频流是否有效,然后重试您的请求。

作为测试,我正在使用文件来流式传输音频。我每秒发送 32k 数据字节(大致模拟实时麦克风流)。如果我流式传输所有 0x00 或所有 0xFF 或随机字节,我什至会收到错误消息。如果我将块大小划分为 16k 并将间隔时间划分为 0.5 秒,那么它会在出错之前再多走一帧......

至于数据,我只是在 EventStream 帧的数据部分按字面意思将字节打包在文件中。显然,事件流包装是正确的(字节布局、CRC),否则我会收到一个错误指示,不是吗?

什么会向 AWSTrans 表明它不可解码?关于如何进行此操作的任何其他想法?

谢谢你的帮助...

这是打包的代码。完整版在这里(如果你敢……现在有点乱) https://pastebin.com/PKTj5xM2

Uint8List createEventStreamFrame(Uint8List audioChunk) {
  final headers = [
    EventStreamHeader(":content-type", 7, "application/octet-stream"),
    EventStreamHeader(":event-type", 7, "AudioEvent"),
    EventStreamHeader(":message-type", 7, "event")
  ];
  final headersData = encodeEventStreamHeaders(headers);
 
  final int totalLength = 16 + audioChunk.lengthInBytes + headersData.lengthInBytes;
  // final prelude = [headersData.length, totalLength];
  // print("Prelude: " + prelude.toString());
 
  // Convert a 32b int to 4 bytes
  List<int> int32ToBytes(int i) { return [(0xFF000000 & i) >> 24, (0x00FF0000 & i) >> 16, (0x0000FF00 & i) >> 8, (0x000000FF & i)]; }
 
  final audioBytes = ByteData.sublistView(audioChunk);
  var offset = 0;
  var audioDataList = <int>[];
  while (offset < audioBytes.lengthInBytes) {
    audioDataList.add(audioBytes.getInt16(offset, Endian.little));
    offset += 2;
  }
 
  final crc = CRC.crc32();
  final messageBldr = BytesBuilder();
  messageBldr.add(int32ToBytes(totalLength));
  messageBldr.add(int32ToBytes(headersData.length));
 
  // Now we can calc the CRC. We need to do it on the bytes, not the Ints
  final preludeCrc = crc.calculate(messageBldr.toBytes());
 
  // Continue adding data
  messageBldr.add(int32ToBytes(preludeCrc));
  messageBldr.add(headersData.toList());
  // messageBldr.add(audioChunk.toList());
  messageBldr.add(audioDataList);
  final messageCrc = crc.calculate(messageBldr.toBytes().toList());
  messageBldr.add(int32ToBytes(messageCrc));
  final frame = messageBldr.toBytes();
  //print("${frame.length} == $totalLength");
  return frame;
}
4

2 回答 2

1

至少在我的情况下,BadRequestException 指的是帧编码错误,而不是音频数据错误。

AWS 事件流编码详细信息在此处

我在字节序和字节大小方面遇到了一些问题。您需要对消息编码和音频缓冲区非常了解。音频需要是 16bit/signed (int)/little-endian(见这里)。消息包装器中的那些长度参数是 32 位(4 字节)大端。 ByteData是你在 Dart 的朋友。这是我更新后的代码片段:

final messageBytes = ByteData(totalLength);

...

for (var i=0; i<audioChunk.length; i++) {
  messageBytes.setInt16(offset, audioChunk[i], Endian.little);
  offset += 2;
}

请注意,16 位 int 实际上占用了 2个字节的位置。如果您未指定 Endian 样式,那么它将默认为您的系统,这将导致标头 int 编码或音频数据出错……输了!

确保一切正确的最佳方法是编写 AWS 响应所需的解码函数,然后对编码帧进行解码,看看结果是否相同。对 audo 使用测试数据,例如 [-32000, -100, 0, 200 31000] 或类似的东西,这样您就可以测试字节顺序等都是正确的。

于 2021-06-25T13:46:23.543 回答
0

这是我的建议(太长,无法发表评论)。随时告诉我更新的信息,以便我可以进一步考虑。

您能否使用 Wireshark 查看传输的数据?(不是必须的,参见下一段) 请检查它们,并查看线路上的数据(即正在传输的数据)是否有效。例如,手动记录这些数据字节并用一些音频播放器打开它。

或者,不要使用wireshark,而是将数据(您最初通过websocket传输的)写入本地文件。打开那个本地文件,看看它是否是一个有效的音频。(注意,一些音频播放器可以容忍格式错误的格式)

其次,你能不能试一下,如果你把那个原本很好的wav文件的所有字节都放在一个websocket的一个包中,它可以播放吗,还是仍然出现错误?

第三,这可能不是最佳实践……你知道,wav 是未压缩的,而且非常庞大。您可能需要 AAC 文件格式之类的东西。或者,更高级的是 OPUS 格式。它们都适用于流媒体,例如,AAC 有一种称为 ADTS 的子格式,可以打包成数据包。

于 2021-06-21T13:21:56.907 回答