Linux-视频监控系统(6)-播放器子系统
2017-08-07 21:46
507 查看
播放器功能及设计思路
播放子系统需要实现和服务器的连接,以及接收并显示图像等功能。主要的界面有2个,一个是连接界面,由用户输入IP地址和端口后连接服务器,另一个是播放界面,显示接收过来的图像,并且显示图像格式,帧率、大小等信息。根据这些要求我们开始设计播放子系统,播放子系统的界面采用GTK来编写。由于一次性难以实现全部功能,这里分为2部分来实现。第一步先把播放界面设计好,第二步在界面的基础上实现和服务器的连接以及图像的显示。我们这里来实现第一步。为了方面系统的实现及信息的保存,我们定义这么几个结构
登录界面的结构:
struct entry_win
{
GtkWidget *win;
GtkWidget *ip_entry;
GtkWidget *port_entry;
GtkWidget *connect_button;
gboolean connected;
int sock;
};
保存客户机的信息:
struct wcam_cli
{
int sock;
pthread_t tid;
bool need_stop;
__u8 req[FRAME_MAX_SZ];
__u8 rsp[FRAME_MAX_SZ + VID_FRMAE_MAX_SZ];
wc_img_porc_t proc;
void *arg;
};
为整个界面创建一个结构:
struct wcam_win
{
GtkWidget *win;
wcc_t client;
entry_win_t entry_win;
GtkWidget *video_area;
guint32 video_format;
guint32 video_width;
guint32 video_height;
gboolean video_fullscreen;
gboolean need_snapshot;
gchar ipaddr[24];
GtkWidget *fps_label;
GtkWidget *frmsize_label;
guint32 frm_cnt;
guint64 last_twenty_frm_us;
GtkWidget *info_area;
GtkWidget *button_area;
GtkWidget *control_area_button;
GtkWidget *control_area;
};
登录界面
/*创建登录界面*/entry_win_t login_create()
{
struct entry_win *c = calloc(1, sizeof(struct entry_win));
if(!c){
perror("entry_win_create");
return NULL;
}
c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
//设置标题,图标,位置等
gtk_window_set_title(GTK_WINDOW(c->win), WIN_TITLE);
gtk_window_set_icon(GTK_WINDOW(c->win), gdk_pixbuf_new_from_file(WIN_ICON,NULL));
gtk_window_set_position(GTK_WINDOW(c->win), GTK_WIN_POS_CENTER);
//不能改变大小
gtk_window_set_resizable(GTK_WINDOW(c->win),FALSE);
//设置窗口大小
gtk_container_set_border_width(GTK_CONTAINER(c->win), 0);
//创建关闭窗口的处理事件
g_signal_connect(GTK_OBJECT(c->win),"destroy",G_CALLBACK(gtk_main_quit),NULL);
//按键输入事件和key_press_event函数关联
g_signal_connect(G_OBJECT(c->win), "key_press_event",G_CALLBACK(key_press_func), c);
//允许窗口绘图
gtk_widget_set_app_paintable(c->win, TRUE);
//绘制图片和按钮
entry_win_draw_face(c);
//显示所有控件
gtk_widget_show_all(c->win);
return c;
}
我们把图标的创建,按钮的创建,IP和端口输入窗口的创建封装成函数
static gboolean entry_win_draw_face(struct entry_win *c)
界面的设计我们采用BOX容器来实现,第一行绘制图标,第二行绘制IP和端口的输入界面,第三行绘制OK和cancel按钮
static gboolean entry_win_draw_face(struct entry_win *c)
{
GtkWidget *vbox;
GtkWidget *hbox;
//创建一个垂直组装盒,控件间隔为5个像素
vbox = gtk_vbox_new(FALSE, 5);
gtk_container_add(GTK_CONTAINER(c->win), vbox);
//创建一个水平组装盒,控件间隔为5个像素
hbox = gtk_hbox_new(FALSE, 5);
gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
//绘制logo
logo_draw(c, hbox);
//绘制IP和端口输入框
hbox = gtk_hbox_new(FALSE, 5);
gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
entry_area_draw(c, hbox);
//绘制OK和cancel按钮
hbox = gtk_hbutton_box_new();
gtk_box_set_spacing(GTK_BOX(hbox), 5);
gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
button_area_draw(c, hbox);
return TRUE;
}
logo绘制,输入窗口的绘制和按钮绘制的函数如下:
static gboolean logo_draw(struct entry_win *c, GtkWidget* box)
{
gtk_box_pack_start_defaults(GTK_BOX(box), gtk_image_new_from_file(LOGO_IMG));
gtk_box_pack_start_defaults(GTK_BOX(box), gtk_image_new_from_file(LOGO_IMG1));
return TRUE;
}
static gboolean entry_area_draw(struct entry_win *c, GtkWidget* box)
{
GtkWidget *label;
GtkWidget *entry;
//创建一个文本标签
label = gtk_label_new("Server Ip:");
gtk_box_pack_start_defaults(GTK_BOX(box), label);
//创建一个输入框
entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), DEF_CONN_IP);
gtk_entry_set_max_length(GTK_ENTRY(entry), 15);
gtk_box_pack_start_defaults(GTK_BOX(box), entry);
c->ip_entry = entry;
label = gtk_label_new("Server Port:");
gtk_box_pack_start_defaults(GTK_BOX(box), label);
entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), DEF_PORT);
gtk_entry_set_max_length(GTK_ENTRY(entry), 5);
gtk_box_pack_start_defaults(GTK_BOX(box), entry);
c->port_entry = entry;
return TRUE;
}
static gboolean button_area_draw(struct entry_win *c, GtkWidget* box)
{
GtkWidget *button;
//创建ok按钮
button = gtk_button_new_with_label("OK");
gtk_box_pack_start_defaults(GTK_BOX(box), button);
c->connect_button = button;
//点击事件关联connect_handler函数
g_signal_connect(button, "clicked", G_CALLBACK(connect_handler), c);
button = gtk_button_new_with_label("Cancel");
gtk_box_pack_start_defaults(GTK_BOX(box), button);
g_signal_connect(button, "clicked", G_CALLBACK(gtk_main_quit), NULL);
return TRUE;
}
需要注意的是,对于输入事件的处理我们分为2种,一种是对按键的处理,比如按下回车默认点击OK,按下ESC默认退出;另一种是对OK和cancel按钮事件的处理,点击OK需要实现对服务器的连接,点击cancel则退出界面。对于第二种情况我们在按钮绘制函数中已经把OK按钮和connect_handler函数关联在一起
/*建立和服务器的连接*/
static void connect_handler(GtkButton *button, gpointer data)
{
struct entry_win *c = data;
const gchar *ip;
gint16 port;
//获取输入的ip和端口,保存新的socket
ip = gtk_entry_get_text(GTK_ENTRY(c->ip_entry));
port = atoi(gtk_entry_get_text(GTK_ENTRY(c->port_entry)));
c->connected = TRUE;
gtk_main_quit();
}
对于第一种,我们在登录界面的创建时已经把key_press_event事件和key_press_func关联在一起,
/*对键盘的响应只支持回车和退出*/
static gboolean key_press_func(GtkWidget* widget, GdkEventKey* key, gpointer data)
{
#define KEY_ENTER 0xff0d
#define KEY_ESC 0xff1b
struct entry_win *c = data;
//如果是回车,默认点击OK
if(KEY_ENTER == key->keyval)
{
connect_handler(GTK_BUTTON(c->connect_button), c);
return TRUE;
}
//退出键
else if(KEY_ESC == key->keyval)
{
c->connected = FALSE;
gtk_main_quit();
return TRUE;
}
return FALSE;
}
最终登录界面的显示如下所示:
别忘了测试一下对事件发生的响应是否准确
播放界面
如果连接成功我们进入播放界面,连接失败则退出,那么怎么连接是否成功在于判断是否获取到了socket,然后通过login_run来判断是否连接成功:int login_run(entry_win_t win) { struct entry_win *c = win; //等待事件的发生 gtk_main(); return c->connected == 4000 TRUE ? 0 : -1; }
连接成功后隐藏登录界面,显示播放界面,主函数如下:
gint main(gint argc, gchar* argv[]) { int res; /*GTK初始化*/ gtk_init(&argc, &argv); g_thread_init(NULL); gdk_threads_init(); struct wcam_win *c = calloc(1,sizeof(struct wcam_win)); c->entry_win = login_create(); res = login_run(c->entry_win); if(res == -1) { goto err_win; } //隐藏登录页面 login_hide(c->entry_win); //创建主工作页面 main_create(c); main_run(); err_win: free(c->entry_win); free(c); return 0; }
播放界面我们称之为主工作界面,他的初始化和登录界面类似,这里不再过多介绍了:
/* *主工作页面 *主要实现显示图片和帧率的功能 */ static gboolean main_create(struct wcam_win *c) { int len; c->video_width = 640; c->video_height = 480; c->video_fullscreen = FALSE; //把ip和端口拼接到ipaddr中 entry_win_get_ip(c->entry_win, c->ipaddr); len = strlen(c->ipaddr); c->ipaddr[len] = ':'; entry_win_get_port(c->entry_win, &c->ipaddr[len+1]); //创建主工作界面 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(c->win), WIN_TITLE); gtk_window_set_icon(GTK_WINDOW(c->win), gdk_pixbuf_new_from_file(WIN_ICON, NULL)); gtk_container_set_border_width(GTK_CONTAINER(c->win), 0); g_signal_connect(c->win, "destroy", G_CALLBACK(main_quit), c); gtk_widget_set_app_paintable(c->win, TRUE); main_win_draw_face(c); gtk_widget_show_all(c->win); gtk_widget_hide(c->win); gtk_window_set_position(GTK_WINDOW(c->win), GTK_WIN_POS_CENTER); gtk_widget_show(c->win); return TRUE; }
主窗口交互信息的绘制:
/*绘制主窗口的控件*/ static gboolean main_win_draw_face(struct wcam_win *c) { GtkWidget *box; GtkWidget *hbox; GtkWidget *hseparator; box = gtk_vbox_new(FALSE, 5); gtk_container_add(GTK_CONTAINER(c->win), box); //设置图片区域及信息显示区域 hbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); draw_area_draw(c, hbox); info_area_draw(c, hbox); //绘制水平分割线 hseparator = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(box), hseparator, FALSE, TRUE, 0); //绘制按钮 main_button_area_draw(c, box); return TRUE; }
完整的工程包括Makefile保存在我的GitHub中:
https://github.com/dayL-W/Video-capture-system
相关文章推荐
- Linux-视频监控系统(7)-播放器子系统2
- Linux-视频监控系统(5)-TCP传输子系统实现
- Linux-视频监控系统(4)-摄像头子系统实现
- 寒冰linux视频教程笔记8 系统监控
- 基于Linux的视频监控系统构建方法
- Linux嵌入式视频直播监控系统(转…
- Linux-视频监控系统(3)-Epoll框架的实现
- Linux-视频监控系统(2)-Epoll的介绍及使用
- ARM-Linux下用Servfox和Spcaview 建立嵌入式视频监控系统
- 阶段4-独挡一面\项目-基于视频压缩的实时监控系统\Sprint3-采集端传输子系统设计
- Linux-视频监控系统(12)-移植到树莓派中
- Linux嵌入式视频直播监控系统(转)
- 基于视频压缩的实时监控系统-A2:linux中最优秀的多路复用机制Epoll
- oks3c6410开发板 linux-3.0.1内核 ZC301P摄像头 构成视频监控系统时内核oops解决办法
- ARM-Linux下用Servfox和Spcaview 建立嵌入式视频监控系统
- OpenCV3.2+Qt5.8.0+Win10开发视频监控系统----(5)linux系统中Qt工程的发布
- Linux嵌入式视频直播监控系统
- Linux-视频监控系统(8)-项目小结
- Linux嵌入式视频直播监控系统
- Linux-视频监控系统(9)-移植到树莓派中的计划