2010年5月9日 星期日

花蓮三日遊 – 5/3 Day III, 溯溪。太魯閣。歸途

今天早上安排快樂的溯溪行程。

八點十分,車子就出現的飯店門口。車上已經有五個人,包含兩個教練,一個教練的同學,還有兩個同樣也是今天溯溪的朋友,其中一位還是外國人,不過聽不太懂他們講話的內容,原來他們是從荷蘭來的,並不是講英文;幸好其中有一位是台灣人,可以翻譯。

到了目的地後,開始換裝,穿上防寒衣、救生衣、防滑鞋、頭盔,開始有溯溪的感覺了。一開始先熱身,把身體弄溼,雖然天氣不錯,但畢竟還是五月初的日子,碰到水還是覺得冷,但很神奇地,過了一會兒,似乎又不那麼冷了,可能是防寒衣發揮了作用。

接下來,我們是來溯溪,不是來登山的,所以都挑溪裡的路走,還有一些仰著跳水、進小瀑布等路線,吃水是免不了的,不過有救生衣護身,安全性倒是沒什麼問題。

到了最後,會有個稍微有點高度的跳水,荷蘭人很勇敢地一馬當先跳下去,我跟在後面助跑準備跳的時候怯場了,縮回來深呼吸一口氣,再度助跑往前衝,跳下去的過程只有一個感想:怎麼這麼久還沒落水啊。

整體來說,溯溪很好玩,沒有想像中累,路程也比想像中短很多,有穿救生衣,不會游泳也沒關係;為了避免透露太多,讓大家失去興緻,這裡就不描述太多細節了。

跳水 最後的跳水

 

溯完溪後,中餐原本要到舊愛新歡去吃,結果沒開;於是決定,把網路上找到的在地人小吃通通吃一遍,不過不知道是不是時間太早,不是找不到就是沒開,結果只吃了山東餡餅,炸蛋蔥油餅,買了兩個菠蘿麵包,不過留到了晚上才吃。

解決完午餐後,路上隨便挑了間曾記,買了幾盒麻糬回家當伴手禮做個交代,在蘇花公路前,臨時又轉進太魯閣逛逛。

太魯閣今天的遊客很驚人,尤其是在長春祠,十幾輛的遊覽車把整條路塞得滿滿的,全部都是大陸旅遊團。記得以前來過幾次,最多人的時候,還不及今天的四分之一。

砂卡礑步道 砂卡礑步道

 

長春祠 長春祠

 

原本想到燕子口走步道,不過只看到了一大片落石,步道似乎封閉了。回來看新聞才知道,前兩天大片落石,還有人被砸傷了。

下午五點多,時間晚了,準備回家。

回程路上,看到一男一女走在路上,其實在上燕子口時,就看到他們了;把車停了下來,問他們要到哪裡,女的回答說太魯閣牌樓,請他們上車後,女友開始跟他們聊起天來;原來女的是新加坡人,男的是印尼人,他們在日本認識,約好一起到台灣來玩。沒想到公車的時間似乎沒算好,車子載上去了,卻沒有公車下來,只好用走的;男的不太會講中文,幾乎都是女的在聊天。

開到過了長春祠後,在路邊又看到一個人背著大背包在走路,自然地又停了下來問,這個人也是從外國來的,要到砂卡礑步道;先把他載到砂卡礑後,心想砂卡礑已經離有人煙的地方不遠了,提醒他天要黑了,注意安全,便載著一男一女回到太魯閣牌樓。

到了太魯閣牌樓,沒看到有公車的蹤跡;問了他們的目的地,知道要到花蓮後,乾脆直接載到新城火車站,讓他們往南坐一站就可以到花蓮市。不巧今天已經要回家了,如果早一天遇到,還可以直接載他們到花蓮市。

放下了兩個人後,踏上蘇花公路,踏上歸途。

花蓮三日遊 – 5/2 Day II, 賞鯨。糖廠。螢火蟲

早上起床後,暈船的感覺退去許多,但喉頭仍然還是有東西卡住的感覺。吃完早餐後,趕緊乖乖地先服用暈船藥。

昨天匆匆忙忙地 check-in 後,又匆匆忙忙地趕往石梯漁港,回來後又匆匆忙忙地躺平,完全不知道千禧山莊附近的景色。早上到陽台往海邊看,千禧山莊離海邊只有一小段距離,今天陽光很大,風也不小,看著海浪上上下下,已經不會有不適感了。可惜早上仍要匆匆忙忙地再度趕往石梯漁港,如果是來悠閒地度假的,到海邊走走也許也很不錯。

千禧 千禧山莊陽台景色

 

到了石梯漁港後,今天人也是不少,其中也有不少人昨天晚上也有參加夜釣,都很勇敢,沒被昨晚的暈船症狀嚇退。

海鯨號報到處海鯨號報到處 

 

九點一到,全部的人被帶到二樓做簡報,乖乖地聽完後,再回到碼頭,穿上救生衣,一行人又快快樂樂地準備出海了。

海鯨號 海鯨號

 

今天坐的是比較大台的海鯨號,有上下兩層,昨天晚上在下面那一層吐的很開心,今天決定到上面那層坐。在簡介時,有提到船的最前方有鏢旗魚的鏢台,講解人員還特別強調那是 VIP 座,只有四個,一上船就有幾個小孩子和媽媽很開心地搶了鏢台的位置坐;此時心裡很壞心地在講著,半小時後看你們是坐著還是躺著。

果然,不用半小時,鏢台人員就全數換人,原本那群已經乖乖地吐完躺在椅子上休息,到下船前都沒再起來過。

不過後來換上去的那群很猛,全程坐在鏢台聊天,面不改色。中間我有到船首位置站兩分鐘,頭就開始暈了,趕緊再回到二樓。

一出海,船長就開始搜尋海豚的蹤跡。今天天氣很好,運氣卻不怎麼好;遠遠地看到有海豚的身影,船開過去後,卻又消失無蹤。船往南開了一個多小時,看到了鬼頭刀、飛魚、海鳥,就是沒看到海豚。

在回程的路上,船長突然轉向,過一下子,就有人大喊看到海豚了。一開始只有四隻,距船一段距離,過不了多久,突然出現一群海豚,就在船的周圍游來游去,到最後是一群在船首旁游,不過我的攝影技術不好,沒有一次抓到海豚跳出水面的畫面。

據解說人員說,這次我們遇到的海豚,名字叫「熱帶斑海豚」。本來是想看到一大群海豚會躍出水面還會旋轉的,不過那似乎是飛旋海豚才會這麼做,這次並未看到。

很幸運地,這次賞鯨完全沒有不適感。

海豚 這已經是最清楚的一張了

 

下午沒有排行程,可以悠閒地亂逛。

吃完午餐後,先到石梯坪走走。今天石梯坪一樣有很多遊客,來來去去。站在大石頭上,看著別人釣魚,想到昨天的夜釣,似乎沒有那麼害怕了;也許下次有機會,會再來嘗試一次。

石梯坪石雕 石梯坪不知名石雕

 

石梯坪釣客石梯坪釣客

 

下午,準備回花蓮,短的路程可以由台十一線原路回去,但我們選擇從瑞港公路,循著秀姑巒溪回到台九線,再往北走;一個原因是想在長虹橋看看泛舟的人們,不過很可惜,時間太晚,似乎都已經收工了;第二個原因,是因為嘴饞,想到光復糖廠吃冰。

到了光復糖廠,人潮比東部海岸多出許多,連買冰都要排隊,不過幸好,排了幾分鐘就排到了;我點了一個巧克力甜筒,女友則是點了忘了名稱,只記得有白蘭地的甜筒。甜筒的脆餅很脆,很好吃。

花蓮糖廠 花蓮糖廠白蘭地甜筒

 

採甘蔗機 光復糖廠採甘蔗機

 

晚上回到花蓮市區後,先到飯店 check-in ,行李丟著,馬上又出發開始晚上的閒逛行程。

首先,先到了七星潭,七星潭今晚的遊客不多,天空飄了一點小雨,兩個人很溫馨地在七星潭慢慢地散步,這就是所謂的幸福吧。

接下來,很開心地到自強夜市覓食;先吃了廟東蚵仔煎,女友建議是不是要先點一份一起吃,等一下再吃棺材板,最後還是決定一人點一份;沒想到看起來似乎不特別大的蚵仔煎,吃完以後,竟然已經有一點飽了;接著當然是要去吃棺材,以前都是吃蔣氏棺材板,這次想試試法氏棺材板;不知是不是已經被蚵仔煎餵飽了,覺得以前記憶中的蔣氏比較合胃口;吃完棺材板,當然要再來一分妙不可言的飲料… 但肚子已經完全被撐飽了,飲料下次再來吧。

廟東蚵仔煎 廟東蚵仔煎

 

晚餐解決後,約晚上七點多,時間還早,兩個人又決定去鯉魚潭看螢火蟲。一到了鯉魚潭遊客中心,外面意外地停了不少台車子;車子停好後,走到遊客中心裡,看到了有人在帶團解說,本著鄉民看熱鬧的本領,神不知鬼不覺地混入人群中;原來是鯉魚潭遊客中心舉辦的鯉魚潭深度旅遊活動,今天的主題是夜賞螢火蟲,有人帶路,還 A 到了兩頂帽子,幸運幸運。

走在晚上的鯉魚潭步道很舒服,走了沒多久,就看到一群一群的螢火蟲出來見客了。人家在求偶,當客人的我們應當盡量不要去打擾牠們。中間雖然有些遊客一看到螢火蟲就忘了自己是誰,拿著手電筒猛照,不然就是伸出手要去抓;幸好大家都還是文明人,經旁人稍微提醒後,大部份都能克制自己的行為。

晚上九點多,我們準備要離去時,還有遊客剛要來,看來鯉魚潭也是個知名的賞螢地點。

明天就要回家了,心裡還是想繼續在花蓮待下來。

2010年5月7日 星期五

花蓮三日遊 – 5/1 Day I, 粉鳥。朝陽。夜釣

早上八點半,從台北出發。

台北到花蓮的路已經走了好幾次了,其中一次還是騎腳踏車。這次有點不太一樣,以往都是直接殺到花蓮,這次則是在途中選了兩個點短暫停了一會兒。

第一站是粉鳥林,很漂亮的沙灘。從蘇澳出發,在蘇花的第一個山的下坡,山腰處有座小廟,有個涼亭,往山腳下看,可以看到一片沙灘,那就是粉鳥林。

東澳是個不大的聚落,街道的中央右手邊有個警察局,對面有間便利商店,從便利商店旁的路開進去,一直走就可以到沙灘了。

沙灘很乾淨,看不到什麼垃圾;往左邊的半山腰上看,可以看到剛剛走過來的蘇花公路,右邊則可以看到烏石鼻躲在山的後面。

粉鳥林沙灘

稍作停留後,起身繼續出發。在路口的便利商店,紅綠燈旁立有一個牌子,指示若要通過,請壓在路面的「S」標誌上。把車子開到「S」標誌上後,以為紅綠燈會自己切換,過了一會兒,看到對面警察局內,有警察探頭出來,看了一下,然後再走進去,接著就變綠燈了。和女友在車上笑著判斷,有可能是壓到 S 標誌後,會通知警察局,然後警察出來看路況,在適當的時候按下鈕變成綠燈。

往前翻過一座山後,就到達南澳了。以前在這兒頂多只是到7-11買個東西吃,到南澳車站上個廁所;這次廁所還是要上的,除此之外,也到了路過好幾次卻從來沒進去過的建華冰店吃了一碗紅豆牛奶冰,夠甜,我喜歡甜的東西。

吃完冰後,前往尋找朝陽步道的地點。在南澳的街上,往朝陽路轉進去直直開,會先穿過朝陽社區,沒多久就到了朝陽漁港。漁港不大,漁船也不多,倒是假日的遊客不少。

走上堤防後,右手邊同樣有一片小沙灘,沙灘上插著幾支釣竿,看起來似乎跟當天晚上夜釣的釣竿一樣。還有一條不知道是做什麼用的小船,真的很小;在堤防上可以看到烏石鼻的另一面。

朝陽漁港

在堤防上散完步後,回到停車的地方,在公車站牌的對面找到了朝陽步道的入口點,看來要繞一圈得花一段時間,其實今天的行程有時間表,六點以前必須到石梯漁港夜釣,在這之前還必須先到千禧山莊check-ing,於是決定直接跳過這個景點,留待下次再來。

到達花蓮後,事先搜集的資料,石梯漁港那附近地點較偏僻,怕晚上肚子餓找不到東西吃,就到了花蓮的家樂福買一些糧食預備;事後證明,這根本是多此一舉,晚上夜釣整個吐翻天,食物完全派不上用場。

女友在台北時,已經先買了兩顆暈船藥,但這次旅程會有兩次坐船的機會,一次是今天晚上的夜釣,一次是明天早上的賞鯨,兩個人共需四顆,於是在往石梯漁港的路上,經過豐濱時,又買了兩顆暈船藥備用。

原本一直擔心會路過千禧山莊沒注意到,在過了長虹橋以後,眼睛就不停地往路邊搜尋,在過了北回歸線紀念碑後,右手邊一個大牌子,裡面一棟大建築物,即是千禧山莊,要錯過還不大容易;大約五點二十分的時候,到了千禧山莊,匆匆忙忙地 check-in 後,就趕緊往石梯漁港出發。

海鯨號的報到處很好找,在石梯漁港的入口處就有很明顯的標誌,順著走就會到碼頭和報到處了。碼頭有一隻很大的… 不知道是鯨魚還是海豚的石像,底下寫著「賞鯨發源地」。

賞鯨發源地

 

六點整,很準時地,開始行前解說,說明釣竿的用法,網子的用法,還有救生衣的穿法跟注意事項,重要的是,到了海上以後,請把平常的身份卸下,把自己當做是漁民。接著,一群人就開開心心地上船了。

出海了

一開始還很興奮地聊天,很開心地左看右看。船開了幾十分鐘後,到達今天的釣場,船停了下來,此時慢慢地感覺到有些暈船了。很快地,我成了今天全船第一個吐的人,暈船藥吃了完全沒有效果。過不了多久,全身似乎開始冒冷汗,連坐在船邊都覺得很困難,於是把釣竿交給女友一個人,自己跑到椅子上去躺了。

躺了沒多久,睜開眼睛看了一下,發現身邊多了許多人,原來大家也陸續暈船了。

原來在暈船是這麼難受的感覺,完完全全地躺平,一坐起來,噁心的感覺馬上就湧上來,把今天原本就吃不多的東西吐光後,還是一直想吐。

終於,在不知道躺了多久以後,船準備回航了。

也不知道開了多久,有感覺船慢慢地進港;等船靠岸後,一踏上陸地,還是一樣不舒服;船東準備了宵夜,稀飯無限吃,和飛魚乾一條;實在是沒什麼胃口,勉強自己吞了幾口稀飯,再咬了幾口飛魚乾;飛魚乾很好吃,第二天賞鯨的時候,知道這是龍船長燻的。

在吃東西的過程中,原本是朝著港口坐的,看到了上下搖動的船,人雖然在陸地上了,噁心的感覺馬上就又湧了上來,趕緊把視線移向陸地。

吃東西的時候,跟女友聊了一下,才知道原來她在我躺平後沒多久也暈船了,自己一個人在船邊吐了幾次,又一直聞到汽油的味道,很不舒服。但是從頭到尾沒聽她抱怨過一句,為什麼要帶她來這裡受苦,直到今天還沒聽到她抱怨。很棒唷!我的女友,是個很乖的乖小孩。

暈船的感覺真的很難受,有一股衝動,要到櫃台把明天早上的賞鯨行程退掉。

上船前把 GPS 打開記錄軌跡,發現原來在夜釣的過程中,船靜止時,被海流帶了一段不短的距離,大約往北一公里多。下圖即是航跡圖,原來我們離岸邊並不遠,大約五公里左右。

夜釣航跡 

吃完宵夜後,開著車慢慢地回千禧山莊,躺在床上完全不想動,勉強起來洗個澡後,咚地一聲躺在床上就睡死了,時間大概才十點多而已。

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