Kurz: Zápis souboru WMA pomocí objektů WMContainer
Tento kurz ukazuje zápis nového zvukového souboru (.wma) extrahováním multimediálního obsahu z nekomprimovaného zvukového souboru (.wav) a následným komprimováním ve formátu ASF. Režim kódování použitý pro převod je kódování konstantní přenosové rychlosti (CBR). V tomto režimu před relací kódování aplikace určuje cílovou přenosovou rychlost, kterou kodér musí dosáhnout.
V tomto kurzu vytvoříte konzolovou aplikaci, která jako argumenty vezme vstupní a výstupní názvy souborů. Aplikace získá nekomprimované multimediální ukázky z aplikace analýzy vlnového souboru, který je k dispozici v tomto kurzu. Tyto ukázky se odesílají do kodéru pro převod do formátu Windows Media Audio 9. Kodér je nakonfigurovaný pro kódování CBR a používá první přenosovou rychlost dostupnou během vyjednávání typu média jako cílovou přenosovou rychlost. Kódované ukázky se odešlou do multiplexeru pro paketování ve formátu dat ASF. Tyto pakety se zapíšou do bajtového datového proudu, který představuje datový objekt ASF. Jakmile bude oddíl dat připravený, vytvoříte zvukový soubor ASF a zapíšete nový objekt záhlaví ASF, který skonsoliduje všechny informace záhlaví, a pak připojíte datový proud datového objektu ASF.
Tento kurz obsahuje následující části:
- požadavky
- Terminologie
- 1. Nastavení Projectu
- 2. Deklarace pomocných funkcí
- 3. Otevření zvukového souboru
- 4. Konfigurace kodéru
- 5. Vytvořte objekt ASF ContentInfo.
- 6. Vytvoření multiplexeru ASF
- 7. Generování nových datových paketů ASF
- 8. Zápis souboru ASF
- 9. Definování Entry-Point funkce
- související témata
Požadavky
V tomto kurzu se předpokládá následující:
- Znáte strukturu souboru ASF a komponenty poskytované službou Media Foundation pro práci s objekty ASF. Mezi tyto komponenty patří ContentInfo, splitter, multiplexer a objekty profilu. Další informace naleznete v tématu WMContainer ASF Components.
- Znáte kodéry Windows Media a různé typy kódování, zejména CBR. Další informace najdete ve Windows Media Encoderu .
- Znáte vyrovnávací paměti médií a bajtové datové proudy: Konkrétně operace se soubory využívající datový proud bajtů a zápis obsahu vyrovnávací paměti médií do bajtového datového proudu.
Terminologie
V tomto kurzu se používají následující termíny:
- Typ zdrojového média: Objekt typu média, zveřejňuje MMFMediaType rozhraní, které popisuje obsah vstupního souboru.
- Zvukový profil: Profilový objekt zahrnuje IMFASFProfile rozhraní, které obsahuje pouze zvukové streamy výstupního souboru.
- Ukázka streamu: Ukázka médií, odhaluje rozhraní IMFSample, představuje mediální data vstupního souboru získaná z kodéru v komprimovaném stavu.
- ContentInfo objekt: OBJEKT ASF ContentInfo, zveřejňuje IMFASFContentInfo rozhraní, které představuje objekt záhlaví ASF výstupního souboru.
- Datový proud bajtů: Objekt proudu bajtů exponuje rozhraní IMFByteStream, které reprezentuje celou část datového objektu ASF výstupního souboru.
- Datový paket: Ukázka média, odhaluje rozhraní IMFSample generované ASF Multiplexerem, představuje datový paket ASF, který bude zapsán do datového proudu bajtů.
- Výstupní bajtový proud: Objekt bajtového proudu, který zveřejňuje rozhraní IMFByteStream a obsahuje obsah výstupního souboru.
1. Nastavení projektu
Do zdrojového souboru zahrňte následující hlavičky:
#include <new> #include <stdio.h> // Standard I/O #include <windows.h> // Windows headers #include <mfapi.h> // Media Foundation platform #include <wmcontainer.h> // ASF interfaces #include <mferror.h> // Media Foundation error codes
Odkaz na následující soubory knihovny:
- mfplat.lib
- mf.lib
- mfuuid.lib
Deklarujte funkci SafeRelease.
Zahrňte do projektu třídu CWmaEncoder. Úplný zdrojový kód této třídy naleznete pod Příklad kódu kodéru.
2. Deklarace pomocných funkcí
Tento kurz používá následující pomocné funkce ke čtení a zápisu z bajtového datového proudu.
-
AppendToByteStream
: Připojí obsah jednoho bajtového datového proudu do jiného bajtového datového proudu. - WriteBufferToByteStream: Zapisuje data z vyrovnávací paměti médií do bajtového datového proudu.
Další informace naleznete v tématu IMFByteStream::Write. Následující kód ukazuje tyto pomocné funkce:
//-------------------------------------------------------------------
// AppendToByteStream
//
// Reads the contents of pSrc and writes them to pDest.
//-------------------------------------------------------------------
HRESULT AppendToByteStream(IMFByteStream *pSrc, IMFByteStream *pDest)
{
HRESULT hr = S_OK;
const DWORD READ_SIZE = 1024;
BYTE buffer[READ_SIZE];
while (1)
{
ULONG cbRead;
hr = pSrc->Read(buffer, READ_SIZE, &cbRead);
if (FAILED(hr)) { break; }
if (cbRead == 0)
{
break;
}
hr = pDest->Write(buffer, cbRead, &cbRead);
if (FAILED(hr)) { break; }
}
return hr;
}
//-------------------------------------------------------------------
// WriteBufferToByteStream
//
// Writes data from a media buffer to a byte stream.
//-------------------------------------------------------------------
HRESULT WriteBufferToByteStream(
IMFByteStream *pStream, // Pointer to the byte stream.
IMFMediaBuffer *pBuffer, // Pointer to the media buffer.
DWORD *pcbWritten // Receives the number of bytes written.
)
{
HRESULT hr = S_OK;
DWORD cbData = 0;
DWORD cbWritten = 0;
BYTE *pMem = NULL;
hr = pBuffer->Lock(&pMem, NULL, &cbData);
if (SUCCEEDED(hr))
{
hr = pStream->Write(pMem, cbData, &cbWritten);
}
if (SUCCEEDED(hr))
{
if (pcbWritten)
{
*pcbWritten = cbWritten;
}
}
if (pMem)
{
pBuffer->Unlock();
}
return hr;
}
3. Otevření zvukového souboru
V tomto kurzu se předpokládá, že vaše aplikace vygeneruje nekomprimovaný zvuk pro kódování. Pro tento účel jsou v tomto kurzu deklarovány dvě funkce:
HRESULT OpenAudioFile(PCWSTR pszURL, IMFMediaType **ppAudioFormat);
HRESULT GetNextAudioSample(BOOL *pbEOS, IMFSample **ppSample);
Implementace těchto funkcí je ponechána čtenáři.
- Funkce
OpenAudioFile
by měla otevřít multimediální soubor určený pszURL a vrátit ukazatel na typ média, který popisuje zvukový stream. - Funkce
GetNextAudioSample
by měla číst nekomprimovaný zvuk PCM ze souboru, který byl otevřenOpenAudioFile
. Po dosažení konce souboru pbEOS obdrží hodnotu TRUE. V opačném případě ppSample obdrží ukázku médií, která obsahuje vyrovnávací paměť zvuku.
4. Konfigurace kodéru
Dále vytvořte kodér, nakonfigurujte ho tak, aby vytvářel ukázky streamů kódovaných CBR, a vyjednával vstup a výstupní typy médií.
V Media Foundation jsou kodéry (zveřejnění rozhraní MMFTransform) implementovány jako Media Foundation Transforms (MFT).
V tomto kurzu se kodér implementuje ve třídě CWmaEncoder
, která poskytuje obálku pro MFT. Úplný zdrojový kód této třídy naleznete v tématu Příklad kódu kodéru.
Poznámka
Volitelně můžete zadat typ kódování jako CBR. Ve výchozím nastavení je kodér nakonfigurovaný tak, aby používal kódování CBR. Další informace naleznete v tématu kódování konstantní přenosové rychlosti. Další vlastnosti můžete nastavit v závislosti na typu kódování. Pro informace o vlastnostech specifických pro režim kódování viz Quality-Based Kódování proměnlivé bitové rychlosti, nekonsolidované kódování proměnlivé bitové rychlostia Peak-Constrained kódování proměnlivé bitové rychlosti.
CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.
hr = OpenAudioFile(sInputFileName, &pInputType);
if (FAILED(hr))
{
goto done;
}
// Initialize the WMA encoder wrapper.
pEncoder = new (std::nothrow) CWmaEncoder();
if (pEncoder == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pEncoder->Initialize();
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetEncodingType(EncodeMode_CBR);
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetInputType(pInputType);
if (FAILED(hr))
{
goto done;
}
5. Vytvořte objekt ASF ContentInfo.
Objekt ASF ContentInfo obsahuje informace o různých objektech záhlaví výstupního souboru.
Nejprve vytvořte profil ASF pro zvukový stream:
- Voláním MFCreateASFProfile vytvořit prázdný objekt profilu ASF. Profil ASF zveřejňuje rozhraní IMFASFProfile. Další informace najdete v tématu Vytváření a konfigurace streamů ASF.
- Získejte kódovaný zvukový formát z objektu
CWmaEncoder
. - Zavolejte IMFASFProfile::CreateStream pro vytvoření nového datového proudu pro profil ASF. Předejte ukazatel na rozhraní MMFMediaType, které představuje formát datového proudu.
- Volání IMFASFStreamConfig::SetStreamNumber pro přiřazení identifikátoru datového proudu.
- Nastavte parametry "leaky bucket" nastavením atributu MF_ASFSTREAMCONFIG_LEAKYBUCKET1 na objekt streamu.
- Zavolejte IMFASFProfile::SetStream k přidání nového datového proudu do profilu.
Nyní vytvořte objekt ASF ContentInfo následujícím způsobem:
- Zavolejte MFCreateASFContentInfo pro vytvoření prázdného objektu ContentInfo.
- Zavolejte IMFASFContentInfo::SetProfile k nastavení profilu ASF.
Následující kód ukazuje následující kroky:
HRESULT CreateASFContentInfo(
CWmaEncoder* pEncoder,
IMFASFContentInfo** ppContentInfo
)
{
HRESULT hr = S_OK;
IMFASFProfile* pProfile = NULL;
IMFMediaType* pMediaType = NULL;
IMFASFStreamConfig* pStream = NULL;
IMFASFContentInfo* pContentInfo = NULL;
// Create the ASF profile object.
hr = MFCreateASFProfile(&pProfile);
if (FAILED(hr))
{
goto done;
}
// Create a stream description for the encoded audio.
hr = pEncoder->GetOutputType(&pMediaType);
if (FAILED(hr))
{
goto done;
}
hr = pProfile->CreateStream(pMediaType, &pStream);
if (FAILED(hr))
{
goto done;
}
hr = pStream->SetStreamNumber(DEFAULT_STREAM_NUMBER);
if (FAILED(hr))
{
goto done;
}
// Set "leaky bucket" values.
LeakyBucket bucket;
hr = pEncoder->GetLeakyBucket1(&bucket);
if (FAILED(hr))
{
goto done;
}
hr = pStream->SetBlob(
MF_ASFSTREAMCONFIG_LEAKYBUCKET1,
(UINT8*)&bucket,
sizeof(bucket)
);
if (FAILED(hr))
{
goto done;
}
//Add the stream to the profile
hr = pProfile->SetStream(pStream);
if (FAILED(hr))
{
goto done;
}
// Create the ASF ContentInfo object.
hr = MFCreateASFContentInfo(&pContentInfo);
if (FAILED(hr))
{
goto done;
}
hr = pContentInfo->SetProfile(pProfile);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppContentInfo = pContentInfo;
(*ppContentInfo)->AddRef();
done:
SafeRelease(&pProfile);
SafeRelease(&pStream);
SafeRelease(&pMediaType);
SafeRelease(&pContentInfo);
return hr;
}
6. Vytvoření multiplexeru ASF
Multiplexer ASF generuje datové pakety ASF.
- Zavolejte MFCreateASFMultiplexer pro vytvoření multiplexeru ASF.
- Volání IMFASFMultiplexer::Initialize k inicializaci multiplexeru. Předejte ukazatel na objekt ASF Content Info, který byl vytvořen v předchozí části.
- Volání IMFASFMultiplexer::SetFlags, chcete-li nastavit příznak MFASF_MULTIPLEXER_AUTOADJUST_BITRATE. Při použití tohoto nastavení multiplexer automaticky upraví přenosovou rychlost obsahu ASF tak, aby odpovídala charakteristikám datových proudů, které jsou multiplexovány.
HRESULT CreateASFMux(
IMFASFContentInfo* pContentInfo,
IMFASFMultiplexer** ppMultiplexer
)
{
HRESULT hr = S_OK;
IMFMediaType* pMediaType = NULL;
IMFASFMultiplexer *pMultiplexer = NULL;
// Create and initialize the ASF Multiplexer object.
hr = MFCreateASFMultiplexer(&pMultiplexer);
if (FAILED(hr))
{
goto done;
}
hr = pMultiplexer->Initialize(pContentInfo);
if (FAILED(hr))
{
goto done;
}
// Enable automatic bit-rate adjustment.
hr = pMultiplexer->SetFlags(MFASF_MULTIPLEXER_AUTOADJUST_BITRATE);
if (FAILED(hr))
{
goto done;
}
*ppMultiplexer = pMultiplexer;
(*ppMultiplexer)->AddRef();
done:
SafeRelease(&pMultiplexer);
return hr;
}
7. Generování nových datových paketů ASF
Dále vygenerujte datové pakety ASF pro nový soubor. Tyto datové pakety budou představovat konečný datový objekt ASF pro nový soubor. Generování nových datových paketů ASF:
- Zavolejte MFCreateTempFile pro vytvoření dočasného bajtového proudu pro uložení datových paketů ASF.
- Voláním funkce
GetNextAudioSample
definované aplikací získáte nekomprimovaná zvuková data kodéru. - Předejte nekomprimovaný zvuk kodéru pro kompresi. Další informace najdete v tématu Zpracování dat v kodéru.
- Zavolejte IMFASFMultiplexer::ProcessSample k odeslání komprimovaných zvukových vzorků do multiplexoru ASF pro paketování.
- Získejte pakety ASF z multiplexeru a zapište je do dočasného bajtového streamu. Další informace najdete v tématu generování nových datových paketů ASF.
- Když se dostanete na konec zdrojového streamu, vyprázdněte kodér a stáhněte zbývající komprimované vzorky z kodéru. Další informace o vyprázdnění MFT naleznete v tématu Základní model zpracování MFT.
- Po odeslání všech vzorků do multiplexeru volejte IMFASFMultiplexer::Flush a vytáhněte zbývající pakety ASF z multiplexeru.
- Zavolejte IMFASFMultiplexer::End.
Následující kód generuje datové pakety ASF. Funkce vrátí ukazatel na bajtový stream, který obsahuje datový objekt ASF.
HRESULT EncodeData(
CWmaEncoder* pEncoder,
IMFASFContentInfo* pContentInfo,
IMFASFMultiplexer* pMux,
IMFByteStream** ppDataStream)
{
HRESULT hr = S_OK;
IMFByteStream* pStream = NULL;
IMFSample* pInputSample = NULL;
IMFSample* pWmaSample = NULL;
BOOL bEOF = FALSE;
// Create a temporary file to hold the data stream.
hr = MFCreateTempFile(
MF_ACCESSMODE_READWRITE,
MF_OPENMODE_DELETE_IF_EXIST,
MF_FILEFLAGS_NONE,
&pStream
);
if (FAILED(hr))
{
goto done;
}
BOOL bNeedInput = TRUE;
while (TRUE)
{
if (bNeedInput)
{
hr = GetNextAudioSample(&bEOF, &pInputSample);
if (FAILED(hr))
{
goto done;
}
if (bEOF)
{
// Reached the end of the input file.
break;
}
// Encode the uncompressed audio sample.
hr = pEncoder->ProcessInput(pInputSample);
if (FAILED(hr))
{
goto done;
}
bNeedInput = FALSE;
}
if (bNeedInput == FALSE)
{
// Get data from the encoder.
hr = pEncoder->ProcessOutput(&pWmaSample);
if (FAILED(hr))
{
goto done;
}
// pWmaSample can be NULL if the encoder needs more input.
if (pWmaSample)
{
hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
if (FAILED(hr))
{
goto done;
}
//Collect the data packets and write them to a stream
hr = GenerateASFDataPackets(pMux, pStream);
if (FAILED(hr))
{
goto done;
}
}
else
{
bNeedInput = TRUE;
}
}
SafeRelease(&pInputSample);
SafeRelease(&pWmaSample);
}
// Drain the MFT and pull any remaining samples from the encoder.
hr = pEncoder->Drain();
if (FAILED(hr))
{
goto done;
}
while (TRUE)
{
hr = pEncoder->ProcessOutput(&pWmaSample);
if (FAILED(hr))
{
goto done;
}
if (pWmaSample == NULL)
{
break;
}
hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
if (FAILED(hr))
{
goto done;
}
//Collect the data packets and write them to a stream
hr = GenerateASFDataPackets(pMux, pStream);
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pWmaSample);
}
// Flush the mux and get any pending ASF data packets.
hr = pMux->Flush();
if (FAILED(hr))
{
goto done;
}
hr = GenerateASFDataPackets(pMux, pStream);
if (FAILED(hr))
{
goto done;
}
// Update the ContentInfo object
hr = pMux->End(pContentInfo);
if (FAILED(hr))
{
goto done;
}
//Return stream to the caller that contains the ASF encoded data.
*ppDataStream = pStream;
(*ppDataStream)->AddRef();
done:
SafeRelease(&pStream);
SafeRelease(&pInputSample);
SafeRelease(&pWmaSample);
return hr;
}
Kód pro funkci GenerateASFDataPackets
je uveden v tématu Generování nových datových paketů ASF.
8. Zápis souboru ASF
Dále zapište hlavičku ASF do vyrovnávací paměti médií voláním IMFASFContentInfo::GenerateHeader. Tato metoda převede data uložená v objektu ContentInfo na binární data ve formátu ASF Header Object. Další informace naleznete v tématu Generování nového objektu záhlaví ASF.
Po vygenerování nového objektu záhlaví ASF vytvořte datový proud bajtů pro výstupní soubor. Nejprve napište objekt Header do výstupního bajtového datového proudu. Postupujte podle objektu Header s datovým objektem obsaženým v datovém toku bajtů.
HRESULT WriteASFFile(
IMFASFContentInfo *pContentInfo,
IMFByteStream *pDataStream,
PCWSTR pszFile
)
{
HRESULT hr = S_OK;
IMFMediaBuffer* pHeaderBuffer = NULL;
IMFByteStream* pWmaStream = NULL;
DWORD cbHeaderSize = 0;
DWORD cbWritten = 0;
//Create output file
hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST,
MF_FILEFLAGS_NONE, pszFile, &pWmaStream);
if (FAILED(hr))
{
goto done;
}
// Get the size of the ASF Header Object.
hr = pContentInfo->GenerateHeader (NULL, &cbHeaderSize);
if (FAILED(hr))
{
goto done;
}
// Create a media buffer.
hr = MFCreateMemoryBuffer(cbHeaderSize, &pHeaderBuffer);
if (FAILED(hr))
{
goto done;
}
// Populate the media buffer with the ASF Header Object.
hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
if (FAILED(hr))
{
goto done;
}
// Write the ASF header to the output file.
hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
if (FAILED(hr))
{
goto done;
}
// Append the data stream to the file.
hr = pDataStream->SetCurrentPosition(0);
if (FAILED(hr))
{
goto done;
}
hr = AppendToByteStream(pDataStream, pWmaStream);
done:
SafeRelease(&pHeaderBuffer);
SafeRelease(&pWmaStream);
return hr;
}
9. Definujte funkci Entry-Point
Teď můžete předchozí kroky spojit do kompletní aplikace. Před použitím jakéhokoli objektu Media Foundation inicializovat platformu Media Foundation voláním MFStartup. Až budete hotovi, zavolejte MFShutdown. Další informace naleznete v tématu inicializace Media Foundation.
Následující kód ukazuje kompletní konzolovou aplikaci. Argument příkazového řádku určuje název souboru, který se má převést, a název nového zvukového souboru.
int wmain(int argc, WCHAR* argv[])
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (argc != 3)
{
wprintf_s(L"Usage: %s input.wmv, %s output.wma");
return 0;
}
const WCHAR* sInputFileName = argv[1]; // Source file name
const WCHAR* sOutputFileName = argv[2]; // Output file name
IMFMediaType* pInputType = NULL;
IMFASFContentInfo* pContentInfo = NULL;
IMFASFMultiplexer* pMux = NULL;
IMFByteStream* pDataStream = NULL;
CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.
HRESULT hr = CoInitializeEx(
NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
{
goto done;
}
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
goto done;
}
CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.
hr = OpenAudioFile(sInputFileName, &pInputType);
if (FAILED(hr))
{
goto done;
}
// Initialize the WMA encoder wrapper.
pEncoder = new (std::nothrow) CWmaEncoder();
if (pEncoder == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pEncoder->Initialize();
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetEncodingType(EncodeMode_CBR);
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetInputType(pInputType);
if (FAILED(hr))
{
goto done;
}
// Create the WMContainer objects.
hr = CreateASFContentInfo(pEncoder, &pContentInfo);
if (FAILED(hr))
{
goto done;
}
hr = CreateASFMux(pContentInfo, &pMux);
if (FAILED(hr))
{
goto done;
}
// Convert uncompressed data to ASF format.
hr = EncodeData(pEncoder, pContentInfo, pMux, &pDataStream);
if (FAILED(hr))
{
goto done;
}
// Write the ASF objects to the output file.
hr = WriteASFFile(pContentInfo, pDataStream, sOutputFileName);
done:
SafeRelease(&pInputType);
SafeRelease(&pContentInfo);
SafeRelease(&pMux);
SafeRelease(&pDataStream);
delete pEncoder;
MFShutdown();
CoUninitialize();
if (FAILED(hr))
{
wprintf_s(L"Error: 0x%X\n", hr);
}
return 0;
}
Související témata