2010年9月20日 星期一

C preprocessor: # and ## for concatenation

## preprocessor:
It is often useful to merge two tokens into one while expanding macros. This is called token pasting or token concatenation. The `##' preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each `##' operator are combined into a single token, which then replaces the `##' and the two original tokens in the macro expansion. Usually both will be identifiers, or one will be an identifier and the other a preprocessing number. When pasted, they make a longer identifier. This isn't the only valid case. It is also possible to concatenate two numbers (or a number and a name, such as 1.5 and e3) into a number. Also, multi-character operators such as += can be formed by token pasting.

However, two tokens that don't together form a valid token cannot be pasted together. For example, you cannot concatenate x with + in either order. If you try, the preprocessor issues a warning and emits the two tokens. Whether it puts white space between the tokens is undefined. It is common to find unnecessary uses of `##' in complex macros. If you get this warning, it is likely that you can simply remove the `##'.

Both the tokens combined by `##' could come from the macro body, but you could just as well write them as one token in the first place. Token pasting is most useful when one or both of the tokens comes from a macro argument. If either of the tokens next to an `##' is a parameter name, it is replaced by its actual argument before `##' executes. As with stringification, the actual argument is not macro-expanded first. If the argument is empty, that `##' has no effect.

Keep in mind that the C preprocessor converts comments to whitespace before macros are even considered. Therefore, you cannot create a comment by concatenating `/' and `*'. You can put as much whitespace between `##' and its operands as you like, including comments, and you can put comments in arguments that will be concatenated. However, it is an error if `##' appears at either end of a macro body.

Consider a C program that interprets named commands. There probably needs to be a table of commands, perhaps an array of structures declared as follows:
struct command
     {
       char *name;
       void (*function) (void);
     };
     
     struct command commands[] =
     {
       { "quit", quit_command },
       { "help", help_command },
       ...
     };
It would be cleaner not to have to give each command name twice, once in the string constant and once in the function name. A macro which takes the name of a command as an argument can make this unnecessary. The string constant can be created with stringification, and the function name by concatenating the argument with `_command'. Here is how it is done:
#define COMMAND(NAME)  { #NAME, NAME ## _command }
     
     struct command commands[] =
     {
       COMMAND (quit),
       COMMAND (help),
       ...
     };

# preprocessor:
Formal parameters are not replaced within quoted strings. If, however, a parameter name is preceded by a # in the replacement text, the combination will be expanded into a quoted string with the parameter replaced by the actual argument. This can be combined with sting concatenation to make, for example, a debugging print macro:
#define dprint(expr) printf(#expr " = %g\n", expr)
When this is invoked, as in
dprint(x/y);
the macro is expaned into
printf("x/y" " = %g\n", x/y);
and the strings are concantenated, so the effect is
printf("x/y = %g\n", x/y);

Within the actual argument, each " is replaced by \" and each \ by \\, so the result is a legal string constant.

Refer to:
1. http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html#Concatenation
2. The C Programming Language (Book)

How to use oprofile on embedded system for Android

The detailed steps are referred from http://www.omappedia.org/wiki/Android_Debugging#OProfile
Here, I just write down the steps for my developing environment.
My developing environment is an Android platform on embedded system. So, I have a host machine and a target machine which is an embedded system with ARM based processor.

On host machine:
1. build a linux kernel enabling oprofile
2. edit $MYDROID/external/oprofile/opimport_pull (for instance $MYDROID = ~/sk886x/source/skydroid1.6/platform/) as below
Remove the python version number from the first line eg. change
      #!/usr/bin/python2.4 -E
  to
      #!/usr/bin/python -E

  Append the following lines at the end of the file to generate cpuloads.txt and callgraph.png for further analysis 
      os.system(oprofile_event_dir + "/bin/opreport --session-dir=. >> cpuloads.txt") 
      os.system(oprofile_event_dir + "/bin/opreport --session-dir=. -p $OUT/symbols -l -t 0.1 >> cpuloads.txt") 
      os.system(oprofile_event_dir + "/bin/opreport -cg --session-dir=. -p $OUT/symbols > callgraph.txt") 
      os.system("cat callgraph.txt | gprof2dot.py -s -w -f oprofile -n 0.1 -e 0.1 | dot -Tpng -o callgraph.png")  

On target machine

3. turn on busybox
# busybox ash
4. make a dummy linux vmlinux
# echo 0 > /vmlinux
5. If you see "Cannot create directory /dev/oprofile: File exists do_setup failed#", it means that, OProfile is not built in the Kernel.
# opcontrol --setup
6.
# busybox grep " _text" /proc/kallsyms 
c0023000 T _text          
# busybox grep " _etext" /proc/kallsyms 
c054e000 A _etext 
7.
# opcontrol --vmlinux=/vmlinux --kernel-range=0xc0023000,0xc054e000 --event=CPU_CYCLES:64
8.
# echo 16 > /dev/oprofile/backtrace_depth
9. check the configuration status
# opcontrol --status
10. start oprofile
opcontrol --start

11. Run your program for analyzing the performance

12. Stop oprofile
opcontrol --stop

On host machine
13.
$ cd $MYDROID 
$ source build/envsetup.sh 
$ setpaths 
$ export ADBHOST=
14.
$ mkdir ~/oprofilepackage && cd ~/oprofilepackage 
$ tar xvjf 
$ cd $MYDROID
$ source build/envsetup.sh 
$ sed -i -e 's_$(call inherit-product, frameworks/base/data/sounds/OriginalAudio.mk)_#$(call inherit-product, frameworks/base/data/sounds/OriginalAudio.mk)_g' build/target/product/AndroidProducts.mk
$ setpaths
$ export MYDROID=${PWD}
$ ln -s $MYDROID/out/target/product/sk886x $MYDROID/out/target/product/generic
15. Set soft links of vmlinux and target program, for example mplayer
$ ln -s /path/to/linux/kernel/soruce/folder/vmlinux $OUT/symbols/vmlinux 
$ ln -s /path/to/mplayer/source/folder/mplayer $OUT/sybmols/system/bin/mplayer
16. pull the oprofile result to host machine and generate the analysis report
#MYDROID/external/oprofile/opimport_pull /path/to/new/folder/to/store/dump/and/results/

Then you can see cpuloads.txt in /path/to/new/folder/to/store/dump/and/results/

2010年9月14日 星期二

Data type definitions of integer in C language

In stdint.h
typedef signed char int8_t;
typedef unsigned char   uint8_t;
typedef short  int16_t;
typedef unsigned short  uint16_t;
typedef int  int32_t;
typedef unsigned   uint32_t;
typedef long long  int64_t;
typedef unsigned long long   uint64_t;

如何在codeblocks使用libcurl

1. 首先先去下載libcurl
2. 將lib folder裡面的檔案跟include裡面的檔案複製到codeblocks所使用gcc編譯器的相對lib和include folder裡面.
3. 在所要開發的project的setting裡面的project's build options..的link settings中的Other linker options 加入 -lcurl  和  -lcurldll
4. 完成程式編譯後, 將 libcurl的bin folder 裡面的 *.dll 複製到程式執行檔的位置

完成

2010年8月30日 星期一

(轉) YouTube打算在年底開通好萊塢電影按次收費服務

ref: here

根據Financial Times網站所 提,Google旗下YouTube視訊網站目前正與好萊塢(Hollywood)電影協商,將於2010年底推展全球按次計費電影點播服務。若所言屬實 的話,那麼很可能創造新的形態。畢竟目前我們絕大多數人觀看新的電影,是以上電影院或是買 / 租 DVD片的方式(當然也有人是以「下載」的形態啦)。此外,也形成了在數位影視內容的發行市場上與蘋果硬碰硬情形,後續發展,值得繼續觀察。

在知道這樣的消息後,除了想到可能創造一個新的看電影形態外,也想到iVideo這個網站服務(說起來,YouTube這樣的方式,才更適合稱得上 iVideo吧)。目前當然沒有更進一步的消息,但若台灣也能有YouTube這樣的電影計次收費服務,iVideo可能會很辛苦吧。

2010年8月24日 星期二

C語言的label

對於C的label 以前都只知道是與goto一起搭配使用, 使用方法如下

{
    int a = 1;
    int i = 0;
    for (i=1; i<10; i++)
    {
        if ((a % 5) == 0 )
            goto exit1;
        ++a;
    }
    goto exit2;
exit1:
    printf("a = %d\n", a);
    return a;
exit2:
    printf("a = %d\n", a);
    return a;
}
Label就是在程式中的某一段你想要goto到的點插入 "lable:", 然後就可以利用goto label; 來直接跳到那個地方. 不過後來發現還有另外一種宣告label的方式, 可以用來記錄label 的address, 然後可以在任何地方都直接跳到你要的點(需要同一個有效空間)
例子如下

int main(void)
{
       __label__ something; // something_1
       int foo;
       foo=0;
       char *ptr1 = &&something;  // *ptr 記錄了 something_1的位址, 此時goto *ptr1 == goto something;
       printf("%X\n", *ptr1);
       goto something;
       {
               __label__ something; // something_2
               char *ptr2 = &&something;
               printf("%X\n", *ptr2);
               goto something;
               something: // something_2
                       foo++;
       }
       something:    // something_1
               return foo;
}
上面例子可以發現,  label可以命名相同名字但必須在不同的scope裡面, 如果在內部scope想跳到外部scope的label則可以利用__label__的宣告來事先記錄下該label的address,  如 *ptr1


end.

Build openssl for ARM

雖然網路上可以搜尋到很多人提供的方法跟經驗分享,在此我還是紀錄一下我自己的使用經驗
步驟:
1. ./Configure linux-generic32 no-shared --prefix=/path/to/your/prefer/openssl/path
2. 修改Makefile as below
  modify CC = /path/to/your/gcc/for/arm
               AR = /path/to/your/ar/for/arm
               RANLIB = /path/to/your/ranlib/for/arm
3. make
4. (Optional) make install
ps. 如果想要build成static版本, 可以修改Makefile裡面的CFLAGS,加入 -static

After step 3, the openssl executable file will be generated in apps/ folder
之後就可以把openssl copy到 target machine上去執行

2010年6月10日 星期四

轉 -如何去寫Android init.rc

參考來源:http://embeddeder.spaces.live.com/blog/cns!8006E36532087D51!160.entry

如 何去寫Android init.rc (Android init language)
Android初始化語言由四大類聲明組 成:行為類(Actions),命令類(Commands),服務類(Services),選項類(Options).

* 初始化語言以行為單位,由以空格間隔的語言符號組成。C風格的反斜槓轉義符可以用來插入空白到語言符號。雙引號也可以用來防止文本被空格分成多個語言符 號。當飯斜槓在行末時,作為折行符。

* 以#開始(前面允許有空格)的行為註釋行。

* Actions和Services隱含聲明一個新的段落。所有該段落下Commands或Options的聲明屬於該段落。第一段落前的Commands 或Options被忽略。

* Actions和Services擁有獨一無二的命名。在它們之後聲明相同命名的類將被當作錯誤並忽略。

Actions

-------

Actions 是一系列命令的命名。Actions擁有一個觸發器(trigger)用來決定action何時執行。當一個action在符合觸發條件被執行時,如果它 還沒被加入到待執行隊列中的話,則加入到隊列最後。

隊列中的action依次執行,action中 的命令也依次執行。Init在執行命令的中間處理其他活動(設備創建/銷毀,property設置,進程重啟)。

Actions 表現形式為:

on





Services

--------

Services 是由init啟動,在它們退出時重啟(可選)。Service表現形式為:

service [ ]*



...


Options

-------

Options 是Services的修飾,它們影響init何時、如何運行service.


critical

這是一個設備關鍵服務(device-critical service) .如果它在4分鐘內退出超過4次,設備將重啟並進入恢復模式。


disabled

這個服務的級別將不會自動啟動,它必須被依照服務名指定啟動才可以啟動。


setenv

設置已啟動的進程的環境變量的值


socket [ [ ] ]

創建一個名為/dev/socket/的unix domin socket,並傳送它的fd到已啟動的進程。必須 為"dgram"或"stream".用戶和組默認為0.


user

在執行服務前改變用戶名。當前默認為root.如果你的進程需要linux能力,你不能使用這個命令。你必須在還是root時請求能力,並下降到你需要的 uid.


group [ ]*

在執行服務前改變組。在第一個組後的組將設為進程附加組(通過setgroups()).當前默認為root.


oneshot

在服務退出後不重啟。


class

為service指定一個類別名。同樣類名的所有的服務可以一起啟動或停止。如果沒有指定類別的服務默認為"default"類。


onrestart

當服務重啟時執行一個命令。


Triggers

--------

Triggers(觸發器)是一個字串,可以用來匹配某種類型的事件並執行一個action。


boot

這是當init開始後執行的第一個觸發器(當/init.conf被加載)


=

當property 被設為指定的值時觸發。


device-added-

device-removed-

當設備節點被添加或移除時觸發。


service-exited-

當指定的服務存在時觸發



Commands

--------


exec [ ]*

Fork並執行一個程序().這將被block直到程序執行完畢。最好避免執行例如內建命令以外的程序,它可能會導致init被阻 塞不動。


export

設定全局環境變量的值, 當這個命令執行後所有的進程都可以取得。


ifup

使網絡接口連 線。


import

解析一個init配置文件,擴展當前配置文件。


hostname

設置主機名


chmod

改變文件訪問權限


chown

改變文件所屬和組


class_start

當指定類別的服務沒有運行,啟動該類別所有的服務。


class_stop

當指定類別的服務正在運行,停止該類別所有的服務。


domainname

設置域名。


insmod

加載該路徑的模塊


mkdir [mode] [owner] [group]

創 建一個目錄,可選選項:mod,owner,group.如果沒有指定,目錄以755權限,owner為root,group為root創建.


mount [ ]*

嘗試mount 到目錄. 可以用mtd@name格式以命名指定一個mtd塊設備。包 含"ro","rw","remount","noatime".


setkey

暫時沒有


setprop

設置系統property 的值.


setrlimit

設置resource的rlimit.


start

啟動一個沒有運行的服務。


stop

停止一個正在運行的服務。


symlink

創建一個的 符號鏈接到


sysclktz

設置系統時區(GMT為0)


trigger

觸發一個事件。用於調用其他action。


write [ ]*

打開的文件並寫入一個或多個字串。



Properties

----------

Init 會更新一些系統property以提供查看它正在幹嘛。

init.action

當前正在執行的action,如果沒有則為""


init.command

被執行的命令,如果沒有則為""


init.svc.

命名為的服務的狀態("stopped", "running", "restarting")



init.rc 示例:

-----------------


# not complete -- just providing some examples of usage

#

on boot

export PATH /sbin:/system/sbin:/system/bin

export LD_LIBRARY_PATH /system/lib


mkdir /dev

mkdir /proc

mkdir /sys


mount tmpfs tmpfs /dev

mkdir /dev/pts

mkdir /dev/socket

mount devpts devpts /dev/pts

mount proc proc /proc

mount sysfs sysfs /sys


write /proc/cpu/alignment 4


ifup lo


hostname localhost

domainname localhost


mount yaffs2 mtd@system /system

mount yaffs2 mtd@userdata /data


import /system/etc/init.conf


class_start default


service adbd /sbin/adbd

user adb

group adb


service usbd /system/bin/usbd -r

user usbd

group usbd

socket usbd 666


service zygote /system/bin/app_process -Xzygote /system/bin --zygote

socket zygote 666


service runtime /system/bin/runtime

user system

group system


on device-added-/dev/compass

start akmd


on device-removed-/dev/compass

stop akmd


service akmd /sbin/akmd

disabled

user akmd

group akmd


調試

---------------

默 認情況下,init執行的程序輸出的信息和錯誤到/dev/null.為了debug,你可以通過Android程序logwrapper執行你的程序。 這將重定向輸出/錯誤輸出到Android logging系統(通過logcat訪問)。

例如

service akmd /system/bin/logwrapper /sbin/akmd

2010年5月28日 星期五

(轉載)Ubuntu 10.04 換成 sun-java-jdk 的步驟

ref: http://blog.cheyingwu.tw/index.php/2010/05/02/ubuntu-10-04-switch-to-sun-jdk/

話說沒讀 release notes 中地雷的人還蠻多的,我也是其中之一
在 ubuntu 10.04 預設是 openjdk 要換成 sun-java-jdk 也不是很難
首先先要加入 repository
sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
然後安裝 sun-jdk
sudo apt-get update
sudo apt-get install sun-java6-jdk sun-java6-plugin
然後手動切換成 sun-jdk
sudo update-java-alternatives -s java-6-sun
這樣就可以換成 sun-jdk 了
話說沒讀 release notes 中地雷的人還蠻多的,我也是其中之一
在 ubuntu 10.04 預設是 openjdk 要換成 sun-java-jdk 也不是很難
首先先要加入 repository
sudo add-apt-repository 『deb http://archive.canonical.com/ lucid partner』
然後安裝 sun-jdk
sudo apt-get update
sudo apt-get install sun-java6-jdk sun-java6-plugin
然後手動切換成 sun-jdk
sudo update-java-alternatives -s java-6-sun
這樣就可以換成 sun-jdk 了

=========================================
順道一題,由於想要在ubuntu上安裝android的開發套件
所以需要安裝eclipse and ADT
之前第一次安裝的ubuntu是32位元版,沒碰到什麽問題
後來第二次安裝ubuntu時選擇安裝64位元版本, 結果在安裝ADT確遇到問題了(這邊有提供解法 http://developer.android.com/sdk/installing.html#troubleshooting)
為了省去麻煩, 建議還是安裝32位元版的ubuntu就好
不然後續可能要多作一些額外處理才能正常使用android開發環境

補充:要安裝Android開發套件,安裝完eclipse之後還要確定有安裝Eclipse Java development tools (JDT), 之後才能安裝ADT

2010年5月27日 星期四

Thinkpad Trackpoint Scrolling in ubuntu 10.04

轉錄 (http://psung.blogspot.com/2010/04/thinkpad-trackpoint-scrolling-in-ubuntu.html)

Thinkpad TrackPoint Scrolling in Ubuntu Lucid/10.04

Another Ubuntu release, another set of X.org shakeups.
Some things in X changed in Lucid (xorg 1:7.5+5 and higher), breaking existing Thinkpad TrackPoint scrolling configurations that modify files in /etc/hal/fdi/policy (like those you may have seen in this previous post). You can use gpointing-device-settings to apply the same policy, but I found that even that stops working after a suspend/resume cycle.
Samson Yeung pointed out to me the following fix which can be applied on Ubuntu Lucid/10.04:
Create a new file /usr/lib/X11/xorg.conf.d/20-thinkpad.conf with the following contents:
Section "InputClass"
    Identifier "Trackpoint Wheel Emulation"
    MatchProduct "TrackPoint"
    MatchDevicePath "/dev/input/event*"
    Driver "evdev"
    Option "EmulateWheel" "true"
    Option "EmulateWheelButton" "2"
    Option "Emulate3Buttons" "false"
    Option "XAxisMapping" "6 7"
    Option "YAxisMapping" "4 5"
EndSection
Then restart X.
This configuration seems to be general enough that it works for both Thinkpad laptops with TrackPoints and the Thinkpad TrackPoint keyboard.
Source: instructions derived from these instructions by Samson Yeung. Thanks, Samson!

2010年5月26日 星期三

mplayer video out driver (轉載)

在很多應用中的圖形輸出數據是存放在一些指定的內存區域的,比如說我現在實現的基於 minigui圖形中間件的mplayer播放器的,在播放前需要告訴mplayer當前播放窗口所在的內存地址和當前的屏幕像素深度,bpp,窗口大小等 等一系列的信息,這就有必要給mplayer寫個自己的video_out driver了。和大 多數驅動一樣,mplayer也提供了一個驅動函數結構體,其結構體定義如下(在libvo/video_out.h文件中定義):
typedef struct vo_functions_s{
 
vo_info_t *info;
 
/*
  
* Preinitializes driver (real INITIALIZATION)
  
* arg - currently it's vo_subdevice
  
* returns: zero on successful initialization, non-zero on error.
  
*/
 
int (*preinit)(const char *arg);
 
/*
 
* Initialize (means CONFIGURE) the display driver.
  
* params:
  
* width,height: image source size
  
* d_width,d_height: size of the requested window size, just a hint
  
* fullscreen: flag, 0=windowd 1=fullscreen, just a hint
  
* title: window title, if available
  
* format: fourcc of pixel format
  
* returns : zero on successful initialization, non-zero on error.
  
*/
 
int (*config)(uint32_t width, uint32_t height, uint32_t d_width,
 
uint32_t d_height, uint32_t fullscreen, char *title,
 
uint32_t format);

 
/*
  
* Control interface
  
*/
 
int (*control)(uint32_t request, void *data, ...);

 
/*
 
* Display a new RGB/BGR frame of the video to the screen.
 
* params:
 
* src[0] - pointer to the image
 
*/
 
int (*draw_frame)(uint8_t *src[]);

 
/*
  
* Draw a planar YUV slice to the buffer:
  
* params:
  
* src[3] = source image planes (Y,U,V)
  
* stride[3] = source image planes line widths (in bytes)
  
* w,h = width*height of area to be copied (in Y pixels)
  
* x,y = position at the destination image (in Y pixels)
  
*/
  
int (*draw_slice)(uint8_t *src[], int stride[], int w,int h, int x,int y);

  
/*
   
* Draws OSD to the screen buffer
   
*/
  
void (*draw_osd)(void);

  
/*
  
* Blit/Flip buffer to the screen. Must be called after each frame!
  
*/
  
void (*flip_page)(void);

  
/*
   
* This func is called after every frames to handle keyboard and
   
* other events. It's called in PAUSE mode too!
   
*/
   
void (*check_events)(void);

   
/*
    
* Closes driver. Should restore the original state of the system.
    
*/
   
void (*uninit)(void);
} vo_functions_t;
1:初始化驅動(preinit 函數、config函數和uninit 函數):當驅動初始化時將調用preinit ,退出時則調用uninit函數(注意:播放器在每播放一個媒體文件時都會初始化一次video驅動的)。在preinit函數和config函數里可以做我們一些初始化工作,比如指定存放音頻的內存地址,當前設備支持的像素,窗口大小,和 bpp(每像素點需要用多少bit存儲))等工作。而uninit 函數則可以做些收尾工作了....
2:告訴mplayer設備支持的圖形顯示格式在mplayer調用完preinit 函數後, 它就要開始輸出數據啦。它在輸出前 會去查詢下驅動支持的像素格式等一些大小,這時候它將調用驅動的control函數,並且傳遞VOCTRL_QUERY_FORMAT參數, 這時候我們就應該把preinit函數中設置的參數告訴mplayer,然後mplayer就按設備支持的 格式輸出數據,
3:獲取 mplayer圖形數據:獲取mplayer數據有很多種方法,這些方法可以根據設備來選擇,這裡我們只使用最簡單的方法VOCTRL_DRAW_IMAGE。同樣還是在control函數中,當mplayer輸出數據將會調用control函數並傳遞VOCTRL_DRAW_IMAGE參 數和mp_image_t指針,我們可以通過mp_image_t指針來獲取圖像數據的大小,存放的位置等必要信息,然後將這些存放在顯示內存中顯示出來...int dx = mpi->wint dy = mpi->huint8_t *buf = mpi->planes[0]int stride = mpi->stride[0]
4:其它:config 函數:mplayer將調用config函數來通知驅 動,mplayer顯示圖形的原始大小,和播放的最佳大小,和影片標題等信息,這些信息只是給驅動提供參考值的.還有其它draw_frame,draw_slice,draw_osd,flip_page等接口可以按需使用,充分利用 mplayer提供的接口可以打造一個性能較優的video_out驅動的。
5:結尾現在只是使用了簡單的mplayer驅動的方法,其實還有很多點可以去優化的,比如說讓mplayer直接將數據寫到顯示內存中,而 不再先放入mp_image_t結構體中,充分利用其它的幾個接口來提高驅動的性能。

mplayer音頻解碼分析

轉錄(http://dev.firnow.com/course/3_program/c++/cppjs/20090903/173578.html)
一.入口
    
main函數中的入口如下~  

/*======== PLAY AUDIO ===============*/  
if (mpctx->sh_audio)
    
if (!fill_audio_out_buffers())
    
// at eof, all audio at least written to ao
    
由mpctx->sh_audio引出,我想重 點強調一下sh_audio_t結構,這是音頻流頭部結構,要仔細看看(註釋也別放過)~  

// Stream headers: typedef struct {
  
int aid;
  
demux_stream_t *ds;
  
struct codecs_st *codec;
  
unsigned int format;
  
int initialized;
  
float stream_delay; // number of seconds stream should be delayed (according to dwStart or similar)
  
// output format:
  
int sample_format;
  
int samplerate;
  
int samplesize;
  
int channels;
  
int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec)
  
int i_bps; // == bitrate (compressed bytes/sec)
  
// in buffers:
  
int audio_in_minsize; // max. compressed packet size (== min. in buffer size )
  
char* a_in_buffer;
  
int a_in_buffer_len;
  
int a_in_buffer_size;
  
// decoder buffers:
  
int audio_out_minsize; // max. uncompressed packet size (==min. out buffsize )
  
char* a_buffer;
  
int a_buffer_len;
  
int a_buffer_size;
  
// output buffers:
  
char* a_out_buffer;
  
int a_out_buffer_len;
  
int a_out_buffer_size;
  
struct af_stream_s *afilter; // the audio filter stream
  
struct ad_functions_s* ad_driver; #ifdef DYNAMIC_PLUGINS
  
void *dec_handle; #endif
  
// win32-compatible codec parameters:
  
AVIStreamHeader audio;
  
WAVEFORMATEX* wf;
  
// codec-specific:
  
void* context; // codec-specific stuff (usually HANDLE or struct pointer)
  
unsigned char* codecdata; // extra header data passed from demuxer to codec

  
int codecdata_len;
  
double pts; // last known pts value in output from decoder
  
int pts_bytes; // bytes output by decoder after last known pts
  
char* lang; // track language
  
int default_track; } sh_audio_t;
二.step by step 1.下面我們來分析 fill_audio_out_buffers()函數 static int fill_audio_out_buffers(void)
1.1查看音頻驅動有多大的空間可以填充解碼後的音頻 數據 bytes_to_write = mpctx->audio_out->get_space();
如果有空閒的空間,Mplayer會調用 ao->play()函數來填充這個buffer,並播放音頻數據。 這個音頻驅動的buffer的大小要設置合適,太小會 導致一幀圖像還沒播放完,buffer就已 經空了,會導致音視頻嚴重不同步;太大會導致 Mplayer需要不停地解析媒體文件來填充這 個音頻buffer,而視頻可能還來不及播放。
1.2 對音頻流進行解碼 if (decode_audio(sh_audio, playsize) < 0) 注:這裡的decode_audio只是一個接口,並 不是具體的解碼api。 這個函數從 sh_audio->a_out_buffer獲得至少playsize bytes的解碼或過濾後的音頻數據 ,成功返回0,失敗返回-1
1.3 將解碼後的音頻數據進行播放 playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags) ; 注:從 sh_audio->a_out_buffer中copy playsize bytes數據,注意這裡一定要copy出來, 因為當play調用後,這部分數據就會被覆蓋了。這些數據不一定要用完,返回的值是用掉 的數據長度。另外當 flags|AOPLAY_FINAL_CHUNK的值是真時,說明音頻文件快結束了。

1.4 if (playsize > 0) {
            
sh_audio->a_out_buffer_len -= playsize;
            
memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize],
                    
sh_audio->a_out_buffer_len);
            
mpctx->delay += playback_speed*playsize/(double)ao_data.bps;
        
} 播放成功後就從 sh_audio->a_out_buffer中移出這段數據,同時減去 sh_audio->a_out_buffer_len 的長度

2.剛才說了,decode_audio()函數並不 是真正的解碼函數,它只是提供一個接口,下面我 們就來看看這個函數到底做了哪些事情 int decode_audio(sh_audio_t *sh_audio, int minlen)
2.1 解碼後的視頻數據都被cut成大小相同的一個個區間~
    
int unitsize = sh_audio->channels * sh_audio->samplesize * 16;
2.2 如果解碼器設置了audio_out_minsize,解碼可以等價於
    
while (output_len < target_len) output_len += audio_out_minsize; 因此我們必需保證a_buffer_size大於我們 需要的解碼數據長度加上audio_out_minsize, 所以我們最大解碼長度也就有瞭如下的限制:(是不是有 點繞口?~~)
    
int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize ;
2.3 如果a_out_buffer中沒有我們需要的足夠多的解碼後數據,我們當然要繼續解碼撒~
只不過我們要注意,a_out_buffer中的數據 是經過filter過濾了的(長度發生了變化),這 裡會有一個過濾係數,我們反方向求過濾前的數據長度, 所以要除以這個係數。 while (sh_audio->a_out_buffer_len < minlen) {
        
int declen = (minlen - sh_audio->a_out_buffer_len) / filter_multiplier
            
+ (unitsize << 5); // some extra for possible filter buffering
        
declen -= declen % unitsize;
        
if (filter_n_bytes(sh_audio, declen) < 0)
            
return -1;
    
}

3.我們來看看這個filter_n_bytes函數 static int filter_n_bytes(sh_audio_t *sh, int len)
3.1 還記得我們剛才說的那個最大解碼長度嗎?這裡是要確保不會發生解碼後數據溢出。
assert(len-1 + sh->audio_out_minsize <= sh->a_buffer_size);
3.2 按需要解碼更多的數據
    
while (sh->a_buffer_len < len) {
        
unsigned char *buf = sh->a_buffer + sh->a_buffer_len;
        
int minlen = len - sh->a_buffer_len;
        
int maxlen = sh->a_buffer_size - sh->a_buffer_len;
        
//這裡才是調用之前確定的音頻解碼器進行真正音頻解 碼
        
int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen);
        
if (ret <= 0) {
            
error = -1;
            
len = sh->a_buffer_len;
            
break;
        
}
        
//解碼之後a_buffer_len增加
        
sh->a_buffer_len += ret;
    
}
3.3 在前面我們提到過過濾這個步驟,下面的圖描述了整個過程 filter_output = af_play(sh->afilter, &filter_input); 注:這裡實際上做的是過濾這部分的工作

    ------------                    ----------               ------------  
|        | 解碼|                    | 過濾|                  | 播放 
| 未解碼數據| ----->|解碼後數據| ------>| 播放的數據| ------>  
| a_in_buffer|              | a_buffer |          |a_out_buffer|
   ------------                     ----------                   ------------
3.4 if (sh->a_out_buffer_size < sh->a_out_buffer_len + filter_output->len) 注:如果過濾後的數據長度太長,需要擴展 a_out_buffer_size
3.5 將過濾後的數據從decoder buffer移除:
    
sh->a_buffer_len -= len;
    
memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len);
三.結語
    
回顧一下今天的內容,我們從sh_audio_t結構 體出發,依次分析了fill_audio_out_b uffers()函數,decode_audio() 函數,filter_n_bytes()函數,但是並沒有分析具體的de code和play函數。
    
順便說點題外話,看Mplayer的代碼,結構化模塊 化的特點很突出,通常是先定義一組 接口,然後具體的程序去實現這些接口,這樣子對
代碼的維護和擴展都很有好處~

2010年5月25日 星期二

ubuntu熱鍵

Ctrl + N New 開新檔案
Ctrl + O Open 開啟舊檔
Ctrl + A Select all 全選
Ctrl + X Cut 剪下
Ctrl + C Copy 複製
Ctrl + V Paste 貼上
Ctrl + Z Undo 重來、上一步
Ctrl + Y Redo 復原(與上一 步相反功能)
Ctrl + S Save 儲存
Ctrl + W Close 關閉檔案
Ctrl + Q Quit 離開程式
Alt + Tab
切換視窗
Alt + Shift+Tab
反向切換視窗
Alt + Space
視窗控制選單
(與在視窗標題列案滑鼠右鍵同功能)
Alt + Print Screen
擷取目前視窗畫面
Ctrl + Alt + d
全部視窗最小化
Alt + F1
應用程式下拉選單
alt + 滑鼠左鍵
可以移動視窗
alt + 滑鼠中鍵
可以resize視窗
Ctrl + Alt + right arrow
切換桌面
Ctrl + Alt + left arrow
切換桌面
Ctrl + Alt + up arrow
切換桌面
Ctrl + Alt + down arrow
切換桌面
Ctrl + Alt + F1 = 跳到文字模式1(virtual terminals)
Ctrl + Alt + F2(F3)(F4)(F5)(F6) = 跳到文字模式2~6
Ctrl + Alt + F7 = 回到桌面
Ctrl + Alt + Backspace 跳出桌面,進入登入畫面

2010年5月24日 星期一

How to disable touchpad on ubuntu 9.10/10.04


If you want to disable your touchpad on Ubuntu 9.10/10.04 then issue the following commnad once:
gconftool-2 --type boolean --set /desktop/gnome/peripherals/mouse/touchpad_enabled false
Then to disable the touchpad issue the following command:
synclient TouchpadOff=1
This command is just active for the session so when you reboot your machine you will have to issue it again.  What I have done is to add it to my Startup Programs.  You will find Startup Programs under System|Preferences|Startup Applications.

2010年5月17日 星期一

建構ltrace for ARM

steps:
1. 從 http://packages.qa.debian.org/l/ltrace.html 下載ltrace source code (ltrace-0.5.3-2.tar.gz)
2. tar -zxv -f ltrace-0.5.3-2.tar.gz
3. ./configure --host=arm-linux CC=arm-linux-gcc LD=arm-linux-ld   ,  配置makefile
4. make
5. (可做可不做) arm-linux-strip ltrace  ,  縮減ltrace的size

完成, 之後就可以把strace copy到ARM 的 linux平台上執行了

2010年5月14日 星期五

建構strace for ARM

steps:
1. 從 http://sourceforge.net/projects/strace/files/strace/ 下載strace source code, 目前最新版為4.5.20 (strace-4.5.20.tar.bz2)
2. tar -jxv -f strace-4.5.20.tar.bz2
3. ./configure --host=arm-linux CC=arm-linux-gcc LD=arm-linux-ld   ,  配置makefile
4. make CFLAGS="-static"   , 產生靜態可執行檔
5. (可做可不做) arm-linux-strip strace  ,  縮減strace的size

完成, 之後就可以把strace copy到ARM 的 linux平台上執行了

2010年5月13日 星期四

在MinGW / MSYS環境下使用GDB Ctrl+C來突然中斷程式卻失敗的workaround

我是GDB的初學者,正確說Linux的環境其實都還不熟, 但公司整個開發環境都是以linux為base, 所以必須要熟析上面的開發工具, GDB就是其中一個, 因此我自己先在Windows系統上架設了仿Linux bash的環境也就是(MinGW + MSYS), 想要先在windows下執行程式並用GDB去debug trace, 藉此瞭解程式的運作和workflow, 但因為該程式是multi-thread的, 但我又不清楚整個運作流程, 所以想要先讓程式run一段時間後在中途中斷, 觀察一下thread的create情況, 但每次當我想要這樣做時(先用GDB 載入要trace的程式, 然後讓它執行一段時間) , 按下Ctrl+C想藉此中斷該程式, 每次這樣做都會讓整個程式停止並跳出執行, 根本無法順利的中途中斷它然後繼續使用GDB commands來一步一步trace. 這是前情提要

後來在網路上(經由Orson的幫忙搜尋) 找到了這個討論串GDB Ctrl-C Interrupt Fails WORKAROUND(http://cygwin.com/ml/cygwin/2006-06/msg00321.html), 自己動手try了一下, 確實可行耶, 雖然它是for cygwin的, 但在MinGW+MSYS下也是可以運作的, 差別是我compile該程式的方法省略掉了"-mno-cygwin", 也就是compile語法為
   gcc -o debugbreak.exe -mthreads debugbreak.c

以下我還是貼出那個討論串的程式內容

/* BEGIN debugbreak.c */

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#if _WIN32_WINNT < 0x0501
#error Must target Windows NT 5.0.1 or later for DebugBreakProcess
#endif

#include
#include
#include

/* Compile with this line:

gcc -o debugbreak -mno-cygwin -mthreads debugbreak.c

*/

static char errbuffer[256];

static const char *geterrstr(DWORD errcode)
{
    size_t skip = 0;
    DWORD chars;
    chars = FormatMessage(
    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL, errcode, 0, errbuffer, sizeof(errbuffer)-1, 0);
    errbuffer[sizeof(errbuffer)-1] = 0;
    if (chars) {
        while (errbuffer[chars-1] == '\r' || errbuffer[chars-1] == '\n') {
            errbuffer[--chars] = 0;
        }
    }
    if (chars && errbuffer[chars-1] == '.') errbuffer[--chars] = 0;
    if (chars >= 2 && errbuffer[0] == '%' && errbuffer[1] >= '0' && errbuffer[1] <= '9')
    {
        skip = 2;
        while (chars > skip && errbuffer[skip] == ' ') ++skip;
        if (chars >= skip+2 && errbuffer[skip] == 'i' && errbuffer[skip+1] == 's')
        {
        skip += 2;
        while (chars > skip && errbuffer[skip] == ' ') ++skip;
        }
    }
    if (chars > skip && errbuffer[skip] >= 'A' && errbuffer[skip] <= 'Z') {
        errbuffer[skip] += 'a' - 'A';
    }
    return errbuffer+skip;
}

int main(int argc, char *argv[])
{
    HANDLE proc;
    unsigned proc_id = 0;
    BOOL break_result;

    if (argc != 2) {
        printf("Usage: debugbreak process_id_number\n");
        return 1;
    }
    proc_id = (unsigned) strtol(argv[1], NULL, 0);
    if (proc_id == 0) {
        printf("Invalid process id %u\n", proc_id);
        return 1;
    }
    proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)proc_id);
    if (proc == NULL) {
        DWORD lastError = GetLastError();
        printf("Failed to open process %u\n", proc_id);
        printf("Error code is %lu (%s)\n", (unsigned long)lastError,
            geterrstr(lastError));
        return 1;
    }
    break_result = DebugBreakProcess(proc);
    if (!break_result) {
        DWORD lastError = GetLastError();
        printf("Failed to debug break process %u\n", proc_id);
        printf("Error code is %lu (%s)\n", (unsigned long)lastError,
            geterrstr(lastError));
        CloseHandle(proc);
        return 1;
    }
    printf("DebugBreak sent successfully to process id %u\n", proc_id);
    CloseHandle(proc);
    return 0;
}

/* END debugbreak.c */

2010年1月13日 星期三

開版首波

我現在在公司,紀念一下..