简易视频播放器-全屏播放

一、课程说明
上一次我们使用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来获得
首先添加事件处理

  1. 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

  1. $ git <span class="hljs-keyword">clone</span> -b full_screen https:<span class="hljs-comment">//github.com/shiyanlou/gtk-vlc-video-player.git</span>

演示视频

  1. <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实现的简易视频播放器(二)的更多相关文章

  1. C语言基于GTK+Libvlc实现的简易视频播放器

    小编心语:现下,各种视频播放软件层出不穷,竞争也越演越烈,不知道大家有木有这个想法,小编有时在想能不能做一款属于自己的视频播放器呢~小编特意去实验楼,整理出了这篇关于如何实现简易视频播放器的博文.简易 ...

  2. 【Harmony OS】【ArkUI】ets开发 简易视频播放器

    ​前言:这一次我们来使用ets的Swiper组件.List组件和Video组件制作一个简易的视频播放器.本篇是以HarmonyOS官网的codelab简易视频播放器(eTS)为基础进行编写.本篇最主要 ...

  3. 简易视频播放器2 (基于Qt、opencv)

    因项目需要,需要实现一个对以保存的监测视频快速查看功能. 查询网上一些资料,初步简易的实现了一下. 实际效果图: 该程序基于Qt5.4,opencv248,开发环境为win8.1 结构为: video ...

  4. 痞子衡嵌入式:基于恩智浦i.MXRT1060的MP4视频播放器(RT-Mp4Player)设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是基于i.MXRT1062的MP4播放器参考设计. i.MXRT1062是恩智浦i.MXRT四位数系列的中端型号,外设搭配上很均衡,辅以6 ...

  5. FFmpeg简易播放器的实现-视频播放

    本文为作者原创:https://www.cnblogs.com/leisure_chn/p/10047035.html,转载请注明出处 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...

  6. FFmpeg简易播放器的实现-音视频同步

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10284653.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...

  7. FFmpeg简易播放器的实现-音视频播放

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10235926.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...

  8. FFmpeg简易播放器的实现-音频播放

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10068490.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...

  9. FFmpeg简易播放器的实现-最简版

    本文为作者原创:https://www.cnblogs.com/leisure_chn/p/10040202.html,转载请注明出处 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...

随机推荐

  1. JavaScript之自我总结篇

    最近在看汤姆大叔的"深入理解JavaScript系列",写得真的不错,对于我而言特别是12章到19章,因为大叔研究的点,就主要是从底层来研究JavaScript为什么会出现钟种特有 ...

  2. 如何设置文本文件的默认保存编码为UTF-8

    原文:http://answers.microsoft.com/en-us/windows/forum/windows_7-windows_programs/default-utf-8-encodin ...

  3. tomcat匹配请求流程(原创)

    tomcat8.0.36 配置Connector属性allowTrace为true时,允许TRACE方法,默认禁止. tomcat8.0.36有一个BUG,该BUG在8.0.37里被修复,就是一个解析 ...

  4. git取消跟踪文件

    取消跟踪文件: $git rm --cached FILENAME 取消跟踪目录: $git rm --cached FILENAME -r

  5. 浅谈Hibernate入门

    前言 最近打算做一个自己的个人网站,经过仔细思考,打算使用hibernate作为开发的ORM框架,因此各种找资料,由于本人是刚刚接触这技术的,所以就找了比较基础的知识来分享下 基本概述 Hiberna ...

  6. Net设计模式实例系列文章总结

    1 什么是设计模式 设计模式是对在软件设计过程中重复出现的问题提出了一种比较好的解决方案.正如一位专家所说:设计模式是对程序设计人员经常遇到的设计问题的可再现的解决方案(The Smalltalk C ...

  7. compilation与编译

    关于本配置节的内容不算多,但关于ASP.NET编译的内容还是有一点的,鄙人认为只是了解一下即可,主要影响到部署. 在 App_Code 文件夹中使用多种编程语言 因为 App_Code 文件夹中的源代 ...

  8. PPT里面添加3Dvia Composer Player 控件

    本实例是Office 2013 版本 1打开文件-选项---自定义功能区--主选项卡 ---开发工具 :如图 4然后将在菜单栏里面看到 开发工具 5然后将出出一个十字 绘制你想要的区域 6 鼠标右击 ...

  9. SharePoint 列表的导出导入

    有一群友问到关于 SharePoint 列表的导入与导出的问题,而最近也要做相关操作且好久没写博客了,所以记录下来,过程其实相当简单. 方法:将 列表 保存为 模板(可包含数据),下载模板文件,上传到 ...

  10. php图片验证码为什么必须加上ob_clean();才能正常显示。

    ob_clean这个函数的作用就是用来丢弃输出缓冲区中的内容,如果你的网站有许多生成的图片类文件,那么想要访问正确,就要经常清除缓冲区. If you work on an extremely lar ...