Kurz: Kopírování streamů ASF pomocí objektů WMContainer
Jedním ze způsobů, jak vytvořit soubor ASF, je kopírování datových proudů ASF z existujícího souboru. Uděláte to tak, že z zdrojového souboru načtete mediální data a zapíšete je do výstupního souboru. Pokud je zdrojový soubor souborEM ASF, můžete kopírovat ukázky datových proudů bez dekomprese a rekomprimace.
Tento kurz ukazuje tento scénář extrahováním prvního zvukového streamu ze zvukového videosouboru ASF (.wmv) a jeho zkopírováním do nového zvukového souboru ASF (.wma). V tomto kurzu vytvoříte konzolovou aplikaci, která jako argumenty vezme vstupní a výstupní názvy souborů. Aplikace pomocí rozdělovače ASF parsuje ukázky vstupních datových proudů a pak je odešle do multiplexeru ASF k zápisu datových paketů ASF pro zvukový stream.
Tento kurz obsahuje následující kroky:
- požadavky
- terminologie
- 1. Nastavení Projectu
- 2. Deklarace pomocných funkcí
- 3. Otevření vstupního souboru ASF
- 4. Inicializace objektů pro vstupní soubor
- 5. Vytvoření zvukového profilu
- 6. Inicializace objektů pro výstupní soubor
- 7. Generování nových datových paketů ASF
- 8. Zápis objektů ASF do nového souboru
- 9 Napište funkci Entry-Point
- 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 proces analýzy objektu záhlaví ASF a datových paketů ASF existujícího souboru a generování komprimovaných ukázek datových proudů pomocí rozdělovače. Další informace naleznete v tématu Kurz: Čtení souboru ASF.
- 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. (Viz 2. Deklarace pomocných funkcí.)
Terminologie
V tomto kurzu se používají následující termíny:
- Zdrojový bajtový proud: Objekt bajtového proudu vystavuje rozhraní IMFByteStream , které obsahuje obsah vstupního souboru.
- Zdrojový ContentInfo objekt: ContentInfo objekt, zveřejňuje MMFASFContentInfo rozhraní, které představuje ASF Header Object vstupního souboru.
- Zvukový profil: Objekt profilu zpřístupňuje rozhraní IMFASFProfile, které obsahuje pouze zvukové streamy vstupního souboru.
- Ukázka streamu: Ukázka médií, poskytuje rozhraní IMFSample, vygenerované rozdělovačem, představuje mediální data vybraného streamu získaná ze vstupního souboru v komprimovaném stavu.
- Výstupní objekt ContentInfo: Objekt ContentInfo poskytuje rozhraní IMFASFContentInfo, které představuje ASF Header Object výstupního souboru.
- Datový bajtový proud: Objekt bajtového proudu zveřejňuje rozhraní IMFByteStream, které představuje celou část datového objektu ASF výstupního souboru.
- Datový paket: Ukázka média, vystavuje rozhraní IMFSample . Datový paket ASF, který generuje multiplexer, bude zapsán do datového bajtového proudu.
- Výstupní bajtový stream: Objekt bajtového streamu, zpřístupňuje IMFByteStream rozhraní, které obsahuje obsah výstupního souboru.
1. Nastavení projektu
Do zdrojového souboru zahrňte následující hlavičky:
#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:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
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.
Funkce AllocReadFromByteStream
čte data z bajtového proudu a alokuje novou mediální vyrovnávací paměť pro uložení dat. Další informace naleznete v tématu IMFByteStream::Read.
//-------------------------------------------------------------------
// AllocReadFromByteStream
//
// Reads data from a byte stream and returns a media buffer that
// contains the data.
//-------------------------------------------------------------------
HRESULT AllocReadFromByteStream(
IMFByteStream *pStream, // Pointer to the byte stream.
DWORD cbToRead, // Number of bytes to read.
IMFMediaBuffer **ppBuffer // Receives a pointer to the media buffer.
)
{
HRESULT hr = S_OK;
BYTE *pData = NULL;
DWORD cbRead = 0; // Actual amount of data read.
IMFMediaBuffer *pBuffer = NULL;
// Create the media buffer.
// This function allocates the memory for the buffer.
hr = MFCreateMemoryBuffer(cbToRead, &pBuffer);
// Get a pointer to the memory buffer.
if (SUCCEEDED(hr))
{
hr = pBuffer->Lock(&pData, NULL, NULL);
}
// Read the data from the byte stream.
if (SUCCEEDED(hr))
{
hr = pStream->Read(pData, cbToRead, &cbRead);
}
// Update the size of the valid data in the buffer.
if (SUCCEEDED(hr))
{
hr = pBuffer->SetCurrentLength(cbRead);
}
// Return the pointer to the caller.
if (SUCCEEDED(hr))
{
*ppBuffer = pBuffer;
(*ppBuffer)->AddRef();
}
if (pData)
{
pBuffer->Unlock();
}
SafeRelease(&pBuffer);
return hr;
}
Funkce WriteBufferToByteStream
zapisuje data z vyrovnávací paměti médií do bajtového datového proudu. Další informace naleznete v tématu IMFByteStream::Write.
//-------------------------------------------------------------------
// 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;
}
Funkce AppendToByteStream
připojí obsah jednoho bajtového datového proudu k jinému:
//-------------------------------------------------------------------
// 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;
}
3. Otevřete vstupní soubor ASF.
Otevřete vstupní soubor zavoláním funkce MFCreateFile. Metoda vrátí ukazatel na objekt bajtů stream, který obsahuje obsah souboru. Název souboru je určen uživatelem prostřednictvím argumentů příkazového řádku aplikace.
Následující příklad kódu přebírá název souboru a vrátí ukazatel na bajt stream objektu, který lze použít ke čtení souboru.
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
4. Inicializace objektů pro vstupní soubor
Dále vytvoříte a inicializujete zdrojový objekt ContentInfo a rozdělovač pro generování ukázek streamu.
Tento zdrojový bajtový stream vytvořený v kroku 2 se použije k analýze objektu záhlaví ASF a naplnění zdrojového objektu ContentInfo. Tento objekt se použije k inicializaci rozdělovače, aby se usnadnila analýza datových paketů ASF pro zvukový stream ve vstupním souboru. Také zjistíte délku datového objektu ASF ve vstupním souboru a offset k prvnímu paketu dat ASF vzhledem ke začátku souboru. Tyto atributy použije rozdělovač k vygenerování ukázek zvukového streamu.
Vytvoření a inicializace komponent ASF pro vstupní soubor:
- Zavolejte MFCreateASFContentInfo k vytvoření objektu ContentInfo. Tato funkce vrátí ukazatel na rozhraní MMFASFContentInfo.
- Volání IMFASFContentInfo::ParseHeader k analýze hlavičky ASF. Další informace o tomto kroku naleznete v tématu Čtení objektu záhlaví ASF existujícího souboru.
- Voláním MFCreateASFSplitter vytvořit objekt rozdělovače ASF. Tato funkce vrátí ukazatel na rozhraní IMFASFSplitter.
- Volání IMFASFSplitter::Initialize, s předáním ukazatele IMFASFContentInfo. Další informace o tomto kroku naleznete v tématu Vytvoření objektu rozdělovače ASF.
- Zavolejte IMFASFContentInfo::GeneratePresentationDescriptor, abyste získali popisovač prezentace pro soubor ASF.
- Získá hodnotu atributu MF_PD_ASF_DATA_START_OFFSET z popisovače prezentace. Tato hodnota je umístění datového objektu ASF v souboru jako posun bajtů od začátku souboru.
- Získá hodnotu atributu MF_PD_ASF_DATA_LENGTH z popisovače prezentace. Tato hodnota je celková velikost datového objektu ASF v bajtech. Další informace naleznete v tématu Získání informací z objektů záhlaví ASF.
Následující příklad kódu ukazuje funkci, která slučuje všechny kroky. Tato funkce přebírá ukazatel na zdrojový bajtový proud a vrací ukazatele na zdrojový ContentInfo objekt a rozdělovač. Obdrží také délku a posuny (offsety) datového objektu ASF.
//-------------------------------------------------------------------
// CreateSourceParsers
//
// Creates the ASF splitter and the ASF Content Info object for the
// source file.
//
// This function also calulates the offset and length of the ASF
// Data Object.
//-------------------------------------------------------------------
HRESULT CreateSourceParsers(
IMFByteStream *pSourceStream,
IMFASFContentInfo **ppSourceContentInfo,
IMFASFSplitter **ppSplitter,
UINT64 *pcbDataOffset,
UINT64 *pcbDataLength
)
{
const DWORD MIN_ASF_HEADER_SIZE = 30;
IMFMediaBuffer *pBuffer = NULL;
IMFPresentationDescriptor *pPD = NULL;
IMFASFContentInfo *pSourceContentInfo = NULL;
IMFASFSplitter *pSplitter = NULL;
QWORD cbHeader = 0;
/*------- Parse the ASF header. -------*/
// Create the ASF ContentInfo object.
HRESULT hr = MFCreateASFContentInfo(&pSourceContentInfo);
// Read the first 30 bytes to find the total header size.
if (SUCCEEDED(hr))
{
hr = AllocReadFromByteStream(
pSourceStream,
MIN_ASF_HEADER_SIZE,
&pBuffer
);
}
// Get the header size.
if (SUCCEEDED(hr))
{
hr = pSourceContentInfo->GetHeaderSize(pBuffer, &cbHeader);
}
// Release the buffer; we will reuse it.
SafeRelease(&pBuffer);
// Read the entire header into a buffer.
if (SUCCEEDED(hr))
{
hr = pSourceStream->SetCurrentPosition(0);
}
if (SUCCEEDED(hr))
{
hr = AllocReadFromByteStream(
pSourceStream,
(DWORD)cbHeader,
&pBuffer
);
}
// Parse the buffer and populate the header object.
if (SUCCEEDED(hr))
{
hr = pSourceContentInfo->ParseHeader(pBuffer, 0);
}
/*------- Initialize the ASF splitter. -------*/
// Create the splitter.
if (SUCCEEDED(hr))
{
hr = MFCreateASFSplitter(&pSplitter);
}
// initialize the splitter with the ContentInfo object.
if (SUCCEEDED(hr))
{
hr = pSplitter->Initialize(pSourceContentInfo);
}
/*------- Get the offset and size of the ASF Data Object. -------*/
// Generate the presentation descriptor.
if (SUCCEEDED(hr))
{
hr = pSourceContentInfo->GeneratePresentationDescriptor(&pPD);
}
// Get the offset to the start of the Data Object.
if (SUCCEEDED(hr))
{
hr = pPD->GetUINT64(MF_PD_ASF_DATA_START_OFFSET, pcbDataOffset);
}
// Get the length of the Data Object.
if (SUCCEEDED(hr))
{
hr = pPD->GetUINT64(MF_PD_ASF_DATA_LENGTH, pcbDataLength);
}
// Return the pointers to the caller.
if (SUCCEEDED(hr))
{
*ppSourceContentInfo = pSourceContentInfo;
(*ppSourceContentInfo)->AddRef();
*ppSplitter = pSplitter;
(*ppSplitter)->AddRef();
}
SafeRelease(&pPD);
SafeRelease(&pBuffer);
SafeRelease(&pSourceContentInfo);
SafeRelease(&pSplitter);
return S_OK;
}
5. Vytvoření zvukového profilu
Dále vytvoříte objekt profilu pro vstupní soubor získáním ze zdrojového objektu ContentInfo. Pak nakonfigurujete profil tak, aby obsahoval pouze zvukové streamy vstupního souboru. Provedete to tak, že vypíšete streamy a odeberete ze seznamu nezvukové streamy. Objekt zvukového profilu se použije později v tomto kurzu k inicializaci výstupního objektu ContentInfo.
Vytvoření zvukového profilu
- Získejte objekt profilu pro vstupní soubor ze zdrojového ContentInfo objektu voláním MMFASFContentInfo::GetProfile. Metoda vrátí ukazatel na objekt profilu, který obsahuje všechny datové proudy ve vstupním souboru. Další informace naleznete v Vytvoření profilu ASF.
- Odeberte všechny objekty vzájemného vyloučení z profilu. Tento krok je povinný, protože streamy bez zvuku budou z profilu odebrány, což by mohlo zneplatnit objekty vzájemného vyloučení.
- Ze profilu odeberte všechny streamy bez zvuku následujícím způsobem:
- Zavolejte IMFASFProfile::GetStreamCount pro získání počtu datových proudů.
- Ve smyčce volejte funkci IMFASFProfile::GetStream, aby získali každý datový proud podle indexu.
- Zavolejte IMFASFStreamConfig::GetStreamType, abyste získali typ datového proudu.
- Pro jiné než zvukové streamy zavolejte IMFASFProfile::RemoveStream k odebrání datového proudu.
- Uložte číslo prvního zvukového streamu. Tato možnost bude vybrána na rozdělovači, aby se vygenerovaly ukázky datových proudů. Pokud je číslo datového proudu nula, volající může předpokládat, že v souboru nejsou žádné zvukové proudy.
Následující kód zahrnuje tyto kroky:
//-------------------------------------------------------------------
// GetAudioProfile
//
// Gets the ASF profile from the source file and removes any video
// streams from the profile.
//-------------------------------------------------------------------
HRESULT GetAudioProfile(
IMFASFContentInfo *pSourceContentInfo,
IMFASFProfile **ppAudioProfile,
WORD *pwSelectStreamNumber
)
{
IMFASFStreamConfig *pStream = NULL;
IMFASFProfile *pProfile = NULL;
DWORD dwTotalStreams = 0;
WORD wStreamNumber = 0;
GUID guidMajorType = GUID_NULL;
// Get the profile object from the source ContentInfo object.
HRESULT hr = pSourceContentInfo->GetProfile(&pProfile);
// Remove mutexes from the profile
if (SUCCEEDED(hr))
{
hr = RemoveMutexes(pProfile);
}
// Get the total number of streams on the profile.
if (SUCCEEDED(hr))
{
hr = pProfile->GetStreamCount(&dwTotalStreams);
}
// Enumerate the streams and remove the non-audio streams.
if (SUCCEEDED(hr))
{
for (DWORD index = 0; index < dwTotalStreams; )
{
hr = pProfile->GetStream(index, &wStreamNumber, &pStream);
if (FAILED(hr)) { break; }
hr = pStream->GetStreamType(&guidMajorType);
SafeRelease(&pStream);
if (FAILED(hr)) { break; }
if (guidMajorType != MFMediaType_Audio)
{
hr = pProfile->RemoveStream(wStreamNumber);
if (FAILED(hr)) { break; }
index = 0;
dwTotalStreams--;
}
else
{
// Store the first audio stream number.
// This will be selected on the splitter.
if (*pwSelectStreamNumber == 0)
{
*pwSelectStreamNumber = wStreamNumber;
}
index++;
}
}
}
if (SUCCEEDED(hr))
{
*ppAudioProfile = pProfile;
(*ppAudioProfile)->AddRef();
}
SafeRelease(&pStream);
SafeRelease(&pProfile);
return S_OK;
}
Funkce RemoveMutexes
odebere z profilu všechny objekty vzájemného vyloučení:
HRESULT RemoveMutexes(IMFASFProfile *pProfile)
{
DWORD cMutex = 0;
HRESULT hr = pProfile->GetMutualExclusionCount(&cMutex);
if (SUCCEEDED(hr))
{
for (DWORD i = 0; i < cMutex; i++)
{
hr = pProfile->RemoveMutualExclusion(0);
if (FAILED(hr))
{
break;
}
}
}
return hr;
}
6. Inicializace objektů pro výstupní soubor
Dále vytvoříte výstupní objekt ContentInfo a multiplexer pro generování datových paketů pro výstupní soubor.
Zvukový profil vytvořený v kroku 4 se použije k naplnění výstupního objektu ContentInfo. Tento objekt obsahuje informace, jako jsou globální atributy souboru a vlastnosti datového proudu. Výstupní objekt ContentInfo se použije k inicializaci multiplexeru, který vygeneruje datové pakety pro výstupní soubor. Po vygenerování datových paketů musí být objekt ContentInfo aktualizován tak, aby odrážel nové hodnoty.
Vytvoření a inicializace komponent ASF pro výstupní soubor
- Vytvořte prázdný objekt ContentInfo voláním MFCreateASFContentInfo a naplněním informací ze zvukového profilu vytvořeného v kroku 3 voláním MMFASFContentInfo::SetProfile. Další informace najdete v tématu Inicializace objektu ContentInfo nového souboru ASF.
- Vytvořte a inicializujte multiplexerový objekt pomocí výstupního objektu ContentInfo. Další informace naleznete v tématu Vytvoření objektu Multiplexer.
Následující příklad kódu ukazuje funkci, která konsoliduje kroky. Tato funkce vezme ukazatel na objekt profilu a vrátí ukazatele na výstupní ContentInfo objekt a multiplexer.
//-------------------------------------------------------------------
// CreateOutputGenerators
//
// Creates the ASF mux and the ASF Content Info object for the
// output file.
//-------------------------------------------------------------------
HRESULT CreateOutputGenerators(
IMFASFProfile *pProfile,
IMFASFContentInfo **ppContentInfo,
IMFASFMultiplexer **ppMux
)
{
IMFASFContentInfo *pContentInfo = NULL;
IMFASFMultiplexer *pMux = NULL;
// Use the ASF profile to create the ContentInfo object.
HRESULT hr = MFCreateASFContentInfo(&pContentInfo);
if (SUCCEEDED(hr))
{
hr = pContentInfo->SetProfile(pProfile);
}
// Create the ASF Multiplexer object.
if (SUCCEEDED(hr))
{
hr = MFCreateASFMultiplexer(&pMux);
}
// Initialize it using the new ContentInfo object.
if (SUCCEEDED(hr))
{
hr = pMux->Initialize(pContentInfo);
}
// Return the pointers to the caller.
if (SUCCEEDED(hr))
{
*ppContentInfo = pContentInfo;
(*ppContentInfo)->AddRef();
*ppMux = pMux;
(*ppMux)->AddRef();
}
SafeRelease(&pContentInfo);
SafeRelease(&pMux);
return hr;
}
7. Generování nových datových paketů ASF
Dále vygenerujete ukázky zvukového streamu ze zdrojového bajtového datového proudu pomocí rozdělovače a odešlete je do multiplexeru, aby se vytvořily datové pakety ASF. Tyto datové pakety budou představovat konečný datový objekt ASF pro nový soubor.
Generování ukázek zvukového streamu
- Vyberte první zvukový stream na rozdělovači voláním MMFASFSplitter::SelectStreams.
- Čtení bloků s pevnou velikostí mediálních dat ze zdrojového bajtového proudu do mediální vyrovnávací paměti.
- Shromážděte ukázky datových proudů jako ukázky médií z rozdělovače opakovaným voláním IMFASFSplitter::GetNextSample, dokud bude v parametru pdwStatusFlags přijímán příznak ASF_STATUSFLAGS_INCOMPLETE. Další informace naleznete v tématu Generování ukázek pro datové pakety ASF v generování ukázek datových proudů z existujícího datového objektu ASF.
- Pro každý mediální vzorek zavolejte IMFASFMultiplexer::ProcessSample, aby odeslal mediální vzorek do multiplexeru. Multiplexer generuje datové pakety pro datový objekt ASF.
- Zapište datový paket vygenerovaný multiplexerem do datového bajtového proudu.
- Po vygenerování všech datových paketů zavolejte IMFASFMultiplexer::End, aby byl výstupní objekt ContentInfo aktualizován informacemi shromážděnými během generování datových paketů ASF.
Následující ukázkový kód vygeneruje ukázky datových proudů z rozdělovače ASF a odešle je do multiplexeru. Multiplexer generuje datové pakety ASF a zapisuje je do datového proudu.
//-------------------------------------------------------------------
// GenerateASFDataObject
//
// Creates a byte stream that contains the ASF Data Object for the
// output file.
//-------------------------------------------------------------------
HRESULT GenerateASFDataObject(
IMFByteStream *pSourceStream,
IMFASFSplitter *pSplitter,
IMFASFMultiplexer *pMux,
UINT64 cbDataOffset,
UINT64 cbDataLength,
IMFByteStream **ppDataStream
)
{
IMFMediaBuffer *pBuffer = NULL;
IMFByteStream *pDataStream = NULL;
const DWORD READ_SIZE = 1024 * 4;
// Flush the splitter to remove any pending samples.
HRESULT hr = pSplitter->Flush();
if (SUCCEEDED(hr))
{
hr = MFCreateTempFile(
MF_ACCESSMODE_READWRITE,
MF_OPENMODE_DELETE_IF_EXIST,
MF_FILEFLAGS_NONE,
&pDataStream
);
}
if (SUCCEEDED(hr))
{
hr = pSourceStream->SetCurrentPosition(cbDataOffset);
}
if (SUCCEEDED(hr))
{
while (cbDataLength > 0)
{
DWORD cbRead = min(READ_SIZE, (DWORD)cbDataLength);
hr = AllocReadFromByteStream(
pSourceStream,
cbRead,
&pBuffer
);
if (FAILED(hr))
{
break;
}
cbDataLength -= cbRead;
// Push data on the splitter.
hr = pSplitter->ParseData(pBuffer, 0, 0);
if (FAILED(hr))
{
break;
}
// Get ASF packets from the splitter and feed them to the mux.
hr = GetPacketsFromSplitter(pSplitter, pMux, pDataStream);
if (FAILED(hr))
{
break;
}
SafeRelease(&pBuffer);
}
}
// Flush the mux and generate any remaining samples.
if (SUCCEEDED(hr))
{
hr = pMux->Flush();
}
if (SUCCEEDED(hr))
{
hr = GenerateASFDataPackets(pMux, pDataStream);
}
// Return the pointer to the caller.
if (SUCCEEDED(hr))
{
*ppDataStream = pDataStream;
(*ppDataStream)->AddRef();
}
SafeRelease(&pBuffer);
SafeRelease(&pDataStream);
return hr;
}
Pokud chcete získat pakety z rozdělovače ASF, předchozí kód volá funkci GetPacketsFromSplitter
, jak je znázorněno tady:
//-------------------------------------------------------------------
// GetPacketsFromSplitter
//
// Gets samples from the ASF splitter.
//
// This function is called after calling IMFASFSplitter::ParseData.
//-------------------------------------------------------------------
HRESULT GetPacketsFromSplitter(
IMFASFSplitter *pSplitter,
IMFASFMultiplexer *pMux,
IMFByteStream *pDataStream
)
{
HRESULT hr = S_OK;
DWORD dwStatus = ASF_STATUSFLAGS_INCOMPLETE;
WORD wStreamNum = 0;
IMFSample *pSample = NULL;
while (dwStatus & ASF_STATUSFLAGS_INCOMPLETE)
{
hr = pSplitter->GetNextSample(&dwStatus, &wStreamNum, &pSample);
if (FAILED(hr))
{
break;
}
if (pSample)
{
//Send to the multiplexer to convert it into ASF format
hr = pMux->ProcessSample(wStreamNum, pSample, 0);
if (FAILED(hr))
{
break;
}
hr = GenerateASFDataPackets(pMux, pDataStream);
if (FAILED(hr))
{
break;
}
}
SafeRelease(&pSample);
}
SafeRelease(&pSample);
return hr;
}
Funkce GenerateDataPackets
získá datové pakety z multiplexeru. Další informace naleznete v tématu Získávání datových paketů ASF.
//-------------------------------------------------------------------
// GenerateASFDataPackets
//
// Gets data packets from the mux. This function is called after
// calling IMFASFMultiplexer::ProcessSample.
//-------------------------------------------------------------------
HRESULT GenerateASFDataPackets(
IMFASFMultiplexer *pMux,
IMFByteStream *pDataStream
)
{
HRESULT hr = S_OK;
IMFSample *pOutputSample = NULL;
IMFMediaBuffer *pDataPacketBuffer = NULL;
DWORD dwMuxStatus = ASF_STATUSFLAGS_INCOMPLETE;
while (dwMuxStatus & ASF_STATUSFLAGS_INCOMPLETE)
{
hr = pMux->GetNextPacket(&dwMuxStatus, &pOutputSample);
if (FAILED(hr))
{
break;
}
if (pOutputSample)
{
//Convert to contiguous buffer
hr = pOutputSample->ConvertToContiguousBuffer(&pDataPacketBuffer);
if (FAILED(hr))
{
break;
}
//Write buffer to byte stream
hr = WriteBufferToByteStream(pDataStream, pDataPacketBuffer, NULL);
if (FAILED(hr))
{
break;
}
}
SafeRelease(&pDataPacketBuffer);
SafeRelease(&pOutputSample);
}
SafeRelease(&pOutputSample);
SafeRelease(&pDataPacketBuffer);
return hr;
}
8. Zápis objektů ASF do nového souboru
Dále napíšete obsah výstupního ContentInfo objektu do vyrovnávací paměti média 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 zapište výstupní soubor tak, že nejprve zapíšete objekt Header do výstupního bajtového datového proudu vytvořeného v kroku 2 voláním pomocné funkce WriteBufferToByteStream. Postupujte podle objektu Header s datovým objektem obsaženým v datovém toku bajtů. Ukázkový kód ukazuje funkci, která přenáší obsah proudu datových bajtů do výstupního proudu bajtů.
//-------------------------------------------------------------------
// WriteASFFile
//
// Writes the complete ASF file.
//-------------------------------------------------------------------
HRESULT WriteASFFile(
IMFASFContentInfo *pContentInfo, // ASF Content Info for the output file.
IMFByteStream *pDataStream, // Data stream.
PCWSTR pszFile // Output file name.
)
{
IMFMediaBuffer *pHeaderBuffer = NULL;
IMFByteStream *pWmaStream = NULL;
DWORD cbHeaderSize = 0;
DWORD cbWritten = 0;
// Create output file.
HRESULT hr = MFCreateFile(
MF_ACCESSMODE_WRITE,
MF_OPENMODE_DELETE_IF_EXIST,
MF_FILEFLAGS_NONE,
pszFile,
&pWmaStream
);
// Get the size of the ASF Header Object.
if (SUCCEEDED(hr))
{
hr = pContentInfo->GenerateHeader(NULL, &cbHeaderSize);
}
// Create a media buffer.
if (SUCCEEDED(hr))
{
hr = MFCreateMemoryBuffer(cbHeaderSize, &pHeaderBuffer);
}
// Populate the media buffer with the ASF Header Object.
if (SUCCEEDED(hr))
{
hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
}
// Write the header contents to the byte stream for the output file.
if (SUCCEEDED(hr))
{
hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
}
if (SUCCEEDED(hr))
{
hr = pDataStream->SetCurrentPosition(0);
}
// Append the data stream to the file.
if (SUCCEEDED(hr))
{
hr = AppendToByteStream(pDataStream, pWmaStream);
}
SafeRelease(&pHeaderBuffer);
SafeRelease(&pWmaStream);
return hr;
}
9 Napiš 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[])
{
if (argc != 3)
{
wprintf_s(L"Usage: %s input.wmv, %s output.wma\n");
return 0;
}
HRESULT hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
wprintf_s(L"MFStartup failed: 0x%X\n", hr);
return 0;
}
PCWSTR pszInputFile = argv[1];
PCWSTR pszOutputFile = argv[2];
IMFByteStream *pSourceStream = NULL;
IMFASFContentInfo *pSourceContentInfo = NULL;
IMFASFProfile *pAudioProfile = NULL;
IMFASFContentInfo *pOutputContentInfo = NULL;
IMFByteStream *pDataStream = NULL;
IMFASFSplitter *pSplitter = NULL;
IMFASFMultiplexer *pMux = NULL;
UINT64 cbDataOffset = 0;
UINT64 cbDataLength = 0;
WORD wSelectStreamNumber = 0;
// Open the input file.
hr = OpenFile(pszInputFile, &pSourceStream);
// Initialize the objects that will parse the source file.
if (SUCCEEDED(hr))
{
hr = CreateSourceParsers(
pSourceStream,
&pSourceContentInfo, // ASF Header for the source file.
&pSplitter, // Generates audio samples.
&cbDataOffset, // Offset to the first data packet.
&cbDataLength // Length of the ASF Data Object.
);
}
// Create a profile object for the audio streams in the source file.
if (SUCCEEDED(hr))
{
hr = GetAudioProfile(
pSourceContentInfo,
&pAudioProfile, // ASF profile for the audio stream.
&wSelectStreamNumber // Stream number of the first audio stream.
);
}
// Initialize the objects that will generate the output data.
if (SUCCEEDED(hr))
{
hr = CreateOutputGenerators(
pAudioProfile,
&pOutputContentInfo, // ASF Header for the output file.
&pMux // Generates ASF data packets.
);
}
// Set up the splitter to generate samples for the first
// audio stream in the source media.
if (SUCCEEDED(hr))
{
hr = pSplitter->SelectStreams(&wSelectStreamNumber, 1);
}
// Generate ASF Data Packets and store them in a byte stream.
if (SUCCEEDED(hr))
{
hr = GenerateASFDataObject(
pSourceStream,
pSplitter,
pMux,
cbDataOffset,
cbDataLength,
&pDataStream // Byte stream for the ASF data packets.
);
}
// Update the header with new information if any.
if (SUCCEEDED(hr))
{
hr = pMux->End(pOutputContentInfo);
}
//Write the ASF objects to the output file
if (SUCCEEDED(hr))
{
hr = WriteASFFile(pOutputContentInfo, pDataStream, pszOutputFile);
}
// Clean up.
SafeRelease(&pMux);
SafeRelease(&pSplitter);
SafeRelease(&pDataStream);
SafeRelease(&pOutputContentInfo);
SafeRelease(&pAudioProfile);
SafeRelease(&pSourceContentInfo);
SafeRelease(&pSourceStream);
MFShutdown();
if (FAILED(hr))
{
wprintf_s(L"Could not create the audio file: 0x%X\n", hr);
}
return 0;
}
Související témata