C语言基于GTK+Libvlc实现的简易视频播放器(二)

摘要:
通过单击浮动控件窗体上的退出全屏按钮使用API函数gtk _ window_ Unfullscreen可以退出全屏状态,gpointerdata){//设置正常窗体以进入全屏状态gtk_window_fullscreen(gtk_window(窗口));//隐藏窗口gtk_widget_Hide(hbox)底部的控制栏和顶部的菜单栏;
简易视频播放器-全屏播放
一、课程说明
上一次我们使用gtk+libvlc实现了一个最简单的视频播放器,可以实现点击按钮暂定和停止播放视频,以及同步显 示视频播放进度,但即使作为一个视频播放器,只有这些功能也还是不够的,至少我们还应该有全屏播放的功能吧,所以这一次我们就来为上一次的视频播放器添加 上全屏播放功能。这个功能实现起来思路很简单,只是具体实现过程中有很多坑罢了,需要我们注意很多细节问题,还要解决一些bug等等。这次我们的代码出了 增加功能之外,也还会对上一次的基础代码做一些修改。

二、功能实现思路
使用gtk提供的API函数,gtk_window_fullscreen可以将窗口设置为全屏,即隐藏标题栏、菜单栏和工具栏,同时隐藏窗口下端的视频 控制条,只保留视频播放绘图构件。在全屏状态时视频的播放控制及进度显示,由另一个单独的浮动弹出式窗体实现。通过点击浮动控制窗体上的退出全屏按钮使用 API函数gtk_window_unfullscreen可以退出全屏状态,此时再还原之前隐藏的正常模式下的控制条构件,及隐藏浮动弹出式窗体

C语言基于GTK+Libvlc实现的简易视频播放器(二)第1张
三、要实现的功能点

  • 全屏播放
  • 全屏状态浮动控制条
  • 无鼠标动作时浮动控制条和鼠标指针自动隐藏
  • 鼠标动作激活浮动控制条和鼠标指针
  • 单击播放区域暂停/播放,双击全屏/退出全屏

四、功能点具体实现
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, 0);
    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) == 1)
            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, 0);
        gtk_box_pack_start(GTK_BOX(ctrl_hbox), full_screen_stop_button, FALSE, TRUE, 0);
        gtk_box_pack_end(GTK_BOX(ctrl_hbox), quit_full_screen_button, FALSE, TRUE, 0);
        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, 0);
        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), 0);
        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(5000, (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)/2, height-50);
        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(2 ,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实现的简易视频播放器(二)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇一个基于 Vue3 的开源项目,3个月时间 star 终于破千!pthread_exit/pthread_kill之后局部对象之析构下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JAVA入门到精通-第30讲-布局管理器

JFrame默认是边界布局的; 流式布局FlowLayout默认是居中对齐的; 右对齐: 按道理每个按钮可以设置大小; 一旦把布局管理器设置好,按钮大小会自动设置; null设置按钮大小; 禁止用户改变窗口大小; 网格布局适合做计算器; 对象数组需要NEW一下; 设置网格布局: (几行,几列) 运用for循环;...

(21)C#VS快捷键

1.移动光标 ctrl+ 右键:按单词移动 home:移动到一行得开头 ,end:移动到一行得末尾 ctrl+home:移动到文本得第一行初始位置,ctrl+end文本最末尾 pageDn:往文件下面翻一个屏幕, ctrl+向下:视角向下移动一行 2.文本选择 shift+右键:向右选中一个字母, ctrl+shift+右键:向右选中一个单词 ctrl+...

Qt5.3.2_CentOS6.4_基本编程环境__20160306【勿删,繁琐】

20160306 全程没有f/q ZC:使用的虚拟机环境是:博客园VMwareSkill 的 “CentOS6.4_x86_120g__20160306.rar” 1、 执行命令“gcc -v”,显示: “ [root@centos ~]# gcc -vbash: gcc: command not found[root@centos ~]# ” 说明没有...

WPF 纯代码生成界面(不使用XAML)

    对于编写 WPF 应用程序,只是用代码进行开发而不使用任何 XAML 不是常见的方式(但是仍然完全支持)。只使用代码进行开发的明显缺点是,有可能会使用编写 WPF 应用程序成为极端乏味的工作。 WPF 控件没有包含参数化的构造函数,因此即使为窗体添加一个简单的按钮也需要编写几行代码。只使用代码进行开发的一个潜在的优点是可以随意定制应用程序。例如,可...

vscode折叠/展开所有区域代码快捷键

vscode代码编辑器折叠所有区域的代码快捷键 查看了使用说明,快捷键如下: 1. 折叠所有区域代码的快捷:ctrl + kctrl + 0; 先按下ctrl 和 K,再按下 ctrl 和 0 ; ( 注意这个是零,不是欧 ) 2. 展开所有折叠区域代码的快捷:ctrl +kctrl + J; 先按下ctrl 和 K,再按下 ctrl 和 J...

winform窗体 控件【菜单和工具栏控件】【容器控件】

  winform的菜单栏和工具栏      1.ContextMenuStrip   -- 右键菜单        可以绑定在任何一个控件上,添加操作快捷键,并可以设置多层       每行相当于一个按钮,输入-可添加分割线       将控件拖入窗体——添加操作名称——找到要进行右键操作的控件的 ContextMenuStrip 属性——委托    ...