以手為半徑是我的世界範圍! 在這個世界裡頭沒有爭吵,沒有公司派的勾心鬥角,當然也沒有那種跟你搏軟然後在背後捅你一百多刀的恐怖smile! 所以這是屬於我的半徑! 閒人勿進,因為結界的關西。 半徑內的咖啡、半徑內的電波、半徑變成了一個獨立的單位在這個獨立的世界與國家中。 而我的半徑中我是完全自由的非固體! 因為我是法系人物!
2009年12月28日
黑的部落
10個提升新陳代謝的方法
|
2009年12月13日
時光
2009年11月30日
(轉載)資料庫系統是鎯頭; MapReduce 則是螺絲起子
資料庫系統是鎯頭; MapReduce 則是螺絲起子
最近年來不管是雲端運算(Cloud computing) 或是網格運算(Grid computing) 都相當的熱門, 而它們的核心技術 MapReduce 則受到相當程度的重視. 04 年時 MapReduce 一開始被 Google 所提出來, 它本身是一種程式開發的模式,可以用來處理大量的資料. 但因為長時間以來, 不管是在學校或是業界, 資料庫系統都廣泛的被教授和使用,很多人一看到"處理大量的資料", 自然的就會把 MapReduce 跟資料庫系統放在一起, 拿來做比較等等, 因而產生一些錯誤的認知.
目前任職在 Google 的 Mark 日前撰寫了一篇文章 Databases are hammers; MapReduce is a screwdriver 來說明這兩者的不同. 這篇文章是我個人認為目前在網路上說明 MapReduce 文章當中寫的最貼切且淺顯易懂的. 經過原作者的同意 , 我把它翻譯成中文, 希望讓更多人藉著閱讀這篇文章來瞭解 MapReduce.
資料庫系統是鎯頭; MapReduce 則是螺絲起子.
有一群朋友們把一篇關於批評MapReduce的文章傳 給我. 我曾經猶豫是否我應該寫些什麼, 因為目前為止有很多關於MapReduce的東西正被開發中, 而且被Google的同仁們廣泛的使用. 但是這篇文章真的是有點惱人, 我理當回應它. 所以我在這邊先做個澄清, 我並不是以一位Google員工的身份來評論它.(事實上, 我在工作上並沒有使用MapReduce). 這純粹是用我自己的時間, 發表我個人的意見. 如果您覺得這是一篇愚蠢的文章, 那是我個人的錯跟Google無關. 如果您覺得這篇文章寫的很出色, 那也是我個人的榮幸跟Google無關. Google 並沒有要求我寫這篇文章, 同樣的我也沒有要求 Google 的同意. 這僅只是我個人的行為, 瞭解嗎?
我會對這件事情有興趣的原因是因為這跟我的博士研究有關. 回想起研究所那時候, 我的研究是關於在非科學性的應用軟體上, 將平行運算的技術應用在結構化資料上.這差不多是 MapReduce 所要做的事情. 而我所提出的解決方案是某種階層式的分散(scatter)/收集(gather)運算子,非常接近 MapReduce 運作的方式. 那最大的差異在哪邊? 那就是 MapReduce 打敗了我, 設計MapReduce 的人注意到我沒注意到的東西, 並且利用了它使得 MapReduce 程式更乾淨, 更容易開發.
讓我們從頭開始, MapReduce 是啥, 它能做什麼?
今天假設你在工作, 你需要做某件事情, 但是它在你的電腦上會跑蠻久的時間, 你不想等但是你也不想要花幾百萬去買台超級電腦, 那你要怎樣才能夠讓它跑快一點? 有一個方法是買一群便宜的電腦, 讓它們同時一起幫你執行工作. 另一方面你也注意到辦公室內有很多台電腦, 幾乎每個員工座位上都有一台, 在某個時間點上大部分的電腦並沒有在做太多事情, 那為何不利用它們呢? 當你的機器不甚忙碌的時候, 你允許你的同事借用你當下並不在使用的資源, 當你需要的時候也可以借用他們的機器. 所以當你需要執行大的工作的時候, 你可以輕鬆的找到好幾打的機器來用.
這個方法最大的問題在於, 大部分的程式並沒撰寫成可以在多台的機器上執行的模式, 它們被寫成只能在一臺機器上執行. 而要把一件困難的工作分到很多台機器執行上是件困難的事情.
MapReduce 是一種程式語言開發模式, 它讓你可以透過撰寫特有的制式化程式, 輕鬆的把工作分散到一群機器上面去執行. 它基本的概念在於你把工作分成兩部份 Map 以及 Reduce. Map 基本上負責把問題分成好幾份並且送到不同的機器去,所以它們可以同時被處理. Reduce 則接收每份問題的處理的結果, 最後組合成單一的答案.
MapReduce 運作的關鍵在於, 把輸入的資料在概念上當成成一個包含很多筆紀錄的 list, 透過 map 這些紀錄被分配到不同的機器上去處理. map 計算的結果是一個包含許多 key/value 組的 list. Reduce 會取得同樣 key 值的所有 value, 並組合成為最後的單一 value. 也就是 Map 將資料產生成為 key/value 組, reduce 則組合結果給你單一的結果. 你無法知道這個工作是被分成100份或是2份, 而最後應該是跟只有單一 map 時候是一樣的. (這就是 MapReduce 和我博士研究方法不同的地方, 領悟到把結果當作是 key/value map 來看,你會得到非常乾淨且一致的歸納流程. 雖然我的 scatter/gather 模式是同樣的概念 , 但是我的 reduces 跟 Map/Reduce 比起來相對的醜陋許多.)
MapRedue 美麗的地方在於撰寫它是多麼的簡單, 在平行處理程式語言的領域上從來沒這麼簡單過.
回到該篇批評 MapReduce 的文章, 他們批評 MapRduce, 其實基本上並不是基於關聯式資料庫(Relational databaes) 的概念.
當我第一次花時間學習關聯式資料庫的時候, 我的老闆告訴我ㄧ個關於資料庫專家的故事, 是我認為最貼切的故事. 這個故事是說關聯式資料庫的專家們發現了這個世界上最美麗,最漂亮,最完美的鎯頭, 完美到不會太重,也不會太輕,恰到好處的可以把釘子給釘進去,它的柄是根據所有者手的角度去特製化的, 所以釘一整天也不會起水泡.同時它也是裝飾的很漂亮的鎯頭, 上面有寶石鑲嵌跟黃金的工飾在適當的地方, 絲毫不會減損鎯頭的功能. 它真的是一個最偉大的鎯頭.資料庫專家們熱愛他們的鎯頭, 因為它是多麼美妙的工具. 而且他們真的利用這個工具做出許偉大的東西. 他們是如此的喜歡關聯式資料庫,以致於認為這是這是他們唯一需要的工具. 如果你給他們一個螺絲釘, 他們會把它當成釘子一樣的釘進去. 當你提醒他們說, 嘿這樣會弄壞東西 , 那是螺絲釘不是釘子, 他們會說"我知道阿, 但是我有這隻極好的鎯頭 , 你不能期待我使用那鱉腳的小螺絲起子"
現況是這樣, 他們有關聯式資料庫, 關聯式資料庫絕對是個傑出的東西, 是個驚人的工具可以幫我們做出驚人的軟體. 我自己曾經利用關聯式資料庫完成許多工作, 沒有它, 我將沒有辦法完成一些令我感到驕傲的事情. 我完全不想貶低關聯式資料庫, 它真的是偉大的系統.但是不是每件事情都是關聯式資料庫, 不是每件事情天生就適合把它當作關聯來看待. 所有對 Mapreduce 的指責都源於"這不是我們關聯式資料庫會做的方式", 而並沒有瞭解到事情的重點. 關聯式資料庫的平行處理並不是很好, 你知道有多少關聯式資料庫可以有效的把工作分給 1,000 台普通機器? 關聯式資料庫無法很好的處理非表格式的資料, 像是面對遞迴資料的處理上可以說是是聲名狼藉. MapReduce 不是為了要取代關聯式資料庫, 它是提供一種輕便的程式語言開發方式, 讓大家可以快速且以平行的方式在一群機器上面執行, 僅止於這樣.
2009年11月28日
MYSQL 權限賦予語法
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE ON opencms.* TO root@192.168.1.52 IDENTIFIED BY '123123';
GRANT ALL PRIVILEGES ON *.* TO root@192.168.1.52 IDENTIFIED BY '123123' WITH GRANT OPTION;
FLUSH PRIVILEGES;
- GRANT ALL ON *.* TO 'username'@'localhost' IDENTIFIED BY 'passowrd'
- ALL: 授權的權限(SELECT, INSERT .... etc)
- *.*: Table Name, Db_name.*, 指定可以存取哪些Db/Table
- username: 要新增的 username
- localhost: 可從哪邊來存取, 可用 %
- password: 密碼
2009年11月22日
Postgresql Notes
-> sudo -u postgres psql template1
-> alert user postgres with pasword [Password]
-> create user [UserName] with password [Password]
2009年11月21日
Bugzilla Install guideline
1. you need have perl, apache, MTA
2. Install Perl (the verison depend on what version of Bugzilla release)
Step:
1. Download Bugzilla from offical website
2. Install Apache and Apache-dev, mod_perl
3. untar Bugzilla tar ball
4. mv Bugzilla to your www root dir , optional you could install bugzilla to where you prefer and make symbol link
5. change to directory of bugzilla
6. run ./checksetup.pl --check-modules and follow the instruction
7. install perl module for database, mysql is my so [/usr/bin/perl install-module.pl DBD:mysql]
8. add user named 'bugs
and login to mysql and create table name 'bugs' which depend on the localconfig(generate from checksetup command)
Note:
Edit {apache_root}\site-enabled\000-default to add Directory for Bugzilla
2009年10月19日
他夏了夏天
詞曲/吳青峰
幾點鐘 結束夢 他按下鬧鐘
如往常 開始了一天生活
忙工作 忙收穫 早餐吃什麼
他和他 維持齒輪的脈搏
汗水在他的身上化成了彩虹
步伐的節奏延續生命的河流
默默在崗位戰鬥的每個小小英雄
富有和貧窮、卑微和偉大相同
他從不害怕自己被人群淹沒
中午吃便當是他最大享受
幾點鐘 也許是 月出的時候
如往常 結束了一天工作
他心中 幻想著 晚餐吃什麼
家裡的 讓他不怕往前衝
疲累在他的身上化成了笑容
步伐的節奏開始不那麼沈重
輕輕旋轉著夏天地面悶熱的晚風
平凡或特別、笨拙或聰明相同
他從不擔心自己被世界折磨
甜蜜的負荷是他最大依託
帶著笑容的睡意化成了彩虹
在他夢中一口氣走上了星空
喧囂地亮起整個夏天渴望的揮霍
清淡與濃烈、好與壞他都嚐過
他從不介意自己被命運作弄
按下了鬧鐘開啟另一個夢
2009年10月1日
[PHP] Parse xml file
";
";
";
";
";
";
";
";
";
";
2009年9月26日
夢的那悟
最近常夢見自己,夢見自己像似般的未來。
或許是因為是種徵兆、也或許這是一種真實未來。
或許開始認為滿足是一種簡單也很複雜的事情因此而感到疲
或許是一個夢夢見自己感受了那樣的迷失而覺得頓時需要做
不過這就是世界的想法突然間出現在身體裡的某個地方,
可能會像是一種簡單的低語,低沉的呢喃在自己的身體某個
漸漸的了解這個世界變成了一種左腳右腳的結界石,
開始覺得不是因為如此而那樣。
開始覺得應該會是這樣。
儘管的讓這世界變成世界,也讓空間變成空間,影像變成影
為了生活而活,而不要為了活而生活!
或許是因為我而誤解、也或許是因為世界而世界,
不過說穿了都是個故事罷了!
2009年9月11日
WINCE Configuration Files
Binary Image Builder (.Bib)
The .bib files describe the memory structure (ROM/RAM) and specify what files need to be included into the image; they also contain additional configuration parameters related to the memory. A merged .bib file named CE.bib, which the Romimage.exe utility uses for forming a monolithic image, contains the following .bib files:
BSP files (Config.bib, Platform.bib).
Files of selected Windows CE components (Common.bib, and so on).
OS design files (Project.bib and subproject .bib files).
Object Store Initialization Files (.Dat)
The .dat files are used for initializing a file system in the memory (RAM file system). During the MAKEIMG stage, .data files are merged into the InitObj.dat file. The resulting InitObj.dat file is used by Filesys.dll for creating a directory tree of the file system in the memory. Entries in the DAT files have the following format:
Registry Initialization Files (.Reg)
Registry files form the initial registry of the operating system. The format of Windows Embedded CE registry files is similar to that of the desktop version of Windows. During the MAKEIMG stage, all registry files in the build directory (_FLATRELEASEDIR) are merged into the RegInit.ini file
Database Initialization Files (.Db)
During the MAKEIMG stage, .db files are merged into an InitDB.ini file and are used for initializing EDB databases that are included in the image. The entry format is described in detail in the supplied .db files.
WinCE Interrupt (中斷)
WinCE Interrupt (中斷)
1.何謂IRQ:
IRQ 即所謂的中斷請求(Interrupt Request) ,是指硬體裝置發給CPU的訊號,要求CPU暫時先放下目前正在處理的工作,將工作的優先權暫時先讓給此發送訊號的硬體裝置,如Touch panel, keyboard, audio的I/O處理... 等等相關硬體裝置,都是透過IRQ來對CPU發出中斷請求。
2.何謂ISR:
ISR 即所謂的中斷服務常式(Interrupt Service Routine),指用來回應硬體裝置發出IRQ的軟體常式,屬於OAL層,主要的工作為判斷硬體發生中斷的原因,並回傳一特定的Interrupt ID(SYSINTR)給Kernel,Kernel則會喚醒IST對中斷做進ㄧ步的處理。
3.何謂Interrupt ID:
Interrupt ID即所謂邏輯中斷識別碼(Interrupt Identifier),每個都是獨一無二的值,代表著某個硬體裝置發出的邏輯中斷要求。藉由Interrupt ID,kernel可以表示是否該處理可以完成,或是需要IST (Interrupt Service Threads)進ㄧ步處理該中斷請求。
•IST is a thread that does most of the interrupt processing
•IST performs necessary I/O operations in the device to collect the data and process it
•IST must associate an event object with an interrupt identifier by InterruptInitialize
•IST can boost themselves to a higher priority by calling CeSetThreadPriority
針對 data page 的 Copy-on-Write/XIP (轉貼紀錄)
October 23, 2006
針對 data page 的 Copy-on-Write/XIP
CE Linux Forum 的 wiki 上,有一篇由 Panasonic Mobile Communications Co., Ltd. 所提出的新技術 [Data Read-In-Place],不同以往針對 Linux kernel 的大幅修改,這個作法只修改了 dynamic loader (ld-linux.so)。在描述這個作法之前,先來觀察 ELF 執行檔的 .data section 在執行時期的行為,參考下圖:對多數應用程式來說,Dynamic loader 必須將 .data section 予以 linear mapped 至 virtual address 中,於是我們可以發現,當進行 read 或 write 動作時,行為如下圖:
而 Application-level XIP (eXecution-In-Place) 會希望 .data section 能永久保存於 Flash 儲存裝置中,除非 .data section 本身發生變化 (被 write),所以 [Data Read-In-Place] 提出一個簡單有效的機制,將原本 mmap 的範圍從:
mmap(..., PROT_READ | PROT_WRITE);修改為:
mmap(..., PROT_READ);當進行 mmap ELF segment 時,捨棄 PROT_WRITE 這個 bit,這使得 cramfs 行為變成「如同 XIP .text 一般,去 mapping 每個在 .data section 的 page 到 ROM pages」,而透過 mprotect 去設定 PROT_WRITE,這使得:
mprotect(..., PROT_READ | PROT_WRITE);
- vm_area_struct 允許作 write
- PTE (page table entry) 的 write 權限則被抑制
Panasonic Mobile Communications Co., Ltd. 提出的方法,在最低程度的修改下,實做 In-Place 的 data read,依據官方說法:
- "The total effect for one system measured by Panasonic was a reduction of 26% of the page cache allocated to processes, when the product was in the stand-by state."
cramfs 的 XIP 有許多可最佳化的空間,或許有機會等某個產品設計完畢後,可試著整理,而我也開始觀察 [AXFS - Advanced XIP File System] 的實做,相關的討論可見 LKML [RFC: Advanced XIP File System]。
由 jserv 發表於 October 23, 2006 10:07 PM
2009年9月9日
PASCAL and WINAPI 的用意
使用PASCAL調用方式,函數在返回到調用者之前將參數從棧中刪除。
使用C調用方式,參數的刪除是調用者完成的。
WinMain函數是由系統調用的,Windows系統規定由系統調用的函數都遵守PASCAL調用方式,但是VC中函數的缺省調用方式是__cdecl,也就是C調用方式所以在WinMain前顯示的聲明:
int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)
{
//
}
在Windows編程中將遇到很多聲明修飾符,如CALLBACK,WINAPI,PASCAL這些在IntelCPU的計算機上都是__stdcall,詳細的聲明細節可以參考windef.h :
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
然而 __stdcall 只能擁有固定的參數數量,若需要使用可變動的參數就要使用 __cdecl。
MPEG 2 Layer 3與MPEG 1 Layer 3的區別
而MPEG1中主要有Layer1、Layer2和Layer3三種。
MPEG1 Audio編碼方式之間的主要區別從外部看來主要是對音頻文件的壓縮率和要求播放媒體提供數據的速率不同,
並且內部採取的算法也有很大的不同,基本上是隨Layer數增大而越來越複雜。
經Layer1編碼的音頻文件後綴為MP1,另外兩種分別為MP2和MP3。
MPEG-1和MPEG-2用同一個家族的聲音多媒體數字信息編碼器,不管是第一層,第二層還是第三層。
MPEG-2的新的音頻特性是「低採樣頻率的擴展」和「多聲道擴展」。
「低採樣頻率的擴展」是指為那些限制了帶寬需求的位速率非常低的應用系統服務的場合,
新的採樣頻率是16,22.05或24kHz,位速率擴展到8kbps以下。
「多聲道擴展」是指服務於那些擁有5個主要聲道(左、右、中置、左環繞和右環繞)的環繞聲系統,
有的環繞聲系統甚至還要額外加一個低頻增進聲道來處理低頻音信號,對這種系統,「多聲道擴展」允許包含直至7個聲道。
2009年9月7日
.NET CTS (Common Type System)
.NET CTS (Common Type System)
作者:蔡學鏞
2004 年 5 月
CTS 非常重要,CTS (Common Type System) 是 .NET CLR 的核心,這樣的說法並不為過。任何 .NET 的程式員都需要對於 CTS 有最基本的認識,也因此本文的目的在於讓大家瞭解 CTS,但是由於 CTS 的定義相當繁瑣,所以本文章只能包含一小部分。請注意,由於 CTS 的定義中有一些地方很容易造成學習上的混淆,所以本文並未完全依照 CTS 的定義來解說。
任何程式語言,如果想要在 .NET CLR 上執行,就必需提供一個編譯器,將此語言的程式編譯成 .NET CLR 所認識的 metadata 以及 IL,符合 CTS 的規定。
並非所有的語言都能和 C# 一樣符合 CTS 的規範,畢竟許多語言出現在先,CTS 出現在後,所以有一些舊的語言未能符合 CTS 的規定。這類的程式語言在 .NET 中有下列三種因應方式:
- 改變語言本身以符合 CTS 的規定。例如 Visual Basic 6 就為此做了大幅度的擴充與改變,加上繼承的特性,這使得新版的 Visual Basic .NET 差不多可以算是一個全新的語言了,猶如當年 C 衍生出 C++ 一樣。
- 擴 充語言本身以接近 CTS 的規定,但仍保留不相容於 CTS 的語法,如此一來,程式中符合 CTS 規定者以 CTS 的方式編譯,不符合 CTS 規定者則以傳統的方式編譯成 native code。例如 C++ with Managed Extension (簡稱 MC++) 就是如此。
- 語 言本身儘量維持不變,一切都是透過超強的編譯器設計來達成和 CTS 的相容,Eiffel 就是如此的作法。例如,CTS 不支援多重繼承 (multiple inheritance),但是 Eiffel 支援多重繼承,透過 Eiffel 的編譯器可以利用 interface 以及 attribute 來達到多重繼承 (這樣的作法相當巧妙,有興趣的讀者不妨去研究一下)。
我 們可以將 CTS 看成是所有 .NET 語言的 superset (union),而符合 CTS 的各種不同的語言,其實都只是 CTS 的 subset (intersection)。這些語言所寫出來的程式,如果想要有最佳的相容性,以便互相調用 (invoke) 或繼承,這些語言之間就必需取得一個共同的 subset,有共同遵守的規範,這就是 CLS (Common Language Specification,或 Common Language Subset)。
圖一是整個概念的示意圖。
圖 1:CTS 和其他語言的關係
資料的內容稱為值 (Value),每個值一定屬於某些型別 (Type),而最完整地表達此值的型別稱為 Exact Type (精準型別)。
根據 Value 是否可以再切割,Value 分成兩種:
- Simple Value (簡單值):指的是不可再分割 (atomic) 的資料。
- Complex Value:Complex Object 指的是由多個 Simple Value 所組成的資料。
根據 Value 是否可以直接使用,Type 分成兩類:
- Value Type:直接使用 Value Type 的 Value。
- Reference Type:不能直接使用 Reference Type 的 Value,只能間接存取資料。
Value Type 和 Reference Type 的處置方式很不同:Value Type 是直接存取資料,Reference Type 則是間接存取資料。Reference Type 對程式員來說是最方便的作法,但其實 Value Type 的效率比較好,所以兩者各有適合的地方。請參考圖二。
有些時候,我們需要把 Value Type 當成 Reference Type 來使用,所以我們需要一套機制來將 Value Type 的值裝箱 (box) 成 Reference Type 的值,這套機制就叫做 Boxing。當 Boxed Value 需要再度從 Reference Type 被轉回原來的 Value Type,這套機制就叫做 Unboxing (拆箱)。
圖 2:各種值的記憶體配置方式
CTS 對於 Type 有一個很詳細的分類,但是它的分類方式我不喜歡,因為容易造成混淆。所以我對其做了一些修改,我所做的修改包括了:
- 將 Interface 自 Reference Type 中獨立出來,因為 Interface 可以是 Value Type,也可以是 Reference Type。(這點和 Java 不同)
- 將 Pointer 自 Reference Type 中獨立出來,因為 Pointer 其實和 Reference Type 的差異很大。
- 將 Build-in Value Type 之下的 Integer、Floating Point、Typed Reference 刪除,因為這三者未能包含 Boolean 以及 Void。
- 將 Build-in Value Type 改名為 Primitive Type,新名稱乃是遵循 IL Assembly 設計者 Serge Lidin 的稱呼方式。(這也是 Java 慣用的稱呼方式)
- 將 Enumeration 自 User Defined 中獨立出來。將 User Defined 改名為 Structure。這乃是根據繼承的父類別來分類。
- 刪除 Build-in Reference Type 以及其子節點 String 與 Object。因為 String 與 Object 只有在 Signature 中和其他 Class 不同之外,並無特殊之處。我認為不需要特別分成一類。
- 將 Self-Describing 及其子節點大幅更動,因為原來的方式太混亂。更動後的版本等下會看到。
- 將 Box Value Type 及其子節點刪除,因為 Box Value Type 只是一個概念,並非真的存在,其實使用的還是原來的 Type。
- 將 Array 再細分成兩種,分別是 Vector 以及 Multi-Dimensional Array。(請注意,這裡的 Vector 和 Java 的 Vector 是不同的意思)
如果你對於原本的 CTS 分類方式感興趣,請自行參考相關資料,這裡不再列出。下面列出的是我修改後的版本。
如果你對於原本的 CTS 分類方式感興趣,請自行參考相關資料,這裡不再列出。下面列出的是我修改後的版本。
- Value Type
- Structure (Extended From ValueType directly or indirectly)
- Enumeration (Sealed, and Extended From ValueType directly)
- Reference Type
- Regular Class
- Array (Sealed, and Extended From Array directly and implicitly, Don't have a type name)
- Delegate (Sealed, and Extended From MulticastDelegate directly)
- Interface
- Pointer
- Function Pointer
- Managed Data Pointer
- Unmanaged Data Pointer
後面會一一解說這些型別。圖三是這些型別的繼承架構。
圖 3:CTS 的一些重要型別
除了 Interface 以及 Pointer 之外,所有的型別都是直接或間接繼承自 System.Object。這張圖所列出來的每個型別都很特別,對於 CTS 而言都有特殊的意涵。
所有的 Value Type 都直接或間接地繼承自 System.ValueType。若不繼承自 System.ValueType,則一定是 Reference Type。
Enumeration 一定是直接繼承自 System.Enum;而 Structure 一定是直接繼承自 System.ValueType。Structure 型別中有一些很特殊者,在 .NET CLR 中有特殊的處理方式,這些 Structure 被稱為 Primitive Type,詳列於下面的表格:
Code | Type Name | Comments |
0x01 | Void |
|
0x02 | Boolean | Single-byte value, true = 1, false = 0 |
0x03 | Char | 2-byte unsigned integer, presenting a Unicode character |
0x04 | Sbyte | Signed 1-byte integer, the same as char in C/C++ |
0x05 | Byte | Unsigned 1-byte integer |
0x06 | Int16 | Signed 2-byte integer |
0x07 | UInt16 | Unsigned 2-byte integer |
0x08 | Int32 | Signed 4-byte integer |
0x09 | UInt32 | Unsigned 4-byte integer |
0x0A | Int64 | Signed 8-byte integer |
0x0B | UInt64 | Unsigned 8-byte integer |
0x0C | Single | 4-byte floating-point |
0x0D | Double | 8-byte floating-point |
0x16 | TypedReference | Typed reference, carrying both reference to a type and information identifying the referenced type |
0x18 | IntPtr | Pointer-size integer; size dependent on the underlying platform. |
0x19 | UIntPtr | Pointer-size unsigned integer |
Delegate 是一種物件導向的 Function Pointer (不同於傳統的 Function Pointer)。所有的 Delegate 都必需直接繼承自 System.MulticastDelegate。
任 何 Array 都必需直接繼承自 System.Array,但是這樣的繼承關係是「由 .NET CLR 內部自行認定如此」,我們並不會在 .NET PE 檔內部看到這樣的繼承關係。事實上,在 .NET PE 檔內部連 Array 型別 (這裡指的不是 System.Array) 的定義都不可能出現,因為 Array 型別的存在完全是透過 Signature,而非 TypeDef 或 TypeRef 的 Metadata Table (關於 Array 的許多特點,.NET 的作法很類似 Java)。
還有一點值得我們注意的,CTS 支援兩種 Array,分別是:
- Vector:是一維的 Array,且 index 值由 0 開始。
- Multi-Dimension Array:是多維的 Array (當然也可以當一維使用),且 index 值可以不必由 0 開始。
千萬不要輕忽這兩種 Array 的差別,兩者在彈性和效率上各有擅場,你甚至可以組合出許多的變化,例如:利用「Vector 的 Vector」,製作出非矩形的 Array。
任何直接或間接繼承自 System.Object,且未直接或間接繼承自 System.ValueType、System.Delegate、System.Array 者,都是屬於 Regular Class。
Interface 是一種很特殊的型別,Interface 不能當作 Exact Type,所以由其當時的 Exact Type 來決定 Value 所屬的型別是 Value Type 或 Reference Type。
CTS 也支援三種 Pointer,分別是:
- Function Pointer:記憶體位址,指向一個 function 的起點。請注意:C#「不」支援 Function Pointer。
- Managed Data Pointer:記憶體位址,指向一個 managed data。請注意:C#「不」支援 Managed Data Pointer。
- Unmanaged Data Pointer:記憶體位址,指向一個 unmanaged data。請注意:C#「支援」Unmanaged Data Pointer,但不常用,也不鼓勵使用。
Value Type、Reference Type、以及 Interface,可以有兩種不同的 Accessibility (請參考圖四)。分別是:
- Public:開放所有的程式使用此 Type。
- Assembly:也就是 C# 的 Internal,是預定的。僅開放同一個 Assembly 的程式使用此 Type。
圖 4:Value Type、Reference Type、以及 Interface 的 Accessibility
如果是 Nested Type (定義在另一個 Type 內部的 Type) 的話,則有比較多種 Accessibility。如下圖五所示:
圖 5:Nested Type 的 Accessibility
- Public:開放所有的程式使用此 Type。
- Family-or-Assembly:開放給同一個 Assembly 的程式,或者 Sub-Type (繼承自此 Type 的 Type) 的程式。在 C# 中稱為 Protected Internal (或 Internal Protected 亦可)。
- Family:開放給 Sub-Type 的程式。在 C# 中稱為 Protected。
- Assembly:開放給同一個 Assembly 的程式。在 C# 中稱為 Internal。
- Family-and-Assembly:開放給同一個 Assembly 的 Sub-Type 程式。C# 不支援這種 accessibility。
- Private:僅能在同一個 Enclosing Type (包含此 Nested Type 的外部 Type) 程式內使用。這也是 C# 的預定。
關於 CTS,如果讀者需要更進一步的資料,可以參考下面三本書:
- The Common Language Infrastructure Annotated Standard (by James S. Miller ) Addison Wesley
- Inside Microsoft .NET IL Assembler (by Serge Lidin) Microsoft Press
- Compiling for the .NET Common Language Runtime (by John Gough) Prentice Hall
2009年9月4日
DirectShow實踐經驗雜談
流媒體的處理,以其複雜性和技術性,一向廣受工業界的關注。
特別伴隨著英特網的普及,流媒體在網路上的廣泛套用,怎樣使流媒體的處理變得簡單而富有成效逐漸成為了焦點問題。選項一種合適的套用方案,事半功倍。此時,微軟的DirectShow,給了我們一個不錯的選項。
DirectShow是微軟公司提供的一套在Windows平台上進行流媒體處理的開發包,與DirectX開發包一起發佈。目前,DirectX最新版本為8.1。
那麼,DirectShow能夠做些什麼呢?
且看,DirectShow為多媒體流的捕捉和回放提供了強有力的支持。運用DirectShow,我們可以很方便地從支持WDM驅動模型的採集卡上捕獲資料,並且進行相應的後期處理乃至存儲到文件中。
它廣泛地支持各種媒體格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒體資料的回放變得輕而易舉。
另外,DirectShow還集成了DirectX其它部分(譬如DirectDraw、DirectSound)的技術,直接支持DVD的播放,視瀕的非線性編輯,以及與數字攝像機的資料交換。
更值得一提的是,DirectShow提供的是一種開放式的開發環境,我們可以根據自己的需要定製自己的元件。
接下去,我們需要對DirectShow系統有個整體的印象。參見以下DirectShow的系統示意圖:
圖中央最大的一塊即是DirectShow系統。
DirectShow使用一種叫Filter Graph的模型來管理整個資料流的處理程序;參與資料處理的各個功能模組叫做Filter;各個Filter在Filter Graph中按一定的順序連接成一條「流水線」協同工作。
大家可以看到,按照功能來分,Filter大致分為三類:Source Filters、Transform Filters和Rendering Filters。
Source Filters主要負責取得資料,資料來源可以是文件、英特網、或者電腦裡的採集卡、數字攝像機等,然後將資料往下傳輸;Transform Fitlers主要負責資料的格式轉換、傳輸;Rendering Filtes主要負責資料的最終去向,我們可以將資料送給音效卡、顯示卡進行多媒體的演示,也可以輸出到文件進行存儲。值得注意的是,三個部分並不是都只 有一個Filter去完成功能。恰恰相反,每個部分往往是有幾個Fitler協同工作的。
譬如,Transform Filters可能包含了一個Mpeg的解碼Filter、以及視瀕色彩空間的轉換Filter、音瀕採樣頻率轉換Filter等等。
除了系統提供的大量Filter外,我們可以定製自己的Filter,以完成我們需要的功能。下圖是一條典型的Avi文件回放Filter Graph鏈路:
在DirectShow系統之上,我們看到的,即是我們的應用程式(Application)。
應用程式要按照一定的意圖建立起相應的Filter Graph,然後通過Filter Graph Manager來控制整個的資料處理程序。
DirectShow能在Filter Graph執行的時候接收到各種事件,並通過消息的方式傳送到我們的應用程式。這樣,就實現了應用程式與DirectShow系統之間的交互。下圖給出了DirectShow應用程式開發的一般程序:
以上簡單介紹了DirectShow的系統結構,希望大家對這個鑽勁的套用框架已經有了大概的認識。如果你有興趣,可以詳細研究DirectX的說明 我的文件。
DirectShow是一個強大的開發包;另外,它是關於COM的,因此要求程序員具有COM編程的一些基本知識。關於如何深入學習DirectShow 套用結構以及開發自己的Filter,請參閱筆者的後續文章。筆者將從編程的角度,詳細講述來源於實際工作中的經驗之談。
在上一講中,筆者介紹了DirectShow的總體系統框架。從這一講開始,我們要從程序員的角度,進一步深入探討一下DirectShow的套用以及Filter的開發。
在這之前,筆者首先要特別提一下微軟提供的一個Filter測試工具——GraphEdit,它的路徑在DXSDK\bin\DXUtils\GraphEdit.exe。
(如果您還沒有安裝DirectX SDK,請到微軟的網站上去下載。)通過這個工具,我們可以很直觀地看到Filter Graph的執行及處理流程,方便我們進行程序偵錯。
(如果您手邊就有電腦,還等什麼,馬上體驗一下吧:
執行GraphEdit,執行File->Render Media File…選項一個媒體文
件;當Filter Graph構建成功後,按下工作列的執行按鈕;您就能看到剛才選項的媒體文件被回放出來了!看到了吧,寫一個媒體播放器也就這麼回事!)
接下去,我們開講Filter的開發。
學習DirectShow Filter的開發,不外乎以下幾種方法:看說明 我的文件、看示例程式碼和看SDK基類來源碼。
看說明 我的文件,應著重於總體概念上的理解;看示例程式碼應與基類來源碼的研究同步進行,因為自己寫Filter,關鍵的第一步是選項一個合適的Filter基 類和Pin的基類。對於Filter的把握,一般認為要掌握以下三方面的內容:Filter之間Pin的連接、Filter之間的資料傳輸以及流媒體的隨 機訪問(或者說流的定位)。下面就開始分別進行闡述。
所謂的Filter Pin之間的連接,實際上是Pin之間Media Type(媒體類型)的一個協商程序。連接總是從輸出Pin指向輸入Pin的。要想深入瞭解直接的連接程序,就必須認真研讀SDK的基類來源碼(位於 DXSDK\samples\Multimedia\DirectShow\BaseClasses\amfilter.cpp,類CBasePin的 Connect方法)。連接的大致程序為,枚舉欲連接的輸入Pin上所有的媒體類型,逐一用這些媒體類型與輸出Pin進行連接,如果輸出Pin也接受這種 媒體類型,則Pin之間的連接宣告成功;如果所有輸入Pin上枚舉的媒體類型輸出Pin都不支持,則枚舉輸出Pin上的所有媒體類型,並逐一用這些媒體類 型與輸入Pin進行連接。如果輸入Pin接受其中的一種媒體類型,則Pin之間的連線到此也宣告成功;如果輸出Pin上的所有媒體類型,輸入Pin都不支 持,則這兩個Pin之間的連接程序宣告失敗。
有一點需要注意的是,上述的輸入Pin與輸出Pin一般不屬於同一個Filter,典型的是上一級Filter(也叫Upstream Filter)的輸出Pin連向下一級Filter(也叫Downstream Filter)的輸入Pin。如下圖所顯示:
當Filter的Pin之間連接完成,也就是說,連接雙方通過協商取得了一種大家都支持的媒體類型之後,即開始為資料傳輸做準備。這些準備工作中,最重要 的是Pin上的記憶體分配器的協商,一般也是由輸出Pin發起。在DirectShow Filter之間,資料是通過一個一個資料包傳送的,這個資料包叫做Sample。
Sample本身是一個COM對象,擁有一段記憶體用以裝載資料,Sample就由記憶體分配器(Allocator)來統一管理。
已成功連接的一對輸出、輸入Pin使用同一個記憶體分配器,所以資料從輸出Pin傳送到輸入Pin上是無需記憶體拷貝的。而典型的資料拷貝,一般發生在 Filter內部,從Filter的輸入Pin上讀取資料後,進行一定意圖的處理,然後在Filter的輸出Pin上填充資料,然後繼續往下傳輸。下面, 我們就直接闡述一下Filter之間的資料傳送。
首先,大家要區分一下Filter的兩種主要的資料傳輸模式:推模式(Push Model)和拉模式(Pull Model)。參考圖如下:
所謂推模式,即源Filter(Source Filter)自己能夠產生資料,並且一般在它的輸出Pin上有獨立的子線程負責將資料傳送出去,一般的情況如代表WDM模型的採集卡的Live Source Filter;而所謂拉模式,即源Filter不具有把自己的資料送出去的能力,這種情況下,一般源Filter後緊跟著接一個Parser Filter或Splitter Filter,這種Filter一般在輸入Pin上有個獨立的子線程,負責不斷地從源Filter索取資料,然後經過處理後將資料傳送下去,一般的情況如 文件源。推模式下,源Filter是主動的;拉模式下,源Filter是被動的。而事實上,如果將上圖拉模式中的源Filter和Splitter Filter看成另一個虛擬的源Filter,則後面的Filter之間的資料傳輸也與推模式完全相同。
那麼,資料到底是怎麼通過連接著的Pin傳輸的呢?首先來看推模式。
在源Filter後面的Filter輸入Pin上,一定實現了一個IMemInputPin接頭,資料正是通過上一級Filter使用這個接頭的 Receive方法進行傳輸的。值得注意的是(上面已經提到過),資料從輸出Pin通過Receive方法使用傳輸到輸入Pin上,並沒有進行記憶體拷 貝,它只是一個相當於資料到達的「通知」。
再看一下拉模式。拉模式下的源Filter的輸出Pin上,一定實現了一個IAsyncReader接頭;其後面的Splitter Filter,就是通過使用這個接頭的Request方法或者SyncRead方法來獲得資料。
Splitter Filter然後像推模式一樣,使用下一級Filter輸入Pin上的IMemInputPin接頭Receive方法實現資料的往下傳送。深入瞭解這部分內容,請認真研讀SDK的基類來源碼(位於
DXSDK\samples\Multimedia\DirectShow\BaseClasses\source.cpp和pullpin.cpp)。
下面,我們來講一下流的定位(Media Seeking)。在GraphEdit中,當我們成功構建了一個Filter Graph之後,我們就可以播放它。在播放中,我們可以看到進度條也在相應地前進。當然,我們也可以通過移到進度條,實現隨機訪問。
要做到這一點,在應用程式級別應該可以知道Filter Graph總共要播放多長時間,當前播放到什麼位置等等。那麼,在Filter級別,這一點是怎麼實現的呢?
我們知道,若干個Filter通過Pin的相互連接組成了Filter Graph。
而這個Filter Graph是由另一個COM對像Filter Graph Manager來管理的。通過Filter Graph Manager,我們就可以得到一個IMediaSeeking的接頭來實現對流媒體的定位。
在Filter級別,我們可以看到,Filter Graph Manager首先從最後一個Filter(Renderer Filter)開始,詢問上一級Filter的輸出Pin是否支持IMediaSeeking接頭。如果支持,則返回這個接頭;如果不支持,則繼續往上一 級Filter詢問,直到源Filter。
一般在源Filter的輸出Pin上實現IMediaSeeking接頭,它告訴使用者總共有多長時間的媒體內容,當前播放位置等信息。
(如果是文件源,一般在Parser Filter或Splitter Filter實現這個接頭。)對於Filter開發者來說,如果我們寫的是源Filter,我們就要在Filter的輸出Pin上實現 IMediaSeeking這個接頭;如果寫的是中間的傳輸Filter,只需要在輸出Pin上將用戶的獲得接頭請求往上傳遞給上一級Filter的輸出 Pin;如果寫的是Renderer Filter,需要在Filter上將用戶的獲得接頭請求往上傳遞給上一級Filter的輸出Pin。
進一步的瞭解,請認真研讀SDK的基類來源碼(位於DXSDK\samples\Multimedia\DirectShow\BaseClasses \transfrm.cpp的類方法CTransformOutputPin::NonDelegatingQueryInterface實現和 ctlutil.cpp中類CPosPassThru的實現)。
以上我們介紹了一下如何學習DirectShow Filter開發,以及一些開始寫自己的Filter之前的預備知識。下一講,筆者將根據自己開發Filter的經驗,一步一步教你如何寫自己的Filter。
在上兩講中,筆者介紹了DirectShow的套用原理以及開發Filter之前的一些預備知識。
這一講,筆者就要一步一步教你如何寫自己的Filter啦。
首先,從VC++的專案開始(請驗證你已經給VC++配置好了DirectX的開發環境)。寫自己的Filter,第一步是使用VC++建立一個Filter的專案。
由於DirectX SDK提供了很多Filter的例子專案(位於DXSDK\samples\Multimedia\DirectShow\ Filters目錄下),最簡單的方法就是拷貝一個,然後再在此基礎上修改。但如果你是Filter開發的初學者,筆者並不贊成這麼做。
自己新增一個Filter專案也很簡單。使用VC++的嚮導,建立一個空的」Win32 Dynamic-link Library」專案。
注意,幾個文件是必須有的:.def文件,定義四個匯出函數;定義Filter類的.cpp文件和.h文件,並在.cpp文件中定義Filter的註冊信息以及兩個Filter的註冊函數:DllRegisterServer和DllUnregisterServer。(
註:Filter的註冊信息是Filter在註冊時寫到註冊表裡的內容,格式可以參考SDK的示例程式碼,Filter相關的GUID務必使用 GuidGen.exe產生。)接下去進行專案的設定(Project->Settings…)。此時,你可以開啟一個SDK的例子專案進行對比, 有些巨集定義完全可以照抄,最後注意將輸出文件的副檔名改為.ax。
上一講曾經提到過,在寫Filter之前,選項一個合適的Filter基類是至關重要的。為此,你必須對幾個Filter的基類有相當的瞭解。
在實際套用中,Filter的基類並不總是選項CBaseFilter的。相反,因為我們絕大部分寫的都是中間的傳輸Filter(Transform Filter),所以基類選項CTransformFilter和CTransInPlaceFilter的居多。
如果我們寫的是源Filter,我們可以選項CSource作為基類;如果是Renderer Filter,可以選項CBaseRenderer或CBaseVideoRenderer等。
總之,選項好Filter的基類是很重要的。當然,選項Filter的基類也是很靈活的,沒有絕對的標準。
能夠通過CTransformFilter實現的Filter當然也能從CBaseFilter一步一步實現。下面,筆者就從本人的實際經驗出發,對Filter基類的選項提出幾點建議供大家參考。
首先,你必須明確這個Filter要完成什麼樣的功能,即要對Filter專案進行需求分析。
請儘量保持Filter實現的功能的單一性。如果必要的話,你可以將需求分解,由兩個(或者更多的)功能單一的Filter去實現總的功能需求。
其次,你應該明確這個Filter大致在整個Filter Graph的位置,這個Filter的輸入是什麼資料,輸出是什麼資料,有幾個輸入Pin、幾個輸出Pin等等。你可以畫出這個Filter的草圖。
弄清這一點十分重要,這將直接決定你使用哪種「模型」的Filter。譬如,如果Filter僅有一個輸入Pin和一個輸出Pin,而且一進一處的媒體類 型相同,則一般採用CTransInPlaceFilter作為Filter的基類;如果媒體類型不一樣,則一般選項CTransformFilter作 為基類。
再者,考慮一些資料傳輸、處理的特殊性要求。譬如Filter的輸入和輸出的Sample並不是一一對應的,這就一般要在輸入Pin上進行資料的緩衝,而 在輸出Pin上使用專門的線程進行資料處理。這種情況下,Filter的基類選項CSource為宜(雖然這個Filter並不是源Filter)。
當Filter的基類選定了之後,Pin的基類也就相應選定了。接下去,就是Filter和Pin上的程式碼實現了。有一點需要注意的是,從軟體設計的角 度上來說,應該將你的邏輯類程式碼同Filter的程式碼分開。下面,我們一起來看一下輸入Pin的實現。你需要實現基類所有的純虛函數,譬如 CheckMediaType等。在CheckMediaType內,你可以對媒體類型進行檢驗,看是否是你期望的那種。因為大部分Filter採用的是 推模式傳輸資料,所以在輸入Pin上一般都實現了Receive方法。有的基類裡面已經實現了Receive,而在Filter類上留一個純虛函數供用戶 重載進行資料處理。這種情況下一般是無需重載Receive方法的,除非基類的實現不符合你的實際要求。而如果你重載了Receive方法,一般會同時重 載以下三個函數EndOfStream、BeginFlush和EndFlush。我們再來看一下輸出Pin的實現。
一般情況下,你要實現基類所有的純虛函數,除了CheckMediaType進行媒體類型檢查外,一般還有DecideBufferSize以決定Sample使用記憶體的大小,GetMediaType提供支持的媒體類型。
最後,我們看一下Filter類的實現。首先當然也要實現基類的所有純虛函數。除此之外,Filter還要實現CreateInstance以提供COM的入口,實現NonDelegatingQueryInterface以暴露支持的接頭。
如果我們新增了自訂的輸入、輸出Pin,一般我們還要重載GetPinCount和GetPin兩個函數。
Filter框架的實現大致就是這樣。
你或許還想知道怎樣在Filter上實現一個自訂的接頭,以及怎麼實現Filter的內容頁等等。限於篇幅,筆者就不展開闡述了。其實,這些問題都能在SDK的示例專案中找到答案。其他的,關於在實際編程中應該注意的一些問題,筆者整理了一下,供大家參考。
1. 鎖(Lock)問題
DirectShow應用程式至少包含有兩條線程:一條主線程和一條資料傳輸線程。既然是多線程,肯定會碰到線程同步的問題。
Filter有兩種鎖:Filter對像鎖和資料流鎖。Filter對像鎖用於Filter級別的如Filter狀態轉換、BeginFlush、 EndFlush等;資料流鎖用於資料處理線程內,譬如Receive、EndOfStream等。如果這兩種鎖沒有搞清楚,很容易產生程序的死鎖,這一 點特別需要提醒
。
2. EndOfStream問題
當Filter接收到這個「消息」,意味著上一級Filter的資料都已經傳送完畢。在這之後,如果Receive再有資料接收,也不應該去理睬它。如果 Filter對輸入Pin上的資料進行了緩衝,在接收到EndOfStream後應確保所有緩衝的資料都已經處理過了才能返回。
3. Media Seeking問題
一般情況下,你只需要在Filter的輸出Pin上實現NonDelegatingQueryInterface方法,當用戶申請得到 IID_ImediaPosition接頭或IID_IMediaSeeking接頭時將請求往上一級Filter的輸出Pin上傳遞。當Filter Graph進行Mediaseeking的時候,一般會使用Filter上的BeginFlush、EndFlush和NewSegment。如果你的 Filter對資料進行了緩衝,你就要重載它們,並做出相應的處理。如果你的Filter負責給傳送出去的Sample打時間戳,那麼,在 Mediaseeking之後應該重新從零開始打起。
4. 關於使用專門的線程
如果你使用了專門的線程進行資料的處理和傳送,你需要特別小心,不要讓線程進行死循環,並且要讓線程處理函數能夠去時時檢查線程指令。應該確保在 Filter結束工作的時候,線程也能正常地結束。有時候,你把GraphEdit程序關掉,但GraphEdit工作仍在記憶體中,往往就是因為資料線 程沒有安全關閉這個原因。
5. 如何從媒體類型中獲取信息
譬如,你想在輸入Pin連接的媒體類型中,獲取視瀕圖像的寬、高等信息,你應該在輸入Pin的CompleteConnect方法中實現,而不要在SetMediaType中。
DirectX媒體對像(DirectX Media Objects,簡稱DMOs),是微軟提供的另一種流資料處理COM元件。與DirectShow filter相比,DMO有很多相似之處。對filter原理的熟悉,將會大大說明 你對DMO的學習。另外,DMO也因其結構簡單、易於新增和使用而倍受微軟推崇。
下面,我們來看一看DMO與filter的對比。
1. DMO比filter實現的功能要少很多,這使得DMO「體積」很小;
2. DMO使用起來比filter更有靈活性。DMO的使用不需要filter graph,應用程式可以直接與DMO交互。而DMO也可以通過一個DMO wrapper filter工作於DirectShow環境;
3. DMO總是同步處理資料,不像filter有獨立的資料傳送線程,需要考慮多線程編程問題;
4. 與傳統的編解碼管理器ACM、VCM相比,用DMO開發的編解碼器是關於COM的,更易於擴展。並且DMO支持多個輸入和多個輸出;
5. DMO不需要像filter一樣分配資料傳送的記憶體,而有DMO的使用者負責;
6. DMO是一個獨立功能模組,不需要像filter一樣連接成一條鏈路;
7. DMO不需要像filter一樣將資料「推」下去,資料的輸入輸出都是由DMO的使用者完成的;
所有這些優點,使得DMO成為微軟對於Encoder和Decoder開發的重點推薦模式。
DirectX 9.0 SDK中,微軟更是把DMO從DirectShow中分離出來,而對於一些transform filter,微軟也推薦用DMO的方式來替換。
關於DMO的使用方式,目前大概有兩種:
一種是應用程式直接使用DMO,另一種就是在DirectShow filter中的套用。
後者比較簡單,只是使用了一個DMO wrapper filter。在DirectShow應用程式中,DMO是對用戶透明的,所有使用DMO的工作均由DMO wrapper filter來完成。參見下面的程式碼。
// Create the DMO Wrapper filter.
IBaseFilter *pFilter;
HRESULT hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
reinterpret_cast
if (SUCCEEDED(hr))
{
// Query for IDMOWrapperFilter.
IDMOWrapperFilter *pDmoWrapper;
hr = pFilter->QueryInterface(IID_IDMOWrapperFilter,
reinterpret_cast
if (SUCCEEDED(hr))
{
// Initialize the filter.
hr = pDmoWrapper->Init(CLSID_MyDMO, DMOCATEGORY_VIDEO_EFFECT);
pDmoWrapper->Release();
if (SUCCEEDED(hr))
{
// Add the filter to the graph.
hr = pGraph->AddFilter(pFilter, L"My DMO");
}
}
pFilter->Release();
}
而對於DMO的直接使用,以下幾點是要特別注意的。
1. 在處理資料之前,必須為每條輸入輸出stream設定media type(Optional stream除外);
2. 從DMO從獲取的media type未必包含format塊,但是在給DMO設定media type時,務必帶上這部分信息(MIDI除外);
3. 應用程式必須自己負責分配資料緩衝。
緩衝的大小可以通過使用DMO的IMediaObject::GetInputSizeInfo或 IMediaObject::GetOutputSizeInfo得到。DMO使用的資料緩衝也是一個COM對象,支持ImediaBuffer接頭,與 DirectShow filter的Media Sample類似。
4. 一般的DMO依次使用IMediaObject::ProcessInput和IMediaObject::ProcessOutput處理資料,In-Place的DMO使用IMediaObjectInPlace::Process處理資料。
兩套方法不能混用。
5. 在使用ProcessOutput時,如果返回的標記是DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE,說明資料的資料還沒有完全取出,需要再次使用ProcessOutput。
6. 所有輸入資料都已輸入完成,應該使用DMO的IMediaObject:Discontinuity方法。
7. 如果你想中斷資料處理流程,使用DMO的IMediaObject::Flush。
區別兩種不同的可丟棄stream,標記分別為DMO_OUTPUT_STREAMF_OPTIONAL和DMO_OUTPUT_STREAMF_DISCARDABLE。注意,後者是要設定media type的。
2009年9月3日
.NET Compact Framework and the .NET Framework, What Are the Difference?
1 – Common Language Runtime
The common language runtimes in both Frameworks benefit from managed code execution, just-in-time (JIT) code compilation, and garbage collection. They support the Common Language Specification (CLS).
Both Frameworks have built-in primitive types as well as other types that you can use and derive from when you build your application.
2 – Classes and Types
The .NET Compact Framework supports a subset of the .NET Framework class library. This subset is appropriate for applications that are designed to run on resource-constrained devices and is semantically compatible with same-named classes in the .NET Framework.
3 – Assemblies and Global Assembly Cache
The .NET Compact Framework does not currently support multi-module assemblies, but does support satellite assemblies.
4 – How about Deploying Applications ?
To deploy an application, you can easily copy the assembly to the target device by using a cable from the desktop computer, its infrared port, or a wireless Internet or intranet connection. In Microsoft Visual Studio 2005, you can deploy directly to the device while debugging.
5 – ASP.NET Features
The .NET Compact Framework is primarily a rich client platform and does not provide ASP.NET support. To develop Web pages for mobile devices, you can use ASP.NET mobile Web controls. To develop Web pages for personal computers or Web service providers, see your ASP.NET documentation.
To get the latest .NET 3.5 Framework hosting enabled hosting plan and support, log on to SeekDotNet.com to view more information realize how you can deploy your .net Framework application right away.
2009年8月20日
What is the Intent
例如,在一個聯繫人維護的應用中,當我們在一個聯繫人列表屏幕(假設對應的Activity為listActivity)上,點擊某個聯繫人後,希望能夠 跳出此聯繫人的詳細信息屏幕(假設對應的Activity為detailActivity),為了實現這個目的,listActivity需要構造一個 Intent,這個Intent用於告訴系統,我們要做「查看」動作,此動作對應的查看對象是「某聯繫人」,然後調用startActivity (Intent intent),將構造的Intent傳入,系統會根據此Intent中的描述,到ManiFest中找到滿足此Intent要求的Activity,系 統會調用找到的Activity,即為detailActivity,最終傳入Intent,detailActivity則會根據此Intent中的描 述,執行相應的操作。
一、抽象描述要描述什麼
在Android參考文檔中,對Intent的定義是執行某操作的一個抽象描述(確實很抽象)。我們先來看看這裡的抽象描述,到底描述了什麼。
首先,是要執行的動作(action)的一個簡要描述,如VIEW_ACTION(查看)、EDIT_ACTION(修改)等,Android為我們定義了一套標準動作:
- MAIN_ACTION
- VIEW_ACTION
- EDIT_ACTION
- PICK_ACTION
- GET_CONTENT_ACTION
- DIAL_ACTION
- CALL_ACTION
- SENDTO_ACTION
- ANSWER_ACTION
- INSERT_ACTION
- DELETE_ACTION
- RUN_ACTION
- LOGIN_ACTION
- CLEAR_CREDENTIALS_ACTION
- SYNC_ACTION
- PICK_ACTIVITY_ACTION
- WEB_SEARCH_ACTION
其次,是執行動作要操作的數據 (data),Android中 採用指向數據的一個URI來表示,如在聯繫人應用中,一個指向某聯繫人的URI可能為:content://contacts/1。這種URI表示,通過 ContentURI這個類來描述,具體可以參考android.net.ContentURI類的文檔。
以聯繫人應用為例,以下是一些action / data對,及其它們要表達的意圖:
- VIEW_ACTION content://contacts/1 -- 顯示標識符為"1"的聯繫人的詳細信息
- EDIT_ACTION content://contacts/1 -- 編輯標識符為"1"的聯繫人的詳細信息
- VIEW_ACTION content://contacts/ -- 顯示所有聯繫人的列表
- PICK_ACTION content://contacts/ -- 顯示所有聯繫人的列表,並且允許用戶在列表中選擇一個聯繫人,然後把這個聯繫人返回給父activity。例如:電子郵件客戶端可以使用這個Intent,要求用戶在聯繫人列表中選擇一個聯繫人
另外,除了action和data這兩個重要屬性外,還有一些附加屬性:
- category(類別),被執行動作的附加信息。例如 LAUNCHER_CATEGORY 表示Intent 的接受者應該在Launcher中作為頂級應用出現;而ALTERNATIVE_CATEGORY表示當前的Intent是一系列的可選動作中的一個,這 些動作可以在同一塊數據上執行。
- type(數據類型),顯式指定Intent的數據類型(MIME)。一般Intent的數據類型能夠根據數據本身進行判定,但是通過設置這個屬性,可以強制採用顯式指定的類型而不再進行推導。
- component(組件),指定Intent的的目標組件的類名稱。通常 Android會根據Intent 中包含的其它屬性的信息,比如action、data/type、category進行查找,最終找到一個與之匹配的目標組件。但是,如果 component這個屬性有指定的話,將直接使用它指定的組件,而不再執行上述查找過程。指定了這個屬性以後,Intent的其它所有屬性都是可選的。
- extras(附加信息),是其它所有附加信息的集合。使用extras可以為組件提供擴展信息,比如,如果要執行「發送電子郵件」這個動作,可以將電子郵件的標題、正文等保存在extras裡,傳給電子郵件發送組件。
總之,action、 data/type、category和extras 一起形成了一種語言。這種語言使系統能夠理解諸如「查看某聯繫人的詳細信息」之類的短語。隨著應用不斷的加入到系統中,它們可以添加新的action、 data/type、category來擴展這種語言。應用也可以提供自己的Activity來處理已經存在的這樣的「短語」,從而改變這些「短語」的行 為。
二、Android如何解析Intent
在應用中,我們可以以兩種形式來使用Intent:
- 直接Intent:指定了component屬性的Intent(調用setComponent(ComponentName)或者setClass(Context, Class)來指定)。通過指定具體的組件類,通知應用啟動對應的組件。
- 間接Intent:沒有指定comonent屬性的Intent。這些Intent需要包含足夠的信息,這樣系統才能根據這些信息,在在所有的可用組件中,確定滿足此Intent的組件。
Intent解析機制主要是通過查找已註冊在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找 到匹配的Intent。在這個解析過程中,Android是通過Intent的action、type、category這三個屬性來進行判斷的,判斷方 法如下:
- 如果Intent指明定了action,則目標組件的IntentFilter的action列表中就必須包含有這個action,否則不能匹配;
- 如果Intent沒有提供type,系統將從data中得到數據類型。和action一樣,目標組件的數據類型列表中必須包含Intent的數據類型,否則不能匹配。
- 如果Intent中的數據不是content: 類型的URI,而且Intent也沒有明確指定它的type,將根據Intent中數據的scheme (比如 http: 或者 mailto: ) 進行匹配。同上,Intent 的scheme必須出現在目標組件的scheme列表中。
- 如果Intent指定了一個或多個category,這些類別必須全部出現在組建的類別列表中。比如Intent中包含了兩個類別:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目標組件必須至少包含這兩個類別。
2009年8月13日
Convert int to char
char * itoa ( int value, char * str, int base );
ex:
int i=3;
char buffer [10];
itoa (i,buffer,10);
其中 i 是要轉成char的integer, buffer是要存的char, 而10表示轉成十進位.
但是itoa()在某些Linux底下就是無法被使用, 簡單說就是無法被系統reference到. 此時可以使用另外一種方式sprintf(), 用法如下:
int sprintf ( char * str, const char * format, ... );
ex:
char buffer [50];
int n, a=5, b=3;
n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
其中 buffer 裡就是"引號"中的字元(char), 而 n 則是"引號"的位元大小(int).
2009年8月10日
Android Process Lifecycle
Android Process Lifecycle
1. 最早被移除的是 Empty Process(空行程):
Empty process 是指那些沒有跟Activity綁定, 也沒有跟任何的應用程式元件(比如Service或IntentReceiver)綁定在一起的process, 這些空行程一定是最早被系統考慮移除的.
2. 第2順位考慮被移除的是 Background Activity.
Background Activity指這個activity是無法被使用者看到的的情況, 表示Activity已處於stop的狀態, 系統移除這些 Activity 是安全的. 通常有多個 Background Activity同時運行, 這些Activity被存放在一個 LRU (least recent used) list中, 系統可以根據 LRU list 判斷哪些 Activity可以被移除, 哪一個應該是最先被移除的.
3. 第3順位被移除的是 Service Process.
在 Android 應用程式裡, 有一種沒有 UI 的類別(android.app.Service), 稱之為 Service. Service Process 通常是由startService()方式啟動. 簡單來說,Service 屬於 background(背景)程序, 透過背景程序, 我們可以製作一些不需要 UI 的功能, 例如: 在背景撥放音樂, 上傳或下載文件等. 系統通常會保護它, 除非真的沒有記憶體可用.
4. 接著輪到 Visible Activity / Visible Process:
Visible Process是一個可被Visible的, 但是沒有顯示在最上端 (onPause被使用時). 舉例來說, 當一個新的對話框Activity出現時, 原來的Activity仍然visible, 仍然被系統認為是重要的, 通常不會被移除. 但若不得不移除時, 由於屬於 Paused狀態, 相對來說, 它已經處於一個比較安全的位置.
5. 最後被移除的是 Foreground Activity:
Foreground是一個在螢幕最上端與使用者做互動的Activity, 它的優先權最高. 原則上會是最後一個被移除的程式, 除非這個Activity所需要的記憶體大小已經超出系統所能給的. 系統之所以會移除這些程序, 是為了不讓使用者介面停止回應.
由於Android應用程式的lifecycle並不是由程式本身直接控制的, 而是由系統平台進行管理. 所以對於開發者而言, 許要瞭解不同元件 Activity, Serivce 和 IntentReceiveer的Lifecycle. 要切記: 如果元件使用不當, 雖然正在進行重要的Process, 仍有可能被系統主動移除.
Android Activity生命週期簡介
Android Activity生命週期簡介
Active (活動):
Active狀態是使用者啟動Application 或 Activity 後,Activity 在運行中的狀態.
在 Android 平台上, 一個時刻只會有一個 Activity 處於Active 或 Running 狀態。其他的 Activity 都處於未Dead, Stopped 或是Paused 的狀態.
Dead狀態是 Activity 尚未被啟動, 已經被手動終止, 或已經被系統回收的狀態.
要手動終止 Activity, 可以在程式中呼叫 finish 函式.
如果是被系統回收, 可能是因為記憶體不足, 系統根據記憶體不足時的回收規則, 將處於Stopped狀態的 Activity 所佔用的記憶體回收.
簡單的總結幾個動作:
onPause(1) -> onCreate(2) -> onStart(2) - onResume(2) -> onStop(1)
onPause(2) -> onRestart(1) -> onStart(1) -> onResume(1) -> onStop(2) -> onDestroy(2)
onPause -> onStop -> onDestroy
onCreate -> onStart -> onResume
Android 的名詞觀念
Android 的名詞觀念
1. Android Package (.apk):
2. Task
Task 為"工作"的意思, 可以看成 Application (應用程式)本身. 也就是手機上的應用程式的圖示,使用者可點擊圖示啟動 task。從開發者的角度來看
3. Process
Process 的定義上,指的是"執行中的程式",在 Android 的Application 環境中,代表低階的執行程式,屬於 Kernel 部份。一個 package 的所有程式,都是在一個 process 中執行。
4. Activity
Activity是Android開發中非常重要的一個基礎類。就字面上來說, Activity 就是"活動"的意思. 可以簡單的解釋為, Activity 是一個與使用者互動的物件(object)。
如果一個Activity被其它的Activity完全的遮住時, 這個被遮的Activity處於stop的狀態, 但它仍然保有全部的狀態及資料. 由於它已不被使用者看見,所以它的畫面是被隱藏起來的, 當系統記憶體不足時,這種stop狀態的activity是最先被系統考慮移除以釋放記憶體.
當一個Activity處於pause或stop的狀態時, 系統可以要求Activity結束(finish)或移除(kill)它. 當它需要再度呈現在使用者面前時, 它必需要能完整的重新啟動(restart)及回復(resume)先前的狀態。
5. View
簡單來說,android.app.View 類別就是手機的 User Interface. View 負責繪製UI與 event (處理事件). Android 利用 View 建立所謂的 Widgets(元件), 利用 Widget 就可以製作互動式的使用者介面(interactive GUI).
2009年6月25日
清晨, 發現自己生活在緩慢的空間中
或許這是台北人生活的本質能力,能力歸屬在於城市所賦予的那種能力與步調。
是阿! 我是一個台北人。
我本能性的想要拒絕,也因此我在腦袋裡思索著一點點可以抗拒這個名詞的一丁點的文字..... Running
可惜的是除了[貢寮]、[獅頭]、[爸媽]這樣的文字之外我沒有能力跟立場去否認這個不屬於我的名詞。
感覺像是被冠上了甚麼而且無可奈何那樣的歸順。
耳朵迴盪著蘇打綠的歌詞[我們都是一個人加上一個人的長像...],
音符跳耀著在我的腦袋裡!
出現的畫面是今天早晨那緩慢的空間,我想我走進了一個緩慢的空間,而且就那短短的半小時。
像是捷運開進了一面薄膜般的邊線。
而我到了一個不屬於台北的台北!
仔細看看路上吸壤來往的人們,大部分的老人是今天的景象,這是一種徵兆嗎?
或許是一種徵兆。
因為緩慢的感覺需要有點緩慢的佈景,也需要緩慢的氣氛與感觸。
感觸是最直接的!
因為像是肌膚受電般的快速傳達。
該上班了,好像需要寫上上萬個字形容一個有感覺的早上。
好像需要再延續一點或是更多的秒、分、時去闡述今天雙手半徑世界的初發感。
像音樂那樣[拉...啦啦啦啦啦啦....啦啦啦啦]:蘇打綠這樣的延續著...
我也延續著,延續著我的生命,延續著我的感覺,延續著無可厚非的不太在意。
2009年6月5日
以手為半徑是我的世界範圍!
在這個世界裡頭沒有爭吵,沒有公司派的勾心鬥角,當然也沒有那種跟你搏軟然後在背後捅你一百多刀的smile!
所以這是屬於我的半徑! 閒人勿進,因為結界的關西。
半徑內的咖啡、半徑內的電波、半徑變成了一個獨立的單位在這個獨立的世界與國家中。
而我的半徑中我是完全自由的非固體!
因為我是法系人物!
這世界上第一套的浮潛裝備
今天一整個下午處在海洋的空間感中,當然會這樣說的意思就是我依然在這惱人的都市裡頭並且活在社會裡。
突然而來的一股腦的衝動促使我的著去收集我一直想要的浮潛裝備!
打從哪時候我一直囔囔著自己要去浮潛的? 三年、五年? 管他的!但是就是很久。
印象中應該是第一次到墾丁去浮潛之後就一直想做的事情。
拖了久了! 真得很久~
既然有FU了! 有什麼比這還重要? Answer是NO! 這他媽的世界上沒什麼東西是比FU更重要的了。
所以晚上就去COSCO買下這世界上獨一無二的第一套浮潛裝備!
在靈魂的感招下在這幾百年沒寫的BLOG加上一筆記錄!
Maybe有一天這會是一篇感招自己心靈,遙遠的傳送電波到台灣海峽、太平洋還是巴士海峽的一篇電文。
先記錄這歷史性的一刻!
The moment in history~
當在海裡遇到鯊魚的武器 : 蛙鞋
躲在水裡的利器: 呼吸管與蛙鏡
大團圓!
突然間讓我想起了碧海藍天這部電影裡頭的尚雷諾(天阿!他變得好老)。
2009年4月30日
就是自己的命運吧!我想~
原來到頭來感覺自己可有可無的。
或許自己太過努力的想要付出所以產生反效果吧。
有很多事情不管自己怎樣努力,怎樣的經營都無效。
maybe這就是命運吧!
2009年4月21日
[C++] Virtual inheritance
因此為了解決這個問題就需要用到 virtual inheritance,
假設P1 and P2 都繼承了 A class,而P3繼承了P1 and P2.
這種inheritance的關西下在P3裡就會有兩份的A, 存取的話就需要使用P1::x and P2::x去存取你想要的成員。
但是問題是,我們不需要兩份A!
virtual inheritance 可以解決這個問題讓多重繼承不會有重複的類別重複。
2009年3月13日
2009-03-13 的我
因為沒人認為我試地球人阿~
被問過我的翅膀是什麼顏色的,我直覺就認為一定是黑色的,
為甚麼?
我哪知道為甚麼!
咖啡跟我有緣份嗎?
當然有阿!因為我每天都會親吻她不下數十次,那是種超溫柔的香醇!
有什麼比她更讓我幸福的呢?
如果有天我可以選擇跳躍,我要跳躍到那天空與世界的結界去,
因為我喜歡消失在那結界的感覺,可能我可以享受那一秒剎那的消失感!
也maybe我就這樣失去所有的感覺~
不過沒辦法阿! 我是法系的人物,儘管我只是個吟遊詩人~
於是乎! 這是我的最新介紹~
2009年3月7日
超自然品牌 ...
有些字句總是充滿回憶的!
有些東西總是充滿記憶的!
記憶跟回憶佔據腦袋很大的一部分,於是乎我很好奇動物是否也有記憶與回憶?
有時候看到熟悉的文字、看到熟悉的物件、從抽屜中無意看到塵封很久的物品。
然後你會記起很多東西。
像是深深沉沉的壓在櫃子底下的那見露出衣角的衣服!
那是精華吧! 感覺是無可取代的!
因為不會在有第二件了,無可取代般的就是獨特無比。
想著、回憶著,然後為他上色~
於是乎腦袋中那個記憶拼圖突然間出現了一塊!
神奇吧!
像是坐在山與湖中那懸空咖啡座喝著一杯香醇的咖啡,
又或是拿著杯有著三塊冰的威士忌一樣的感覺。
平靜、微笑著、享受~這是我的某一天下午間的那一個被我標籤的1.5個小時~
不會在有第二個這樣的1.5個小時~
2009年3月5日
喃喃的自語著
我想~ 我真的活在我的世界吧 ~ right~
um.... 果然是挺挫折的~
而且原來我的腦袋有太多地方沒有開發到....
突然想到~ 會不會我會向村上的書一樣,總是有種會死在自己的世界那樣的感受
村上很多書看了之後就會感覺到像是死在一個自己的空間裡頭,但是想像力還是持續的在飛揚著~~
所以黑暗跟奔騰著的幻想思考力以同樣的速度馳乘著~~~
於是! 每次看完一次村上的書,會有段時間進入那樣的結界一樣的久久走不出來。
我想我真的不是法系的人物!
我真的很希望自己是法系的人物! 因為我喜歡法師~
但是我想我應該是屬於吟遊詩人或是遊俠般的人物
但我想我永遠走不出結界也就沒法換個role再進去了吧! 就這樣的遊在結界的空間裡頭那樣的生活著~
2009年2月28日
2009/02/28 編寫自己
條件是不能干涉而只能從旁影響。
不免強的情況下去玩弄人,是玩弄?是意志? 個人覺得是好笑的一場遊戲,
不幸的我是那眾多棋子中的一顆!
左右我的是什麼? 撒旦還是上帝? 我接近天使還是魔鬼?
我看過兩本聞名於世的聖經! 我求知於兩種對立信仰!
最終,我決定我還是我。
他們不過是他媽的玩家,而我不過是簡單的人類。
長不大的眼神? 邪氣的眼神?
可愛? 玩家?
長時間天秤的傾倒於一邊,短時間頃倒於一邊。
其實我渴望一股力量?上帝?撒旦?
結果我還是我,依舊不變~
2009年2月20日
鞋貓夫人,Madame!!!
能夠給的我都給了 你要挺起胸膛更坦蕩蕩的活
能夠說的我都說了 只是緊要關頭不一定行得通
這個世界轉眼就要崩毀
為了你我都開心so be my baby
何必擔心明天的天氣
也許人生不必要so complicated
寂寞 又算什麼 並不是每個夢想都能有著落
再說 我還能夠 邀請你與我一起活在夢裡頭
我的強壯勇敢都只為你
你是我眼前的唯一so be my baby
何必擔心未來會有風雨
也許人生不必要so complicated
失落 又算什麼 並不是每個夢想都會有感動
再說 我還能夠 邀請你與我一起活在夢裡頭
你可以 笑我做夢天生好手 偏偏 我理直氣壯四處大步走 ya~你是忌妒我單純你沒有 比起你我卻輕輕鬆鬆更舒暢又更自由
再說 我還能夠 邀請你與我一起活在夢裡頭
你可以 笑我做夢天生好手 偏偏 我理直氣壯四處大步走 ya~
你是忌妒我單純你沒有 比起你我卻輕輕鬆鬆更舒暢又更自由
能夠給的我都給了 你要挺起胸膛堅強的活
能夠說的我都說了 不過不巧遇上another rainy day
擦乾眼淚 不要哭了 你要像我一樣驕傲才對
我是英雄 你就跟著我 我會帶你看遍世界的美
能夠給的我都給了 你要挺起胸膛坦蕩蕩的活
能夠說的我都說了 但若運氣不好失敗我不賠
擦乾眼淚 不要哭了 你要像我一樣驕傲才對 這才對
我是英雄 你就跟著我 我會帶你看遍世界的美
Yeah dude, you gonna be in my group,
and that's why I wanted you, see all the thing I will do for you
Yeah it's true 眼睛要看著遠方 意志鋼鐵般堅強 跌倒也不要害怕
我最強 我最強 世界上只我最強 我可以為你抵擋黑暗的力量
好快活 好快活 跟著我你會快活 因為我是獨一無二的我 Yeah~
吳聽徹【她】MV
作詞/曲:吳聽徹
琴弓面對著我的手掌
它們的誤會大過於緊張
氧氣稀薄的很平常
怎麼是這樣
琴弓拉扯著絃的去向
顫動的旋律像在說著謊
寂寞理所當然的住進左心房
血液也不抵抗 靜靜的在缺氧 不說話
唉 就這樣吧
(愛 就是這樣吧)
習慣上了一個人是怎樣
一不聽不想就開始慌張
用遺忘收場 我還要堅強
開始習慣一個人獨自對話
學著一個人的天份我有吧
而我也開始忘了妳是她
我試著慢慢忘了 她
【希臘神話】邱比特與賽姬〈Cupid and Psyche〉
賽姬(Psyche, 意為「心靈」或「蝴蝶」)是國王的第三個女兒,她及兩個姊姊都貌美如花,小公主賽姬尤其美得不可方物。她的美貌使得大家將她視為女神般地愛慕與尊敬者,甚 至認為她就是美的女神的化身。因此,前去祭拜愛芙洛黛蒂(Aphrodite,真正的美神【羅馬名Venus,維納斯】)的人就變得少之又少,祂的聖壇變 得破舊不堪,積滿許多灰塵。這下子,便惹惱了尊貴的愛芙洛黛第女神。
Jean-Baptiste Greuze, 1725-1805 (1786)
Psyche Honored by the People,Luca Giordano (1634-1705)
The Wedding of Psyche, by Sir Edward Burne-Jones (1895)
賽姬向姊姊們獻寶,導致了她和邱比特的分離。
Psyche Showing Her Sisters Her Gifts, by Jean-Honore Fragonard (1732-1806) (1753)
賽 姬拿著燭火慢慢靠近,結果卻讓賽姬嚇一跳,只見一個俊俏少年躺臥在床上。金色的鬈髮、象牙白的皮膚,自己的丈夫哪裡是一條大蛇呢?賽姬傻傻地看著他,而忘 了她與大蛇之間的約定。結果,蠟油一不小心滴到了邱比特的肩上,邱比特驚醒過來。邱比特發現賽姬竟毀了他倆的誓言,傷心的坐了起來,他說:「噢!愚蠢的賽 姬啊,你就是這樣回報我的愛嗎?在違背我母親的命令與讓你成為我的妻子之後,你還認為我是妖怪想砍下我的頭嗎?回去找建議你這樣做的姊姊們吧!去找建議你 這樣做的人吧!我給你最大的懲罰就是妳再也見不到我了。愛情是不能存在懷疑之中的。」(註1)說完,邱比特就像一陣風似的不見了,就連城堡也消失了。
Psyche Abandoned by Cupid, by Charles-Antoine Coypel, 1694-1752 (1730)
Psyche Opening the Golden Box, oil on canvas by John William Waterhouse, c.1903
(我很喜歡最後一句話:Love cannot dwell with suspicion.意即「當賽姬(心靈)存有懷疑時,愛情(邱比特)根本無法安棲。」)