C语言基于GTK+Libvlc实现的简易视频播放器(二)
上一次我们使用gtk+libvlc实现了一个最简单的视频播放器,可以实现点击按钮暂定和停止播放视频,以及同步显
示视频播放进度,但即使作为一个视频播放器,只有这些功能也还是不够的,至少我们还应该有全屏播放的功能吧,所以这一次我们就来为上一次的视频播放器添加
上全屏播放功能。这个功能实现起来思路很简单,只是具体实现过程中有很多坑罢了,需要我们注意很多细节问题,还要解决一些bug等等。这次我们的代码出了
增加功能之外,也还会对上一次的基础代码做一些修改。
二、功能实现思路
使用gtk提供的API函数,gtk_window_fullscreen可以将窗口设置为全屏,即隐藏标题栏、菜单栏和工具栏,同时隐藏窗口下端的视频
控制条,只保留视频播放绘图构件。在全屏状态时视频的播放控制及进度显示,由另一个单独的浮动弹出式窗体实现。通过点击浮动控制窗体上的退出全屏按钮使用
API函数gtk_window_unfullscreen可以退出全屏状态,此时再还原之前隐藏的正常模式下的控制条构件,及隐藏浮动弹出式窗体
三、要实现的功能点
- 全屏播放
- 全屏状态浮动控制条
- 无鼠标动作时浮动控制条和鼠标指针自动隐藏
- 鼠标动作激活浮动控制条和鼠标指针
- 单击播放区域暂停/播放,双击全屏/退出全屏
四、功能点具体实现
1.全屏播放
我们先实现点击全屏按钮进入全屏状态,所以要在之前的底部控制条添加一个全屏按钮,并为其绑定一个点击事件处理函数
- full_screen_button = gtk_button_new_from_icon_name("view-fullscreen", GTK_ICON_SIZE_BUTTON);
- gtk_box_pack_end(GTK_BOX(hbox), full_screen_button, FALSE, FALSE, );
- g_signal_connect(full_screen_button, "clicked", G_CALLBACK(on_full_screen), NULL);
on_full_screen事件处理函数如下:
- void on_full_screen(GtkWidget *widget, gpointer data)
- {
- // 设置正常窗体进入全屏状态
- gtk_window_fullscreen(GTK_WINDOW(window));
- // 设置一个已进入全屏状态的标志,也可以通过GDK获得窗口状态,不过会略麻烦
- is_fullscreen = TRUE;
- // 隐藏窗口底部控制条和顶部菜单栏
- gtk_widget_hide(hbox);
- gtk_widget_hide(menubar);
- // 显示进入全屏状态后的浮动控制条窗体,后面会说明该窗体的具体实现
- gtk_widget_show_all(GTK_WIDGET(ctrl_window));
- // 根据当前播放状态同步进入全屏状态后的按钮图标,原本考虑在点击按钮时同时设置控制条上的按钮图标和浮动控制条上的
- // 但因为始终有一个为隐藏状态,设置就会不成功反而会影响当前的按钮图标,故简单的改在进入全屏状态时单独进行同步
- if(libvlc_media_player_is_playing(media_player) == )
- play_icon_image = gtk_image_new_from_icon_name("media-playback-pause", GTK_ICON_SIZE_BUTTON);
- else
- play_icon_image = gtk_image_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_BUTTON);
- gtk_button_set_image(GTK_BUTTON(full_screen_pause_button), play_icon_image);
- ...
- //下面还会有与实现自动隐藏浮动控制条窗体的代码,后面会说明
on_quit_full_screen事件处理函数如下:
- void on_quit_full_screen(GtkWidget *widget, gpointer data)
- {
- gtk_window_unfullscreen(GTK_WINDOW(window));
- is_fullscreen = FALSE;
- gtk_widget_show(hbox);
- gtk_widget_show(menubar);
- gtk_widget_hide(ctrl_window);
- g_signal_handlers_block_by_func(G_OBJECT(player_widget), on_mouse_motion, NULL);
- }
2.创建一个浮动控制条窗体
因为我们是要创建一个特殊的窗体即没有边框,考虑这一点可以使用gtk的弹出窗体类型GTK_WINDOW_POPUP来创建,另外我们还需要固定该窗体的大小和位置,其上的构件基本和主窗体底部的控制条基本一致。下面我们单独使用一个函数来创建和初始化该窗体
- void control_window_init()
- {
- GtkWidget *ctrl_hbox, *quit_full_screen_button, *full_screen_stop_button;
- ctrl_window = gtk_window_new(GTK_WINDOW_POPUP);
- gtk_window_set_position(GTK_WINDOW(ctrl_window), GTK_WIN_POS_CENTER);
- //这里使用了两个宏来设置该窗体的大小
- gtk_window_set_default_size(GTK_WINDOW(ctrl_window), CTRL_WINDOW_WIDTH, CTRL_WINDOW_HEIGHT);
- g_signal_connect(ctrl_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
- ctrl_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, TRUE);
- gtk_container_add(GTK_CONTAINER(ctrl_window), ctrl_hbox);
- full_screen_pause_button = gtk_button_new_from_icon_name("media-playback-pause", GTK_ICON_SIZE_BUTTON);
- full_screen_stop_button = gtk_button_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_BUTTON);
- quit_full_screen_button = gtk_button_new_from_icon_name("view-restore", GTK_ICON_SIZE_BUTTON);
- gtk_box_pack_start(GTK_BOX(ctrl_hbox), full_screen_pause_button, FALSE, TRUE, );
- gtk_box_pack_start(GTK_BOX(ctrl_hbox), full_screen_stop_button, FALSE, TRUE, );
- gtk_box_pack_end(GTK_BOX(ctrl_hbox), quit_full_screen_button, FALSE, TRUE, );
- g_signal_connect(G_OBJECT(full_screen_pause_button), "clicked", G_CALLBACK(on_playpause), NULL);
- g_signal_connect(G_OBJECT(full_screen_stop_button), "clicked", G_CALLBACK(on_stop), NULL);
- g_signal_connect(G_OBJECT(quit_full_screen_button), "clicked", G_CALLBACK(on_quit_full_screen), NULL);
- ctrl_process_scale = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, process_adjuest);
- gtk_box_pack_start(GTK_BOX(ctrl_hbox), ctrl_process_scale, TRUE, TRUE, );
- gtk_scale_set_draw_value (GTK_SCALE(ctrl_process_scale), FALSE);
- gtk_scale_set_has_origin (GTK_SCALE(ctrl_process_scale), TRUE);
- gtk_scale_set_value_pos(GTK_SCALE(ctrl_process_scale), );
- g_signal_connect(G_OBJECT(ctrl_process_scale),"value_changed", G_CALLBACK(on_scale_value_change), NULL);
- }
3.设置浮动控制条窗体自动隐藏
还记得我们上一节实现同步进度条时用过的定时器么,g_timeout_add(),这里还是通过它来实现自动隐藏,我们设置超时5s
自动隐藏浮动窗体。但超时要从什么开始计时呢?前面已经说过了嘛,即当我们鼠标没有动作时即开始计时(准确的应该是,计时是循环计时的,我们只是通过判断
鼠标是否在动作来在计时器中设置是否隐藏)
首先我们应该解决如何知道鼠标是否移动,这就又要用到gtk/gdk的事件了,motion_notify_event事件就是在鼠标移动时被触发的,那
么我们可以为play_widget构件绑定该事件的事件处理,但是默认情况下,该构件的motion_notify_event事件是被屏蔽了的(因为
不同的构件功能不同,如果都开启所有事件,那必然会十分混乱),我们就得手动添加该事件,同时我们也开启鼠标点击事件,后面会用来实现单击播放区域暂定等
在主窗体初始化函数中添加(上一节代码的main函数中,这一节使用单独的函数创建和初始化主窗体)
- // 添加时间,打开屏蔽事件
- gtk_widget_add_events(GTK_WIDGET(player_widget), GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK);
- // 绑定事件处理
- g_signal_connect(G_OBJECT(player_widget), "motion_notify_event", G_CALLBACK(on_mouse_motion), NULL);
- // 在非全屏状态先阻塞该事件处理,进入全屏后再取消阻塞
- g_signal_handlers_block_by_func(G_OBJECT(player_widget), on_mouse_motion, NULL);
我们还要在进入全屏状态后在on_full_screen添加定时器
- g_signal_handlers_unblock_by_func(G_OBJECT(player_widget), on_mouse_motion, NULL);
- g_timeout_add(, (GSourceFunc)_hide_ctrl_window, NULL);
on_mouse_motion事件处理函数
- void on_mouse_motion(GtkWidget *widget, gpointer data)
- {
- // 设置是否移动状态标志,避免在移动过程中定时器超时将浮动窗体和鼠标箭头隐藏
- is_moving = TRUE;
- // 鼠标有动作,恢复隐藏窗体
- gtk_widget_show(GTK_WIDGET(ctrl_window));
- // 恢复鼠标箭头
- gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), cur);
- // 重新设置浮动窗体显示位置
- width = gdk_screen_get_width(gdk_screen_get_default());
- height = gdk_screen_get_height(gdk_screen_get_default());
- // 水平屏幕居中,垂直距屏幕最下端50pix
- gtk_window_move(GTK_WINDOW(ctrl_window), (width-CTRL_WINDOW_WIDTH)/, height-);
- is_moving = FALSE;
- }
_hide_ctrl_window定时器处理函数
- gboolean _hide_ctrl_window(gpointer data)
- {
- if (!is_fullscreen || is_moving ) {
- return FALSE;
- }
- // 设置鼠标箭头为GDK_BLANK_CURSOR,即空
- gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_BLANK_CURSOR));
- // 隐藏浮动控制窗体
- gtk_widget_hide(GTK_WIDGET(ctrl_window));
- return TRUE;
- }
不过这里还有一个需要注意的地方,你之前可能会发现,默认情况下,即我们没有添加自动隐藏的功能,当我们把鼠标移到播放区域时,一段时间不动它也是会被自 动隐藏的,你可能会觉得这很好啊,那我们就可以省掉gdk_window_set_cursor了。我之前就是这么想的,不过后来发现这里会有问题,如果 单单只是去掉这个鼠标是可以隐藏了,但是它也再不会出现了,除非移出播放显示区。后来发现这是libvlc的问题,vlc它本身也实现了这个鼠标箭头超时 自动隐藏的功能,你使用如下命令会发现,vlc有一个设置这个超时时间的参数
- $ vlc --help | grep mouse
但是我却没发现有禁止该功能的参数,所以没办法了,我只能给它设置一个尽可能大的超时时间值来达到禁用的功能。在代码里面我们就需要手动在创建vlc的media_player时指定参数,如下:
- const char * const vlc_args[] = {
- //libvlc 鼠标指针自动隐藏事件与gtk的“motion_notify_event”事件有冲突会导致一些问题,
- //故这里设置最大超时时间(相当于禁止vlc的该功能)
- "--mouse-hide-timeout=2147483647",//在 x 毫秒后隐藏光标和全屏控制器
- "--no-xlib"
- };
- ...
- vlc_inst = libvlc_new( ,vlc_args);
- media_player = libvlc_media_player_new(vlc_inst);
4.单击播放区域暂停/播放,双击全屏/退出全屏
这功能不用多解释,基本每个播放器都会实现
前面我们为play_widget添加了点击事件,下面只需为其绑定事件相应的处理处理即可。不过我们如何判断是单击还是双击呢?这需要通过GdkEvent来获得
首先添加事件处理
- g_signal_connect(player_widget, "button-press-event", G_CALLBACK(on_play_widget_button_press), NULL);
on_play_widget_button_press函数实现
- void on_play_widget_button_press(GtkWidget *widget, GdkEvent *event, gpointer data)
- {
- // 判断为单击还是双击
- if (event->button.type == GDK_BUTTON_PRESS){ //单击
- // 直接手动调用前面绑定到暂停按钮的事件处理函数
- on_playpause(widget, data);
- }
- else if(event->button.type == GDK_2BUTTON_PRESS){ //双击
- if(is_fullscreen)
- // 直接手动调用前面绑定到退出全屏按钮的事件处理函数
- on_quit_full_screen(widget, data);
- else
- on_full_screen(widget, data);
- }
- }
至此我们就实现了预期的全部功能点。
五、总结
这一节我们又为这个播放器增加了一些功能,但它依然很简单,但相信最为一个学习gtk的入门小项目还是够了,之后就期待感兴趣的用户能自己接着完善这个简易播放器,让其成为一个真正可以作为你日常使用的播放器(以下内容需要在实验楼的虚拟平台上运行,不添加也没有关系,是为了帮助童鞋们更方便学习和理解)
本节完整代码git
- $ git <span class="hljs-keyword">clone</span> -b full_screen https:<span class="hljs-comment">//github.com/shiyanlou/gtk-vlc-video-player.git</span>
演示视频
- <span class="hljs-title">wget</span> <span class="hljs-url">http://anything-about-doc.qiniudn.com/gtk_libvlc_video_player/video_demo_02.mp4</span>
有更多基础课、项目课欢迎大家登陆实验楼官方网站http://www.shiyanlou.com。
现在登陆实验楼更有感恩好礼相送http://www.shiyanlou.com/huodong/thanks.html
C语言基于GTK+Libvlc实现的简易视频播放器(二)的更多相关文章
- C语言基于GTK+Libvlc实现的简易视频播放器
小编心语:现下,各种视频播放软件层出不穷,竞争也越演越烈,不知道大家有木有这个想法,小编有时在想能不能做一款属于自己的视频播放器呢~小编特意去实验楼,整理出了这篇关于如何实现简易视频播放器的博文.简易 ...
- 【Harmony OS】【ArkUI】ets开发 简易视频播放器
前言:这一次我们来使用ets的Swiper组件.List组件和Video组件制作一个简易的视频播放器.本篇是以HarmonyOS官网的codelab简易视频播放器(eTS)为基础进行编写.本篇最主要 ...
- 简易视频播放器2 (基于Qt、opencv)
因项目需要,需要实现一个对以保存的监测视频快速查看功能. 查询网上一些资料,初步简易的实现了一下. 实际效果图: 该程序基于Qt5.4,opencv248,开发环境为win8.1 结构为: video ...
- 痞子衡嵌入式:基于恩智浦i.MXRT1060的MP4视频播放器(RT-Mp4Player)设计
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是基于i.MXRT1062的MP4播放器参考设计. i.MXRT1062是恩智浦i.MXRT四位数系列的中端型号,外设搭配上很均衡,辅以6 ...
- FFmpeg简易播放器的实现-视频播放
本文为作者原创:https://www.cnblogs.com/leisure_chn/p/10047035.html,转载请注明出处 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- FFmpeg简易播放器的实现-音视频同步
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10284653.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- FFmpeg简易播放器的实现-音视频播放
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10235926.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- FFmpeg简易播放器的实现-音频播放
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10068490.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- FFmpeg简易播放器的实现-最简版
本文为作者原创:https://www.cnblogs.com/leisure_chn/p/10040202.html,转载请注明出处 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
随机推荐
- 2、Redis入门介绍
1.什么是Redis Redis:REmote DIctionary Server(远程字典服务器) 是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分布式内存数 ...
- Java中的网络编程
Java中的网路编程主要是Java的Socket编程,属于JavaEE中的高级的部分,以下内容是对java网路编程的一个小结,代码都是经过编译调试的 C/S程序应用:客户/服务器模式,如QQ客户端 ...
- sqoop数据导出导入命令
1. 将mysql中的数据导入到hive中 sqoop import --connect jdbc:mysql://localhost:3306/sqoop --direct --username r ...
- Oracle层次查询
Oracle层次查询的语法如下: 下面根据两道“烧脑”的题具体来体现: 1. 根据时间先后顺序,十二星座的英文名称用逗号串起来为'Aries,Taurus,Gemini,Cancer,Leo,Virg ...
- table-cell实现宽度自适应布局
利用table-cell可以实现宽度自适应布局. table-cell有一些比较好用的属性,比如垂直居中,自适应高度宽度等,为元素设置table-cell布局之后,元素的margin失效,paddin ...
- 小白Linux入门 三
环境变量 shell 变量: 内存空间 ,命名的内存空间 echo $SHELL 其中SHELL是变量 里面是/bin/bash sudo su 进入root printenv 命令 命令: 内部命 ...
- 一个网站完整详细的SEO优化方案
根据自己的个人经验完成了这篇文章,希望对SEOer有点帮助,高手直接跳过,请勿喷水... 一个完整的SEO优化方案主要由四个小组组成: 一.前端/页编人员 二.内容编辑人员 三.推广人员 四.数据分析 ...
- Net设计模式实例之单例模式( Singleton Pattern)
一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...
- js中 javascript:void(0) 用法详解
点击链接不做任何事情: <a href="#" onclick="return false">test</a> <a href=& ...
- Nancy之结合TinyFox调试备忘
最近把一个小项目的数据库换成MongoDB,同时用了MongoRepository 这个开源组件来对数据进行操作. 通过NuGet安装之后,它会自动在web.config文件生成一个连接字符串.但是却 ...