microsoft foundation applications_2

30
C3 : Media Playback MF sử dụng phần mở rộng COM nhưng không phải là chuẩn COM API. 1 COM API chuẩn chỉ bao gồm đối tượng COM mà dc chỉ đc thể hiện qua hàm CoCreateInstance(). Thay vào đó, MF sử dụng hỗn hợp của COM và đối tượng thường. Khi bắt đầu ứng dụng, phải cài đặt hệ thống COM bằng cách gọi hàm CoInitializeEx(). Nó sẽ khởi tạo thi viện COM và chuẩn bị sử dụng nó. Sau khi ứng dụng hoàn thành, gọi CoUninitialize() để tắt thư viện COM. Note: MF là hệ thống free-threaded, nghĩa là phương thức giao diện COM có thể cài đặt từ thread tùy ý. Do đó, khi gọi CoInitializeEx() , bạn phải khởi tạo đồng thời COM với đối tượng apartment-threaded bằng cách truyền vào tham số COINIT_APARTMENTTHREADED. Đối tượng của bạn cũng cần sử dụng synchronization primitives , như locks, để điều khiển truy cập tới biến cục bộ bằng thread chạy đồng thời. Trong hầu hết các trường hợp, bạn nên cài đặt giao diện mà đc cung cấp bởi MF. 1 số MF dựa trên chức năng thêm mà chỉ đc thể hiện trong đối tượng MF mà cài đặt giao diện. Ví dụ nếu bạn muốn sử dụng default media session, bạn không nên cài đặt đối tượng IMFTopology của bạn mà sử dụng đối tượng trả về bởi MFCreateTopology() API. Trong chương này, bạn sẽ khám phá 2 class chính tạo nên player: Cplayer và CtopoBuilder. Lớp Cplayer gồm mọi thứ bạn cần để tạo đối tượng core playback MF và điều khiển chức năng video playback. CtopoClass gồm hàm và đối tượng cần để tạo 1 phần MF topology. Topology( cấu trúc) đc sử dụng để cài đặt và kết nối đối tượng MF chịu trách nhiệm việc xử lý dữ liệu media. Cơ bản về render file với Media Sessions Như DirectShow, kiến trúc MF sử dung 1 chuỗi component kết nối với nhau để xử lý media file và hiển thị nó. Mặc dù những đối tượng này có thể đc cài đặt và gắn với nhau theo nhiều cách khác nhau , nhưng 1 trong những phương tiện phổ biến là với 1 đối tượng media session. Bằng cách sử dụng 1 đối tượng media session , bạn có thể tạo là 1 media pipeline và điều khiển playback.

Upload: duy-khanh

Post on 30-Oct-2014

81 views

Category:

Documents


7 download

TRANSCRIPT

Page 1: Microsoft Foundation Applications_2

C3 : Media PlaybackMF sử dụng phần mở rộng COM nhưng không phải là chuẩn COM API. 1 COM API chuẩn chỉ bao gồm đối tượng COM mà dc chỉ đc thể hiện qua hàm CoCreateInstance(). Thay vào đó, MF sử dụng hỗn hợp của COM và đối tượng thường.

Khi bắt đầu ứng dụng, phải cài đặt hệ thống COM bằng cách gọi hàm CoInitializeEx(). Nó sẽ khởi tạo thi viện COM và chuẩn bị sử dụng nó. Sau khi ứng dụng hoàn thành, gọi CoUninitialize() để tắt thư viện COM.

Note: MF là hệ thống free-threaded, nghĩa là phương thức giao diện COM có thể cài đặt từ thread tùy ý. Do đó, khi gọi CoInitializeEx() , bạn phải khởi tạo đồng thời COM với đối tượng apartment-threaded bằng cách truyền vào tham số COINIT_APARTMENTTHREADED. Đối tượng của bạn cũng cần sử dụng synchronization primitives , như locks, để điều khiển truy cập tới biến cục bộ bằng thread chạy đồng thời.

Trong hầu hết các trường hợp, bạn nên cài đặt giao diện mà đc cung cấp bởi MF. 1 số MF dựa trên chức năng thêm mà chỉ đc thể hiện trong đối tượng MF mà cài đặt giao diện. Ví dụ nếu bạn muốn sử dụng default media session, bạn không nên cài đặt đối tượng IMFTopology của bạn mà sử dụng đối tượng trả về bởi MFCreateTopology() API.

Trong chương này, bạn sẽ khám phá 2 class chính tạo nên player: Cplayer và CtopoBuilder. Lớp Cplayer gồm mọi thứ bạn cần để tạo đối tượng core playback MF và điều khiển chức năng video playback. CtopoClass gồm hàm và đối tượng cần để tạo 1 phần MF topology. Topology( cấu trúc) đc sử dụng để cài đặt và kết nối đối tượng MF chịu trách nhiệm việc xử lý dữ liệu media.

C b n v render file v i Media Sessionsơ ả ề ớNhư DirectShow, kiến trúc MF sử dung 1 chuỗi component kết nối với nhau để xử lý media file và hiển thị nó. Mặc dù những đối tượng này có thể đc cài đặt và gắn với nhau theo nhiều cách khác nhau , nhưng 1 trong những phương tiện phổ biến là với 1 đối tượng media session. Bằng cách sử dụng 1 đối tượng media session , bạn có thể tạo là 1 media pipeline và điều khiển playback.

Media session giữ tất cả MF componets xử lý dữ liệu và cho bạn khả năng để start và stop video.

Lớp Cplayer sử dụng media session để xây dựng 1 media pipeline, và điều khiển chơi video, và dọn dẹp tài nguyên . Thêm vào đó lớp Cplayer cũng bọc và ẩn sự phức tạp của hệ thống messaging của MF không đồng bộ từ ứng dụng WIN32

Các bước thực hiện :

1. Ứng dụng tạo đối tượng Player của lớp Player

2. Ứng dụng gửi URL file bằng cách gọi Cplayer::OpenURL().

Page 2: Microsoft Foundation Applications_2

3. Player tạo đối tượng media session. Trong phiên tạo, player đăng ký nó như 1 receiver của synchronous MF events của session bằng cách truyền con trỏ của nó tới hàm IMFMediaSession::BeginEvent().

4. Player tạo đối tượng topology : CtopoBuilder

5. Player truyền topology cho media session

6. Sau khi topology đc nạp, media session tạo đối tượng tất cả các component đc chỉ ra trong topology và bắn ra 1 sự kiện MF không đồng bộ chỉ ra rằng topology đã sẵn sàng. Vì player đã đăng ký nhận sự kiện của session ở bước 3 , nó sẽ được gọi bởi session qua sự kiện “topology-ready”

7. Sự kiện “topology-ready” làm cho player bắt đầu chơi video

MF sử dụng kiểu không đồng bọ cho hầu hết thao tác. Bên trong, MF có đối tượng worker thread đặc biệt mà thực hiện lời gọi không đồng bộ đã lên kế hoạch trên những thread riêng biệt.

T o playerạHàm khởi tạo player nhận handle của main window là 1 tham số, nó sử dụng handle này khi xây dựng topology , để chỉ ra nơi mà video sẽ đc vẽ lên khi render.

Player cũng khởi tạo COM bằng việc gọi CoInitializeEx() , và MF bằng việc gọi MFStartup().

Kh i t o Media Sessionở ạHầu hết các khởi tạo bắt đầu khi ứng dụng truyền URL file cần chơi cho player. Player sử dụng URL đó để cài đặt topology sẽ chơi nội dung,

HRESULT CPlayer::OpenURL(PCWSTR sURL)

{

}

Như bạn thấy , công việc chính của OpenURL() là truyền lời gọi đến các hàm khác. 3 bước mà player cài đặt là:

- Tạo và khởi tạo media session- Xây dựng playback topology- Truyền topology cho session

HRESULT CPlayer::CreateSession(void)

{

}

Page 3: Microsoft Foundation Applications_2

Để tạo 1 media session , bạn cần gọi MFCreateMediaSession(). Hàm này trả về 1 con trỏ đến 1 media session mới. Sau khi session đc tạo, bạn cần chỉ ra 1 đối tượng callback nhận asyn events từ session bằng việc gọi hàm IMFMediaEventGenerator::BeginGetEvent() , hàm mà sẽ thông báo cho media event generator ( trong trường hợp này chinh là session) rằng event tiếp sẽ gửi cho đối tượng callback.

Để code đc đơn giản , trong trường hợp này, chính bản thân lớp Cplayer sẽ là đối tượng callback. Nó cài đặt IMFAsyncCallback interface đc sử dụng bởi event generator để truyền lại những event trước. Khi có event xảy ra trong media session , player sẽ đc thông báo với 1 lời gọi tới phương thức IMFAsyncCallback::Invoke() của nó.

Cuối cùng, sau khi topology đc xây dựng bởi lớp CtopoBuilder, player sẽ truyền topology tới media session bằng việc gọi hàm IMFMediaSession::SetTopology(). Topology chứa thông tin về MF components mà session cần để tạo đối tượng để chơi file.

Tham số thứ nhất của hàm IMFMediaSession::SetTopology() là tổ hợp của những cờ sau:

- MFSESSION_SETTOPOLOGY_IMMEDIATE Stop playback of the currently loaded media (if

any) and apply the topology immediately.

- MFSESSION_SETTOPOLOGY_NORESOLUTION This is a full (not partial) topology and

doesn’t need to be resolved. MF doesn’t need to attempt to add any missing MFTs, and the

session can accept the topology as is.

- MFSESSION_SETTOPOLOGY_CLEAR_CURRENT If the second parameter is NULL, clear any

topology association with the session. If the second parameter is not NULL, reset the session

only if the passed-in topology matches the one currently associated with the session.

Truyền giá trị 0 tương đương với không dùng cờ nào. Nếu cờ MFSESSION_SETTOPOLOGY_ IMMEDIATE không được dùng và 1 topology đã có trong hàng đợi của session, topology mới sẽ được thên vào hàng đó. Khi đó, sau khi session chơi xong topology thứ nhất, topology thứ 2 sẽ đc bắt đầu.

Media Session Asynchronous eventsTất cả ứng dụng Windows vốn là không đồng bộ. Ứng dụng Windows không chạy trên chỉ 1 thread. Thay vào đó, chúng có 1 vòng lặp message thường xuyên đợi event và thực hiện các hành động khác nhau dựa trên thông tin của event đó. Ví dụ như mỗi lần bạn ấn 1 phím, hệ thống gửi 1 event tới vòng lặp message của ứng dụng với ID phím. Ứng dụng sẽ giải mã event và thực hiện các hành động tương ứng.

Để phù hợp với ứng dụng Windows, MF cũng sử dụng kiến trúc không đồng bộ tương tự. Sự lựa chọn thiết kế này sẽ làm tăng đáng kể độ đáp ứng của ứng dụng MF. Nếu một ứng dụng Windows trực tiếp gọi một hàm không đồng bộ mà mất một thời gian dài để thực hiện, lời gọi đó sẽ chặn vòng lặp event của ứng dụng , gây ra hiện tượng treo ứng dụng. Kết quả là, giao diện sẽ đứng lại, người dùng không thể

Page 4: Microsoft Foundation Applications_2

bấm nút , thay đổi kích cỡ của sổ, hay thao tác với menu. Để giải quyết vấn đề này tận gốc, hay ít nhất là làm cho việc viết ứng dụng không đồng bộ dễ dàng hơn, mà không phải viết thêm hệ thống message thứ hai , MF cũng sử dụng kiểu không đồng bộ.

Trong hệ thống không đồng bộ, hầu hết các hàm MF không trực tiếp thực hiện bất cứ thao tác nào; thay vào đó ,lời gọi chỉ sắp xếp công việc với đối tượng chính và trở về ngay lập tức. Bản thân các mục công việc đó sẽ thực hiện ở thread ngầm phía sau. Sau khi công việc được hoàn thành , các đối tượng sẽ phát ra các event thông báo cho đối tượng gọi trạng thái của mục công việc.

Để thuận tiện cho hệ thống liên lạc không đồng bộ, media session nhận một con trỏ trỏ tới event callback object. Đối tượng callback này sẽ được gọi khi có sự kiện xảy ra như topology đã sẵn sàng cho việc chơi video, hay khi việc chơi video kết thúc, hay bất cứ lỗi nào xảy ra. Ví dụ này sử dụng chính player như là asynchronous event callback object. Player thực thi giao diện IMFAsyncCallback, đăng ký nó với media session trong phương thức Cplayer::CreateSession(), và được gọi bởi session bất cứ khi nào sự kiện xảy ra.

Vòng đời MF event thực ra khá phức tạp và chứa nhiều tầng. Hình sau miêu tả MF events làm việc như thế nào trong ví dụ của chúng ta. Nó thể hiện một chuỗi các lời gọi được tạo ra để xử lý một event không đồng bộ phát ra từ media session tới đối tượng player.

1. Player tạo một session mới bằng việc gọi MFCreateMediaSession() từ hàm Cplayer::CreateSession().

2. Player đăng ký nó với bộ quản lý sự kiện (event handler) cho những event tiếp theo từ media session. Player cũng truyền một con trỏ tới một đối tượng IMFAsyncCallback (trong trường hợp này con trỏ là chính nó) tới phương thức IMFMediaEventGenerator::BeginGetEvent(). Lời gọi trả về ngay lập tức. Lời gọi này báo cho media event generator đối tượng nào cần được gọi khi có bất cứ một event xảy ra.

3. Event generator ( trong trường hợp này là session) gọi IMFAsyncCallback::Invoke() để thông báo cho event handler một envent mới trong hàng đợi. Để ý rằng việc này được thực hiện ở một thread riêng biệt với thread chính – nó là lý do vì sao đây được gọi là hệ thống không đồng bộ. Những thứ khác có thể xảy ra đồng thời trong player trên những thread khác nhau.

4. Event hander gọi lại event generator và lấy event thật sự từ hàng đợi bằng phương thức IMFMediaEventGenerator::EndGetEvent() . Lời gọi này trả về ngay lập tức.

5. Event handler xử lý event vừa lấy về.6. Player đăng ký nó để nhận sự kiện tiếp theo giống như bước 2.7. Quay lại bước 3.

Bằng cách sử dụng cơ chế quay vòng này, MF đảm bảo các event luôn có đối tượng có thể xử lý chúng, và trách nhiệm cho việc xử lý các event có thể được truyền suôn sẻ giữa một số đối tượng. Ví dụ như một đối tượng event-handling (một thực thi của giao diện IMFAsyncCallback) có thể dễ dàng bàn giao việc xử lý các event tiếp theo tới đối tượng mới bằng việc truyền con trỏ của đối tượng mới trong lời gọi BeginGetEvent() tiếp theo.

Page 5: Microsoft Foundation Applications_2

Note: Bạn có thể để ý rằng giao diện IMFAsyncCallback cũng định nghĩa một phương thức giúp đỡ GetParameter() . Phương thức này được sử dụng bởi media event

generator để phát hiện đối tượng callback sẽ xử lý các lời gọi như thế nào. Hầu hết các trường hợp, bạn sẽ muốn sử dụng cách xử lý mặc định và có phương thức trả về

E_NOTIMPL HRESULT.

M_pSession->BeginGetEvent(this)

This::Invoke()

{

m_pSession->EndGetEvent( event );

Xuly(event);

If (không là Event cuối từ Session )

{

m_pSession->BeginGetEvent(this);

}

}

Event Processing and Player BehaviorCPlayer::ProcessMediaEvent()

{

// Get the event type.

// Get the event status.

}

Hàm xử lý event CPlayer::ProcessMediaEvent() xác định event và thực hiện các thao tác khác nhau dựa trên lại event. Đầu tiên, hàm xác định kiểu event – kiểu liệt kê MediaEventType. Kiểu event sẽ cung cấp các manh mối về thông tin trong event , ai phát ra nó và vì sao. Ví dụ, khi topology ban đầu được tạo trên session , player nhận một media event với kiểu MESessionTopologySet; khi session được bắt đầu, player nhận event với kiểu MESessionStarted. Quy ước đặt tên rất hữu ích cho việc xác định thông tin và khung cảnh của một event.

Khi hàm ProcessMediaEvent() đã xác định kiểu event , nó kiểm tra event đó là kiểu lỗi hay không. Nếu đối tượng IMFMediaEvent được phát ra bởi một lỗi , HRESULT được chứa trong đối tượng event sẽ chỉ

Page 6: Microsoft Foundation Applications_2

ra loại lỗi đó là gì. Lấy giá trị lỗi HRESULT bằng hàm IMFMediaEvent::GetStatus(). Nếu event biểu thị một lỗi , hàm sẽ thoát ra, hủy bỏ việc xử lý event .

Nếu trạng thái event không biểu thị lỗi , ProcessMediaEvent() phân tích kiểu event để quyết định bước tiếp theo. Nếu kiểu event là MESessionTopologyStatus, và trạng thái của topology đã sẵn sàng chơi video, hàm gọi OnTopologyReady() để bắt đầu chơi. Nếu kiểu event là MEEndOfPresentation, biểu thị việc kết thúc video, hàm sẽ cập nhập trạng thái của player để báo hiệu cho player dừng. Cuối cùng, nếu kiểu event là MESessionClosed, hàm sẽ báo hiệu tới bất cứ thread nào đang đợi m_closeCompleteEvent Win32 event rằng thao tác đóng đã xong , và đặt giá trị HRESULT thành S_FALSE. Nó báo cho hàm gọi Invoke() rằng không có thêm sự kiện mới nào từ session.

Như bạn thấy , hàm ProcessMediaEvent() có cách giải quyết đặc biệt cho event MESessionClosed. Điều này cần thiết để thật sự hủy tất cả các tài nguyên liên kết với session. Vấn đề với việc kết thúc session là bạn cần chắc rằng thao tác đóng được thực hiện xong trước khi bạn có thể hủy một cách an toàn session. Bởi vì hàm Close() là không đồng bộ, tuy nhiên, bạn cần một cơ chế đồng bộ hóa để cho thread có nhiệm vụ kết thúc session ( thi hành hàm CPlayer::CloseSession() ) đợi thực hiện xong lời gọi Close() của sesion trước khi tiếp tục với việc dọn dẹp.

Để giải quyết vấn đề trên, bạn có thể sử dụng một Win32 event để đồng bộ hóa từng thread chịu trách nhiệm cho việc kết thúc session. Thread chính trong player bắt đầu lời gọi IMFMediaSession::Close() từ hàm CPlayer::CloseSession(), và sau đó đợi khi m_closeCompleteEvent được thiết lập. Tuy nhiên, việc gọi hàm Close() phát ra một thread riêng mà thực sự thực hiện thao tác đóng. Thread thứ hai thực hiện công việc đóng session, và sau đó gọi phương thức CPlayer::Invoke(). Phương thức CPlayer::Invoke() sẽ thiết lập m_closeCompleteEvent, mà thread đầu vẫn đang chờ trong hàm CloseSession(). Sau khi thread đầu tiếp tục lại , nó hoàn thành việc kết thúc.

Phương thức CPlayer::CloseSession() bắt đầu việc kết thúc session . Đầu tiên phương thức đặt player vào đúng trạng thái bằng việc thiết lập biến cục bộ m_state, và sau đó gửi lời gọi IMFMediaSession::Close() tới session. Sau đó, thread dừng lại , đợi event m_closeCompleteEvent được phát ra. Như bạn thấy ở trước, event này phát ra từ thread riêng, từ phương thức CPlayer::ProcessMediaEvents(). Cuối cùng , khi event hoàn tất việc đóng đã được phát đi , hàm sẽ gửi lời gọi Shutdown() tới session , giải phóng session bằng việc thiết lập biến m_pSession là NULL , thiết lập biến m_state để biểu thị rằng player đã đóng.

Các bước thực hiện

1. Ứng dụng tạo đối tượng player2. Ứng dụng thiết lập URL của file lên đối tượng layer

a. Player tạo media sessionb. Player tạo topology 1c. Player xếp topology 1 vào media session

3. Khi topology sẵn sàng, session phát ra event topology-ready

Page 7: Microsoft Foundation Applications_2

4. Khi player nhận event topology, nó thông báo cho session để bắt đầu chơi5. Ứng dụng thiết lập URL của file mới

a. Player tạo topology 2b. Player xếp topology 2 vào session

6. Sau khi topology 1 kết thúc chơi, media session phát ra event end-of-presentation7. Media session lấy topology tiếp theo và chuẩn bị nó để chơi. Khi topology 2 sẵn sàng , session

phát ra event topology-ready8. Event topology-ready thông báo cho player để bắt đầu chơi

Thực tế lệnh chơi video của session đước kích hoạt bởi event topology-ready. Cụ thể, session gọi hàm CPlayer::Invoke() để báo cho player rằng topology đã sẵn sàng. Hàm Invoke() lấy event từ sesion bằng việc gọi hàm thực thi IMFMediaEventGenerator::End-Get Event() của nó. Sau đó hàm Invoke() truyền event đó tới phương thức ProcessMediaEvent() , phương thức đó sẽ lần lượt chơi video. Việc chơi video bắt đầu bằng việc gọi phương thức CPlayer::OnTopologyReady().

//

// Handler for MESessionTopologyReady event - starts video playback.

//

HRESULT CPlayer::OnTopologyReady(void)

{

}

Trong phương thức OnTopologyReady() có 2 thao tác chính bên trong nó:

1. Khi 1 topology sẵn sàng, phương thức này sẽ truy vấn session về giao diện được lấy ra từ video renderer IMFVideoDisplayControl. Giao diện này được sử dụng để vẽ lại ảnh của video, điều khiển tỉ lệ, khích thước video …

2. Sau khi session phát ra sự kiện thông báo topology đã sẵn sàng, phương thức lập tức chơi video bằng việc gọi hàm StartPlayback(). Hàm StartPlayback() được gọi trong phương thức CPlayer::Play().

Hàm MFGetService() là hàm con được sử dụng để truy vấn với các thành phần cho những đối tượng liên kết với chúng. Trong ví dụ này nó dùng để lấy ra giao diện Enhanced Video Renderer (EVR) , bạn cũng có thể truyền các cờ khác để lấy ra MF source, audio renderer, mixer components, byte srtreams, proxies cho remote object …

Sau khi lấy được con trỏ EVR cho việc điều khiển video-renderer, bạn có thể chơi video bằng cách gọi phương thức CPlayer::StartPlayback().

Page 8: Microsoft Foundation Applications_2

//

// Start playback from the current position.

//

HRESULT CPlayer::StartPlayback(void)

{

}

Để bắt đầu chơi, bạn cần sử dụng phương thức IMFMediaSession::Start() và truyền vào một số tham số. Tham số này biểu diễn nơi bắt đầu chơi video. Bạn biểu diễn điểm bắt đầu chơi video bằng cấu trúc PROPVARIANT, về cơ bản là kiểu dữ liệu chuẩn có thể chứa bắt cứ giá trị nào. Tham số vị trị bắt đầu chơi biểu thị nội dung được chơi từ một vị trí tuyệt đối, từ vị trí hiện hành hay thậm chí từ một vị trí tương đối trong một playlist.

Để dịch tham số vị trí, hàm MFMediaSession::Start() nhận GUID là tham số thứ nhất. GUID mô tả phương thức nên dịch tham số thứ hai như thế nào:

- GUID_NULL mô tả tham số thứ hai sẽ là kiểu cấu trúc PROPVARIANT được thiết lập hoặc là VT_EMPTY - video được chơi từ vị trí hiện thời, hoặc là VT_I8 ( một số nguyên có dấu 8 byte) – video được chơi tại vị trí cách vị trí đầu tiên của video một khoảng bội của 100-nano giây. Bạn có thể dùng định dạng này để cài đặt phương thức seek video. Ví dụ như truyền vào GUID_NULL tham số VT_EMPTY sẽ chơi tiếp từ vị trí hiện thời khi video đang dừng. Truyền vào GUID_NULL tham số VT_I8 được đặt là 300,000,000 sẽ chơi từ thời điểm 30s tính từ vị trí ban đầu của video.

- MF_TIME_FORMAT_SEGMENT_OFFSET một định dạng tùy biến được hỗ trọ bởi sequencer source.

Note: Để biết thời điểm hiện tại trong video, bạn gọi phương thức IMFMedia Session::GetClock() để lấy đối tượng presentation clock. Sau đó dùng đối tượng clock đó để lấy ra thời điểm chơi hiện tại. Bạn sẽ biết thêm về đối tượng clock trong chương 8.

Building the Media Pipeline Xây d ng đ ng ng mediaự ườ ốChương trước chúng ta đã giải thích về các bước xây dựng topology. Lớp Cplayer ủy nhiệm tất cả các công việc cần thiết để xây dựng đường ống media. Chương này xem xét làm thế nào đối tượng CtopoBuilder có thể tập hợp các thành phần MF khác nhau vào một cấu trúc được sử dụng để chơi video, cũng như làm thế nào player có thể sử dụng cấu trúc liên kết cục bộ để xây dựng đường ống media được sử dụng để chơi file đó.

Mục đích của lớp CtopoBuilder là tách việc xây dựng cấu trúc liên kết phức tạp ra khỏi lớp player chính. Lớp này lấy đường dẫn từ một phần nội dung( thường là xâu chứa trong đường dẫn đến một file) , thạo ra một MF sources có thể nạp nội dung đó, và sau đó xây dựng một cấu trúc liên kết từ nó. CtopoBuilder chỉ tạo ra một phần cấu trúc liên kết, chỉ thêm nút nguồn, nút đích và sau đó nối chúng lại với nhau.

Page 9: Microsoft Foundation Applications_2

Những nút nguồn và nút đích này chính là gơi ý để media session tìm những thành phần còn lại cần cho việc chơi video. Ngay trước khi chơi cấu trúc liên kết, session sẽ tìm những kết hợp thích hợp cho MF tranform , cái mà có thể chuyển đổi dòng dữ liệu media nguồn thành một định dạng mà bộ renderer đích có thể xử lý được. Quá trình lấp những chỗ trống trong cấu trúc liên kết gọi là “resolving” hay“rendering”.

Hình sau miêu tả cấu trúc liên kết được xây dựng bởi CTopoBuilder và MF session khi chuẩn bị chơi video

Lớp CtopoBuilder tạo ra nút nguồn và nút renderer và nối chúng với nhau ( nét đứt đậm ). Sau đó, session sẽ xem xét dữ liệu lưu trong nút cấu trúc liên kết và tự động tìm ra các nút cần thiết bổ xung cho cấu trúc liên kết. Quá trình này được biểu diễn bởi hai ô vuông “Auto resolved” được định hướng bởi các mũi tên bên dưới ô vuông.

Các bước cần thiết để tạo một đường ống media :

1. Lớp CtopoBuilder tạo thành phần MF source. CtopoBuilder cần source này để tìm ra kiểu và số lượng stream có trong file.

2. Đối tượng CtopoBuilder tạo một phần cấu trúc liên kết bằng cách lặp các bước sau trên mỗi media stream trong file:

a. Tạo một nút nguồn source nodeb. Tạo một nút đích sink nodec. Nối hai nút này lại với nhau

3. Cuối cùng , sau khi một phần cấu trúc liên kết được tạo ra, nó được đưa tới một thành phần chịu trách nhiệm giải cấu trúc liên kết đó, tìm ra tất cả những thành phần còn thiếu và tạo đối tượng cho nó. Trong ví dụ này, media session sẽ thực hiện công việc trên.

Bạn sẽ được giải thích rõ hơn trong phần tiếp theo.

Page 10: Microsoft Foundation Applications_2

Creating the Media Foundation SourceBan đầu , ứng dụng chỉ có tên file. Để chơi được file đó, ứng dụng cần thành phần có thể nạp file đó, mở gói dữ liệu từ file container, và giao dữ liệu cho hệ thống MF. Công việc này được thực hiện bởi MF source.

Để tạo ra một đối tượng media source có thể hiểu container và dữ liệu trong file cụ thể, MF sử dụng một thành phần có sẵn gọi là source resolver. Source resolver lấy đường dẫn file hoặc stream url và thử tạo thành phần media source thích hợp cho kiểu file đó.

HRESULT CTopoBuilder::CreateMediaSource(PCWSTR sURL)

{

}

Sau khi tạo source resolver ( dùng hàm MFCreateSourceResolver() ) , đối tượng IMFSourceResolver sẽ tạo một MF source. Chú ý rằng tham số thứ hai của hàm MFSourceResolver::CreateObjectFromURL() là tập các cờ mô tả loại đối tượng nào được tạo ra và làm thế nào để tìm source phù hơp. Cờ này mô tả giải thuật mà source resolver sử dụng để chọn ra source thích hợp cho kiểu media đó - một source có thể hiểu và phân tích được file đó.

Hình sau biểu diễn một cách logic source resolver làm việc với cờ MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE để tìm ra source phù hợp cho file.

Trong trường hợp này , mã nguồn sử dụng hàm đồng bộ IMFSourceResolver::CreateObjectFromURL() . Tuy nhiên , source resolver cũng hỗ trợ tạo source bằng các hàm không đồng bộ : BeginCreateObjectFromURL() và EndCreateObjectFromURL(). Việc tạo đối tượng không đồng bộ hữu ích

Page 11: Microsoft Foundation Applications_2

khi đối phó với stream qua mạng hay source dữ liệu khác mà mất một thời gian dài để xử lý. Nếu thao tác này quá lâu, ví dụ như mạng lỗi thì việc tạo đối tượng không đồng bộ có thể bị hủy.

Bạn có thể dùng source resolver để tạo không chỉ đối tượng media source mà còn có thể tạo đối tượng IMFByteStream. Đối tượng byte stream mở stream và truyền chuỗi byte chưa phân tích tới một source. Source này sẽ phân tích dòng stream của byte, mở gói dữ liệu trong file container, tách các media stream ra riêng rẽ, và đưa những stream này ra cho phần còn lại của cấu trúc liên kết. Nếu bạn sử dụng hàm CreateObjectFromURL() để tạo trực tiếp source, MF tự động ngầm tạo đối tượng byte stream thích hợp. Đối tượng byte stream sẽ là một kiểu thật sự nạp nội dung. Ví dụ như bạn nạp một network Url (như “http://www.contoso.com/file.wmv”), source resolver sẽ tự động tạo một đối tượng network byte stream. Nếu bạn nạp một đường dẫn file, source resolver sẽ tạo ra một stream byte nạp dữ liệu từ file đó.

Đây là sơ đồ chỉ ra dữ liệu di chuyển qua cấu trúc liên kết như thế nào và cũng được biến đổi trong các thành phần riêng lẻ ra sao.

Building the Partial topology – Xây d ng b ph n c u trúc liên k tự ộ ậ ấ ếSau khi đối tượng CtopoBuilder tạo source cho file, nó bắt đầu xây dựng topology. Điều này đỏi hỏi phải phân tích các stream riêng biệt , tạo nút nguồn và đích, và nối nút nguồn với nút đích tương ứng. Chú ý là CtopoBuilder không tạo ra đối tượng nguồn và đích. Thay vào đó, nó tạo ra các nút topology như là chỗ chứa cho các đối tượng đó. Sau khi tạo ra các nốt đó, CtopoBuilder sắp xếp chúng thành một cấu trúc có thể được sử dụng sau đó để tạo đối tượng thành phần MF thật sự và tạo ra đường ống media.

Các bước xây dựng topo :

1. Lấy presentation descriptor từ source. presentation descriptor liệt kê danh sách các tream riêng rẽ và trạng thái của chúng. a container object that holds a list of the streams and allows selection of streams that will be used.

2. Lấy stream discriptor từ presentation descriptor3. Tạo nút nguồn từ stream discriptor4. Tạo nút đích của kiểu media thích hợp cho tream này5. Nối nút đích và nguồn lại với nhau6. Nếu còn stream , tiếp tục bước 2

Page 12: Microsoft Foundation Applications_2

Code sau biểu diễn chức năng làm bước 1 và 2. Hàm này tạo một presentation descriptor cho tream được nạp từ source, sau đó lấy ra stream discriptor. Stream discriptor biểu diễn tream thật sự . đay là hàm chính tạo ra topo.

HRESULT CTopoBuilder::CreateTopology(void)

{

hr = MFCreateTopology(&m_pTopology);

hr = m_pSource->CreatePresentationDescriptor(&pPresDescriptor);

hr = pPresDescriptor->GetStreamDescriptorCount(&nSourceStreams);

hr = pPresDescriptor->GetStreamDescriptorCount(&nSourceStreams);

for (DWORD x = 0; x < nSourceStreams; x++)

{

hr = AddBranchToPartialTopology(pPresDescriptor, x);

}

}

Để xây dựng bất cứ topo nào , đầu tiên cần biết stream nào cần được render bằng cách truy vấn source về presentation descriptor, được thể hiện qua giao diện IMFPresentationDescriptor.

MF Presentation Descriptor mô tả những kiểu tream nào có thể chơi được. Nó cho phép bạn quyết định những stream nào có thể kích hoạt và chơi được. Ví dụ nếu một file có hai luồng âm thanh, bạn muốn chơi một trong hai luồng. Presentation Descriptor cho phép bạn kích hoạt (select) và ngưng kích hoạt (deselect) một luồng bằng cách sử dụng hàm IMFPresentationDescriptor::SelectStream() và DeselectStream(). Nếu một luồng là ngưng kích hoạt, thì source không thể gửi dữ liệu đến luồng đó được. Mặc định , tất cả các luồng có trong source đều là kích hoạt.

Note: Nếu một luồng mới xuất hiện ở giữa file , presentation descriptor sẽ không chứa luồng đó. Điều này còn phụ thuộc vào định dạng file và thiết kế của chính source. Nếu định dạng file cho phép dễ dàng liệt kê được các luồng có trong file, và nếu source đủ thông minh để tìm ra các thông tin đó, presentation descriptor sẽ có đầy đủ thông tin. Tuy nhiên , nếu source cơ bản không cung cấp thông tin này và một luồng tự nhiên xuất hiện thì bạn sẽ cần phải tạo ra một presentation descriptor hoàn toàn mới và tạo lại topo.

Sau khi tìm ra được có những luồng nào trong file, bạn bắt đầu xây dựng đường dữ liệu cho chúng. Bắt đầu bằng việc tạo nút nguồn cho mỗi luồng, tạo nút đích, và cuối cùng nối chúng lại với nhau.

Page 13: Microsoft Foundation Applications_2

Hình sau mô tả một phần topo được tạo ra bởi hàm CTopoBuilder::CreateTopology().

Topo này có thể nối bất cứ nút nguồn và nút đích lại với nhau kể cả khi nó không phù hợp kiểu . Điều này được cho phép bởi vì kết nối này chỉ là gợi ý cho bộ topo resolver. Trong quá trình giải mã topo, session sẽ thử những đơn vị MF tranform khác nhau để tìm ra cách kết hợp đúng mà có khả năng dùng dữ liệu từ nút nguồn và tạo ra kiểu dữ liệu mong đợi ở nút đích.

Dưới đây là hàm tạo cặp nút nguồn đích cho một phần topo.

Page 14: Microsoft Foundation Applications_2

Ch ng 4 : Trancoding – Chuy n mãươ ểMột trong những tính năng mạnh của MF là việc chuyển mã đơn giản và hiệu suất cao. Thuật ngữ trancoding – chuyển mã – đề cập đến việc chuyển luồng video hoặc audio từ định dạng này sang định dạng khác. Nó có thể đơn giản là mở gói luồng từ một file chứa và đóng gói nó thành loại khác, hay phức tạp như việc giải mã dữ liệu và mã hóa nó sử dụng bộ mã hóa – giải mã khác.

Trong chương trước, để thay đổi bộ giải mã của luồng video, bạn phải đối mặt với rất nhiều vấn đề đồng bộ luồng thường xảy ra trong quá trình xử lý luồng độc lập, giờ đây, MF cung cấp các hàm API đơn giản cho việc chuyển mã và cung cấp cho nhà phát triển các cách xây dựng topo rất mạnh. Bạn sẽ xem xét một vài phương thức trong chương này.

Nói thêm về API, MF mở ra khả năng chuyển mã bằng phần cứng.

Dưới đây là sơ đồ về chuyển mã. Thành phần nguồn source nạp dữ liệu, bộ giải mã decoders giải mã dữ liệu thành một số định dạng phổ biến, bộ mã hóa sẽ mã hóa lại dữ liệu thành định dạng đích, và nguồn sẽ gói dữ liệu trong một container. Nguồn và đích cho việc chuyển mã thường là file.

MF cung cấp ba cách để tạo ra những topo đó : bạn có thể xây dựng topo thủ công, bạn có thể sử dụng API chuyển mã, hoặc bạn có thể tạo ra một source reader kết hợp với một sink writer. Mỗi cách có ưu nhược điểm riêng, nên hãy chắc chắn rằng bạn chọn đúng cách cho từng tình huống cụ thể.

- Xây dựng topo thủ công : Quá trình ghép tất cả các thành phần MF vào một topo. Bạn đã nhìn thấy cách này trong chương 3 “Media playback”. Cách này rất linh hoạt bởi vì bạn có thể cấu hình các thành phần một cách độc lập, nhưng nó cũng rất phức tạp

- API Chuyển mã : API này là một tập các hàm được giới thiệu trong Windows 7 với mục tiêu đơn giản hóa hầu hết các tình huống chuyển mã – chuyển mã từ một file đến một file khác. Các hàm API chuyển mã được thiết kế để đơn giản hóa quá trình tạo ra một topo chuyển mã. Mọi thứ khác là giống với quá trình chuyển mã thủ công.

- Chuyển mã dùng source reader và sink writer : Cách này có sự linh hoạt ở giữa xây dựng topo thủ công và API chuyển mã. Nó cho phép bạn cấu hình thành phần nguồn và đích, nhưng tự động hóa quá trình tạo và cấu hình bộ mã hóa và bộ giải mã.

Phần tiếp theo mô tả cách dùng API chuyển mã và source reader trong chuyển mã.

Page 15: Microsoft Foundation Applications_2

The Trancode API – API chuy n mãể

Chuy n mã v i source readerể ớĐể đơn giản hóa quá trình xử lý luồng media, MF giới thiệu khái niệm về đối tượng source reader. Một source reader cho phép ứng dụng truy cập trực tiếp tới dữ liệu media mà không cần quan tâm tới việc xây dựng topo, tạo session, chạy session, v.v. Source cung cấp truy cập đồng bộ trực tiếp tới một media file, trong khi chỉ đòi hỏi kiến thức tối thiểu về MF. Bạn có thể sử dụng source reader cho những ứng dụng xử lý media khác nhau, như chuyển mã.

Bên cạnh source reader, MF cũng có khả năng tạo đối tượng sink writer. Sink writer là hình ảnh phản chiếu của source reader – nó cho phép ứng dụng ghi ra một file media mà không cần quan tâm tới việc bộ mã hóa nào được nạp hay session, topo, hoặc thậm chí mô hình lập trình nào liên quan đến.

Để hiểu rõ quá trình làm việc bên trong của source reader và sink writer, xem mô hình chuyển mã sau

Quá trình của reader khá đơn giản – để lấy một mẫu từ reader, bạn gọi hàm IMFSourceReader::ReadSample(). Hàm này làm source reader lấy một mẫu từ một luồng media trong file nguồn và trả về nó trong định dạng đã được yêu cầu. Sink writer có một hàm tương đương cho phép ghi dữ liệu một cách đồng bộ - IMFSinkWriter::WriteSample().

Note: Source reader và sink writer thật ra là hàm kiểu không đồng bộ. Có nghĩa là lời gọi lõi hàm của chúng trả về ngay lập tức, và kết quả đến trên một thread độc lập khác khi nó sẵn sàng. Để thiết lập chế độ không đồng bộ, bạn cần tạo đối tượng IMFSourceReaderCallback và IMFSinkWriterCallback và truyền nó cho reader và writer. Để thiết lập đối tượng callback, bạn cần thiết lập thuộc tính MF_SOURCE_READER_ASYNC_CALLBACK và MF_SINK_WRITER_ASYNC_CALLBACK trong reader và writer.

Như bạn thấy trong hình , source reader và sink writer trong cùng ứng dụng được kết nối với nhau qua hàm RunTranscode(). Hàm này lấy mẫu từ source reader và đẩy vào sink writer.

Phần dưới đây sẽ giải thích một vài khía cạnh của ứng dụng chuyển mã dựa trên source reader và sink writer. Để chuyển mã một file với những đối tượng này , bạn cần thực hiện các bước sau:

Page 16: Microsoft Foundation Applications_2

1. Tạo đối tượng source reader và sink writer.2. Nối source reader với sink writer.

a. Quyết định luồng cụ thể nào trong file nguồn bạn muốn chuyển mã , và thông báo source reader biết bạn muốn xử lý dữ liệu từ những luồng đó.

b. Tạo luồng sink writer tương ứng.c. Dàn xếp kiểu media thích hợp giữa source reader và sink writer tương ứng.

3. Lấy những mẫu riêng rẽ từ source reader và kiểm tra chúng.4. Gửi những mẫu riêng rẽ tới sink writer, trong khi xử lý các sự kiện và điều kiện luồng khác nhau.

Mặc dù bước cài đặt thứ hai của ứng dụng chuyển mã khá là thừa , nhưng nó giúp bạn hiểu sâu hơn về sự phức tạp liên quan tới việc kết nối các thành phần vào một MF source. Nó cũng giúp bạn hiểu rõ hơn quá trình xảy ra trong tất cả các ứng dụng chuyển mã, bao gồm cả API chuyển mã .

Thêm vào đó, cách chuyển mã dùng source reader và sink writer còn linh hoạt hơn cách dùng API chuyển mã. Sử dụng reader và writer , bạn có thể chọn từng luồng và cấu hình đối tượng MF source và sink trong source reader và sink writer. Với cách này, bạn có thể tạo ra một ứng dụng có khả năng xử lý nhiều luồng âm thanh và hình ảnh trong một file, xử lý với các luồng không phải là âm thanh và hình ảnh , chuyển mã những loại media ít dùng, và thậm chí chỉnh sửa đổi dữ liệu đang được chuyển mã. Ví dụ như là một ứng dụng cho phép chèn watermark vào một video đang được chuyển mã.

Thêm vào đó, đối tượng source reader và sink writer khá hữu ích trong các công việc khác. Bạn có thể dễ dàng sử dụng reader trong ứng dụng liên quan đến media mà không cần đến chức năng chơi video. Chẳng hạn như source reader được cài đặt đầy đủ đối tượng frame grabber. Nó có thể trích xuất từng frame từ một file để phân tích.

T o Source Reader và Sink WriterạThủ tục để tạo một source reader và sink writer khá đơn giản.Giờ bạn đã có kinh nghiệm trong việc tạo source chuẩn, bạn có thể tạo đối tượng source và sink bằng cách dùng một vài hàm MF chuẩn.

HRESULT CReaderWriterTranscoder::Transcode(LPCWSTR source, LPCWSTR target)

{

hr = MFCreateSourceReaderFromURL(source, pConfigAttrs, &m_pSourceReader);

hr = MFCreateSinkWriterFromURL(target, NULL, pConfigAttrs, &m_pSinkWriter);

hr = MapStreams();

hr = RunTranscode();

}

Mapping sink writer streamsBạn cần chỉ ra kiểu media đích mà sink sử dụng khi mã hóa nội dung thành file đích. Bạn cần quyết định xem luồng media nào bạn muốn thay đổi, luồng nào bạn muốn giữ nguyên.

Page 17: Microsoft Foundation Applications_2

Với ứng dụng trong ví dụ thì chỉ thay đổi luồng audio và video. Luồng video được mã hóa với định dạng H.264 và audio với định dạng AAC. Những luồng khác giữ nguyên.

Hàm sau phát ra sự ánh xạ giữa source reader với sink writer tương ứng của chúng và dàn xếp kiểu media được sử dụng để chuyển đổi giữa hai thành phần

HRESULT CReaderWriterTranscoder::MapStreams(void)

{

Do //Duyệt từng stream

{

//kiểm tra với chỉ số sourceStreamIndex có stream nào ko

hr = m_pSourceReader->GetStreamSelection(sourceStreamIndex, &isStreamSelected);

//Lấy kiểu dữ liệu của stream

hr = m_pSourceReader->GetNativeMediaType(

sourceStreamIndex, // index of the stream you are interested in

0, // index of the media type exposed by the stream decoder

&pStreamMediaType); // media type

//Lấy kiểu dữ liệu chính của media type

hr = pStreamMediaType->GetMajorType(&streamMajorType);

//chọn 1 luồng chỉ ra rằng source cần gửi ra dữ liệu của nó thay vì vứt tất cả các mẫu

hr = m_pSourceReader->SetStreamSelection(sourceStreamIndex, TRUE);

//Nếu đây là luồng video hay audio thì chuyển mã nó và dàn xếp kiểu media giữa

//source reader và sink writer. Nếu là luồng khác (vd phụ đề) thì truyền qua kiểu //media không đổi

if(streamMajorType == MFMediaType_Audio || streamMajorType == MFMediaType_Video)

{

//lấy kiểu media đích – kiểu bạn muốn chuyển mã dữ liệu của luồng source

//hiện tại thành.

Page 18: Microsoft Foundation Applications_2

hr = GetTranscodeMediaType(pStreamMediaType);

//thêm luồng vào sink writer – báo cho sink writer rằng 1 luồng với chỉ số xác định sẽ có kiểu media đích

hr = m_pSinkWriter->AddStream(pStreamMediaType, &sinkStreamIndex);

//nối luồng source và sink – để chúng cùng sử dụng kiểu media trung gian để truyền dữ liệu giữa source và sink

hr = ConnectStream(sourceStreamIndex, streamMajorType);

}

Else //nếu là kiểu media như phụ đề

{

// thêm luồng vào sink writer với kiểu dữ liệu như ở luồng source

hr = m_pSinkWriter->AddStream(pStreamMediaType, &sinkStreamIndex);

}

}

}

Nếu kiểu media đích khác nguồn, thì sink writer cần phải chuyển dữ liệu thành định dạng mới. Nghĩa là dữ liệu của luồng đó phải đc giải mã bởi source reader và mã hóa lại bởi sink writer. Để kết nối bộ giãi mã và mã hóa, ứng dụng cần một định dạng trung gian được sử dụng giữa 2 thành phần MF.

Sự dàn xếp dịnh dạng trung gian được thực hiện bởi hàm CReader Writer Transcoder::ConnectStream()

Intermediate Format negotiationCác đơn giản nhất để chuyển đổi một luồng từ một định dạng sang định dạng khác là giải hóa nó thành định dạng cơ bản nhất sau đó mã hóa lại thành định dạng mới. Ví dụ, bạn có thể giải mã video thành những khung hình không nén (uncompressed frames) và mã hóa lại thành định dạng mới.

Hàm sau mô tả làm thế nào bạn dàn xếp dịnh dạng dữ liệu không nén mà được chấp nhạn ở cả hai bộ mã hóa và giải mã.

// tất cả các bộ mã hóa và giải mã đều hỗ trợ ít nhật một trong những định dạng video này – về cơ bản là những khung hình đã được giải mã ( trong một số trường hợp là biến thể của bitmap)

staticGUID intermediateVideoFormats[] =

Page 19: Microsoft Foundation Applications_2

{

MFVideoFormat_NV12,

MFVideoFormat_YV12,

MFVideoFormat_YUY2,

MFVideoFormat_RGB32

};

Int nIntermediateVideoFormats = 4;

// định dạng audio mà mọi bộ giải mã và mã hóa audio đều hiểu : dữ liệu audio không nén

staticGUID intermediateAudioFormats[] =

{

MFAudioFormat_Float,

MFAudioFormat_PCM,

};

Int nIntermediateAudioFormats = 2

Hàm thực sự dàn xếp giữa từng luồng source reader và sink writer :

HRESULT CReaderWriterTranscoder::ConnectStream(DWORD dwStreamIndex, constGUID& streamMajorType)

{

//tạo một đối tượng media type container ghép stream input và output media type

hr = MFCreateMediaType( &pPartialMediaType );

// thiết lập major type of the partial khớp với media type container

hr = pPartialMediaType->SetGUID( MF_MT_MAJOR_TYPE, streamMajorType );

//lấy danh sách định dạng trung gian phù hợp – định dạng mà mỗi bộ giải mã và mã hóa của kiểu đó đều hiểu,

if(streamMajorType == MFMediaType_Video)

Page 20: Microsoft Foundation Applications_2

{

intermediateFormats = intermediateVideoFormats;

nFormats = nIntermediateVideoFormats;

}

else if(streamMajorType == MFMediaType_Audio)

{

intermediateFormats = intermediateAudioFormats;

nFormats = nIntermediateAudioFormats;

}

//duyệt qua các định dạng trung gian và tìm một định dạng mà luồng source và luồng sink đều dùng được.

for( intx = 0; x < nFormats; x++ )

{

//thiết lập định dạng của partial media type

hr = pPartialMediaType->SetGUID( MF_MT_SUBTYPE, intermediateFormats[x] );

//thiết lập partial media type trên luồng source

hr = m_pSourceReader->SetCurrentMediaType(

dwStreamIndex, // stream index

NULL, // reserved - always NULL

pPartialMediaType ); // media type to try to set

//nếu luồng source không thể dùng kiểu partial media type ,quay lại lặp duyệt định dạng tiếp

if( FAILED(hr) )

{

hr = S_OK;

continue;

Page 21: Microsoft Foundation Applications_2

}

// nếu đến được bước này thì luồng source chấp nhận kiểu partial media type, - extract the full media type for this stream (with all internal fields filled in)

hr = m_pSourceReader->GetCurrentMediaType( dwStreamIndex, &pFullMediaType );

// giờ ghép full media type với luồng sink tương ứng

hr = m_pSinkWriter->SetInputMediaType(

dwStreamIndex, // stream index

pFullMediaType, // media type to match

NULL ); // configuration attributes for the encoder

// nếu luồng sink không chấp nhạn kiểu media này thì quay lại lặp với định dạng tiếp theo

if( FAILED(hr) )

{

hr = S_OK;

continue;

}

}

The target transcode Media typeĐể cài đặt luồng input của sink writer, bạn cần quyết định và xác định từng định dạng luồng . Rõ ràng, định dạng dựa trên luồng tương ứng đi ra từ luồng reader.

Hàm dưới đây chọn media cho luồng stream writer. Hàm trực tiếp xây dựng media type.

// Thiết lập định dạng dữ liệu audio và video với những giá trị trong code. Thiết lập audio thành ACC, video thành 720p H.260

HRESULT CReaderWriterTranscoder::GetTranscodeMediaType(CComPtr<IMFMediaType>& pStreamMediaType)

{

// extract the major type of the source stream from the media type

hr = pStreamMediaType->GetMajorType(&streamMajorType);

Page 22: Microsoft Foundation Applications_2

// if this is an audio stream, configure a hard-coded AAC profile. If this is a

// video stream, configure an H.264 profile

if(streamMajorType == MFMediaType_Audio)

{

hr = GetTranscodeAudioType(pStreamMediaType);

}

else if(streamMajorType == MFMediaType_Video)

{

hr = GetTranscodeVideoType(pStreamMediaType);

}

}

}

//

// Get the target audio media type - use the AAC media format.

//

HRESULT CReaderWriterTranscoder::GetTranscodeAudioType(CComPtr<IMFMediaType>& pStreamMediaType)

{

}

//

// Get the target video media type - use the H.264 media format.

//

HRESULT CReaderWriterTranscoder::GetTranscodeVideoType(

CComPtr<IMFMediaType>& pStreamMediaType)

{

Page 23: Microsoft Foundation Applications_2

}

The Source-reader-to-Sink-Writer LoopSau khi từng luồng được liên kết với nhau , bạn có thể bắt đầu lấy samples từ source và đẩy chúng vào sink.

//

// Main transcoding loop. The loop pulls a sample from the source, pushes

// it into the sink, and repeats until all of the data is sent through.

//

HRESULT CReaderWriterTranscoder::RunTranscode(void)

{

// initialize target file and prepare for writing

hr = m_pSinkWriter->BeginWriting();

hr = m_pSourceReader->ReadSample();

hr = m_pSinkWriter->WriteSample( streamIndex, pSample );

}