532 lines
13 KiB
C++
532 lines
13 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Transcode.cpp
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//
|
|
// This sample demonstrates how to perform simple transcoding
|
|
// to WMA or WMV.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "Transcode.h"
|
|
|
|
HRESULT CreateMediaSource(const WCHAR *sURL, IMFMediaSource** ppMediaSource);
|
|
|
|
//-------------------------------------------------------------------
|
|
// CTranscoder constructor
|
|
//-------------------------------------------------------------------
|
|
|
|
CTranscoder::CTranscoder() :
|
|
m_pSession(NULL),
|
|
m_pSource(NULL),
|
|
m_pTopology(NULL),
|
|
m_pProfile(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// CTranscoder destructor
|
|
//-------------------------------------------------------------------
|
|
|
|
CTranscoder::~CTranscoder()
|
|
{
|
|
Shutdown();
|
|
|
|
SafeRelease(&m_pProfile);
|
|
SafeRelease(&m_pTopology);
|
|
SafeRelease(&m_pSource);
|
|
SafeRelease(&m_pSession);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OpenFile
|
|
//
|
|
// 1. Creates a media source for the caller specified URL.
|
|
// 2. Creates the media session.
|
|
// 3. Creates a transcode profile to hold the stream and
|
|
// container attributes.
|
|
//
|
|
// sURL: Input file URL.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CTranscoder::OpenFile(const WCHAR *sURL)
|
|
{
|
|
if (!sURL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Create the media source.
|
|
hr = CreateMediaSource(sURL, &m_pSource);
|
|
|
|
//Create the media session.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFCreateMediaSession(NULL, &m_pSession);
|
|
}
|
|
|
|
// Create an empty transcode profile.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFCreateTranscodeProfile(&m_pProfile);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// ConfigureAudioOutput
|
|
//
|
|
// Configures the audio stream attributes.
|
|
// These values are stored in the transcode profile.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CTranscoder::ConfigureAudioOutput()
|
|
{
|
|
assert (m_pProfile);
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD dwMTCount = 0;
|
|
|
|
IMFCollection *pAvailableTypes = NULL;
|
|
IUnknown *pUnkAudioType = NULL;
|
|
IMFMediaType *pAudioType = NULL;
|
|
IMFAttributes *pAudioAttrs = NULL;
|
|
|
|
// Get the list of output formats supported by the Windows Media
|
|
// audio encoder.
|
|
|
|
hr = MFTranscodeGetAudioOutputAvailableTypes(
|
|
MFAudioFormat_WMAudioV9,
|
|
MFT_ENUM_FLAG_ALL,
|
|
NULL,
|
|
&pAvailableTypes
|
|
);
|
|
|
|
// Get the number of elements in the list.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pAvailableTypes->GetElementCount( &dwMTCount );
|
|
|
|
if (dwMTCount == 0)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
// In this simple case, use the first media type in the collection.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pAvailableTypes->GetElement(0, &pUnkAudioType);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pUnkAudioType->QueryInterface(IID_PPV_ARGS(&pAudioType));
|
|
}
|
|
|
|
// Create a copy of the attribute store so that we can modify it safely.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFCreateAttributes(&pAudioAttrs, 0);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pAudioType->CopyAllItems(pAudioAttrs);
|
|
}
|
|
|
|
// Set the encoder to be Windows Media audio encoder, so that the
|
|
// appropriate MFTs are added to the topology.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pAudioAttrs->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_WMAudioV9);
|
|
}
|
|
|
|
// Set the attribute store on the transcode profile.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pProfile->SetAudioAttributes( pAudioAttrs );
|
|
}
|
|
|
|
SafeRelease(&pAvailableTypes);
|
|
SafeRelease(&pAudioType);
|
|
SafeRelease(&pUnkAudioType);
|
|
SafeRelease(&pAudioAttrs);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// ConfigureVideoOutput
|
|
//
|
|
// Configures the Video stream attributes.
|
|
// These values are stored in the transcode profile.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CTranscoder::ConfigureVideoOutput()
|
|
{
|
|
assert (m_pProfile);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFAttributes* pVideoAttrs = NULL;
|
|
|
|
// Configure the video stream
|
|
|
|
// Create a new attribute store.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFCreateAttributes( &pVideoAttrs, 5 );
|
|
}
|
|
|
|
// Set the encoder to be Windows Media video encoder, so that the appropriate MFTs are added to the topology.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pVideoAttrs->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3);
|
|
}
|
|
|
|
// Set the frame rate.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFSetAttributeRatio(pVideoAttrs, MF_MT_FRAME_RATE, 30, 1);
|
|
}
|
|
|
|
//Set the frame size.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFSetAttributeSize(pVideoAttrs, MF_MT_FRAME_SIZE, 320, 240);
|
|
}
|
|
|
|
//Set the pixel aspect ratio
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFSetAttributeRatio(pVideoAttrs, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
|
|
}
|
|
|
|
// Set the bit rate.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pVideoAttrs->SetUINT32(MF_MT_AVG_BITRATE, 300000);
|
|
}
|
|
|
|
// Set the attribute store on the transcode profile.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pProfile->SetVideoAttributes( pVideoAttrs );
|
|
}
|
|
|
|
SafeRelease(&pVideoAttrs);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// ConfigureContainer
|
|
//
|
|
// Configures the container attributes.
|
|
// These values are stored in the transcode profile.
|
|
//
|
|
// Note: Setting the container type does not insert the required
|
|
// MFT node in the transcode topology. The MFT node is based on the
|
|
// stream settings stored in the transcode profile.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CTranscoder::ConfigureContainer()
|
|
{
|
|
assert (m_pProfile);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFAttributes* pContainerAttrs = NULL;
|
|
|
|
//Set container attributes
|
|
hr = MFCreateAttributes( &pContainerAttrs, 2 );
|
|
|
|
//Set the output container to be ASF type
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pContainerAttrs->SetGUID(
|
|
MF_TRANSCODE_CONTAINERTYPE,
|
|
MFTranscodeContainerType_ASF
|
|
);
|
|
}
|
|
|
|
// Use the default setting. Media Foundation will use the stream
|
|
// settings set in ConfigureAudioOutput and ConfigureVideoOutput.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pContainerAttrs->SetUINT32(
|
|
MF_TRANSCODE_ADJUST_PROFILE,
|
|
MF_TRANSCODE_ADJUST_PROFILE_DEFAULT
|
|
);
|
|
}
|
|
|
|
//Set the attribute store on the transcode profile.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pProfile->SetContainerAttributes(pContainerAttrs);
|
|
}
|
|
|
|
SafeRelease(&pContainerAttrs);
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// EncodeToFile
|
|
//
|
|
// Builds the transcode topology based on the input source,
|
|
// configured transcode profile, and the output container settings.
|
|
//-------------------------------------------------------------------
|
|
HRESULT CTranscoder::EncodeToFile(const WCHAR *sURL)
|
|
{
|
|
assert (m_pSession);
|
|
assert (m_pSource);
|
|
assert (m_pProfile);
|
|
|
|
if (!sURL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//Create the transcode topology
|
|
hr = MFCreateTranscodeTopology( m_pSource, sURL, m_pProfile, &m_pTopology );
|
|
|
|
// Set the topology on the media session.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pSession->SetTopology(0, m_pTopology);
|
|
}
|
|
|
|
//Get media session events. This will start the encoding session.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = Transcode();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: Transcode
|
|
//
|
|
// Start the encoding session by controlling the media session.
|
|
//
|
|
// The encoding starts when the media session raises the
|
|
// MESessionTopologySet event. The media session is closed after
|
|
// receiving MESessionEnded. The encoded file is finalized after
|
|
// the session is closed.
|
|
//
|
|
// For simplicity, this sample uses the synchronous method for
|
|
// getting media session events.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CTranscoder::Transcode()
|
|
{
|
|
assert (m_pSession);
|
|
|
|
IMFMediaEvent* pEvent = NULL;
|
|
MediaEventType meType = MEUnknown; // Event type
|
|
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrStatus = S_OK; // Event status
|
|
|
|
//Get media session events synchronously
|
|
while (meType != MESessionClosed)
|
|
{
|
|
hr = m_pSession->GetEvent(0, &pEvent);
|
|
|
|
if (FAILED(hr)) { break; }
|
|
|
|
// Get the event type.
|
|
hr = pEvent->GetType(&meType);
|
|
|
|
if (FAILED(hr)) { break; }
|
|
|
|
hr = pEvent->GetStatus(&hrStatus);
|
|
|
|
if (FAILED(hr)) { break; }
|
|
|
|
if (FAILED(hrStatus))
|
|
{
|
|
wprintf_s(L"Failed. 0x%X error condition triggered this event.\n", hrStatus);
|
|
hr = hrStatus;
|
|
break;
|
|
}
|
|
|
|
switch (meType)
|
|
{
|
|
case MESessionTopologySet:
|
|
hr = Start();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
wprintf_s(L"Ready to start.\n");
|
|
}
|
|
break;
|
|
|
|
case MESessionStarted:
|
|
wprintf_s(L"Started encoding...\n");
|
|
break;
|
|
|
|
case MESessionEnded:
|
|
hr = m_pSession->Close();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
wprintf_s(L"Finished encoding.\n");
|
|
}
|
|
break;
|
|
|
|
case MESessionClosed:
|
|
wprintf_s(L"Output file created.\n");
|
|
break;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
SafeRelease(&pEvent);
|
|
}
|
|
|
|
SafeRelease(&pEvent);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Start
|
|
//
|
|
// Starts the encoding session.
|
|
//-------------------------------------------------------------------
|
|
HRESULT CTranscoder::Start()
|
|
{
|
|
assert(m_pSession != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
PROPVARIANT varStart;
|
|
PropVariantInit(&varStart);
|
|
|
|
hr = m_pSession->Start(&GUID_NULL, &varStart);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
wprintf_s(L"Failed to start the session...\n");
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Shutdown
|
|
//
|
|
// Handler for the MESessionClosed event.
|
|
// Shuts down the media session and the media source.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CTranscoder::Shutdown()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Shut down the media source
|
|
if (m_pSource)
|
|
{
|
|
hr = m_pSource->Shutdown();
|
|
}
|
|
|
|
// Shut down the media session. (Synchronous operation, no events.)
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_pSession)
|
|
{
|
|
hr = m_pSession->Shutdown();
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
wprintf_s(L"Failed to close the session...\n");
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// CreateMediaSource
|
|
//
|
|
// Creates a media source from a URL.
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CreateMediaSource(
|
|
const WCHAR *sURL, // The URL of the file to open.
|
|
IMFMediaSource** ppMediaSource // Receives a pointer to the media source.
|
|
)
|
|
{
|
|
if (!sURL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!ppMediaSource)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
|
|
|
|
IMFSourceResolver* pSourceResolver = NULL;
|
|
IUnknown* pUnkSource = NULL;
|
|
|
|
// Create the source resolver.
|
|
hr = MFCreateSourceResolver(&pSourceResolver);
|
|
|
|
|
|
// Use the source resolver to create the media source.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pSourceResolver->CreateObjectFromURL(
|
|
sURL, // URL of the source.
|
|
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
|
|
NULL, // Optional property store.
|
|
&ObjectType, // Receives the created object type.
|
|
&pUnkSource // Receives a pointer to the media source.
|
|
);
|
|
}
|
|
|
|
// Get the IMFMediaSource from the IUnknown pointer.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pUnkSource->QueryInterface(IID_PPV_ARGS(ppMediaSource));
|
|
}
|
|
|
|
SafeRelease(&pSourceResolver);
|
|
SafeRelease(&pUnkSource);
|
|
return hr;
|
|
}
|