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

536 lines
13 KiB
C++

#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mferror.h>
#include <stdio.h>
#include "..\\WavSink\\CreateWavSink.h"
#define USE_LOGGING
#include "common.h"
using namespace MediaFoundationSamples;
// Forward declares
HRESULT CreateWavFile(const WCHAR *sURL, const WCHAR *sOutputFile);
HRESULT CreateTopology(IMFMediaSource *pSource, IMFMediaSink *pSink, IMFTopology **ppTopology);
HRESULT CreateTopologyBranch(
IMFTopology *pTopology,
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFMediaSink *pSink
);
HRESULT CreateSourceNode(
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode // Receives the node pointer.
);
HRESULT CreateOutputNode(IMFMediaSink *pSink, DWORD iStream, IMFTopologyNode **ppNode);
HRESULT RunMediaSession(IMFTopology *pTopology);
///////////////////////////////////////////////////////////////////////
// Name: wmain
// Description: Entry point to the application.
//
// Usage: writewavfile.exe inputfile outputfile
///////////////////////////////////////////////////////////////////////
int wmain(int argc, wchar_t *argv[ ])
{
HRESULT hr;
TRACE_INIT();
if (argc != 3)
{
wprintf(L"Usage: WriteWavFile.exe InputFile OuputFile\n");
return -1;
}
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
wprintf(L"MFStartup failed!\n");
}
if (SUCCEEDED(hr))
{
hr = CreateWavFile(argv[1],argv[2]);
if (SUCCEEDED(hr))
{
wprintf(L"Done!\n");
}
else
{
wprintf(L"Error: Unable to author file.\n");
}
}
MFShutdown();
TRACE_CLOSE();
return 0;
}
///////////////////////////////////////////////////////////////////////
// Name: CreateWavFile
// Description: Creates a .wav file from an input file.
///////////////////////////////////////////////////////////////////////
HRESULT CreateWavFile(const WCHAR *sURL, const WCHAR *sOutputFile)
{
IMFByteStream *pStream = NULL;
IMFMediaSink *pSink = NULL;
IMFMediaSource *pSource = NULL;
IMFTopology *pTopology = NULL;
HRESULT hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, sOutputFile, &pStream);
if (FAILED(hr))
{
wprintf(L"MFCreateFile failed!\n");
}
// Create the WavSink object.
if (SUCCEEDED(hr))
{
hr = CreateWavSink(pStream, &pSink);
}
// Create the media source from the URL.
if (SUCCEEDED(hr))
{
hr = CreateMediaSource(sURL, &pSource);
}
// Create the topology.
if (SUCCEEDED(hr))
{
hr = CreateTopology(pSource, pSink, &pTopology);
}
// Run the media session.
if (SUCCEEDED(hr))
{
hr = RunMediaSession(pTopology);
if (FAILED(hr))
{
wprintf(L"RunMediaSession failed!\n");
}
}
if (pSource)
{
pSource->Shutdown();
}
SAFE_RELEASE(pStream);
SAFE_RELEASE(pSink);
SAFE_RELEASE(pSource);
SAFE_RELEASE(pTopology);
return hr;
}
///////////////////////////////////////////////////////////////////////
// Name: RunMediaSession
// Description:
// Queues the specified topology on the media session and runs the
// media session until the MESessionEnded event is received.
///////////////////////////////////////////////////////////////////////
HRESULT RunMediaSession(IMFTopology *pTopology)
{
IMFMediaSession *pSession = NULL;
HRESULT hr = S_OK;
BOOL bGetAnotherEvent = TRUE;
PROPVARIANT varStartPosition;
PropVariantInit(&varStartPosition);
hr = MFCreateMediaSession(NULL, &pSession);
if (SUCCEEDED(hr))
{
hr = pSession->SetTopology(0, pTopology);
}
while (bGetAnotherEvent)
{
HRESULT hrStatus = S_OK;
IMFMediaEvent *pEvent = NULL;
MediaEventType meType = MEUnknown;
MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.
hr = pSession->GetEvent(0, &pEvent);
if (SUCCEEDED(hr))
{
hr = pEvent->GetStatus(&hrStatus);
}
if (SUCCEEDED(hr))
{
hr = pEvent->GetType(&meType);
}
if (SUCCEEDED(hr) && SUCCEEDED(hrStatus))
{
switch (meType)
{
case MESessionTopologySet:
wprintf(L"MESessionTopologySet\n");
break;
case MESessionTopologyStatus:
// Get the status code.
hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, (UINT32*)&TopoStatus);
if (SUCCEEDED(hr))
{
switch (TopoStatus)
{
case MF_TOPOSTATUS_READY:
wprintf(L"MESessionTopologyStatus: MF_TOPOSTATUS_READY\n");
hr = pSession->Start(&GUID_NULL, &varStartPosition);
break;
case MF_TOPOSTATUS_ENDED:
wprintf(L"MESessionTopologyStatus: MF_TOPOSTATUS_ENDED\n");
break;
}
}
break;
case MESessionStarted:
wprintf(L"MESessionStarted\n");
break;
case MESessionEnded:
wprintf(L"MESessionEnded\n");
hr = pSession->Stop();
break;
case MESessionStopped:
wprintf(L"MESessionStopped.\n");
hr = pSession->Close();
break;
case MESessionClosed:
wprintf(L"MESessionClosed\n");
bGetAnotherEvent = FALSE;
break;
default:
wprintf(L"Media session event: %d\n", meType);
break;
}
}
SAFE_RELEASE(pEvent);
if (FAILED(hr) || FAILED(hrStatus))
{
bGetAnotherEvent = FALSE;
}
}
wprintf(L"Shutting down the media session.\n");
pSession->Shutdown();
PropVariantClear(&varStartPosition);
SAFE_RELEASE(pSession);
return hr;
}
///////////////////////////////////////////////////////////////////////
// Name: CreateTopology
// Description: Creates the topology.
//
// Note: The first audio stream is conntected to the media sink.
// Other streams are deselected.
///////////////////////////////////////////////////////////////////////
HRESULT CreateTopology(IMFMediaSource *pSource, IMFMediaSink *pSink, IMFTopology **ppTopology)
{
IMFTopology *pTopology = NULL;
IMFPresentationDescriptor *pPD = NULL;
IMFStreamDescriptor *pSD = NULL;
HRESULT hr = S_OK;
DWORD cStreams = 0;
hr = MFCreateTopology(&pTopology);
if (SUCCEEDED(hr))
{
hr = pSource->CreatePresentationDescriptor(&pPD);
}
if (SUCCEEDED(hr))
{
hr = pPD->GetStreamDescriptorCount(&cStreams);
}
BOOL fConnected = FALSE;
if (SUCCEEDED(hr))
{
GUID majorType = GUID_NULL;
BOOL fSelected = FALSE;
for (DWORD iStream = 0; iStream < cStreams; iStream++)
{
hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
if (FAILED(hr))
{
break;
}
// If the stream is not selected by default, ignore it.
if (!fSelected)
{
continue;
}
// Get the major media type.
hr = GetStreamMajorType(pSD, &majorType);
if (FAILED(hr))
{
break;
}
// If it's not audio, deselect it and continue.
if (majorType != MFMediaType_Audio)
{
// Deselect this stream
hr = pPD->DeselectStream(iStream);
if (FAILED(hr))
{
break;
}
else
{
continue;
}
}
// It's an audio stream, so try to create the topology branch.
hr = CreateTopologyBranch(pTopology, pSource, pPD, pSD, pSink);
// Set our status flag.
if (SUCCEEDED(hr))
{
fConnected = TRUE;
}
// At this point we have reached the first audio stream in the
// source, so we can stop looking (whether we succeeded or failed).
break;
}
}
if (SUCCEEDED(hr))
{
// Even if we succeeded, if we didn't connect any streams, it's a failure.
// (For example, it might be a video-only source.
if (!fConnected)
{
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
*ppTopology = pTopology;
(*ppTopology)->AddRef();
}
SAFE_RELEASE(pTopology);
SAFE_RELEASE(pPD);
SAFE_RELEASE(pSD);
return hr;
}
//////////////////////////////////////////////////////////////////////
// Name: CreateSourceNode
// Creates a source node for a media stream.
//
// pSource: Pointer to the media source.
// pSourcePD: Pointer to the source's presentation descriptor.
// pSourceSD: Pointer to the stream descriptor.
// ppNode: Receives the IMFTopologyNode pointer.
///////////////////////////////////////////////////////////////////////
HRESULT CreateSourceNode(
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode // Receives the node pointer.
)
{
IMFTopologyNode *pNode = NULL;
HRESULT hr = S_OK;
// Create the node.
hr = MFCreateTopologyNode(
MF_TOPOLOGY_SOURCESTREAM_NODE,
&pNode);
// Set the attributes.
if (SUCCEEDED(hr))
{
hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
}
if (SUCCEEDED(hr))
{
hr = pNode->SetUnknown(
MF_TOPONODE_PRESENTATION_DESCRIPTOR,
pPD);
}
if (SUCCEEDED(hr))
{
hr = pNode->SetUnknown(
MF_TOPONODE_STREAM_DESCRIPTOR,
pSD);
}
// Return the pointer to the caller.
if (SUCCEEDED(hr))
{
*ppNode = pNode;
(*ppNode)->AddRef();
}
SAFE_RELEASE(pNode);
return hr;
}
///////////////////////////////////////////////////////////////////////
// Name: CreateTopologyBranch
// Description: Adds a source and sink to the topology and
// connects them.
//
// pTopology: The topology.
// pSource: The media source.
// pPD: The source's presentation descriptor.
// pSD: The stream descriptor for the stream.
// pSink: The media sink.
//
///////////////////////////////////////////////////////////////////////
HRESULT CreateTopologyBranch(
IMFTopology *pTopology,
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFMediaSink *pSink
)
{
IMFTopologyNode *pSourceNode = NULL;
IMFTopologyNode *pOutputNode = NULL;
HRESULT hr = S_OK;
hr = CreateSourceNode(pSource, pPD, pSD, &pSourceNode);
if (SUCCEEDED(hr))
{
hr = CreateOutputNode(pSink, 0, &pOutputNode);
}
if (SUCCEEDED(hr))
{
hr = pTopology->AddNode(pSourceNode);
}
if (SUCCEEDED(hr))
{
hr = pTopology->AddNode(pOutputNode);
}
if (SUCCEEDED(hr))
{
hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
}
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pOutputNode);
return hr;
}
///////////////////////////////////////////////////////////////////////
// Name: CreateOutputNode
// Description: Creates an output node for a stream sink.
//
// pSink: The media sink.
// iStream: Index of the stream sink on the media sink.
// ppNode: Receives a pointer to the topology node.
///////////////////////////////////////////////////////////////////////
HRESULT CreateOutputNode(IMFMediaSink *pSink, DWORD iStream, IMFTopologyNode **ppNode)
{
IMFTopologyNode *pNode = NULL;
IMFStreamSink *pStream = NULL;
HRESULT hr = S_OK;
hr = pSink->GetStreamSinkByIndex(iStream, &pStream);
if (SUCCEEDED(hr))
{
hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
}
if (SUCCEEDED(hr))
{
hr = pNode->SetObject(pStream);
}
if (SUCCEEDED(hr))
{
*ppNode = pNode;
(*ppNode)->AddRef();
}
SAFE_RELEASE(pNode);
SAFE_RELEASE(pStream);
return hr;
}