2009年9月26日

夢的那悟

不知道是因為這個空間有生物所以存在而或是因為這個空間之外的空間存在。
最近常夢見自己,夢見自己像似般的未來。
或許是因為是種徵兆、也或許這是一種真實未來。
或許開始認為滿足是一種簡單也很複雜的事情因此而感到疲倦,
或許是一個夢夢見自己感受了那樣的迷失而覺得頓時需要做出一些什麼。
不過這就是世界的想法突然間出現在身體裡的某個地方,
可能會像是一種簡單的低語,低沉的呢喃在自己的身體某個可以發出回聲的地方。

漸漸的了解這個世界變成了一種左腳右腳的結界石,
開始覺得不是因為如此而那樣。
開始覺得應該會是這樣。

儘管的讓這世界變成世界,也讓空間變成空間,影像變成影像吧!
為了生活而活,而不要為了活而生活!
或許是因為我而誤解、也或許是因為世界而世界,
不過說穿了都是個故事罷了!


2009年9月11日

WINCE Configuration Files

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)進ㄧ步處理該中斷請求。

4.何謂IST:

•Created by a device driver
•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


interrupt step flow:


針對 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);
    mprotect(..., PROT_READ | PROT_WRITE);
當進行 mmap ELF segment 時,捨棄 PROT_WRITE 這個 bit,這使得 cramfs 行為變成「如同 XIP .text 一般,去 mapping 每個在 .data section 的 page 到 ROM pages」,而透過 mprotect 去設定 PROT_WRITE,這使得:
  • vm_area_struct 允許作 write
  • PTE (page table entry) 的 write 權限則被抑制
換言之,我們已經實做了 CoW (Copy-on-Write),這之間微妙的變化,可參考之前的翻譯文章 [探索 Linux Memory Model (上)] 與 [探索 Linux Memory Model (下)]。在上述機制引入後,系統呈現的效果如下圖:

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."
不過,這也引來額外的 ROM access latency,會導致程式執行時間拉長,對一般的消費性電子裝置來說是可以接受的 (single executable)。wiki 上提供的 patch 似乎不太完整,所以我弄了新 patch:[glibc-2.3.2-DRIP.patch],加入 [OpenEmbedded] 一類的工具中即可。

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 的用意

VC有兩種函數調用方式 一種是__stdcall,另一種是__cdecl,函數的調用方式有兩種一種是PASCAL調用方式,另一種是C調用方式。
使用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的區別

MPEG Audio分為MPEG1和MPEG2兩大類。
而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 中有下列三種因應方式:

  1. 改變語言本身以符合 CTS 的規定。例如 Visual Basic 6 就為此做了大幅度的擴充與改變,加上繼承的特性,這使得新版的 Visual Basic .NET 差不多可以算是一個全新的語言了,猶如當年 C 衍生出 C++ 一樣。
  2. 擴 充語言本身以接近 CTS 的規定,但仍保留不相容於 CTS 的語法,如此一來,程式中符合 CTS 規定者以 CTS 的方式編譯,不符合 CTS 規定者則以傳統的方式編譯成 native code。例如 C++ with Managed Extension (簡稱 MC++) 就是如此。
  3. 語 言本身儘量維持不變,一切都是透過超強的編譯器設計來達成和 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 姓其召語言的裧咳
圖 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︹守綿剛的邕樹樥配置方式
圖 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 的一函重要型吶
圖 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
圖 4:Value Type、Reference Type、以及 Interface 的 Accessibility

如果是 Nested Type (定義在另一個 Type 內部的 Type) 的話,則有比較多種 Accessibility。如下圖五所示:

對 5︹nested 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,如果讀者需要更進一步的資料,可以參考下面三本書:

  1. The Common Language Infrastructure Annotated Standard (by James S. Miller ) Addison Wesley
  2. Inside Microsoft .NET IL Assembler (by Serge Lidin) Microsoft Press
  3. Compiling for the .NET Common Language Runtime (by John Gough) Prentice Hall

2009年9月4日

DirectShow實踐經驗雜談

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(&pFilter));

if (SUCCEEDED(hr))
{
// Query for IDMOWrapperFilter.
IDMOWrapperFilter *pDmoWrapper;
hr = pFilter->QueryInterface(IID_IDMOWrapperFilter,
reinterpret_cast(&pDmoWrapper));

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?

few features that differentiate between .NET Compact Framework and the .NET Framework:

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.