开发者社区> 长征2号> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

用 PyMedia 解码并播放 mp3 文件

简介:
+关注继续查看

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 谷歌给翻译成“合成器”,应该是这个意思吧

第二行代码创建了一个 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 个字节,保证能读到第一帧音频数据
= 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 )
复制代码


//==============================================================================



 本文转自左洸博客园博客,原文链接:http://www.cnblogs.com/myqiao/archive/2011/08/07/2129777.html,如需转载请自行联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
RedisManager使用手册(二) -- 配置文件详解
RedisManager基于SpringBoot开发,目前主要的配置都写在了application.yml文件中,通过@Value注解在代码中直接注入使用。同时还有部分定时job的配置记录在了schedule.properties文件中。
3166 0
强迫症的研究——MediaPlayer播放进度条的优化
强迫症的研究——MediaPlayer播放进度条的优化 如何做一个优美、流畅而且准确的播放进度条,也许很多人觉得很简单,但实际上,这个问题在大部分时间都被忽略了。
717 0
ant 基础及文件操作
ant是一个java程序,用来部署、发布程序。ant命令使用当前目录下的build.xml运行Ant,执行缺省的target。 若想指定执行其他文件,如b2.xml而非build.xml,可以用   ant -f b2.xml指令。 得到ant工具 官网apache上下载,解压到某个目录,然后设置环境变量。新开一个console,输入ant,有反应就对了。 build.xml的目录
994 0
Java操作Excel文件基础--Java Excel API
        Java Excel API是一个成熟的、开源的Java API,主页地址:http://jexcelapi.sourceforge.net/,通过它开发人员可以动态地读取、写入或者修改Excel文件。
1179 0
+关注
1703
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载