summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-09 04:40:38 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-09 04:40:38 +0000
commitcafded64bab3482f0844b4170ec87e402a2971d8 (patch)
treee15cc6c5b8e9a0245157f0a98c756395dadd1bd7 /media
parentc6a9040706c1b5483e0a8b140c40434634445497 (diff)
downloadchromium_src-cafded64bab3482f0844b4170ec87e402a2971d8.zip
chromium_src-cafded64bab3482f0844b4170ec87e402a2971d8.tar.gz
chromium_src-cafded64bab3482f0844b4170ec87e402a2971d8.tar.bz2
A simple media player using Media Foundation.
BUG=none TEST=none Submitted for=imcheng@google.com Reviewed: http://codereview.chromium.org/2806039/show Review URL: http://codereview.chromium.org/2856033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51932 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/media.gyp19
-rw-r--r--media/tools/mfplayer/README.chromium27
-rw-r--r--media/tools/mfplayer/mf_playback_main.cc324
-rw-r--r--media/tools/mfplayer/mfplayer.cc826
-rw-r--r--media/tools/mfplayer/mfplayer.h168
5 files changed, 1364 insertions, 0 deletions
diff --git a/media/media.gyp b/media/media.gyp
index d026195..cc0f513 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -334,6 +334,25 @@
'_CRT_SECURE_NO_WARNINGS=1',
],
},
+ {
+ 'target_name': 'mfplayer',
+ 'type': 'executable',
+ 'dependencies': [
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'tools/mfplayer/mfplayer.h',
+ 'tools/mfplayer/mfplayer.cc',
+ 'tools/mfplayer/mf_playback_main.cc',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE
+ },
+ },
+ },
],
}],
['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
diff --git a/media/tools/mfplayer/README.chromium b/media/tools/mfplayer/README.chromium
new file mode 100644
index 0000000..18a4517
--- /dev/null
+++ b/media/tools/mfplayer/README.chromium
@@ -0,0 +1,27 @@
+This sample demonstrates how to play clear content in Media Foundation. The
+code is taken and modified from Media Foundation sample code website:
+http://code.msdn.microsoft.com/mediafoundation
+
+Requirements: Windows 7
+
+6/28/2010 - Version 1
+6/29/2010 - Added option for saving file in .mp4 format
+7/1/2010 - Changed return types of public methods, disabled audio since the
+purpose is to use Media Foundation's video capabilities only.
+
+Usage: mf_playback.exe (-r|-f) (-s|-h) input-file [output-file]
+There are two flags that needs to be specified, plus an input file.
+
+Flag 1:
+-r: Render to window. This is the playback mode. During playback mode, press
+ spacebar to switch between play/pause.
+-f: Transcode. A media file is transcoded into mp4 format (H.264/AAC). An
+ output file needs to be specified as the argument after input file.
+
+Flag 2:
+-s: Software decoding.
+-h: Hardware decoding using DXVA2. Not implemented yet.
+
+
+Todos:
+ - Add flags to support hardware decoding
diff --git a/media/tools/mfplayer/mf_playback_main.cc b/media/tools/mfplayer/mf_playback_main.cc
new file mode 100644
index 0000000..3ae8164
--- /dev/null
+++ b/media/tools/mfplayer/mf_playback_main.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This header is placed first to avoid compiler warning.
+#include "media/tools/mfplayer/mfplayer.h"
+
+#include <mfapi.h>
+#include <strsafe.h>
+#include <cassert>
+
+namespace mfplayer {
+
+const wchar_t g_window_title[] = L"MFBasicPlayback";
+const wchar_t g_window_class[] = L"Chrome_MFBasicPlayback";
+
+// True if there is no video playing, so we have to paint the window ourselves.
+bool g_repaint_client = true;
+
+// Global player object.
+// Note: After WM_CREATE is processed, g_main_player remains valid until the
+// window is destroyed.
+MFPlayer* g_main_player = NULL;
+
+wchar_t* g_input_file_name = NULL;
+
+void Usage() {
+ fprintf(stderr, "Usage: mf_playback (-r|-f) (-h|-s) file [out-file]\n");
+}
+
+void UpdateUI(HWND window_handle, MFPlayer::PlayerState state) {
+ assert(g_main_player != NULL);
+
+ bool playback_started = state == MFPlayer::STARTED ||
+ state == MFPlayer::PAUSED;
+ g_repaint_client = !playback_started || !g_main_player->HasVideo();
+}
+
+// Shows a message box with an error message.
+void NotifyError(HWND window_handle, PCWSTR error_message, HRESULT error_code) {
+ const size_t kMessageLen = 512;
+ wchar_t message[kMessageLen];
+
+ HRESULT hr = StringCchPrintf(message, kMessageLen, L"%s (HRESULT = 0x%X)",
+ error_message, error_code);
+ if (SUCCEEDED(hr))
+ MessageBox(window_handle, message, NULL, MB_OK | MB_ICONERROR);
+}
+
+// Converts a ANSI string to an Unicode string. It is caller's responsibility
+// to call delete[] on the returned string when it is no longer being used.
+int ConvertANSIStringToUnicode(const char* source, wchar_t** dest) {
+ DWORD string_length = MultiByteToWideChar(CP_ACP, 0, source, -1, NULL, 0);
+ if (string_length == 0) {
+ fprintf(stderr, "Error getting size of ansi string: %s\n", source);
+ return 1;
+ }
+ *dest = new wchar_t[string_length];
+ if (*dest == NULL) {
+ fprintf(stderr, "Error allocating unicode string buffer\n");
+ return 1;
+ }
+ if (MultiByteToWideChar(CP_ACP, 0, source, string_length, *dest,
+ string_length) == 0) {
+ fprintf(stderr, "Error converting ansi string to unicode: %#X",
+ GetLastError());
+ return 1;
+ }
+ return 0;
+}
+
+// Callback when window is created. It has been modified to both create
+// the media player and starts playing the provided file in one go.
+LRESULT OnCreateWindow(HWND window_handle) {
+ // Initialize the player object.
+ HRESULT hr = MFPlayer::CreateInstance(window_handle, window_handle,
+ true, &g_main_player);
+ if (SUCCEEDED(hr)) {
+ UpdateUI(window_handle, MFPlayer::CLOSED);
+ } else {
+ NotifyError(NULL, L"Could not initialize the player object.", hr);
+ return -1; // Destroy the window
+ }
+
+ // Play the video
+ assert(g_input_file_name != NULL);
+
+ // No output URL, just the window.
+ hr = g_main_player->OpenURL(g_input_file_name, NULL);
+ if (SUCCEEDED(hr)) {
+ UpdateUI(window_handle, MFPlayer::OPEN_PENDING);
+ return 0;
+ } else {
+ _fwprintf_p(stderr, L"Fatal error: cannot open file %s\n",
+ g_input_file_name);
+ return -1; // Destroy the window
+ }
+}
+
+void OnPaint(HWND window_handle) {
+ if (g_repaint_client) {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(window_handle, &ps);
+ // The video is not playing, so we must paint the application window.
+ RECT rc;
+ GetClientRect(window_handle, &rc);
+ FillRect(hdc, &rc, reinterpret_cast<HBRUSH>(COLOR_WINDOW));
+ EndPaint(window_handle, &ps);
+ } else {
+ // Video is playing. Ask the player to repaint.
+ g_main_player->Repaint();
+ }
+}
+
+void OnKeyPress(WPARAM key) {
+ switch (key) {
+ // Space key toggles between running and paused.
+ case VK_SPACE:
+ if (g_main_player->state() == MFPlayer::STARTED)
+ g_main_player->Pause();
+ else if (g_main_player->state() == MFPlayer::PAUSED)
+ g_main_player->Play();
+ break;
+ }
+}
+
+void OnPlayerEvent(HWND window_handle, WPARAM event) {
+ HRESULT hr = S_OK;
+
+ hr = g_main_player->HandleEvent(event);
+ if (FAILED(hr))
+ NotifyError(window_handle, L"An error occurred.", hr);
+ UpdateUI(window_handle, g_main_player->state());
+}
+
+LRESULT CALLBACK CallbackProc(HWND window_handle, UINT message, WPARAM w_param,
+ LPARAM l_param) {
+ switch (message) {
+ case WM_CREATE:
+ return OnCreateWindow(window_handle);
+ case WM_PAINT:
+ OnPaint(window_handle);
+ break;
+ case WM_SIZE:
+ g_main_player->ResizeVideo(LOWORD(l_param), HIWORD(l_param));
+ break;
+ case WM_ERASEBKGND:
+ // Suppress window erasing, to reduce flickering while the video is
+ // playing.
+ return 1;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ case WM_CHAR:
+ OnKeyPress(w_param);
+ break;
+ case MFPlayer::WM_APP_PLAYER_EVENT:
+ OnPlayerEvent(window_handle, w_param);
+ break;
+ default:
+ return DefWindowProc(window_handle, message, w_param, l_param);
+ }
+ return 0;
+}
+
+// Creates a window for doing playback.
+bool InitInstance() {
+ HWND window_handle;
+ WNDCLASSEX window_class_info;
+
+ // Register the window class.
+ ZeroMemory(&window_class_info, sizeof(WNDCLASSEX));
+ window_class_info.cbSize = sizeof(WNDCLASSEX);
+ window_class_info.style = CS_HREDRAW | CS_VREDRAW;
+ window_class_info.lpfnWndProc = CallbackProc;
+ window_class_info.hInstance = NULL;
+ window_class_info.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ window_class_info.lpszMenuName = NULL;
+ window_class_info.lpszClassName = g_window_class;
+
+ if (RegisterClassEx(&window_class_info) == 0)
+ return false;
+
+ // Create the application window.
+ window_handle = CreateWindow(g_window_class, g_window_title,
+ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
+ CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
+ if (window_handle == 0) {
+ fprintf(stderr, "Cannot create window\n");
+ return false;
+ }
+
+ ShowWindow(window_handle, SW_SHOW);
+ UpdateWindow(window_handle);
+ return true;
+}
+
+int RunRendererApp() {
+ if (!InitInstance()) {
+ fprintf(stderr, "Could not initialize the application\n");
+ return 1;
+ }
+ MSG message;
+ ZeroMemory(&message, sizeof(message));
+ // Main message loop.
+ while (GetMessage(&message, NULL, 0, 0)) {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+ return 0;
+}
+
+int RunTranscoderApp(const char* output_file_name) {
+ HRESULT hr;
+ int ret = 0;
+
+ // In here, we do not use any windows - just use the media player as a
+ // transcoder.
+ hr = MFPlayer::CreateInstance(NULL, NULL, false, &g_main_player);
+ if (FAILED(hr)) {
+ fprintf(stderr, "Transcoder app: Could not create player\n");
+ return 1;
+ }
+ wchar_t* output_file_name_wchar = NULL;
+ if (ConvertANSIStringToUnicode(output_file_name,
+ &output_file_name_wchar) != 0) {
+ fprintf(stderr, "Fatal error while converting output_file_name\n");
+ return 1;
+ }
+ hr = g_main_player->OpenURL(g_input_file_name, output_file_name_wchar);
+ if (FAILED(hr)) {
+ fprintf(stderr, "Failed to open URL and configure transcoder profile\n");
+ ret = 1;
+ goto cleanup;
+ }
+ fprintf(stderr, "Transcode starting now, writing to %s\n", output_file_name);
+ hr = g_main_player->Transcode();
+ if (FAILED(hr)) {
+ fprintf(stderr, "Transcode failed\n");
+ ret = 1;
+ goto cleanup;
+ }
+ if (SUCCEEDED(hr))
+ fprintf(stderr, "Transcode successful\n");
+ cleanup:
+ delete[] output_file_name_wchar;
+ return ret;
+}
+
+int main2(int argc, char** argv) {
+ bool use_renderer_sink;
+ bool enable_dxva2;
+ if (argc < 4) {
+ Usage();
+ return 1;
+ }
+ if (strcmp(argv[1], "-r") == 0) {
+ use_renderer_sink = true;
+ } else if (strcmp(argv[1], "-f") == 0) {
+ use_renderer_sink = false;
+ if (argc < 5) {
+ fprintf(stderr, "Archive sink specified but out-file missing\n");
+ Usage();
+ return 1;
+ }
+ } else {
+ fprintf(stderr, "Unknown option '%s'\n", argv[1]);
+ Usage();
+ return 1;
+ }
+ if (strcmp(argv[2], "-h") == 0) {
+ enable_dxva2 = true;
+ } else if (strcmp(argv[2], "-s") == 0) {
+ enable_dxva2 = false;
+ } else {
+ fprintf(stderr, "Unknown option '%s'\n", argv[2]);
+ Usage();
+ return 1;
+ }
+
+ // TODO(imcheng): implement the option for hardware acceleration
+ fprintf(stderr, "use_renderer_sink: %s\n",
+ use_renderer_sink ? "TRUE" : "FALSE");
+ fprintf(stderr, "enable_dxva2: %s\n", enable_dxva2 ? "TRUE" : "FALSE");
+
+ HRESULT hr;
+ // Startup is moved from object creation to main method.
+ hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
+ if (FAILED(hr)) {
+ fprintf(stderr, "MFStartup failed: %#X", hr);
+ return 1;
+ }
+
+ // Note: This function allocates space for g_input_file_name on the heap, so
+ // remember to free it at the end.
+ if (ConvertANSIStringToUnicode(argv[3], &g_input_file_name) != 0) {
+ fprintf(stderr, "Fatal error while converting g_input_file_name\n");
+ return 1;
+ }
+ _fwprintf_p(stderr, L"Input file: %s\n", g_input_file_name);
+ int retval;
+ if (use_renderer_sink)
+ retval = RunRendererApp();
+ else
+ retval = RunTranscoderApp(argv[4]);
+
+ // Clean up.
+ if (g_main_player) {
+ g_main_player->Shutdown();
+ g_main_player->Release();
+ }
+ MFShutdown();
+ delete[] g_input_file_name;
+
+ printf("Terminated\n");
+ return retval;
+}
+
+} // namespace mfplayer
+
+
+int main(int argc, char** argv) {
+ return mfplayer::main2(argc, argv);
+}
diff --git a/media/tools/mfplayer/mfplayer.cc b/media/tools/mfplayer/mfplayer.cc
new file mode 100644
index 0000000..fbeb32f
--- /dev/null
+++ b/media/tools/mfplayer/mfplayer.cc
@@ -0,0 +1,826 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/tools/mfplayer/mfplayer.h"
+
+#include <mfapi.h>
+#include <mferror.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#include <cassert>
+
+namespace {
+
+template <class T>
+void SafeRelease(T** pptr) {
+ if (pptr && *pptr) {
+ (*pptr)->Release();
+ *pptr = NULL;
+ }
+}
+
+HRESULT ProbeTopology(IMFMediaEvent* event, IMFTopology** topology_ptr) {
+ HRESULT hr = S_OK;
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ hr = event->GetValue(&var);
+ if (SUCCEEDED(hr)) {
+ if (var.vt != VT_UNKNOWN)
+ hr = E_UNEXPECTED;
+ }
+ if (SUCCEEDED(hr))
+ hr = var.punkVal->QueryInterface(IID_PPV_ARGS(topology_ptr));
+ PropVariantClear(&var);
+ return hr;
+}
+
+} // namespace
+
+namespace mfplayer {
+
+// Public methods
+
+bool MFPlayer::CreateInstance(HWND video_window, HWND event_window,
+ bool render_to_window, MFPlayer** player) {
+ if (!player)
+ return false;
+
+ HRESULT hr = S_OK;
+ MFPlayer* temp_player = new MFPlayer(video_window, event_window,
+ render_to_window);
+ if (!temp_player)
+ return false;
+
+ hr = temp_player->Initialize();
+ if (SUCCEEDED(hr)) {
+ *player = temp_player;
+ (*player)->AddRef();
+ }
+ // If above succeeded, then ref count is now 2, so SafeRelease won't delete
+ // the object.
+ SafeRelease(&temp_player);
+ return SUCCEEDED(hr);
+}
+
+// Override IUnknown
+HRESULT MFPlayer::QueryInterface(REFIID id, void** object_ptr) {
+ static const QITAB qit[] = { QITABENT(MFPlayer, IMFAsyncCallback), {0}};
+ return QISearch(this, qit, id, object_ptr);
+}
+
+ULONG MFPlayer::AddRef() {
+ return InterlockedIncrement(&ref_count_);
+}
+
+ULONG MFPlayer::Release() {
+ ULONG count = InterlockedDecrement(&ref_count_);
+ if (count == 0)
+ delete this;
+ return count;
+}
+
+// IMFAsyncCallback implementation
+// Callback for asynchronous BeginGetEvent method.
+HRESULT MFPlayer::Invoke(IMFAsyncResult* result) {
+ IMFMediaEvent* event = NULL;
+ MediaEventType event_type = MEUnknown;
+
+ HRESULT hr = session_->EndGetEvent(result, &event);
+ if (FAILED(hr))
+ goto done;
+ hr = event->GetType(&event_type);
+ if (FAILED(hr))
+ goto done;
+
+ // |session_|->Close() queues an event of this type.
+ // If the session is closed, the application is waiting on the event
+ // handle. Also, do not request any more events from the session.
+ if (event_type == MESessionClosed) {
+ SetEvent(close_event_);
+ } else {
+ // For all other events, ask the media session for the
+ // next event in the queue.
+ hr = session_->BeginGetEvent(this, NULL);
+ if (FAILED(hr))
+ goto done;
+ }
+ // For most events, post the event as a private window message to the
+ // application. This lets the application process the event on its main
+ // thread.
+
+ // However, if a call to IMFMediaSession::Close is pending, it means the
+ // application is waiting on the m_hCloseEvent event handle. (Blocking
+ // call.) In that case, we simply discard the event.
+
+ // When IMFMediaSession::Close is called, MESessionClosed is NOT
+ // necessarily the next event that we will receive. We may receive any
+ // number of other events before receiving MESessionClosed.
+ if (state_ != CLOSING) {
+ event->AddRef();
+ // Post this event onto the event_window_'s queue. When an event is
+ // received, the app calls MFPlayer::HandleEvent() to handle the event.
+ PostMessage(event_window_, WM_APP_PLAYER_EVENT, (WPARAM)event, NULL);
+ }
+ done:
+ return S_OK;
+}
+
+// This method does the following:
+// 1. Create a new media session.
+// 2. Create the media source.
+// 3. Create the topology.
+// 4. Queue the topology [asynchronous]
+// 5. Start playback [asynchronous - does not happen in this method.]
+bool MFPlayer::OpenURL(const WCHAR* in_url, const WCHAR* out_url) {
+ IMFTopology* topology = NULL;
+ HRESULT hr;
+
+ // Initializes |session_|.
+ hr = CreateSession();
+ if (FAILED(hr))
+ goto done;
+ // Initializes |source_|.
+ hr = CreateMediaSource(in_url);
+ if (FAILED(hr))
+ goto done;
+ if (render_to_window_) {
+ // Create a partial topology.
+ hr = CreateTopologyFromSource(&topology);
+ if (FAILED(hr))
+ goto done;
+ } else {
+ // Creating topology for a transcoder requires a somewhat different setup.
+ fprintf(stderr, "OpenURL: using archive sink\n");
+ hr = MFCreateTranscodeProfile(&transcode_profile_);
+ if (FAILED(hr))
+ goto done;
+ /*
+ hr = ConfigureTranscodeAudioOutput();
+ if (FAILED(hr))
+ goto done;
+ fprintf(stderr, "OpenURL: Configured audio output\n");
+ */
+ hr = ConfigureTranscodeVideoOutput();
+ if (FAILED(hr))
+ goto done;
+ fprintf(stderr, "OpenURL: Configured video output\n");
+ hr = ConfigureContainer();
+ if (FAILED(hr))
+ goto done;
+ fprintf(stderr, "OpenURL: Configured container\n");
+ hr = MFCreateTranscodeTopology(source_, out_url, transcode_profile_,
+ &topology);
+ if (FAILED(hr))
+ goto done;
+ fprintf(stderr, "OpenURL: Created transcode topology\n");
+ }
+ // First argument is flags (which is none).
+ hr = session_->SetTopology(0, topology);
+ if (FAILED(hr))
+ goto done;
+ fprintf(stderr, "Added topology to session\n");
+ // SetTopology() is an asynchronous method. If it succeeds, the media
+ // session will queue an MESessionTopologySet event.
+ // SetTopology() without MFSESSION_SETTOPOLOGY_NORESOLUTION means the
+ // topology is to be resolved.
+ state_ = OPEN_PENDING;
+ done:
+ if (FAILED(hr))
+ state_ = CLOSED;
+ SafeRelease(&topology);
+ return SUCCEEDED(hr);
+}
+
+bool MFPlayer::Play() {
+ if (state_ != PAUSED && state_ != STOPPED)
+ return false;
+ if (!session_ || !source_)
+ return false;
+ HRESULT hr = StartPlayback();
+ return SUCCEEDED(hr);
+}
+
+bool MFPlayer::Pause() {
+ if (state_ != STARTED)
+ return false;
+ if (!session_ || !source_)
+ return false;
+ HRESULT hr = session_->Pause();
+ if (SUCCEEDED(hr))
+ state_ = PAUSED;
+ return SUCCEEDED(hr);
+}
+
+bool MFPlayer::Shutdown() {
+ HRESULT hr = CloseSession();
+ if (close_event_) {
+ CloseHandle(close_event_);
+ close_event_ = NULL;
+ }
+ return SUCCEEDED(hr);
+}
+
+// Callback from application upon receiving an WM_APP_PLAYER event.
+// This method is used to process media session events on the
+// application's main thread.
+HRESULT MFPlayer::HandleEvent(UINT_PTR event_ptr) {
+ IUnknown* unknown_ptr = reinterpret_cast<IUnknown*>(event_ptr);
+ IMFMediaEvent* event = NULL;
+
+ if (!unknown_ptr)
+ return E_POINTER;
+ // Incremented ref count.
+ HRESULT hr = unknown_ptr->QueryInterface(IID_PPV_ARGS(&event));
+ if (FAILED(hr))
+ goto done;
+ MediaEventType event_type;
+ HRESULT event_status = S_OK;
+ hr = event->GetType(&event_type);
+ if (FAILED(hr))
+ goto done;
+ hr = event->GetStatus(&event_status);
+ if (FAILED(hr))
+ goto done;
+ // Check if the async operation succeeded.
+ if (FAILED(event_status)) {
+ hr = event_status;
+ goto done;
+ }
+
+ // Handle the event according to its type.
+ MF_TOPOSTATUS topology_status = MF_TOPOSTATUS_INVALID;
+ switch (event_type) {
+ case MESessionTopologySet:
+ hr = OnTopologyReady(event);
+ break;
+ case MEEndOfPresentation:
+ hr = OnPresentationEnded(event);
+ break;
+ // The MENewPresentation event signals the start of a new presentation.
+ // However, in many cases, you will not receive this event at all.
+ // It seems like there is no need to handle other events, but if there
+ // is a need in the future they can be added here.
+ }
+
+ done:
+ SafeRelease(&event);
+ SafeRelease(&unknown_ptr);
+ return hr;
+}
+
+// Video functionality implementation
+// Called by OnPaint() in application, when it receives a WM_PAINT message.
+// The EVR (video_display_) must be notified, i.e. repaint.
+bool MFPlayer::Repaint() {
+ if (video_display_)
+ return SUCCEEDED(video_display_->RepaintVideo());
+ else
+ return true;
+}
+
+// Called when application receives a WM_SIZE message.
+bool MFPlayer::ResizeVideo(WORD Width, WORD Height) {
+ if (video_display_) {
+ // Fields are: Left (x), Top (y), Right (x), Bottom (y)
+ RECT DestRect = { 0, 0, Width, Height };
+ // First parameter is the "source rectangle" - NULL means show
+ // the entire portion of the original rectangle. Default is
+ // {0, 0, 1, 1}.
+ return SUCCEEDED(video_display_->SetVideoPosition(NULL, &DestRect));
+ } else {
+ return true;
+ }
+}
+
+// Private methods
+
+MFPlayer::MFPlayer(HWND video_window, HWND event_window,
+ bool render_to_window)
+ : ref_count_(1),
+ state_(CLOSED),
+ session_(NULL),
+ source_(NULL),
+ video_display_(NULL),
+ video_window_(video_window),
+ event_window_(event_window),
+ close_event_(NULL),
+ render_to_window_(render_to_window),
+ transcode_profile_(NULL) {
+}
+
+MFPlayer::~MFPlayer() {
+ // If false, app did not call Shutdown().
+ assert(!session_);
+
+ // When MFPlayer calls IMediaEventGenerator::BeginGetEvent on the
+ // media session, it causes the media session to hold a reference
+ // count on the MFPlayer.
+ // This creates a circular reference count between MFPlayer and the
+ // media session. Calling Shutdown breaks the circular reference
+ // count.
+ // If CreateInstance fails, the application will not call
+ // Shutdown. To handle that case, call Shutdown in the destructor.
+ Shutdown();
+}
+
+// This method only creates the close event. This method is called from
+// CreateInstance().
+HRESULT MFPlayer::Initialize() {
+ if (close_event_)
+ return MF_E_ALREADY_INITIALIZED;
+ HRESULT hr = S_OK;
+ close_event_ = CreateEvent(NULL, // Not inheritable by child process, if any
+ FALSE, // Auto-reset
+ FALSE, // Not signaled initially
+ NULL); // No name
+ if (!close_event_)
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+}
+
+HRESULT MFPlayer::CreateSession() {
+ // Close previous section, if any.
+ HRESULT hr = CloseSession();
+ if (FAILED(hr))
+ goto done;
+
+ assert(state_ == CLOSED);
+
+ // First argument is optional configurations. (Which is none)
+ hr = MFCreateMediaSession(NULL, &session_);
+ if (FAILED(hr))
+ goto done;
+
+ state_ = READY;
+ if (render_to_window_) {
+ // Start pulling events from the media session. It calls back the object's
+ // Invoke method when there is a new event available.
+ hr = session_->BeginGetEvent(this, NULL);
+ if (FAILED(hr))
+ goto done;
+ }
+ done:
+ return hr;
+}
+
+// Closes the media session. Called from either Shutdown() or CreateSession().
+// The IMFMediaSession::Close method is asynchronous, but the CloseSession
+// method waits on the MESessionClosed event. The MESessionClosed event is
+// guaranteed to be the last event that the media session fires.
+HRESULT MFPlayer::CloseSession() {
+ HRESULT hr = S_OK;
+
+ SafeRelease(&video_display_);
+ if (session_) {
+ state_ = CLOSING;
+ hr = session_->Close();
+ if (FAILED(hr))
+ goto done;
+ // Wait 5 seconds for the close operation to complete, i.e. the close event
+ // to be signaled. close_event_ only exists for playback mode.
+ if (render_to_window_)
+ WaitForSingleObject(close_event_, 5000);
+ // Now there will (or should) be no more events from this session.
+ }
+ // Complete shutdown operations.
+ // Note: Shutdown()s are synchronous.
+ if (source_)
+ source_->Shutdown();
+ if (session_)
+ session_->Shutdown();
+ SafeRelease(&source_);
+ SafeRelease(&session_);
+ SafeRelease(&transcode_profile_);
+ state_ = CLOSED;
+
+ done:
+ return hr;
+}
+
+// Starts the session. In the context of playback, it means starting the
+// playback. In the context of writing to file, it means starting the encoding
+// operation.
+HRESULT MFPlayer::StartPlayback() {
+ assert(session_);
+ PROPVARIANT var_start;
+ PropVariantInit(&var_start);
+ var_start.vt = VT_EMPTY;
+ HRESULT hr = session_->Start(&GUID_NULL, &var_start);
+ if (SUCCEEDED(hr)) {
+ // Start is an asynchronous operation. However, we can treat our state
+ // as being already started.
+ state_ = STARTED;
+ }
+ PropVariantClear(&var_start);
+ return hr;
+}
+
+HRESULT MFPlayer::CreateMediaSource(const WCHAR* url) {
+ MF_OBJECT_TYPE object_type = MF_OBJECT_INVALID;
+ IMFSourceResolver* resolver = NULL;
+ IUnknown* source = NULL;
+
+ // Release the old source.
+ SafeRelease(&source_);
+
+ HRESULT hr = MFCreateSourceResolver(&resolver);
+ if (FAILED(hr))
+ goto done;
+
+ // Use resolver to create media source.
+ // Synchronous method, use BeginCreateObjectFromUrl for asynchronous.
+ hr = resolver->CreateObjectFromURL(url,
+ MF_RESOLUTION_MEDIASOURCE,
+ NULL, // Not passing in properties.
+ &object_type,
+ &source); // Receives created source.
+ if (FAILED(hr))
+ goto done;
+
+ // Get the IMFMediaSource interface from the media source, i.e. initializes
+ // source_.
+ // This call increments the ref count of returned interface.
+ hr = source->QueryInterface(IID_PPV_ARGS(&source_));
+
+ done:
+ // Free up resources, if any were acquired.
+ SafeRelease(&resolver);
+ SafeRelease(&source);
+ return hr;
+}
+
+HRESULT MFPlayer::CreateTopologyFromSource(IMFTopology** topology) {
+ assert(session_);
+ assert(source_);
+
+ IMFTopology* temp_topology = NULL;
+ IMFPresentationDescriptor* presentation_desc = NULL;
+ DWORD num_streams = 0;
+
+ HRESULT hr = MFCreateTopology(&temp_topology);
+ if (FAILED(hr))
+ goto done;
+ hr = source_->CreatePresentationDescriptor(&presentation_desc);
+ if (FAILED(hr))
+ goto done;
+ hr = presentation_desc->GetStreamDescriptorCount(&num_streams);
+ if (FAILED(hr))
+ goto done;
+ // For each stream, create the topology nodes and add them to the topology.
+ for (DWORD i = 0; i < num_streams; i++) {
+ hr = AddBranchToPartialTopology(temp_topology, presentation_desc, i);
+ if (FAILED(hr))
+ goto done;
+ }
+ *topology = temp_topology;
+ (*topology)->AddRef();
+
+ done:
+ SafeRelease(&temp_topology);
+ SafeRelease(&presentation_desc);
+ return hr;
+}
+
+// Creates a topology node with stream of given index and adds the node to the
+// topology.
+// Add a topology branch for one stream.
+// For each stream, this function does the following:
+// 1. Creates a source node associated with the stream.
+// 2. Creates an output node for the renderer.
+// 3. Connects the two nodes.
+// The media session will add any decoders that are needed.
+HRESULT MFPlayer::AddBranchToPartialTopology(
+ IMFTopology* topology, IMFPresentationDescriptor* presentation_desc,
+ DWORD index) {
+ assert(topology);
+
+ IMFStreamDescriptor* stream_desc = NULL;
+ IMFMediaTypeHandler* handler = NULL;
+ GUID major_type_id = GUID_NULL;
+ IMFTopologyNode* source_node = NULL;
+ IMFTopologyNode* output_node = NULL;
+ BOOL stream_selected = FALSE;
+
+ HRESULT hr = presentation_desc->GetStreamDescriptorByIndex(index,
+ &stream_selected,
+ &stream_desc);
+ if (FAILED(hr))
+ goto done;
+ // Create topology branch only if stream is selected.
+ if (stream_selected) {
+ hr = stream_desc->GetMediaTypeHandler(&handler);
+ if (FAILED(hr))
+ goto done;
+ hr = handler->GetMajorType(&major_type_id);
+ if (FAILED(hr))
+ goto done;
+ if (major_type_id != MFMediaType_Video)
+ goto done;
+ hr = CreateSourceStreamNode(presentation_desc, stream_desc, &source_node);
+ if (FAILED(hr))
+ goto done;
+ hr = CreateOutputNode(stream_desc, &output_node);
+ if (FAILED(hr))
+ goto done;
+ // Add both nodes to the topology.
+ hr = topology->AddNode(source_node);
+ if (FAILED(hr))
+ goto done;
+ hr = topology->AddNode(output_node);
+ if (FAILED(hr))
+ goto done;
+ // Connect the 0-th output stream of source node to the 0-th input stream of
+ // the output node.
+ hr = source_node->ConnectOutput(0, output_node, 0);
+ }
+
+ done:
+ SafeRelease(&stream_desc);
+ SafeRelease(&source_node);
+ SafeRelease(&output_node);
+ SafeRelease(&handler);
+ return hr;
+}
+
+HRESULT MFPlayer::CreateSourceStreamNode(
+ IMFPresentationDescriptor* presentation_desc,
+ IMFStreamDescriptor* stream_desc, IMFTopologyNode** node_ptr) {
+ if (!source_ || !presentation_desc || !stream_desc || !node_ptr)
+ return E_POINTER;
+ IMFTopologyNode* temp_node = NULL;
+ HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &temp_node);
+ if (FAILED(hr))
+ goto done;
+ // Set attribute of top node source to source_.
+ hr = temp_node->SetUnknown(MF_TOPONODE_SOURCE, source_);
+ if (FAILED(hr))
+ goto done;
+ // Set attribute of presentation descriptor.
+ hr = temp_node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR,
+ presentation_desc);
+ if (FAILED(hr))
+ goto done;
+ // Set attribute of stream descriptor.
+ hr = temp_node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, stream_desc);
+ if (FAILED(hr))
+ goto done;
+ *node_ptr = temp_node;
+ (*node_ptr)->AddRef();
+
+ done:
+ SafeRelease(&temp_node);
+ return hr;
+}
+
+// This function does the following:
+// 1. Chooses a renderer based on the media type of the stream.
+// 2. Creates an IActivate object for the renderer. IActivate objects allow the
+// application to defer its creation.
+// 3. Creates an output topology node.
+// 4. Sets the IActivate pointer on the node.
+HRESULT MFPlayer::CreateOutputNode(IMFStreamDescriptor* stream_desc,
+ IMFTopologyNode** node_ptr) {
+ IMFTopologyNode* temp_node = NULL;
+ IMFMediaTypeHandler* handler = NULL;
+ IMFActivate* renderer_activate = NULL;
+ GUID major_type_id = GUID_NULL;
+
+ HRESULT hr = stream_desc->GetMediaTypeHandler(&handler);
+ if (FAILED(hr))
+ goto done;
+
+ // Get the type of the stream (video, audio, etc.)
+ hr = handler->GetMajorType(&major_type_id);
+ if (FAILED(hr))
+ goto done;
+
+ // Is it a video stream, audio stream, etc.?
+ // Create an IMFActivate object for the renderer, based on the media type
+ if (major_type_id == MFMediaType_Video) {
+ assert(video_window_);
+ hr = MFCreateVideoRendererActivate(video_window_, &renderer_activate);
+ } else {
+ hr = E_FAIL;
+ }
+ if (FAILED(hr))
+ goto done;
+
+ hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &temp_node);
+ if (FAILED(hr))
+ goto done;
+
+ // Associate the activate object with the node.
+ hr = temp_node->SetObject(renderer_activate);
+ if (FAILED(hr))
+ goto done;
+
+ *node_ptr = temp_node;
+ (*node_ptr)->AddRef();
+
+ done:
+ SafeRelease(&temp_node);
+ SafeRelease(&handler);
+ SafeRelease(&renderer_activate);
+ return hr;
+}
+
+// Handler for MESessionTopologySet.
+// This situation means the new topology is ready for playback. After this
+// event is received, any calls to IMFGetService will get service interfaces
+// from the new topology.
+HRESULT MFPlayer::OnTopologyReady(IMFMediaEvent* event) {
+//////////
+ IMFTopology* full_topology = NULL;
+ HRESULT hr;
+ hr = ProbeTopology(event, &full_topology);
+ if (FAILED(hr))
+ fprintf(stderr, "Failed to probe topology\n");
+ fprintf(stderr, "Probed topology\n");
+
+ WORD num_nodes_in_topology;
+ hr = full_topology->GetNodeCount(&num_nodes_in_topology);
+ if (FAILED(hr)) {
+ fprintf(stderr, "GetNodeCount failed\n");
+ goto release_topology;
+ }
+ fprintf(stderr, "There are %d nodes in topology\n", num_nodes_in_topology);
+
+ for (WORD i = 0; i < num_nodes_in_topology; i++) {
+ IMFTopologyNode* node = NULL;
+ hr = full_topology->GetNode(i, &node);
+ if (FAILED(hr)) {
+ fprintf(stderr, "failed to get node %d\n", i);
+ } else {
+ fprintf(stderr, "got node %d\n", i);
+ }
+ SafeRelease(&node);
+ }
+
+ release_topology:
+ SafeRelease(&full_topology);
+ if (render_to_window_) {
+ // Release the old IMFVideoDisplayControl.
+ SafeRelease(&video_display_);
+ // Ask for the IMFVideoDisplayControl interface. This interface is
+ // implemented by the EVR and is exposed by the media session as a service.
+ // Note: This call is expected to fail if the source does not have video.
+ MFGetService(session_, MR_VIDEO_RENDER_SERVICE,
+ IID_PPV_ARGS(&video_display_));
+ // Note: Audio, Video acceleration are also services that can be obtained.
+ }
+ hr = StartPlayback();
+ return hr;
+}
+
+// Handler for MEEndOfPresentation event.
+HRESULT MFPlayer::OnPresentationEnded(IMFMediaEvent* event) {
+ state_ = STOPPED;
+ return S_OK;
+}
+
+HRESULT MFPlayer::ConfigureTranscodeAudioOutput() {
+ assert(transcode_profile_);
+
+ HRESULT hr = S_OK;
+ DWORD num_available_formats;
+ IMFCollection* available_types = NULL;
+ IUnknown* unknown_audio_type = NULL;
+ IMFMediaType* audio_type = NULL;
+ IMFAttributes* audio_attributes = NULL;
+ const GUID OUTPUT_AUDIO_SUBTYPE = MFAudioFormat_AAC;
+ // Get all available types of output formats.
+ hr = MFTranscodeGetAudioOutputAvailableTypes(OUTPUT_AUDIO_SUBTYPE,
+ MFT_ENUM_FLAG_ALL,
+ NULL,
+ &available_types);
+ if (SUCCEEDED(hr)) {
+ hr = available_types->GetElementCount(&num_available_formats);
+ if (num_available_formats == 0)
+ hr = E_UNEXPECTED;
+ }
+ if (SUCCEEDED(hr)) {
+ // Just pick the first available format.
+ hr = available_types->GetElement(0, &unknown_audio_type);
+ }
+ if (SUCCEEDED(hr))
+ hr = unknown_audio_type->QueryInterface(IID_PPV_ARGS(&audio_type));
+ if (SUCCEEDED(hr))
+ hr = MFCreateAttributes(&audio_attributes, 0);
+ if (SUCCEEDED(hr))
+ hr = audio_type->CopyAllItems(audio_attributes);
+ if (SUCCEEDED(hr))
+ hr = audio_attributes->SetGUID(MF_MT_SUBTYPE, OUTPUT_AUDIO_SUBTYPE);
+
+ if (SUCCEEDED(hr))
+ hr = transcode_profile_->SetAudioAttributes(audio_attributes);
+
+ SafeRelease(&available_types);
+ SafeRelease(&unknown_audio_type);
+ SafeRelease(&audio_type);
+ SafeRelease(&audio_attributes);
+ return hr;
+}
+
+HRESULT MFPlayer::ConfigureTranscodeVideoOutput() {
+ assert(transcode_profile_);
+
+ HRESULT hr = S_OK;
+ IMFAttributes* video_attributes = NULL;
+ // We set 5 attributes for our video output:
+ // 1. Output subtype
+ // 2. Frame rate
+ // 3. Frame Size
+ // 4. Aspect ratio
+ // 5. Bit rate
+ hr = MFCreateAttributes(&video_attributes, 5);
+ if (SUCCEEDED(hr))
+ hr = video_attributes->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
+ if (SUCCEEDED(hr))
+ hr = MFSetAttributeRatio(video_attributes, MF_MT_FRAME_RATE, 30, 1);
+ if (SUCCEEDED(hr))
+ hr = MFSetAttributeSize(video_attributes, MF_MT_FRAME_SIZE, 320, 240);
+ if (SUCCEEDED(hr))
+ hr = MFSetAttributeRatio(video_attributes, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
+ if (SUCCEEDED(hr))
+ hr = video_attributes->SetUINT32(MF_MT_AVG_BITRATE, 300000);
+
+ if (SUCCEEDED(hr))
+ hr = transcode_profile_->SetVideoAttributes(video_attributes);
+
+ SafeRelease(&video_attributes);
+ return hr;
+}
+
+HRESULT MFPlayer::ConfigureContainer() {
+ assert(transcode_profile_);
+
+ HRESULT hr = S_OK;
+ IMFAttributes* container_attributes = NULL;
+
+ hr = MFCreateAttributes(&container_attributes, 2);
+ // Make the container of MP4 type.
+ if (SUCCEEDED(hr))
+ hr = container_attributes->SetGUID(MF_TRANSCODE_CONTAINERTYPE,
+ MFTranscodeContainerType_MPEG4);
+ // Use the default setting. Media Foundation will use the stream
+ // settings set in ConfigureAudioOutput and ConfigureVideoOutput.
+ if (SUCCEEDED(hr))
+ hr = container_attributes->SetUINT32(MF_TRANSCODE_ADJUST_PROFILE,
+ MF_TRANSCODE_ADJUST_PROFILE_DEFAULT);
+
+ if (SUCCEEDED(hr))
+ hr = transcode_profile_->SetContainerAttributes(container_attributes);
+
+ SafeRelease(&container_attributes);
+ return hr;
+}
+
+// This method will start getting media session events synchronously.
+bool MFPlayer::Transcode() {
+ assert(session_);
+
+ IMFMediaEvent* event = NULL;
+ MediaEventType event_type = MEUnknown;
+ HRESULT hr = S_OK;
+ HRESULT event_status = S_OK;
+
+ while (event_type != MESessionClosed) {
+ hr = session_->GetEvent(0, &event);
+ if (FAILED(hr))
+ break;
+ hr = event->GetType(&event_type);
+ if (FAILED(hr))
+ break;
+ hr = event->GetStatus(&event_status);
+ if (FAILED(hr))
+ break;
+ if (FAILED(event_status)) {
+ hr = event_status;
+ break;
+ }
+ switch (event_type) {
+ case MESessionTopologySet:
+ hr = OnTopologyReady(event);
+ break;
+ case MESessionStarted:
+ break;
+ case MESessionEnded:
+ hr = session_->Close();
+ // Finished encoding.
+ break;
+ case MESessionClosed:
+ // Output file has been created - exit on next iteration.
+ break;
+ }
+ if (FAILED(hr))
+ break;
+ SafeRelease(&event);
+ }
+ // Release the last MESessionClosed event.
+ SafeRelease(&event);
+ return SUCCEEDED(hr);
+}
+
+} // namespace mfplayer
diff --git a/media/tools/mfplayer/mfplayer.h b/media/tools/mfplayer/mfplayer.h
new file mode 100644
index 0000000..6daead1
--- /dev/null
+++ b/media/tools/mfplayer/mfplayer.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_TOOLS_MFPLAYER_MFPLAYER_H_
+#define MEDIA_TOOLS_MFPLAYER_MFPLAYER_H_
+
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#ifdef WIN32
+#undef WIN32
+#endif
+#ifdef WINVER
+#undef WINVER
+#endif
+
+#include <evr.h> // Subclass IMFAsyncCallback
+
+#include <cassert>
+
+#include "base/basictypes.h"
+
+#pragma comment(lib, "d3d9.lib")
+#pragma comment(lib, "dwmapi.lib")
+#pragma comment(lib, "dxva2.lib")
+#pragma comment(lib, "mf.lib")
+#pragma comment(lib, "mfplat.lib")
+#pragma comment(lib, "mfuuid.lib")
+#pragma comment(lib, "shlwapi.lib")
+#pragma comment(lib, "strmiids.lib")
+#pragma comment(lib, "winmm.lib")
+
+struct IMFAsyncResult;
+struct IMFMediaEvent;
+struct IMFMediaSession;
+struct IMFMediaSource;
+struct IMFPresentationDescriptor;
+struct IMFStreamDescriptor;
+struct IMFTopology;
+struct IMFTopologyNode;
+struct IMFTranscodeProfile;
+struct IMFVideoDisplayControl;
+
+namespace mfplayer {
+
+class MFPlayer : public IMFAsyncCallback {
+ public:
+ enum PlayerState {
+ CLOSED = 0, // No session.
+ READY, // Session was created, ready to open a file.
+ OPEN_PENDING, // Session is opening a file.
+ STARTED, // Session is playing a file.
+ PAUSED, // Session is paused.
+ STOPPED, // Session is stopped (ready to play).
+ CLOSING // Application has closed the session, but is waiting for
+ // MESessionClosed.
+ };
+
+ // This is used to represent a type of event posted to the event_window_.
+ static const UINT WM_APP_PLAYER_EVENT = WM_APP + 1;
+
+ // Use this to construct a MFPlayer instead of constructor.
+ static bool CreateInstance(HWND video_window, HWND event_window,
+ bool render_to_window, MFPlayer** player);
+
+ // Override IUnknown. STDMETHODCALLTYPE, which expands to __stdcall, is
+ // necessary for overriding the parent class's method.
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void** object_ptr);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ // Implement IMFAsyncCallback.
+ HRESULT STDMETHODCALLTYPE GetParameters(DWORD* flags, DWORD* queue) {
+ // Implementation of this method is optional, assume default behavior.
+ return E_NOTIMPL;
+ }
+ HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult* result);
+
+ // Opens the file specified in url and sends a message to start playback,
+ // once topology has been completed. For archive sinks, out_url is the
+ // destination at which the encoded file will be saved.
+ bool OpenURL(const WCHAR* in_url, const WCHAR* out_url);
+
+ // Starts playback.
+ bool Play();
+
+ // Pauses playback.
+ bool Pause();
+
+ // Shuts down the media player. Called by the destructor.
+ bool Shutdown();
+
+ // Handles messages in the message queue.
+ HRESULT HandleEvent(UINT_PTR event_ptr);
+ inline PlayerState state() const { return state_; }
+
+ // Video functionality
+ bool Repaint();
+ bool ResizeVideo(WORD width, WORD height);
+ inline bool HasVideo() const { return (video_display_ != NULL); }
+
+ // The following methods are used for archive sink.
+ // Begins the encoding operation. Call this after OpenURL() only.
+ bool Transcode();
+
+ private:
+ // Private constructor: Call CreateInstance() instead.
+ MFPlayer(HWND video_window, HWND event_window,
+ bool render_to_window);
+
+ // Caller should use Release() instead, which keeps track of ref count.
+ ~MFPlayer();
+
+ // Initializes the media player. This actually only creates an Event object
+ // to signal closing the media player.
+ HRESULT Initialize();
+
+ // Creates the media session object of the player, and starts pulling events
+ // from it.
+ HRESULT CreateSession();
+
+ // The meat of Shutdown(). Releases the source and the session.
+ HRESULT CloseSession();
+ HRESULT StartPlayback();
+ HRESULT CreateMediaSource(const WCHAR* url);
+
+ // Creates the partial topology of the player.
+ HRESULT CreateTopologyFromSource(IMFTopology** topology);
+
+ // Called by CreateTopologyFromSource() to add nodes to the topology, if the
+ // stream is either video or audio stream.
+ HRESULT AddBranchToPartialTopology(
+ IMFTopology* topology,
+ IMFPresentationDescriptor* presentation_desc,
+ DWORD index);
+ HRESULT CreateSourceStreamNode(IMFPresentationDescriptor* presentation_desc,
+ IMFStreamDescriptor* stream_desc,
+ IMFTopologyNode** node_ptr);
+ HRESULT CreateOutputNode(IMFStreamDescriptor* stream_desc,
+ IMFTopologyNode** node_ptr);
+
+ // Media event handlers
+ HRESULT OnTopologyReady(IMFMediaEvent* event);
+ HRESULT OnPresentationEnded(IMFMediaEvent* event);
+
+ // These methods are used for archive sink.
+ HRESULT ConfigureTranscodeAudioOutput();
+ HRESULT ConfigureTranscodeVideoOutput();
+ HRESULT ConfigureContainer();
+
+ LONG ref_count_;
+ PlayerState state_;
+ IMFMediaSession* session_;
+ IMFMediaSource* source_;
+ IMFVideoDisplayControl* video_display_;
+ HWND video_window_;
+ HWND event_window_;
+ HANDLE close_event_;
+ bool render_to_window_;
+ IMFTranscodeProfile* transcode_profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(MFPlayer);
+};
+
+} // namespace mfplayer
+
+#endif // MEDIA_TOOLS_MFPLAYER_MFPLAYER_H_