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 */