2024-08-07|閱讀時間 ‧ 約 29 分鐘

【💊 Python的解憂錦囊】使用python-ffmpeg即時串流轉換音檔格式

訊息的即時傳遞已然成為現代社會的趨勢了, 影音也是如此, 即時! 即時! 即時! 已經是目前使用者體驗的必要元素了, 在這邊我們要分享的主題是如何在python程式語言的情境下使用ffmpeg來將音檔串流的轉換格式, 為什麼會有這樣的需求呢? 因為我們處理音檔時可能會需要統一輸出的格式, 當然背後也是考量到成本、效率兩大因素, 檔案越小越好, 節省的硬體成本可以提昇企業的獲利, 而搭上「串流」的應用我們可以怎麼處理音訊的部份呢?


那麼我們今天的demo概念流程如下:


預期會設計一個讀取器, 一包一包的讀取來源音檔, 並透過python-ffmpeg轉碼, 轉換成指定的音檔格式, 最後將轉換後的封包組合並輸出成檔案。


程式碼分析與實作

這邊是我們實際運作後可行的程式碼:

import ffmpeg
import io
import os
import queue
import threading

def read_output(pipe, q):
""" 用於非阻塞讀取 ffmpeg 的 stdout """
while True:
data = pipe.read(4096)
if not data:
break
q.put(data)
q.put(None) # 標記輸出結束

def convert(input_file_path, output_file_path):
# 打開原始文件
with open(input_file_path, 'rb') as input_file:
# 創建一個 BytesIO 物件來處理轉換的輸出
output_stream = io.BytesIO()

# 使用 ffmpeg 進行轉換
process = (
ffmpeg
.input('pipe:0') # 從標準輸入讀取
.output('pipe:1', format='wav') # 輸出到標準輸出,格式為 opus
.run_async(pipe_stdin=True, pipe_stdout=True, pipe_stderr=True)
)

# 創建一個隊列來接收標準輸出
q = queue.Queue()
# 啟動一個線程來讀取標準輸出
threading.Thread(target=read_output, args=(process.stdout, q), daemon=True).start()

# 逐段讀取並寫入轉換過程
chunk_size = 4096
while True:
chunk = input_file.read(chunk_size)
if not chunk:
break
process.stdin.write(chunk)

# 關閉標準輸入流
process.stdin.close()

# 讀取轉換過來的數據並寫入 BytesIO 物件
while True:
out_chunk = q.get()
if out_chunk is None:
break
output_stream.write(out_chunk)

# 確保 process 結束
process.wait()

# 將 BytesIO 物件的內容寫入到指定的輸出文件中
with open(output_file_path, 'wb') as output_file:
output_file.write(output_stream.getvalue())

# 使用範例
convert('2.mp3', '2.wav')


接著我們用圖來分析一下上述的實作範例:

📌 關鍵重點如下:

  1. 多執行緒分別處理標準輸出與輸入。
  2. 由queue搭起ffmpeg輸出與輸入的執行緒溝通橋樑。


讓我們來檢視一下轉換前/後

我們轉出後會有以下的音檔:



接著我們使用ffprobe來檢查這兩個檔案:


ffprobe examples/2.mp3

....
Input #0, mp3, from '2.mp3':
Metadata:
Encoded by : LAME in FL Studio 20
BPM (beats per minute): 120
date : 2018
Duration: 00:00:58.02, start: 0.025057, bitrate: 146 kb/s
Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 146 kb/s
Metadata:
encoder : LAME3.100
ffprobe examples/2.wav

...
Input #0, wav, from '2.wav':
Metadata:
date : 2018
encoder : Lavf59.27.100
Duration: 00:00:57.99, bitrate: 1411 kb/s
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, 2 channels, s16, 1411 kb/s



結語

我們的分享僅將格式進行粗淺的轉換, 更多細部的參數調整還是需要您自行對ffmpeg進行設定, 這部份只是在講述如何將小封包流過ffmpeg再流到檔案的過程, 對了, 如果您對於串流應用感到興趣,也歡迎您加入「🔒 阿Han的軟體心法實戰營 - kafka專區」, 讓我們一起為「串流」的資要搭起橋樑, 讓傳輸更即時可靠。

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.