分割OGG流

问题描述 投票:2回答:1

我正在尝试将OGG_OPUS编码的流发送到Google语音到文本流服务。由于Google对他们的流请求有一个时间限制,因此我必须以固定的时间间隔将音频流路由到另一个Google Speech To Text流会话。

根据我的阅读,OGG流中的页面无法独立读取,因为页面中的数据是通过考虑上一页和下一页的数据来计算的。如果是这样,我们是否可以在某个时间点切断流并使用剩余的数据重新创建全新的流?停止在某个点并在新的流中发送数据是行不通的,因为初始的OGG标头包在第二个流中也不可用。

我知道可以使用PCM数据解决此问题,因为未对其进行编码,因此可以在任何点将PCM流简单地拆分为新的流。由于比特率高,我无法使用PCM流,而且我不愿使用无损质量,因为我正在传输语音数据流。

参考:https://tools.ietf.org/html/rfc7845#section-3

javascript audio google-speech-api ogg opus
1个回答
2
投票

只要文件以Streaming of Stream(BOS)标题页开头,就可以独立读取页面。您可以通过创建新文件来将一个Ogg文件拆分为多个文件,这些文件以Ogg标头页开头,之后是Ogg数据/音频页。例如,此Ogg Opus文件:

*********************************************************
*          *              *              *              *
*  Header  *  Audio Data  *  Audio Data  *  Audio Data  *
*   Page   *    Page 1    *    Page 2    *    Page 3    *
*          *              *              *              *
*********************************************************

可以分为2个文件:

***************************
*          *              *
*  Header  *  Audio Data  *
*   Page   *    Page 1    *
*          *              *
***************************

******************************************
*          *              *              *
*  Header  *  Audio Data  *  Audio Data  *
*   Page   *    Page 2    *    Page 3    *
*          *              *              *
******************************************

我在本地进行了一些测试,并成功将Ogg Opus文件拆分为多个可在Chrome浏览器中播放的文件。希望Google的服务也可以将其识别为有效的解码器。

关于可以分割并跨越多个页面的音频片段,您是正确的。我假设如果页面包含不完整的音频片段,可能会损失几毫秒(最大60ms的帧大小),但这应该太小而不会干扰语音识别。不幸的是,我的本地测试使用了opusenc util生成的Opus文件,该文件没有创建将段分割为多个页面的页面,这似乎是分割文件的好东西!

这里是一个basic,未经优化的JavaScript函数,用于查找页面边界:

// scan file buffer for Ogg Opus page boundaries
getPageBoundaries(buffer) {
  const bufferLen = buffer.byteLength
  const pageBoundaries = []

  // 32-bit page marker. see https://tools.ietf.org/html/rfc3533#page-10
  const pageMarker = new DataView((new TextEncoder().encode('OggS')).buffer).getUint32(0, true);

  const view = new DataView(buffer);
  const scanTo = bufferLen-Uint32Array.BYTES_PER_ELEMENT;

  for (let i=0; i<scanTo; i++) {
    if (pageMarker === view.getUint32(i, true)) {
      pageBoundaries.push(i)
    }
  }

  return pageBoundaries;    
}
© www.soinside.com 2019 - 2024. All rights reserved.