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使用urllib模块的urlopen超时问题解决方法
Nov 08 Python
浅谈Python中copy()方法的使用
May 21 Python
详解Python中类的定义与使用
Apr 11 Python
python实现随机森林random forest的原理及方法
Dec 21 Python
Python实现简单http服务器
Apr 12 Python
Python实现数据结构线性链表(单链表)算法示例
May 04 Python
python或C++读取指定文件夹下的所有图片
Aug 31 Python
Python Opencv中用compareHist函数进行直方图比较对比图片
Apr 07 Python
Pytorch 高效使用GPU的操作
Jun 27 Python
关于django python manage.py startapp 应用名出错异常原因解析
Dec 15 Python
Python数据可视化之用Matplotlib绘制常用图形
Jun 03 Python
Django drf请求模块源码解析
Jun 08 Python
tensorflow+k-means聚类简单实现猫狗图像分类的方法
python实现三阶魔方还原的示例代码
python基于opencv批量生成验证码的示例
python基于tkinter制作下班倒计时工具
Apr 28 #Python
Python爬虫之爬取哔哩哔哩热门视频排行榜
k-means & DBSCAN 总结
秀!学妹看见都惊呆的Python小招数!【详细语言特性使用技巧】
Apr 27 #Python
You might like
ASP和PHP实现生成网站快捷方式并下载到桌面的方法
2014/05/08 PHP
PHP+Javascript实现在线拍照功能实例
2015/07/18 PHP
php实现网站文件批量压缩下载功能
2015/10/28 PHP
深入解析PHP的Yii框架中的event事件机制
2016/03/17 PHP
一些有关检查数据的JS代码
2006/09/07 Javascript
JavaScript网页制作特殊效果用随机数
2007/05/22 Javascript
javascript下给元素添加事件的方法与代码
2007/08/13 Javascript
js程序中美元符号$是什么
2008/06/05 Javascript
jquery $.each()使用探讨
2013/09/23 Javascript
js取消单选按钮选中示例代码
2013/11/14 Javascript
jQuery实现瀑布流的取巧做法分享
2015/01/12 Javascript
javascript常见数据验证插件大全
2015/08/03 Javascript
jquery  实现轮播图详解及实例代码
2016/10/12 Javascript
微信小程序 POST请求(网络请求)详解及实例代码
2016/11/16 Javascript
使用原生js写ajax实例(推荐)
2017/05/31 Javascript
jQuery实现可拖动进度条实例代码
2017/06/21 jQuery
vue-router实现组件间的跳转(参数传递)
2017/11/07 Javascript
解析Vue.js中的组件
2018/02/02 Javascript
使用 vue-i18n 切换中英文效果
2018/05/23 Javascript
vue通过指令(directives)实现点击空白处收起下拉框
2018/12/06 Javascript
layui实现下拉复选功能的例子(包括数据的回显与上传)
2019/09/24 Javascript
基于vue-cli3创建libs库的实现方法
2019/12/04 Javascript
[49:02]KG vs Infamous 2019国际邀请赛淘汰赛 败者组BO1 8.20.mp4
2020/07/19 DOTA
Python脚本实现Web漏洞扫描工具
2016/10/25 Python
Python实现Dijkstra算法
2018/10/17 Python
python实现小球弹跳效果
2019/05/10 Python
Python+OpenCV图像处理——打印图片属性、设置存储路径、调用摄像头
2020/10/22 Python
用CSS3打造HTML5的Logo(实现代码)
2016/06/16 HTML / CSS
华为俄罗斯官方网上商城:购买Huawei手机和平板
2017/04/21 全球购物
比利时家具购买网站:Home24
2019/01/03 全球购物
楼面部长岗位职责范本
2014/02/14 职场文书
交通事故协议书范文
2014/10/23 职场文书
少先队大队委竞选口号
2015/12/25 职场文书
公开致歉信
2019/06/24 职场文书
创业计划书之家教托管
2019/09/25 职场文书
Python实现灰色关联分析与结果可视化的详细代码
2022/03/25 Python