Pymedia 是個 C/C++/Python 的多媒體模組,可以對包括 mp3/ogg/avi等多媒體格式檔案進行編碼解碼和播放,基於 ffmpeg 提供了簡單的 Python 介面。
一、PyMedia 的下載和安裝
官方的 PyMedia 模組已經很久沒有更新了,而且不支援最新的 Python 2.7 和 Python 3.0,使用起來是比較費勁的, 這裡有一個網址,裡麵包含了各種非官方維護的 Python 模組,其中就有支援 Python 2.7 的 PyMedia 。
非官方維護 Python 庫
頁面開啟以後,我的小紅傘報告有惡意檔案,不知道怎麼回事,直接刪除,應該沒什麼影響。下載以後安裝,一切正常。
二、讀取 Mp3 檔案
讀取 mp3 檔案就用普通的 python 函數即可,代碼如下:
data= open( u'鄺美雲 - 我和春天有個約會.mp3', 'rb' )
s= f.read(10000)
這裡要特別注意第二行代碼,它讀取了這個 mp3 檔案的前 10000 個位元組,大概 10K 左右,為什麼不是讀取前 1000 個位元組,或者全部讀取( 5M 左右)呢?
我從網上搜尋了一下,大概搞明白了是怎麼回事,這裡我們需要首先瞭解一下 Mp3 的檔案結構
三、Mp3 檔案結構
MP3檔案大體分為三部分:TAG_V2(ID3V2),Frame, TAG_V1(ID3V1) 。
音頻資料是一幀一幀儲存, 一幀資料的長度為 4 個位元組,而 Frame 正是儲存音頻資料幀的部分。
而檔案頭部和檔案尾部則儲存了一些其他資訊資料,如專輯名稱、歌手、年代等等,這些資料並不能被解碼為音頻。
對於 PyMedia 來說,至少要讀出檔案中的第一幀音頻資料才行,多讀取幾幀沒關係,但是第一幀必須讀取出來,否則後面的程式就沒辦法運行了。
至於第一幀資料從什麼地方開始,這個是不確定的,所以第一次讀取一般多讀一點,保證第一幀能被讀取到。
那麼有沒有辦法來確認下一第一幀是否被讀取到了呢?繼續往下看
四、解析出音頻資料幀
讀取出前 10000 個位元組的資料後,我們就可以把其中的音頻資料幀解析出來,然後播放,代碼如下:
1 import pymedia.muxer as muxer
2 dm= muxer.Demuxer('mp3')
3 frames= dm.parse( data )
4 print len(frames)
第一行代碼匯入 muxer 模組,muxer Google給翻譯成“合成器”,應該是這個意思吧
第二行代碼建立了一個 Mp3 的 Demuxer 對象 dm
第三行代碼是重點,它從我們讀取的第一段 10000 個位元組的資料中,解析出最初的幾幀,並將這些資料幀存放到 frames 數組中
第四行代碼測試了 frames 數組的長度,就可以知道 10000 個位元組中到底包含了幾幀音頻資料。
對於這首歌而言是 2 幀, 如果前面一下讀取了全部資料,這裡會顯示 1103 ,即這個檔案一共包含 1103 幀音頻資料。
五、設定解碼器
這裡要注意一下,解碼和解析可不是一回事,不要搞混了。看代碼:
1 import pymedia.audio.acodec as acodec
2 dec= acodec.Decoder( dm.streams[ 0 ] )
第一行代碼匯入解碼器模組
第二行代碼產生解碼器對象,傳入了一個參數 dm.streams[0]
這裡解釋一下,我們在前面解析資料的時候,dm 對象根據資料內容中自動產生了 dm.streams 數組,其實這數組中就包含一個元素,就是 dm.streams[0],他的內容如下:
{'index': 0, 'block_align': 0, 'type': 1, 'frame_rate_base': 1, 'height': 0, 'channels': 0,'width': 0, 'length': -2077252342, 'sample_rate': 0, 'frame_rate':25, 'bitrate': 0, 'id': 86016}
說白了就是 mp3 檔案的檔案資訊和編碼資訊,在這裡,這個參數我們是從檔案資料中解析出來的,其實我們自己設定也行,像下面這樣:
1 params = {'id': acodec.getCodecID('mp3'), 'bitrate': 128000, 'sample_rate': 44100, 'ext': 'mp3', 'channels': 2}
2 dec= acodec.Decoder(params)六、解碼第一幀音頻資料,並建立音訊輸出對象
有瞭解碼器以後,我們就可以首先解碼第一幀音頻資料,並建立音訊輸出對象,代碼如下:
1 #4.解碼第一幀音頻資料,並建立輸出對象
2 frame = frames[0]
3 #音頻資料在 frame 數組的第二個元素中
4 r= dec.decode( frame[ 1 ] )
5 print "sample_rate:%s , channels:%s " % (r.sample_rate,r.channels)
6 import pymedia.audio.sound as sound
7 snd= sound.Output( r.sample_rate, r.channels, sound.AFMT_S16_LE )
第一行代碼:前面我們說過,frames 數組中存放了最初的幾幀音頻資料,frames[0] 就是第一幀音頻資料
第二行代碼:嚴格來說 frames[0] 也是一個數組,它包含五個元素,其中第二個元素 frames[0][1]才是真正的音頻資料,這隻是 PyMedia 的一個設計,和 Mp3 音訊框架的資料結構沒關係
第六行、第七行代碼,建立了一個音訊輸出對象
七、播放、讀取、解碼......迴圈下去
現在我們有了音訊輸出對象,有了第一幀解碼後的資料,就可以直接播放了
#6.播放
if r: snd.play( r.data )
然後繼續讀取資料、解碼、播放,後面的步驟就簡單了,PyMedia 會自動找出資料幀
#7.繼續讀取、解碼、播放
while True:
data= f.read(512)
if len(data)>0:
r= dec.decode( data )
if r: snd.play( r.data )
else:
break
當讀取完最後一幀資料資料以後,要讓程式延時一會在退出,否則最後一幀資料不會被播放出來,程式就結束了
1 import time
2 while snd.isPlaying(): time.sleep( .5 )完整的代碼#1.二進位方法讀取前 10000 個位元組,保證能讀到第一幀音頻資料
f = open( u'鄺美雲 - 我和春天有個約會.mp3', 'rb' )
data= f.read(10000)
#2.建立合成器對象,解析出最初的幾幀音頻資料
import pymedia.muxer as muxer
dm = muxer.Demuxer('mp3')
frames = dm.parse( data )
print len(frames)
#3.根據解析出來的 Mp3 編碼資訊,建立解碼器對象
import pymedia.audio.acodec as acodec
dec = acodec.Decoder( dm.streams[ 0 ] )
#像下面這樣也行
#params = {'id': acodec.getCodecID('mp3'), 'bitrate': 128000, 'sample_rate': 44100, 'ext': 'mp3', 'channels': 2}
#dec= acodec.Decoder(params)
#4.解碼第一幀音頻資料
frame = frames[0]
#音頻資料在 frame 數組的第二個元素中
r= dec.decode( frame[ 1 ] )
print "sample_rate:%s , channels:%s " % (r.sample_rate,r.channels)
#注意:這一步可以直接解碼 r=dec.decode( data),而不用讀出第一幀音頻資料
#但是開始會有一下噪音,如果是網路流純音頻資料,不包含標籤資訊,則不會出現雜音
#5.建立音訊輸出對象
import pymedia.audio.sound as sound
snd = sound.Output( r.sample_rate, r.channels, sound.AFMT_S16_LE )
#6.播放
if r: snd.play( r.data )
#7.繼續讀取、解碼、播放
while True:
data = f.read(512)
if len(data)>0:
r = dec.decode( data )
if r: snd.play( r.data )
else:
break
#8.延時,直到播放完畢
import time
while snd.isPlaying(): time.sleep( .5 )