"the bare minimum code"
-- 摘自 file:///usr/share/doc/gstreamer0.10-doc/pwg/html/chapter-building-boiler.html
"the bare minimum code",可译为"最小化程序代码",学习代码的一种技巧。
这里指,阅读自由软件源代码,通过简化,以最快的速度,最小化代码的形式,获得自己想要的功能。
下面通过一个实例,进行感性的认识:
目标: 将摄像头的影像显示到Gtk界面的一个GtkDrawingArea中
借鉴的GNU源代码: cheese-2.22.3 (src文件夹,共246.KB)
为了达到目标,最小化cheese程序的源代码后,得到大小共3.5KB的可用源代码包
主要使用的简化技巧:
1. 用函数结果代替函数本身
将检测摄像头参数的函数
cheese_webcam_detect_webcam_devices (webcam);
用其检测结果"一个字符串"进行等价替换
webcam_input="v4l2src name=video_source device=/dev/video0 ! video/x-raw-yuv,width=640,height=480,framerate=30/1 ! identity";
2. 用面向过程思想代替面向对象思想
不使用"struct CheeseWebcamPrivate"等结构体
直接用全局变量代替结构体中的项
GtkWidget *cheese_window,*video_screen;
GstElement *pipeline,*video_display_bin,*webcam_source_bin;
GstElement *video_source;
XF86VidModeGamma normal_gamma;
3. 省略命令行的选项处理,静态地指定你的需求
cheese中的命令选项不多,所以这项技巧简化的代码量不太明显
用spcaview举例,spcaview是一个接收网络摄头像影像的程序,有width,height等命令行参数
在程序中简化过程中,你可以自己指定width,height的大小,从而省略命令行参数的处理
4. 不进行错误检测
if ((video_scale = gst_element_factory_make ("videoscale", "video_scale")) == NULL)
{
cheese_webcam_set_error_element_not_found (error, "videoscale");
}
简化为
video_scale=gst_element_factory_make("videoscale","video_scale");
在编译,运行,调试的过程中,错误已被检查出来,所以错误检测代码可以省略
我们不是要写一个通用的软件,只是写一个适合本机的,能以最快速度了解程序结构,以最小的代码实现想要功能的程序,所以有些错误检测省略,是允许的
5. 理解程序代码后,按需求,用自己的方式,更好地更简化地表达
如在函数"cheese_webcam_create_video_display_bin (webcam);"中,使用了gstreamer插件tee。它主要用于将摄像头的影像显示到GtkDrawingArea的同时将数据流用于另存为图片或另存为ogg视频文件。
这里我们只要影像显示的功能,所以去掉tee插件,设计出一个更简单的gstreamer pipeline。
6. 简化Makefile,简化界面
自己重新编写Makefile,不使用autotool
cheese使用GtkBuilder进行界面设计,可视化程序设计,生成xml格式的界面设计文件。(可参见下面的参考资源1)
在理解好xml文件cheese.ui后,笔者在emacs里自己重写了新的ui文件。
只提取出GtkWindow,GtkVBox,GtkDrawingArea这3个Gtk+组件即能满足需求。
详见下面的代码展示
系统环境参数如下:
$ dpkg -l cheese | grep cheese ii cheese 2.22.3-3 A tool to take pictures and videos from your $ cat /etc/debian_version 5.0.3 $ |
编写代码,编译,运行如下:
$ ls -F 1.0/ cheese.ui main.c Makefile $ make gcc `pkg-config --cflags --libs gtk+-2.0 gstreamer-0.10 gstreamer-interfaces-0.10 gdk-x11-2.0 xxf86vm` main.c $ ls 1.0 Makefile a.out cheese.ui main.c $ ./a.out |
a.out 运行界面如下:

代码列表:
$ cat Makefile all: gcc `pkg-config --cflags --libs gtk+-2.0 gstreamer-0.10 gstreamer-interfaces-0.10 gdk-x11-2.0 xxf86vm` main.c clean: rm -f *~ a.out $ cat cheese.ui <?xml version="1.0"?> <interface> <object class="GtkWindow" id="cheese_window"> <property name="visible">True</property> <property name="default_width">640</property> <property name="default_height">480</property> <child> <object class="GtkVBox" id="video_vbox"> <property name="visible">True</property> <property name="spacing">6</property> <child> <object class="GtkDrawingArea" id="video_screen"> <property name="visible">True</property> <property name="width_request">320</property> <property name="height_request">240</property> </object> </child> </object> </child> </object> </interface> $ cat main.c #include <gtk/gtk.h> #include <gst/gst.h> #include <gst/interfaces/xoverlay.h> #include <gdk/gdkx.h> #include <X11/Xlib.h> #include <X11/extensions/xf86vmode.h> GtkWidget *cheese_window,*video_screen; GstElement *pipeline,*video_display_bin,*webcam_source_bin; GstElement *video_source; XF86VidModeGamma normal_gamma; static gboolean cheese_webcam_create_video_display_bin() { GError *err=NULL; gboolean ok; GstElement *video_display_queue,*video_scale,*video_sink; char *webcam_input; video_display_bin=gst_bin_new("video_display_bin"); webcam_input="v4l2src name=video_source device=/dev/video0 ! video/x-raw-yuv,width=640,height=480,framerate=30/1 ! identity"; webcam_source_bin=gst_parse_bin_from_description(webcam_input,TRUE,&err); if(webcam_source_bin==NULL){ g_error("webcam_source_bin init error"); } video_source=gst_bin_get_by_name(GST_BIN(webcam_source_bin),"video_source"); video_display_queue=gst_element_factory_make("queue","video_display_queue"); video_scale=gst_element_factory_make("videoscale","video_scale"); g_object_set(video_scale,"method",1,NULL); video_sink=gst_element_factory_make("gconfvideosink","video_sink"); gst_bin_add_many(GST_BIN(video_display_bin),webcam_source_bin,video_display_queue,video_scale,video_sink,NULL); ok=gst_element_link_many(webcam_source_bin,video_display_queue,video_scale,video_sink,NULL); if(!ok){ g_error("unable to create video display bin"); } return TRUE; } static void cheese_webcam_set_x_overlay() { GstXOverlay *overlay=GST_X_OVERLAY(gst_bin_get_by_interface(GST_BIN(pipeline),GST_TYPE_X_OVERLAY)); gst_x_overlay_set_xwindow_id(overlay,GDK_WINDOW_XWINDOW(video_screen->window)); } static void window_close(GtkWidget *widget,gpointer data) { gst_element_set_state(pipeline,GST_STATE_NULL); gst_object_unref(GST_OBJECT(pipeline)); gtk_main_quit(); } int main(int argc,char *argv[]) { GtkBuilder *builder; GError *error; g_thread_init(NULL); gdk_threads_init(); gtk_init(&argc,&argv); gst_init(&argc,&argv); builder=gtk_builder_new(); gtk_builder_add_from_file(builder,"cheese.ui",&error); cheese_window=GTK_WIDGET(gtk_builder_get_object(builder,"cheese_window")); video_screen=GTK_WIDGET(gtk_builder_get_object(builder,"video_screen")); g_object_unref(G_OBJECT(builder)); g_signal_connect(cheese_window,"destroy",G_CALLBACK(window_close),NULL); pipeline=gst_pipeline_new("pipeline"); cheese_webcam_create_video_display_bin(); gst_bin_add_many(GST_BIN(pipeline),video_display_bin,NULL); gdk_threads_enter(); XF86VidModeGetGamma(GDK_DISPLAY(),0,&normal_gamma); gdk_threads_leave(); gst_element_set_state(pipeline,GST_STATE_PLAYING); cheese_webcam_set_x_overlay(); gtk_widget_show_all(cheese_window); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return 0; } $ |
"the bare minimum code"学习代码的方式,其实际意义在于,理清程序的结构和原理,在短时间内获得自己想要的功能。通过简化,快速获得完成项目的一个组件。
还有很多阅读源代码的方法,欢迎大家一起交流经验。
GNU真的很好玩,有这么多代码可以给我们学习 : )
参考资源
1. 使用GtkBuilder设计Gtk+界面
http://www.linuxeden.com/html/develop/20091129/69290.html
时间:2009-12-27 23:05
来源:Linuxeden
作者:c-aries
原文链接