最近在搞小程序录音,然后使用百度接口做语音识别。
小程序目前仅支持mp3和aac编码格式。虽然百度接口提供的m4a格式支持能直接识别小程序的录音文件,但由于自己还有其他一系列需求(比如直接读取数据,根据需要进行其他处理等),我还是希望能把m4a文件转换成pcm编码的文件。
后端使用ffmpeg命令如下:
# 基于文件操作
ffmpeg -n -i input-1576685164r111.m4a -acodec pcm_s16le -f wav -ac 1 -ar 8000 output-8k-mono.wav
但是如果你用Python从网上下载一个文件,(比如我把文件存在百度对象存储,BOS),你可能更倾向于使用BytesIO这种内存文件。
这时ffmpeg基于文件的转换操作需要你建立一个临时文件,转换后再将其删除,这需要你在文件系统上进行操作,效率低而且比较麻烦。
是时候了解ffmpeg基于stdin、stdout的操作了:
# 仅输入使用pipe
cat input-1576685164r111.m4a | ffmpeg -n -i pipe: -acodec pcm_s16le -f wav -ac 1 -ar 8000 output-8k-mono.wav
# 或全使用pipe
cat input-1576685164r111.m4a | ffmpeg -n -i pipe: -acodec pcm_s16le -f wav -ac 1 -ar 8000 pipe:
使用python和BytesIO怎么操作呢?好了,我写好了:
#!/usr/bin/env python3
# coding: utf-8
#
# Created by dylanchu on 2019/12/20
from io import BytesIO
from subprocess import Popen, PIPE
def m4a2wav_bytes_io(bytes_io_file):
bytes_io_file.seek(0)
content = bytes_io_file.getvalue()
cmd = ['ffmpeg', '-n', '-i', 'pipe:', '-acodec', 'pcm_s16le', '-f', 'wav', '-ac', '1', '-ar', '8000', 'pipe:']
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
out, _ = p.communicate(input=content)
p.stdin.close()
return BytesIO(out) if out.startswith(b'RIFFxffxffxff') else None
_
省去的是error message,即ffmpeg其他的信息输出。
这几个参考链接零零碎碎提供了一些帮助:
https://stackoverflow.com/questions/20321116/can-i-pipe-a-io-bytesio-stream-to-subprocess-popen-in-python
https://stackoverflow.com/questions/49013020/scipy-io-wavfile-read-the-stdout-from-ffmpeg
https://segmentfault.com/a/1190000016652277?utm_source=tag-newest
如果你想前端使用js直接转换格式,而不是后端转换:
https://segmentfault.com/a/1190000018200927
https://segmentfault.com/a/1190000018215367