Python 流媒体播放器的实现(基于VLC)


Posted in Python onApril 28, 2021

网上关于Python的音视频播放示例都集中在简单的多媒体库或者PyGame这样的游戏库,有些库使用简单,但功能单一,有些库功能丰富,支持的格式多,但使用繁琐。那有没有一种功能丰富全面又使用简单,而且还能支持流媒体播放的库呢?答案是有的。

VLC就是我们今天的主角。官网地址: 点击我

根据官网的介绍,它是一款自由、开源的跨平台多媒体播放器及框架,它全面支持绝大部分的多媒体格式,以及各类流媒体协议。也就是说,使用它既能播放本地音视频文件,也能在线播放各类流媒体资源。

这是目前全网最全面的一篇关于VLC的Python语言绑定的使用教程,本人浏览了其API文档,从文档中直接提炼出了Python语言绑定的使用方法,本篇以Windows平台为主,如果读者朋友觉得有用,请点赞支持!

环境准备

 VLC 安装

VLC实际上是比较知名的开源多媒体播放器,要使用这个库,首先需要在电脑上安装VLC,我们可以直接在上述的官网中下载并安装它,有一点需要特别注意,如果本地安装的Python是32位,则你必须下载32位的VLC,64位则下64位的VLC,必须与Python的版本对应,否则无法使用。

事实上,我并不推荐这样直接安装。试想一下,如果我们使用Python开发一个基于VLC的播放器发布出去,却要求用户在使用之前,先安装一个VLC播放器,岂不是很荒谬?那么如何将VLC集成到Python程序中来,才是问题的关键。

关于这个问题,没有找到相关资料,只能通过查看python-vlc绑定的源码来寻找方法。

安装python-vlc 绑定

VLC是纯C语言开发的框架,Python想要更简单的调用,需要安装一个python-vlc 绑定,实际上就是一个vlc.py模块,它封装了VLC动态库的接口,让我们使用更简单。

python -m pip install python-vlc

完成安装后,我们在site-packages中找到vlc.py源码,查看其对VLC动态库的加载代码,可以发现,在Windows系统上,vlc.py是通过查询Windows注册表的方式来搜索路径并加载VLCdll动态库的。但它其中也提供了一个配置环境变量PYTHON_VLC_MODULE_PATH的加载方式,这样我们就能在尽可能不修改vlc.py源码的前提下完成VLC动态库的集成。

好了,到这里,我们只需要去下载一个VLC的绿色免安装版本即可。由于我的Python环境是64位,这里给出一个Windows 64位下载地址:点我 选择vlc-3.0.6-win64.7z即可

下载完成后,解压目录,进入其中,删除无关内容,保留如下文件

Python 流媒体播放器的实现(基于VLC)

其中plugins中的内容非常多,达到122M,我们可以根据实际情况进行剪裁,例如我们只需要做一个音频播放器,则可将其中的video相关的文件夹删除,还包括gui文件夹,因为我们要自己做界面,不需要gui里面的qt相关的dll。

简单播放示例

创建一个Python工程,将已经剪裁好的vlc-3.0.6文件夹拷贝到工程根目录。然后创建一个python脚本,我们对vlc.py再次封装

import os, time

# 设置VLC库路径,需在import vlc之前
os.environ['PYTHON_VLC_MODULE_PATH'] = "./vlc-3.0.6"

import vlc


class Player:
    '''
        args:设置 options
    '''
    def __init__(self, *args):
        if args:
            instance = vlc.Instance(*args)
            self.media = instance.media_player_new()
        else:
            self.media = vlc.MediaPlayer()

    # 设置待播放的url地址或本地文件路径,每次调用都会重新加载资源
    def set_uri(self, uri):
        self.media.set_mrl(uri)

    # 播放 成功返回0,失败返回-1
    def play(self, path=None):
        if path:
            self.set_uri(path)
            return self.media.play()
        else:
            return self.media.play()

    # 暂停
    def pause(self):
        self.media.pause()

    # 恢复
    def resume(self):
        self.media.set_pause(0)

    # 停止
    def stop(self):
        self.media.stop()

    # 释放资源
    def release(self):
        return self.media.release()

    # 是否正在播放
    def is_playing(self):
        return self.media.is_playing()

    # 已播放时间,返回毫秒值
    def get_time(self):
        return self.media.get_time()

    # 拖动指定的毫秒值处播放。成功返回0,失败返回-1 (需要注意,只有当前多媒体格式或流媒体协议支持才会生效)
    def set_time(self, ms):
        return self.media.get_time()

    # 音视频总长度,返回毫秒值
    def get_length(self):
        return self.media.get_length()

    # 获取当前音量(0~100)
    def get_volume(self):
        return self.media.audio_get_volume()

    # 设置音量(0~100)
    def set_volume(self, volume):
        return self.media.audio_set_volume(volume)

    # 返回当前状态:正在播放;暂停中;其他
    def get_state(self):
        state = self.media.get_state()
        if state == vlc.State.Playing:
            return 1
        elif state == vlc.State.Paused:
            return 0
        else:
            return -1

    # 当前播放进度情况。返回0.0~1.0之间的浮点数
    def get_position(self):
        return self.media.get_position()

    # 拖动当前进度,传入0.0~1.0之间的浮点数(需要注意,只有当前多媒体格式或流媒体协议支持才会生效)
    def set_position(self, float_val):
        return self.media.set_position(float_val)

    # 获取当前文件播放速率
    def get_rate(self):
        return self.media.get_rate()

    # 设置播放速率(如:1.2,表示加速1.2倍播放)
    def set_rate(self, rate):
        return self.media.set_rate(rate)

    # 设置宽高比率(如"16:9","4:3")
    def set_ratio(self, ratio):
        self.media.video_set_scale(0)  # 必须设置为0,否则无法修改屏幕宽高
        self.media.video_set_aspect_ratio(ratio)

    # 注册监听器
    def add_callback(self, event_type, callback):
        self.media.event_manager().event_attach(event_type, callback)

    # 移除监听器
    def remove_callback(self, event_type, callback):
        self.media.event_manager().event_detach(event_type, callback)

调用代码

def my_call_back(event):
    print("call:", player.get_time())


if "__main__" == __name__:
    player = Player()
    player.add_callback(vlc.EventType.MediaPlayerTimeChanged, my_call_back)
    # 在线播放流媒体视频
    player.play("http://hd.yinyuetai.com/uploads/videos/common/"
                                 "22970150925A6BB75E20D95798D129EE.flv?sc\u003d17d6a907580e9892"
                                 "\u0026br\u003d1103\u0026vid\u003d2400382\u0026aid\u003d32"
                                 "\u0026area\u003dML\u0026vst\u003d0")

    # 播放本地mp3
    # player.play("D:/abc.mp3")

    # 防止当前进程退出
    while True:
        pass

VLC 监听器

上面代码中,我们注册了MediaPlayerTimeChanged类型的监听器,表示已播放时间变化时回调,可以看到my_call_back会不断回调,因为每播放一点都会回调。

除了上述的监听器,VLC的监听器实际上非常多,常见的我们列举如下:

  1. MediaPlayerNothingSpecial:vlc处于空闲状态,只是等待发出命令
  2. MediaPlayerOpening:vlc正在打开媒体资源定位器(MRL)
  3. MediaPlayerBuffering(int cache):vlc正在缓冲
  4. MediaPlayerPlaying:vlc正在播放媒体
  5. MediaPlayerPaused:vlc处于暂停状态
  6. MediaPlayerStopped:vlc处于停止状态
  7. MediaPlayerForward:vlc通过媒体快进(这永远不会被调用)
  8. MediaPlayerBackward:vlc正在快退(这永远不会被调用)
  9. MediaPlayerEncounteredError:vlc遇到错误,无法继续
  10. MediaPlayerEndReached:vlc已到达当前播放列表的末尾
  11. MediaPlayerTimeChanged:时间发生改变
  12. MediaPlayerPositionChanged:进度发生改变
  13. MediaPlayerSeekableChanged:流媒体是否可搜索的状态发生改变(true表示可搜索,false表示不可搜索)
  14. MediaPlayerPausableChanged:媒体是否可暂停状态发生改变(true表示可暂停,false表示不可暂停)
  15. MediaPlayerMediaChanged : 媒体发生改变
  16. MediaPlayerTitleChanged: 标题发生改变(DVD/Blu-ray)
  17. MediaPlayerChapterChanged :章节发生改变(DVD/Blu-ray)
  18. MediaPlayerLengthChanged :(在vlc版本<2.2.0仅适用于Mozilla)长度已更改
  19. MediaPlayerVout :视频输出的数量发生改变
  20. MediaPlayerMuted :静音
  21. MediaPlayerUnmuted :取消静音
  22. MediaPlayerAudioVolume :音量发生改变

要查看全部支持的监听器,请访问 官方文档 并搜索EventType类型查看

视频加字幕

在我们上述封装的Player类中添加如下方法

def set_marquee(self):
        self.media.video_set_marquee_int(vlc.VideoMarqueeOption.Enable, 1)
        self.media.video_set_marquee_int(vlc.VideoMarqueeOption.Size, 28)
        self.media.video_set_marquee_int(vlc.VideoMarqueeOption.Color, 0xff0000)
        self.media.video_set_marquee_int(vlc.VideoMarqueeOption.Position, vlc.Position.Bottom)
        self.media.video_set_marquee_int(vlc.VideoMarqueeOption.Timeout, 0)
        self.media.video_set_marquee_int(vlc.VideoMarqueeOption.Refresh, 10000)

    def update_text(self, content):
        self.media.video_set_marquee_string(vlc.VideoMarqueeOption.Text, content)

创建调用代码


 

video_set_marquee_string函数不仅支持直接传入字符串,还支持"%Y-%m-%d %H:%M:%S"这种时间格式,运行上述代码后,会在屏幕下方显示当前时间,且每一秒刷新一次。

关于文本的一些属性设置

  • VideoMarqueeOption.Color :文本颜色,值为16进制数
  • VideoMarqueeOption.Enable:是否开启文本显示,1表示开启
  • VideoMarqueeOption.Opacity:文本透明度,0 透明,255 完全不透明
  • VideoMarqueeOption.Position:文本显示的位置
  • VideoMarqueeOption.Refresh:字符串刷新的间隔(毫秒)对时间格式字串刷新有用
  • VideoMarqueeOption.Size:文字大小,单位像素
  • VideoMarqueeOption.Text:要显示的文本内容
  • VideoMarqueeOption.Timeout:文本停留时间。0表示永远停留(毫秒值)
  • VideoMarqueeOption.marquee_X:设置显示文本的x坐标值
  • VideoMarqueeOption.marquee_Y:设置显示文本的y坐标值

上面的示例仅仅显示了一个固定的时间字符串,下面我们看一下如何显示连续的字幕

if "__main__" == __name__:
    player = Player("--sub-source=marq")

    player.play("http://hd.yinyuetai.com/uploads/videos/common/"
                                 "22970150925A6BB75E20D95798D129EE.flv?sc\u003d17d6a907580e9892"
                                 "\u0026br\u003d1103\u0026vid\u003d2400382\u0026aid\u003d32"
                                 "\u0026area\u003dML\u0026vst\u003d0")
    player.set_marquee()


    i = 0
    while True:
        # 字幕每2秒刷新一条
        time.sleep(2)
        player.update_text("我是字幕君 "+str(i))
        i += 1

VLC的选项参数设置

'''
        args:设置 options
    '''

    def __init__(self, *args):
        if args:
            instance = vlc.Instance(*args)
            self.media = instance.media_player_new()
        else:
            self.media = vlc.MediaPlayer()

我们在封装时,特意预留了选项参数的设置,上面添加字幕时,用到了"--sub-source=marq"参数,实际上VLC有非常多的参数,关于各种参数的详细介绍,可以查看 官方资料
如果看英文太累,这里还有一份 中文版参数详解

音频可视化

VLC众多参数中,有一个非常有用的功能,那就是显示音频的可视化。这里我们以频谱为例

if "__main__" == __name__:
    player = Player("--audio-visual=visual", "--effect-list=spectrum", "--effect-fft-window=flattop")

    player.play("https://api.mlwei.com/music/api/wy/?key=523077333&cache=1&type=url&id=566442496")

    while True:
        pass

Python 流媒体播放器的实现(基于VLC)

--effect-list=<字符串>

当前可用的效果包括: dummy、scope、spectrum(频谱)、spectrometer(频谱仪)与vuMeter

--effect-fft-window=

可选的值{none,hann,flattop,blackmanharris,kaiser}

在Tkinter中嵌入视频

上面的测试代码都是在命令行执行的,虽然运行后启动了一个窗口渲染视频,但是我们无法进行暂停、快进、退出、设置音量等操作,这是因为我们没有写GUI程序,而tkinter作为Python犀利的图形程序库,可以帮助我们快速构建一个界面程序。

完整示例代码如下

import os, platform

# 设置VLC库路径,需在import vlc之前
os.environ['PYTHON_VLC_MODULE_PATH'] = "./vlc-3.0.6"

import vlc


class Player:
    '''
        args:设置 options
    '''

    def __init__(self, *args):
        if args:
            instance = vlc.Instance(*args)
            self.media = instance.media_player_new()
        else:
            self.media = vlc.MediaPlayer()

    # 设置待播放的url地址或本地文件路径,每次调用都会重新加载资源
    def set_uri(self, uri):
        self.media.set_mrl(uri)

    # 播放 成功返回0,失败返回-1
    def play(self, path=None):
        if path:
            self.set_uri(path)
            return self.media.play()
        else:
            return self.media.play()

    # 暂停
    def pause(self):
        self.media.pause()

    # 恢复
    def resume(self):
        self.media.set_pause(0)

    # 停止
    def stop(self):
        self.media.stop()

    # 释放资源
    def release(self):
        return self.media.release()

    # 是否正在播放
    def is_playing(self):
        return self.media.is_playing()

    # 已播放时间,返回毫秒值
    def get_time(self):
        return self.media.get_time()

    # 拖动指定的毫秒值处播放。成功返回0,失败返回-1 (需要注意,只有当前多媒体格式或流媒体协议支持才会生效)
    def set_time(self, ms):
        return self.media.get_time()

    # 音视频总长度,返回毫秒值
    def get_length(self):
        return self.media.get_length()

    # 获取当前音量(0~100)
    def get_volume(self):
        return self.media.audio_get_volume()

    # 设置音量(0~100)
    def set_volume(self, volume):
        return self.media.audio_set_volume(volume)

    # 返回当前状态:正在播放;暂停中;其他
    def get_state(self):
        state = self.media.get_state()
        if state == vlc.State.Playing:
            return 1
        elif state == vlc.State.Paused:
            return 0
        else:
            return -1

    # 当前播放进度情况。返回0.0~1.0之间的浮点数
    def get_position(self):
        return self.media.get_position()

    # 拖动当前进度,传入0.0~1.0之间的浮点数(需要注意,只有当前多媒体格式或流媒体协议支持才会生效)
    def set_position(self, float_val):
        return self.media.set_position(float_val)

    # 获取当前文件播放速率
    def get_rate(self):
        return self.media.get_rate()

    # 设置播放速率(如:1.2,表示加速1.2倍播放)
    def set_rate(self, rate):
        return self.media.set_rate(rate)

    # 设置宽高比率(如"16:9","4:3")
    def set_ratio(self, ratio):
        self.media.video_set_scale(0)  # 必须设置为0,否则无法修改屏幕宽高
        self.media.video_set_aspect_ratio(ratio)

    # 设置窗口句柄
    def set_window(self, wm_id):
        if platform.system() == 'Windows':
            self.media.set_hwnd(wm_id)
        else:
            self.media.set_xwindow(wm_id)

    # 注册监听器
    def add_callback(self, event_type, callback):
        self.media.event_manager().event_attach(event_type, callback)

    # 移除监听器
    def remove_callback(self, event_type, callback):
        self.media.event_manager().event_detach(event_type, callback)



import tkinter as tk


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.player = Player()
        self.title("流媒体播放器")
        self.create_video_view()
        self.create_control_view()

    def create_video_view(self):
        self._canvas = tk.Canvas(self, bg="black")
        self._canvas.pack()
        self.player.set_window(self._canvas.winfo_id())

    def create_control_view(self):
        frame = tk.Frame(self)
        tk.Button(frame, text="播放", command=lambda: self.click(0)).pack(side=tk.LEFT, padx=5)
        tk.Button(frame, text="暂停", command=lambda: self.click(1)).pack(side=tk.LEFT)
        tk.Button(frame, text="停止", command=lambda: self.click(2)).pack(side=tk.LEFT, padx=5)
        frame.pack()

    def click(self, action):
        if action == 0:
            if self.player.get_state() == 0:
                self.player.resume()
            elif self.player.get_state() == 1:
                pass  # 播放新资源
            else:
                self.player.play("http://hd.yinyuetai.com/uploads/videos/common/"
                                 "22970150925A6BB75E20D95798D129EE.flv?sc\u003d17d6a907580e9892"
                                 "\u0026br\u003d1103\u0026vid\u003d2400382\u0026aid\u003d32"
                                 "\u0026area\u003dML\u0026vst\u003d0")
        elif action == 1:
            if self.player.get_state() == 1:
                self.player.pause()
        else:
            self.player.stop()


if "__main__" == __name__:
    app = App()
    app.mainloop()

Python 流媒体播放器的实现(基于VLC)

最后说一点,如果我们在创建Player时,指定音频可视化参数,如下,则当播放音频时,self._canvas中将显示音频可视化频谱。

player = Player("--audio-visual=visual", 
"--effect-list=spectrum", "--effect-fft-window=flattop")

跨平台

如果我们想用Python开发跨平台的播放器,在Linux系统中,不推荐集成VLC二进制文件,我们可以有两种思路,Ubuntu中,我们可以通过调用命令在线安装vlc

sudo apt-get install vlc

另一种思路则是集成VLC源码,调用系统的编译命令进行编译。通常Linux平台都会带有gcc编译器和make构建工具。该方案同样适用于Mac os平台。

音频播放器项目

Python 流媒体播放器的实现(基于VLC)

博主基于VLC编写的简单音频播放器,可支持本地音频文件以及在线流媒体播放,在线接口使用网易云音乐。传送门

对tkinter的界面程序开发感兴趣的朋友,可观看博主的tkinter从入门到实战视频
通过该播放器项目对tkinter界面编程进行详细讲解,突出tkinter使用中的各种坑与细节

到此这篇关于Python 流媒体播放器的实现(基于VLC)的文章就介绍到这了,更多相关Python 流媒体播放器内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python运算符重载详解及实例代码
Mar 07 Python
pytorch: tensor类型的构建与相互转换实例
Jul 26 Python
使用numba对Python运算加速的方法
Oct 15 Python
Linux 修改Python命令的方法示例
Dec 03 Python
django 快速启动数据库客户端程序的方法示例
Aug 16 Python
如何基于pythonnet调用halcon脚本
Jan 20 Python
flask框架自定义url转换器操作详解
Jan 25 Python
TensorFlow keras卷积神经网络 添加L2正则化方式
May 22 Python
python读取xml文件方法解析
Aug 04 Python
详解python tkinter 图片插入问题
Sep 03 Python
python 用struct模块解决黏包问题
Nov 07 Python
Python爬虫之Selenium多窗口切换的实现
Dec 04 Python
tensorflow+k-means聚类简单实现猫狗图像分类的方法
python实现三阶魔方还原的示例代码
python基于opencv批量生成验证码的示例
python基于tkinter制作下班倒计时工具
Apr 28 #Python
Python爬虫之爬取哔哩哔哩热门视频排行榜
k-means & DBSCAN 总结
秀!学妹看见都惊呆的Python小招数!【详细语言特性使用技巧】
Apr 27 #Python
You might like
《OVERLORD》手游英文版即将上线 手机上也能扮演骨王
2020/04/09 日漫
PHP执行批量mysql语句的解决方法
2013/05/02 PHP
ThinkPHP上使用多说评论插件的方法
2014/10/31 PHP
PHP实现的链式队列结构示例
2017/09/15 PHP
从面试题学习Javascript 面向对象(创建对象)
2012/03/30 Javascript
JS连连看源码完美注释版(推荐)
2013/12/09 Javascript
js限制文本框只能输入中文的方法
2015/08/11 Javascript
javascript实现九宫格相加数值相等
2020/05/28 Javascript
JS中sort函数排序用法实例分析
2016/06/16 Javascript
JavaScript设计模式之单体模式全面解析
2016/09/09 Javascript
Bootstrap实现导航栏的2种方式
2016/11/28 Javascript
JS使用正则截取两个字符串之间的字符串实现方法详解
2017/01/06 Javascript
javascript实现table单元格点击展开隐藏效果(实例代码)
2017/04/10 Javascript
用POSTMAN发送JSON格式的POST请求示例
2018/09/04 Javascript
angularJs使用ng-repeat遍历后选中某一个的方法
2018/09/30 Javascript
微信小程序实现笑脸评分功能
2018/11/03 Javascript
JavaScript数组特性与实践应用深入详解
2018/12/30 Javascript
JS拖拽排序插件Sortable.js用法实例分析
2019/02/20 Javascript
Vue组件实现触底判断
2019/06/26 Javascript
基于Vant UI框架实现时间段选择器
2020/12/24 Javascript
python中的sort方法使用详解
2014/07/25 Python
Python3里的super()和__class__使用介绍
2015/04/23 Python
python执行get提交的方法
2015/04/29 Python
Opencv+Python 色彩通道拆分及合并的示例
2018/12/08 Python
python-Web-flask-视图内容和模板知识点西宁街
2019/08/23 Python
Python类的动态绑定实现原理
2020/03/21 Python
用python写爬虫简单吗
2020/07/28 Python
html5 canvas 简单画板实现代码
2012/01/05 HTML / CSS
使用 HTML5 Canvas 制作水波纹效果点击图片就会触发
2014/09/15 HTML / CSS
企业军训感言
2014/02/08 职场文书
募捐倡议书怎么写
2014/05/14 职场文书
安全责任书范文
2014/08/25 职场文书
党员干部公开承诺书范文
2015/04/27 职场文书
《钓鱼的启示》教学反思
2016/02/18 职场文书
使用canvas实现雪花飘动效果的示例代码
2021/03/30 HTML / CSS
Golang 1.18 多模块Multi-Module工作区模式的新特性
2022/04/11 Golang