繁体   English   中英

Google Speech API + Go - 转录未知长度的音频流

[英]Google Speech API + Go - Transcribing Audio Stream of Unknown Length

我有一个视频通话的rtmp流,我想转录它。 我已经在Go中创建了2个服务,但是我得到了结果,但它不是很准确,很多数据似乎都丢失了。

让我解释。

我有一个transcode服务,我使用ffmpeg将视频转码为Linear16音频,并将输出字节放在PubSub队列上,以便处理transcribe服务。 显然,PubSub消息的大小是有限制的,我想在视频通话结束之前开始转录。 因此,我将转码后的数据分成3个第二个剪辑(不是固定长度,似乎是正确的)并将它们放入队列中。

数据转码非常简单:

var stdout Buffer

cmd := exec.Command("ffmpeg", "-i", url, "-f", "s16le", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1", "-")
cmd.Stdout = &stdout

if err := cmd.Start(); err != nil {
    log.Fatal(err)
}

ticker := time.NewTicker(3 * time.Second)

for {
    select {
    case <-ticker.C:
        bytesConverted := stdout.Len()
        log.Infof("Converted %d bytes", bytesConverted)

        // Send the data we converted, even if there are no bytes.
        topic.Publish(ctx, &pubsub.Message{
            Data: stdout.Bytes(),
        })

        stdout.Reset()
    }
}

transcribe服务以每3秒1的速率从队列中提取消息,有助于以与创建时相同的速率处理音频数据。 Speech API流有限制,它不能超过60秒,所以我停止旧流并每30秒启动一个新流,所以我们永远不会达到限制,无论视频通话持续多长时间。

这就是我的抄写方式:

stream := prepareNewStream()
clipLengthTicker := time.NewTicker(30 * time.Second)
chunkLengthTicker := time.NewTicker(3 * time.Second)

cctx, cancel := context.WithCancel(context.TODO())
err := subscription.Receive(cctx, func(ctx context.Context, msg *pubsub.Message) {

    select {
    case <-clipLengthTicker.C:
        log.Infof("Clip length reached.")
        log.Infof("Closing stream and starting over")

        err := stream.CloseSend()
        if err != nil {
            log.Fatalf("Could not close stream: %v", err)
        }

        go getResult(stream)
        stream = prepareNewStream()

    case <-chunkLengthTicker.C:
        log.Infof("Chunk length reached.")

        bytesConverted := len(msg.Data)

        log.Infof("Received %d bytes\n", bytesConverted)

        if bytesConverted > 0 {
            if err := stream.Send(&speechpb.StreamingRecognizeRequest{
                StreamingRequest: &speechpb.StreamingRecognizeRequest_AudioContent{
                    AudioContent: transcodedChunk.Data,
                },
            }); err != nil {
                resp, _ := stream.Recv()
                log.Errorf("Could not send audio: %v", resp.GetError())
            }
        }

        msg.Ack()
    }
})

我认为问题是我的3秒块不一定与短语或句子的开头和结尾对齐,所以我怀疑Speech API是一个反复出现的神经网络,它已经被训练成完整的句子而不是单个单词。 因此,在句子中间开始剪辑会丢失一些数据,因为它无法找出直到词组自然结尾的前几个单词。 此外,我在从旧流更改为新流时丢失了一些数据。 有一些背景丢失了。 我猜重叠的剪辑可能对此有所帮助。

我有一些问题:

1)这种架构是否适合我的约束(音频流的未知长度等)?

2)我可以做些什么来提高准确性并最大限度地减少数据丢失?

(注意我已经简化了可读性的例子。指出是否有任何意义没有意义,因为我一直在努力削减这些例子。)

我认为你是正确的,将文本分成块会导致许多单词被删除。

我看到出版中的另一个问题。 在调用topic.Publishstdout.Reset()会有一些时间通过,而ffmpeg可能会将一些未发布的字节写入stdout,这将被重置清除。

我担心这个架构不适合你的问题。 消息大小的约束导致许多问题。 PubSub系统的想法是发布者通知订阅者事件,但不一定要保留大的有效负载。

你真的需要两项服务吗? 您可以使用两个例程通过频道进行通信。 这将消除pub子系统。

一种策略是使块尽可能大。 可能的解决方案:

  • 使块尽可能大(接近60秒)
  • 使块重叠很短的时间(例如5秒)
  • 以编程方式检测重叠并删除它们

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM