歡迎您光臨本站 註冊首頁

The Bare Minimum Code-最小化程序代碼

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
"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

c gnu


[火星人 ] The Bare Minimum Code-最小化程序代碼已經有598次圍觀

http://coctec.com/docs/program/show-post-71626.html