顯示具有 電腦 標籤的文章。 顯示所有文章
顯示具有 電腦 標籤的文章。 顯示所有文章

2014年11月4日 星期二

消掉 ^M 的方法

用 vim 開啟文字檔時,常常會看到結尾會有 ^M 這種討厭的符號。

如果是在 vim 裡面,可以打 %s/\r//g ,就可以把 ^M 消掉。

如果是在命令列,可以用 sed ‘s/\r//g’ <filename>,就可以得到沒有 ^M 的輸出,再用 > 符號轉向要存的檔案即可;或是使用  sed ‘s/\r//g’ -i <filename> 直接修改原檔。

當然,也可以配合 find 指令,把目前目錄下的所有檔案的 ^M 全部幹掉;命令如下

find . -type f -exec sed -i 's/\r//g' {} \;

2014年7月27日 星期日

Asus VW266h LCD 故障維修 DIY


買了三年多的 Asus VW266h LCD,前一段時間故障了,又剛好過保固沒多久。
查了一下,「VW266h 故障」之類的關鍵字,很多人都是一樣的壞法:一開始畫面會有很多橫條紋雜訊,熱機一下子就正常,關機久一點後再開機,又會出現一樣的橫條紋雜訊;如果不處理它,熱機到正常的時間會越來越久;看到有人的修法是電容全部換掉,但是是哪一顆電容故障也不能確定。
乾脆把 LCD 拆開,把電容一顆顆慢慢換慢慢測試,確定問題出在 C223 這顆 100uF/16V 電容掛掉,可能導致 1.8V 穩壓有問題。
換掉電容後到今天三個多月,使用上一切正常。

2012年12月8日 星期六

Ubuntu 桌面下回到 command line 的方法


安裝完 Ubuntu Server,用 apt-get 安裝桌面系統,本來都 run 的好好的,今天下午某個軟體出問題,導致工具列都消失掉,網路介面也還沒設定好,不能遠端 reboot。

發生這種情況,除了含淚按下 power 鍵外,還有另一種選擇。

按下 ctrl-alt-F1,就可以看到熟悉的 login: 畫面,登入後再打 sudo reboot,或重啟桌面都行。
ctrl-alt-[F1-F6] 可以進入 tty1 到 tty6,若要回到桌面,按 ctrl-alt-F7 即可。

2012年12月6日 星期四

KVM 在 Core i7 上效能低落的原因

在一台 Core i7的機器上,安裝 XenServer,開了一個 Ubuntu 的 Server,速度很快。
在同一台機器上,直接安裝Ubuntu,在KVM上開一台VM起來,速度跟XenServer的VM有明顯落差,開始找原因。

一開始是往 disk 的方向找;XenServer是使用一個LV當做VM的disk,在KVM上我本來是用file當做VM的disk;使用一個LV給KVM的VM當disk後,速度一樣緩慢,這個可能有影響,但不是主因。

以下是使用libvirt,劃一個LV給VM用的domain XML片段
<disk device="disk" type="block"> <driver name="qemu" type="raw"> <source dev="/dev/ubuntu/kvm_3"></source> <target bus="ide" dev="hda"> </target></driver></disk>
其中,/dev/ubuntu/kvm_3即是一個LV。

後來乾脆在Ubuntu安裝GUI (Ubuntu Server 原本無 GUI),再安裝virt-manager,使用virt-manager create一個VM,同樣使用file當VM的image;start VM後,發現VM速度很快,至此幾乎確認問題應該不在 disk。

使用virsh dumpxml將virt-manager create的VM的XML show出來,再一行一行比對,發現最根本原因在於第一行,virt-manager的VM是 <domain id="1" type="kvm">
我原來用libvirt create VM的第一行是 <domain type="qemu">
答案揭曉,若將domain type設定為qemu,會呼叫的emulator為/usr/bin/qemu-system-x86_64;若將domain type設定為kvm,emulator為/usr/bin/kvm。

因為Core i7有 vmx flags,使用kvm的方式會快上許多,若使用qemu當emulator,可能會有一次轉換的動作,導致效能低落。

將所有 VM 的 domain type 換成 KVM 後,速度已有明顯改善。

2012年12月5日 星期三

ssh 登入很慢

問題:每次要登入公司的server,打完 ID 後,要等 password: 出來時,都會等個十秒鐘左右的時間。

原因:ssh進行DNS反查,PC 沒hostname

解決方法:修改 /etc/ssh/sshd_config (需有 root 權限),加入 「UseDNS no」,存檔,再 sudo service ssh restart 即可。

2012年12月1日 星期六

CentOS 和 Ubuntu 改網路設定的方法整理

要修改 CentOS 和 Ubuntu 的網路設定,作法有點不一樣,整理如下

CentOS

  • 修改 /etc/sysconfig/network-scripts/ifcfg-eth0
  • 執行 sudo service network restart


Ubuntu

  • 修改 /etc/network/interfaces
  • 執行 sudo service networking restart


有一些地方需註記一下

  • 在CentOS 6.3中,一改完ifcfg-eth0後,在很短的時間內就會自動生效,不用執行 service network restart 也會有效果。會發現這點是因為我在改的時候,不小心把 IP 的設定打錯,存檔後幾秒鐘就斷線,這時我還未執行 service network restart。Ubuntu 則沒有這個現象,一定要執行 service networking restart 才會生效。在改 CentOS 的設定檔時需小心檢查,如果不在機器旁,設定又改錯,一存檔就糗了。
  • 要改 nameserver 的設定,傳統作法是改 /etc/resolv.conf,現在的作法是改上述的設定檔。在CentOS的 ifcfg-eth0 中,設 DNS1=8.8.8.8;在 Ubuntu 的 interfaces中,設dns-nameservers=8.8.8.8,就可以把 nameserver 設為 google 的 DNS server 了。設定完這些檔後,/etc/resolv.conf會依據這些設定檔自動修改,加入nameserver 8.8.8.8;直接改 /etc/resolv.conf 不是正確的作法。 

2012年11月30日 星期五

lvm 縮減根目錄容量

LVM 可以讓各個 filesystem 的容量自由增減,細節請參照鳥哥網頁超清楚說明
http://linux.vbird.org/linux_basic/0420quota.php#lvm

我的需求如下:原本我的 / 是 50G,/home 20G,我想變成 / 30G,/home 40G,把 / 的 20G 移給 /home,因此,我要先把 / 從 50G 降成 30G,再把 /home 從 20G 變成 40G。

依照說明,reduce size 有以下動作,假設 / 在 /dev/VolGroup/lv_root 上

umount /
e2fsck -f /dev/VolGroup/lv_root
resize2fs /dev/VolGroup/lv_root 20G
lvreduce -L 20G /dev/VolGroup/lv_root

但是第一步就出了問題;/ 沒有辦法 umount,後續動作全部不能做。

解法:使用 DVD 的 Rescue。
  1. 拿出你的 Linux DVD,我是用 CentOS 6.3 的 DVD
  2. 放進去後開機,選 Rescue installed system
  3. 然後會問你 Language、Keyboard Type,這個以大家的聰明才智應該知道怎麼選
  4. Rescue Method 選 Local CD/DVD
  5. Setup Networking 可以選 No
  6. Rescue 選 Skip,才不會幫你 mount 任何 filesystem。
  7. 選 Shell Start shell,跳進 shell 裡面,df 原來的 filesystem 都沒有被 mount,可以開始做你想做的事了

2012年11月28日 星期三

無法 virsh shutdown CentOS

libvirt 的 virsh shutdown 無效,CentOS 不會關機,一定要 login 進 CentOS 再 poweroff 才行。
解決方法很簡單,到 CentOS 下,yum acpid 就好了。

2012年9月27日 星期四

查詢 Linux Distribution 及版本

我也不知道為什麼到今天我才知道這個指令。

以前只會用 uname -a ,然後根據 kernel version 去猜是哪個 distribution 和 version。 原來有個更好用的指令。直接打 lsb_release -a 就可以看到的。

以下是實驗室某 Original Server。

[root@localhost ~]# lsb_release -a
LSB Version: :core-4.0-amd64:core-4.0-ia32:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-ia32:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-ia32:printing-4.0-noarch
Distributor ID: CentOS
Description: CentOS release 5.7 (Final)
Release: 5.7
Codename: Final

如果只要看是用哪個 Distribution,可以用 lsb_release -i

[root@localhost ~]# lsb_release -i
Distributor ID: CentOS

如果只要看版本號碼,可以用 lsb_release -r
[root@localhost ~]# lsb_release -r
Release: 5.7

如果要看 Description 那個欄位,包含是哪個 Distribution 及版號,可以用 lsb_release -d
[root@localhost ~]# lsb_release -d
Description: CentOS release 5.7 (Final)

2011年7月14日 星期四

Android Emulator 問題

今天打開已經長了厚厚一層灰塵的 Elicpse,發現需要更新;全部更新完後,很自然地按下 Run,然後出現…

[2011-07-14 20:34:28 - Emulator] invalid command-line parameter: Files\Android\android-sdk-windows\tools/emulator-arm.exe.
[2011-07-14 20:34:28 - Emulator] Hint: use '@foo' to launch a virtual device named 'foo'.
[2011-07-14 20:34:28 - Emulator] please use -help for more information

奇怪,之前一模一樣的步驟都可以執行的。

原本還懷疑是不是更新不完全,有股衝動想要把整個SDK重新安裝;後來在這個網頁看到了原因

https://code.google.com/p/android/issues/detail?id=18317

原來是SDK安裝在”Program Files”目錄底下,Program跟Files中間的空格在搞鬼。

解決方法:按下Eclipse的Windows→Preferences,在跳出來的視窗選擇Android,然後在SDK Location把目錄位址改成C:\Progra~1\Android\android-sdk-windows,一切就正常了。

2011年1月4日 星期二

使用ffmpeg判斷壓縮的CODEC

要判斷一個影片檔使用的Video CODEC和Audio CODEC,可以藉ffmpeg實現。

程式碼如下:

 

#include <libavformat/avformat.h>

int main(int argc, char *argv[]) {

    AVFormatContext *avformat;

    av_register_all();
    av_open_input_file(&avformat,argv[1],NULL,0,NULL);
    av_find_stream_info(avformat);
    dump_format(avformat, 0, argv[1], 0);

    return 0;
}

 

記得要先安裝ffmpeg library,並且在compile上面的程式時,要link libavformat library, 即 gcc xxx.c -lavformat。

用法很簡單,就執行檔後面跟著要判斷的影片檔即可。輸出範例如下:

> ./a.out ~/iPod_video/dance.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/khchung/iPod_video/dance.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf52.47.0
  Duration: 00:06:17.42, start: 0.000000, bitrate: 1262 kb/s
    Stream #0.0(und): Video: h264, yuv420p, 480x320 [PAR 1:1 DAR 3:2], 1132 kb/s, 24 fps, 24 tbr, 24 tbn, 48 tbc
    Stream #0.1(und): Audio: aac, 48000 Hz, stereo, s16, 126 kb/s

可以看出,Video 部份是做用H.264壓縮,audio 則是用 AAC 壓縮。

2010年12月28日 星期二

在Linux下計算程式行數

find . \( -name *.cpp -or -name *.h -or -name *.c \) -type f -exec cat {} \; | wc –l

看起來有點長。

簡單地說,就是使用find, 利用name找出所有cpp、h、c副檔名的檔案,cat出來,然後轉給wc去計算行數。

find很好用,但是容易讓不熟悉的人退卻;有機會再來寫一篇簡介find的基礎用法。

2010年12月15日 星期三

簡述 Linux Kernel source 管理

安裝

首先,要先有一份kernel source,到 http://www.kernel.org/pub/linux/kernel/v2.6/ 目錄下抓你要的版本。例如,你想要抓 2.6.34 的 kernel source,就打

> wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.34.tar.bz2

注意,在這個目錄下,有兩種檔案;linux開頭的是完整的source,patch開頭的,就只是patch,之後要做升級動作時用得到patch檔。

這個地方由於我們是要抓source,所以選擇linux-2.6.34.tar.bz2,而不是patch-2.6.34.tar.bz2。

抓下來後,先到 /usr/src/kernels 目錄下。

> cd /usr/src/kernels

接著,將剛剛抓下來的 kernel source 解開。

> bzip2 -dc linux-2.6.XX.tar.bz2 | tar xvf -

然後,就可以看到螢幕狂跑;解完後,打個 ls ,就可以看到 linux-2.6.34 的目錄,cd 進去,再 ls ,就可以看到完整的 kernel source 乖乖躺在你的硬碟裡了。

patch

patch有兩種,一種是2.6.x的patch,一種是2.6.x.y的patch。

2.6.x的patch,例如,想從2.6.34 patch至2.6.35,到http://www.kernel.org/pub/linux/kernel/v2.6/ 目錄下抓 patch-2.6.35.tar.bz2

> wget http://www.kernel.org/pub/linux/kernel/v2.6/patch-2.6.35.tar.bz2

假設放在 /usr/src/kernels 目錄下;接著,進行 patch 動作

> bzip2 -dc ../patch-2.6.35.bz2 | patch –p1

然後就可以看到又是一堆訊息跑出來,告訴你更新了哪些檔,跑完後,你的 kernel source 就變成 2.6.35 了,所以,最好改一下目錄名。

> cd ../; mv linux-2.6.34 linux-2.6.35

若要從2.6.34升到2.6.36,也必須乖乖地先patch至2.6.35,再patch至2.6.36。

至於2.6.x.y的patch,方法跟2.6.x的一樣,不同的是,不需要一步一步漸增式的慢慢patch上去,而是一次到位即可。

例如,想從2.6.34 patch至2.6.34.5,只要直接抓2.6.34.5的patch,做一次即可,不用再做2.6.34.1、2.6.34.2、2.6.34.3、2.6.34.4、2.6.34.5這麼多次。

但是,若是要從2.6.34.2 patch成2.6.34.5,則必須先reverse至2.6.34,再patch至2.6.34.5。如下:

> patch –p1 –R < ../patch-2.6.34.2

> patch –p1 < ../patch-2.6.34.5

上面的 –R ,就是指 reverse。

 

文件可參考kernel source內的README及Documentation/applying-patches.txt兩個檔。

以上,報告完畢。

2010年6月23日 星期三

CentOS + Linux kernel 2.6.3x

如果直接抓Linux Kernel (2.6.31以上) 回來,使用make oldconfig的方式做出來的image,開不了機的話,可以試著修改兩個地方。

  1. 打開.config,找到CONFIG_SYSFS_DEPRECATED_V2的字串,改成=y
  2. 將initrd解開,打開init這個張,會看到dm_region_hash被insmod了兩次,把後面那次拿掉

重新開機,可以動的話,恭喜恭喜。

2010年4月20日 星期二

將數字將成 QString 的方法

如果要將一個數字轉成 QString class,可以使用下列用法:

int i = 127;
QString str;
str = QString::number(i, 10);

其中,number的第二個參數指的是幾進位,如果沒填,預設是十進位。

這個用法我已經用了又忘好幾次了,筆記一下。

2010年4月18日 星期日

記載 MP3 的資訊 -- ID3v1

打開檔案總管,對著一個MP3檔案按下右鍵→內容,在跳出的視窗選摘要,切到進階,可以看到演出者、專輯標題、年份、曲目、類型、標題、註解等資訊;同時,使用大部份的MP3播放器,也可以看到這些資訊。

mp3_info

 

這些資訊可以由數種方式帶在MP3的檔案內,其中,最簡單的方式,即是ID3 v1。

ID3v1有兩種版本,ID3v1及ID3v1.1版;不同之處,在於1.1多出了可紀錄track number的功能。

ID3v1的資訊帶在檔案的最後面,128 bytes 的地方。使用UltraEdit或是PSPad或任何可以觀看十六進位的編輯器,開一個MP3檔,移到檔案的最後,可以看到記載ID3v1的資訊。

放在檔案最後面,最大的好處在於,當使用者決定要新增或移除ID3v1的全部資訊時,只要將最後128 bytes做加入或移除動作即可;如果放在檔案開頭,則需要對整個檔案的內容做移動的動作。

mp3_info_2

ID3v1

下表即是ID3v1的結構圖。

‘TAG’ 3 bytes
標題 30 bytes
演出者 30 bytes
專輯名稱 30 bytes
年份 4 bytes
註解 30 bytes
歌曲類型 1 bytes

 

ID3v1以‘TAG’ 三個字元起始,後面接著30 bytes的歌曲名稱,30 bytes的演出者,30 bytes的專輯名稱,4 bytes 的年份,30 bytes的註解及 1 bytes的歌曲類型。連同 TAG ,共 128 bytes 大小。

因此,實作上非常容易,只要fopen一個檔案,fread最後128 bytes的資料,判斷前三個bytes的內容是不是 TAG 三個字元,即可知道是否帶 ID3v1 的資訊;接著,再依照上表,就可以分別讀出所需的資訊。

 

ID3v1.1

如前所述,1.1 版新增了track的資訊;但1.1考慮到向後相容,且ID3v1無任何空間,也無任何保留的欄位可供利用,要怎麼做才不會導致原有的ID3v1無法運作?

首先,需先說明,在ID3v1裡,未填資料的地方,需填上0;例如,演出者為 ‘Vivaldi’,共七個字元,剩下的 23 bytes,均需填0。

這裡採用的作法,是將註解的30 bytes,挪出最後 2 bytes 記載 track number;這 2 個 bytes 的第一個 bytes 必須要為 0,後一個 bytes 則記載 track 的資訊;因此,只要註解的第 29 bytes 不為 0,即無 track 存在的可能性;但第 29 個 bytes 為 0,接著必需檢查第 30 個 bytes,如果不為 0,這個就是 track 的資訊。

 

下圖為ID3v1及ID3v1.1的圖示。

id3v1_blocks id3v1.1_blocks

 

可以看出,v1.1的Comment較短,且新增了Album track的欄位;差別即在於此。

如需更詳細的資料,可以至 http://www.id3.org 查閱。

2010年4月15日 星期四

PuTTY 按下 Ctrl-S 畫面凍結的解決方法

常發生這種情況:使用 PuTTY 連到工作站,開了 vim 寫程式,寫到一半很自然會想存檔,很自然地就按了 Ctrl-S,然後整個視窗就停住了!只好含著淚開另一個視窗把 process kill 掉...

其實這是 PuTTY 一個好用的功能,當在 compile 或跑程式的時候,log 拼命地跑出來,這時想把畫面停下來,看看 log 訊息,就可以按下Ctrl-S,整個畫面就會凍結住;等看完後,要讓畫面回復正常,只要再按下 Ctrl-Q 就可以了。

2009年12月7日 星期一

ffmpeg 中 swscale 的用法

如果想將某個PixelFormat轉換至另一個PixelFormat,例如,將YUV420P轉換成YUYV422,或是想變換圖的大小,都可以使用swscale達成。

其中,PixelFormat 的列表在 libavutil/pixfmt.h 內定義。

swscale的用法可以參考libswscale/swscale-example.c的sample code。主要function有三個

  • sws_getContext()
  • sws_scale()
  • sws_freeContext()

其中,我們可以把sws_getContext() 看成初始化函數,把sws_freeContext()看成結束函數。這兩個函數分別在起始及結束各執行一次即可。

真正主要的函數,是sws_scale()。

sws_getContext() 的宣告如下

SwsContext *sws_getContext(int srcW, int srcH, enum PixelFormat srcFormat, int dstW, int dstH, enum PixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param)

總共有十個參數,其中,較重要的是前七個;
前三個參數分別代表 source 的寬、高及PixelFormat;
四到六個參數分別代表 destination 的寬、高及PixelFormat;
第七個參數則代表要使用哪種scale的方法;此參數可用的方法可在 libswscale/swscale.h 內找到。

最後三個參數,如無使用,可以都填上NULL。

sws_getContext會回傳一個 SwsContext struct,我們可以把這個 struct 看成是個 handler,之後的sws_scale和sws_freeContext皆會用到。

以下是一個sws_getContext的簡單例子:

struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getContext(in_width, in_height, PIX_FMT_YUV420P,
out_width, out_height, PIX_FMT_YUV420P, SWS_POINT,
NULL, NULL, NULL);

一開始,我們宣告img_convert_ctx 為指向 SwsContext 的一個 pointer;接著,我們將 sws_getContext 的回傳值賦予給img_convert_ctx。

注意sws_getContext的參數;in_width及in_height分別代表 source 的寬及高,out_width及out_height分別代表轉換後的寬與高;input 和 output 的 PixelFormat 皆為 YUV420P;使用SWS_POINT的scale方法。

初始化完成後,接著就要進行主要的 scale 動作;我們透過 sws_scale() 完成。sws_scale() 的宣告如下

int sws_scale(SwsContext *c, uint8_t* src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t* dst[], int dstStride[])

總共有七個參數;
第一個參數即是由 sws_getContext 所取得的參數。
第二個 src 及第六個 dst 分別指向input 和 output 的 buffer。
第三個 srcStride 及第七個 dstStride 分別指向 input 及 output 的 stride;如果不知道什麼是 stride,姑且可以先把它看成是每一列的 byte 數。
第四個 srcSliceY,就註解的意思來看,是指第一列要處理的位置;這裡我是從頭處理,所以直接填0。想知道更詳細說明的人,可以參考 swscale.h 的註解。
第五個srcSliceH指的是 source slice 的高度。

舉一個例子如下

sws_scale(img_convert_ctx, inbuf, inlinesize, 0, in_height, outbuf, outlinesize);

這裡應該比較好懂,可以參考上面的參數說明。

最後,全部處理完後,需呼叫sws_freeContext() 結束。用法很簡單,把sws_getContext 取得的參數填入即可。如下

sws_freeContext(img_convert_ctx);

最後再整理一次,要使用swscale,只要使用 sws_getContext() 進行初始化、sws_scale() 進行主要轉換、sws_freeContext() 結束,即可完成全部動作。

以下為一個簡單的範例程式,可從foreman.yuv內取出第一張圖,轉換大小後存成另一張圖。

=====================================================================================

/*
* 需設定 SRCFILE 及 DSTFILE, 長寬等資訊
* 需 link libswscale
* 主要有三個 function
* sws_getContext() 是 initial 用, sws_freeContext() 是結束用
* sws_scale() 是主要運作的 function
* 預設只會轉換第一張 YUV, 如果要轉換整個檔, 可以把 Decoding loop 的註解拿掉
*/

#include "libswscale/swscale.h"

#define SRCFILE "foreman_cif.yuv"
#define DSTFILE "out.yuv"

int main()
{
// 設定原始 YUV 的長寬
const int in_width = 352;
const int in_height = 288;
// 設定目的 YUV 的長寬
const int out_width = 640;
const int out_height = 480;

const int read_size = in_width * in_height * 3 / 2;
const int write_size = out_width * out_height * 3 / 2;
struct SwsContext *img_convert_ctx;
uint8_t *inbuf[4];
uint8_t *outbuf[4];
int inlinesize[4] = {in_width, in_width/2, in_width/2, 0};
int outlinesize[4] = {out_width, out_width/2, out_width/2, 0};

uint8_t in[352*288*3>>1];
uint8_t out[640*480*3>>1];

FILE *fin = fopen(SRCFILE, "rb");
FILE *fout = fopen(DSTFILE, "wb");

if(fin == NULL) {
printf("open input file %s error.\n", SRCFILE);
return -1;
}

if(fout == NULL) {
printf("open output file %s error.\n", DSTFILE);
return -1;
}

inbuf[0] = malloc(in_width*in_height);
inbuf[1] = malloc(in_width*in_height>>2);
inbuf[2] = malloc(in_width*in_height>>2);
inbuf[3] = NULL;

outbuf[0] = malloc(out_width*out_height);
outbuf[1] = malloc(out_width*out_height>>2);
outbuf[2] = malloc(out_width*out_height>>2);
outbuf[3] = NULL;

// ********* Initialize software scaling *********
// ********* sws_getContext **********************
img_convert_ctx = sws_getContext(in_width, in_height, PIX_FMT_YUV420P,
out_width, out_height, PIX_FMT_YUV420P, SWS_POINT,
NULL, NULL, NULL);
if(img_convert_ctx == NULL) {
fprintf(stderr, "Cannot initialize the conversion context!\n");
return -1;
}

fread(in, 1, read_size, fin);

memcpy(inbuf[0], in, in_width*in_height);
memcpy(inbuf[1], in+in_width*in_height, in_width*in_height>>2);
memcpy(inbuf[2], in+(in_width*in_height*5>>2), in_width*in_height>>2);

// ********* 主要的 function ******
// ********* sws_scale ************
sws_scale(img_convert_ctx, inbuf, inlinesize,
0, in_height, outbuf, outlinesize);

memcpy(out, outbuf[0], out_width*out_height);
memcpy(out+out_width*out_height, outbuf[1], out_width*out_height>>2);
memcpy(out+(out_width*out_height*5>>2), outbuf[2], out_width*out_height>>2);

fwrite(out, 1, write_size, fout);

// ********* 結束的 function *******
// ********* sws_freeContext *******
sws_freeContext(img_convert_ctx);

fclose(fin);
fclose(fout);

return 0;
}

=====================================================================================

以下兩張圖為執行結果

Input Image

foreman_352x288

Output Image

foreman_640x480

2009年8月22日 星期六

Qt Debug 方法

最近為了好幾件 project 忙的焦頭爛額,其中一個案子,是負責GUI及整合各組件,其中GUI的部份,是以Qt實作。

當然,這麼重的工作量不會是一個人完成,不過也沒好到哪裡去,兩個人。

和另一個同事co-work了一個星期。project leader一直希望我們分工,我則是以兩個人都是Qt新手,一起co-work,彼此發現盲點,會比較快上手的理由,兩個人坐在同一張桌子,隨時coding,隨時討論,連續工作了一個禮拜。

這個星期工作的進度還不少,整個UI的架構已經有個雛型出來了,而且也漸漸上手了。不過星期五又被project leader壓schedule,本來是拉到十月中的schedule,一句如果兩個人分工,時間可以縮短一半的說法,聽到我下巴差點掉下來。

這星期的工作,發現了一件事:有些地方,用了很腦殘的寫法,另一個同事偶爾會發現,五分鐘指出來,當場修正;如果一個人枯坐在那邊找,可能找了兩個小時也找不出來。五分鐘和兩個小時,縮短一半的時間,嗯……

怎麼會扯這麼多,本來是要寫Qt簡易的debug方法而已 = =

星期五早上,一到辦公室,很快樂地把前一天晚上的程式碼 copy 到另一個目錄,很快樂地執行,死掉!跑回原來的目錄,正常執行。檢查程式內的圖檔是否有使用絕對路徑,檢查了dll檔有沒有在正確的目錄,檢查了各個object有沒有去指到Null pointer,檢查了所有想得到的點,還是一樣。

後來請jarsing來看,make release的可以執行,make debug的不能執行。好吧,將就一點,以後就用 make release。

到了下午,連make release都不行了。問題大了,也不知道怎麼去debug。

在網路上搜尋了一下子,發現有個很簡單的作法:原來Qt也可以用 gdb 去debug,而且安裝完成後,裡面就有gdb了。

進gdb,run,backtrace,找到了錯誤的點,很腦殘,一個為了測試用的button,設定為按↑鍵後,會跳到另一個object,可是那個object還沒實體化。就這樣,看運氣,運氣不好踩到錯誤的記憶體空間,就當了。

可是,在windows下怎麼做傳統C的printf動作?還是沒解。

假日無聊,想起jarsing有寫過一系列Qt的文章,跑上去看,果然有寫,使用qDebug(),然後在pro裡面加上CONFIG = console就行了。

後來,洗澡的時候,想到這件事,一直覺得應該有個方法可以在compile時決定qDebug()要不要把訊息印出來,這樣,在release的時候,就可以把debug message關掉。

晚上找了一下qt的官方文件,在 http://qt.nokia.com/doc/4.5/debug.html 有說明 debug 的方法,其中提到,只要define QT_NO_DEBUG_OUTPUT 這個 symbol,就可以不讓qDebug()的訊息印出來;另外,QT_NO_WARNING_OUTPUT則可以不讓qWarning()印出來。

報告完畢!玩了一個星期,發現,GUI還真的有點意思。

 

最後,誰是 jarsing?他是我的一個好朋友,在 google 上找找這個人就知道他是何方神聖了。

2009年2月16日 星期一

Demo 之 Murphy’s Law

"Anything that can go wrong, will"

“如果有件事有可能會出錯,它就一定會出錯。”

 

不只!就連不太可能會出錯的,在關鍵時刻也會出錯!!

 

星期四到台北 Demo,之前已經演練了非常多次,測試了很久的時間,整個影像就是很順。真正到了 Demo 現場,機器架好,很有信心地把程式跑起來。

喵喵樂!為什麼昨天下午還跑得好好的程式,現在開始抖起來了!我的心也跟著抖起來了。

原本很順的畫面,現在變成會回頭;換句話說,就是人往前走,會後退抽一下,再往前走,再後退抽一下;嚴重的時候還會來回抽動好幾下。

把所有的設定全部檢查過,無誤;重新 reboot 再 run,照抖;全部的線重新接過,再抖。

火大換 CODEC,換成原本因為效能考量不 demo 的 H.264,咦?不抖了。見鬼了。

再換回 H.263,咦?又抖了,哪有這樣的啦!

在現場以為前一天 H.263 改到什麼東西,導致整個影像不正常。馬上決定改 demo 會卡卡的 H.264,至少畫面不會跳來跳去。

 

回到新竹後,開始查原因。查出來的時候差點飆淚:原因出在光線。

我們測試的環境,一向是在日光燈底下;當天 demo 現場是鵝黃色美術燈那種燈光,導致 webcam 認為環境很暗;又很巧,webcam 會自動隨著環境的光線調整光量;調整進光量的兩個因素,一個是快門,一個是光圈;webcam 的光圈已經固定,所以能動的只有快門;也就是說,光線越暗,快門的時間越長;快門越長,我們從下指令要從 webcam capture 一張畫面等待的時間要越久。

在每秒鐘 10 張 frame 的情況下,display 每 100ms 就會跑一次,只要 capture 並 encode 一張 frame 的時間超過 100 ms,就有可能發生 capture 一張,display 兩張以上的情況。也就是說,display 跑了兩次,會拿到同樣的東西。

照這麼說來,理論上畫面只會停在最後一個畫面,為什麼會跳來跳去?原因在於,實作 display 的同事,參考的 sample code,使用了 ping-pong buffer;簡單地說,buffer 有兩個,分別存了最後一張和倒數第二張的 frame;display 在存取時,會照著 buffer 1, buffer 2, buffer 1, buffer 2 的順序不斷抓 frame 出來 play。當 decoder 沒有資料寫入,把 buffer 更新時, display 就會一直抓到上一張、上上一張、上一張、上上一張;最後結果就是變成我們看到的,我又跳過來啦,我又跳過去啦,我又跳過來啦,打我啊笨蛋的情況。

 

所以,天時、地利、人和,缺一不可,在 demo 的時候,集所有不可能於一身的情況發生了,它就開始抖了。

 

再次證明了 Murphy’s Law 的真理。