我有一个视频通话的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的速率从队列中提取消息,有助于以与创建时相同的速率处理音频数据。语音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)我可以做些什么来提高准确性并最大限度地减少数据丢失?
(注意我已经简化了可读性的例子。指出任何事情都没有意义,因为我已经狠狠地削减了这些例子。)
答案 0 :(得分:1)
我认为你将文本拆分成块是正确的,这会导致很多词被删除。
我在发布中看到了另一个问题。在调用topic.Publish
和stdout.Reset()
之间会有一些时间过去,而ffmpeg可能会将一些未发布的字节写入stdout,这将被重置清除。
我担心这个架构不适合您的问题。消息大小的约束导致许多问题。 PubSub系统的想法是发布者通知订阅者事件,但不一定要保留大的有效负载。
你真的需要两项服务吗?您可以使用两个例程通过频道进行通信。这将消除pub子系统。
策略是使块尽可能大。一个可能的解决方案: