2025-11-28 00:35:46 +09:00

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;
}