Självstudie: Att kopiera ASF (Advanced Systems Format)-strömmar med hjälp av WMContainer-objekt
Ett sätt att skapa en ASF-fil är att kopiera ASF-strömmar från en befintlig fil. För att göra detta kan du hämta mediedata från källfilen och skriva till utdatafilen. Om källfilen är en ASF-fil kan du kopiera strömexempel utan att dekomprimera och komprimera dem igen.
Den här självstudien visar det här scenariot genom att extrahera den första ljudströmmen från en ASF-ljudvideofil (.wmv) och kopiera den till en ny ASF-ljudfil (.wma). I den här självstudien skapar du ett konsolprogram som tar in- och utdatafilnamnen som argument. Programmet använder ASF Splitter för att parsa indataströmsexemplen och skickar dem sedan till ASF Multiplexer för att skriva ASF-datapaketen för ljudströmmen.
Den här självstudien innehåller följande steg:
- Förkunskapskrav
- Terminologi
- 1. Konfigurera projekt
- 2. Deklarera hjälpfunktioner
- 3. Öppna indata-ASF-filen
- 4. Initiera objekt för indatafilen
- 5. Skapa en ljudprofil
- 6. Initiera objekt för utdatafilen
- 7. Generera nya ASF-datapaket
- 8. Skriv ASF-objekten i den nya filen
- 9 Skriv Entry-Point-funktionen
- Relaterade ämnen
Förutsättningar
Den här självstudien förutsätter följande:
- Du är bekant med strukturen för en ASF-fil och de komponenter som tillhandahålls av Media Foundation för att arbeta med ASF-objekt. Dessa komponenter inkluderar ContentInfo, splitter, multiplexer och profilobjekt. Mer information finns i WMContainer ASF-Komponenter.
- Du är bekant med processen att parsa ASF-huvudobjektet och ASF-datapaketen för en befintlig fil och generera komprimerade strömexempel med hjälp av delningsfunktionen. Mer information finns i Självstudie: Läsa en ASF-fil.
- Du är bekant med mediebuffertar och byteströmmar: Mer specifikt filåtgärder som använder en byteström och hur du skriver innehållet i en mediebuffert till en byteström. (Se 2. Deklarera hjälpfunktioner.)
Terminologi
I den här självstudien används följande termer:
- Källbyteström: Byteströmobjekt, exponerar IMFByteStream--gränssnitt, som innehåller innehållet i indatafilen.
- ContentInfo-källobjekt: ContentInfo-objekt, exponerar IMFASFContentInfo--gränssnitt, som representerar ASF-huvudobjektet för indatafilen.
- Ljudprofil: Profilobjekt, exponerar IMFASFProfile gränssnitt, som endast innehåller ljudströmmar av indatafilen.
- Strömexempel: Medieexempel som exponerar IMFSample--gränssnittet. Detta genereras av splittern och representerar den valda strömmens mediedata som hämtas från indatafilen i komprimerat tillstånd.
- Output ContentInfo-objekt: ContentInfo-objekt, exponerar IMFASFContentInfo--gränssnitt, som representerar ASF-huvudobjektet för utdatafilen.
- Databyteström: Byteströmobjekt, exponerar IMFByteStream--gränssnitt, som representerar hela ASF-dataobjektdelen i utdatafilen.
- Datapaket: Mediaprov, exponerar IMFSample-gränssnittet, som representerar ett ASF-datapaket, som genereras av multiplexern och skrivs till databytströmmen.
- Utdatabyteström: Byteströmobjekt, exponerar IMFByteStream--gränssnitt, som innehåller innehållet i utdatafilen.
1. Konfigurera projektet
Inkludera följande rubriker i källfilen:
#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
Länka till följande biblioteksfiler:
- mfplat.lib
- mf.lib
- mfuuid.lib
Deklarera funktionen SafeRelease:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
2. Deklarera hjälpfunktioner
I den här självstudien används följande hjälpfunktioner för att läsa och skriva från en byteström.
Funktionen AllocReadFromByteStream
läser data från en byteström och allokerar en ny mediebuffert för att lagra data. Mer information finns i IMFByteStream::Läs.
//-------------------------------------------------------------------
// 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;
}
Funktionen WriteBufferToByteStream
skriver data från en mediebuffert till en byteström. Mer information finns i 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;
}
Funktionen AppendToByteStream
lägger till innehållet i en byteström till en annan:
//-------------------------------------------------------------------
// 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. Öppna asf-indatafilen
Öppna indatafilen genom att anropa funktionen MFCreateFile. Metoden returnerar en pekare till byteströmsobjektet som innehåller innehållet i filen. Filnamnet anges av användaren via kommandoradsargument för programmet.
Följande exempelkod tar ett filnamn och returnerar en pekare till ett byteströmobjekt som kan användas för att läsa filen.
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
4. Initiera objekt för indatafilen
Därefter skapar och initierar du ContentInfo-källobjektet och splittern för att generera strömprover.
Den här källbyteströmmen som skapades i steg 2 används för att parsa ASF-huvudobjektet och fylla i innehållsinfo-källobjektet. Det här objektet används för att initiera splittern för att underlätta parsningen av ASF-datapaketen för ljudströmmen i indatafilen. Du kommer också att hämta längden på ASF-dataobjektet i inputfilen och offseten till det första ASF-datapaketet i förhållande till filens början. Dessa attribut används av splittern för att generera ljudströmsexempel.
Så här skapar och initierar du ASF-komponenter för indatafilen:
- Anropa MFCreateASFContentInfo för att skapa ContentInfo-objektet. Den här funktionen returnerar en pekare till gränssnittet IMFASFContentInfo.
- Anropa IMFASFContentInfo::ParseHeader för att analysera ASF-huvudet. Mer information om det här steget finns i Läsa ASF-huvudobjektet för en befintlig fil.
- Anropa MFCreateASFSplitter för att skapa ASF-splitterobjektet. Den här funktionen returnerar en pekare till gränssnittet IMFASFSplitter.
- Anropa IMFASFSplitter::Initialize, genom att överföra IMFASFContentInfopekare. Mer information om det här steget finns i Skapande av ASF Splitter-objektet.
- Anropa IMFASFContentInfo::GeneratePresentationDescriptor för att hämta en presentationsbeskrivning för ASF-filen.
- Hämta värdet för attributet MF_PD_ASF_DATA_START_OFFSET från presentationsbeskrivningen. Det här värdet är platsen för ASF-dataobjektet i filen, som en byteförskjutning från början av filen.
- Hämta värdet för attributet MF_PD_ASF_DATA_LENGTH från presentationsbeskrivningen. Det här värdet är den totala storleken på ASF-dataobjektet i byte. Mer information finns i Hämta information från ASF-huvudobjekt.
Följande exempelkod visar en funktion som konsoliderar alla steg. Den här funktionen tar en pekare till källbyteströmmen och returnerar pekare till källobjektet ContentInfo och splittern. Dessutom tar den emot längden och förskjutningar till ASF-dataobjektet.
//-------------------------------------------------------------------
// 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. Skapa en ljudprofil
Därefter skapar du ett profilobjekt för indatafilen genom att hämta det från contentinfo-källobjektet. Sedan konfigurerar du profilen så att den endast innehåller ljudströmmarna i indatafilen. Det gör du genom att räkna upp strömmarna och ta bort icke-ljudströmmar från profilen. Ljudprofilobjektet används senare i den här handledningen för att initiera ContentInfo-objektet.
Skapa en ljudprofil
- Hämta profilobjektet för indatafilen från contentinfo-källobjektet genom att anropa IMFASFContentInfo::GetProfile. Metoden returnerar en pekare till ett profilobjekt som innehåller alla strömmar i indatafilen. Mer information finns i Skapa en ASF-profil.
- Ta bort alla objekt för ömsesidig uteslutning från profilen. Det här steget krävs eftersom icke-ljudströmmar tas bort från profilen, vilket kan göra objekten för ömsesidig uteslutning ogiltiga.
- Ta bort alla icke-ljudströmmar från profilen enligt följande:
- Ring IMFASFProfile::GetStreamCount för att hämta antalet strömmar.
- I en loop anropar du IMFASFProfile::GetStream för att hämta varje ström efter index.
- Anropa IMFASFStreamConfig::GetStreamType för att hämta strömtypen.
- För icke-ljudströmmar anropar du IMFASFProfile::RemoveStream för att ta bort strömmen.
- Lagra strömnumret för den första ljudströmmen. Detta väljs på splittern för att generera strömexempel. Om strömnumret är noll kan anroparen anta att det inte fanns någon ljudströmfil.
Följ dessa steg:
//-------------------------------------------------------------------
// 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;
}
Funktionen RemoveMutexes
tar bort alla objekt för ömsesidig uteslutning från profilen:
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. Initiera objekt för utdatafilen
Därefter skapar du ContentInfo-objektet för utdata och multiplexern för att generera datapaket för utdatafilen.
Ljudprofilen som skapades i steg 4 används för att fylla i ContentInfo-utdataobjektet. Det här objektet innehåller information som globala filattribut och strömegenskaper. ContentInfo-utdataobjektet används för att initiera multiplexern som genererar datapaket för utdatafilen. När datapaketen har genererats måste ContentInfo-objektet uppdateras för att återspegla de nya värdena.
Skapa och initiera ASF-komponenter för utdatafilen
- Skapa ett tomt ContentInfo-objekt genom att anropa MFCreateASFContentInfo och fylla i det med information från ljudprofilen som skapades i steg 3 genom att anropa IMFASFContentInfo::SetProfile. För mer information, se Initiera ContentInfo-objektet för en ny ASF-fil.
- Skapa och initiera multiplexerobjektet med hjälp av utdataobjektet ContentInfo. Mer information finns i Skapa Multiplexer-objektet.
Följande exempelkod visar en funktion som konsoliderar stegen. Den här funktionen tar en pekare till ett profilobjekt och returnerar pekare till ContentInfo-objektet och multiplexern.
//-------------------------------------------------------------------
// 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. Generera nya ASF-datapaket
Därefter genererar du ljudströmsexempel från källbyteströmmen med hjälp av splittern och skickar dem till multiplexern för att skapa ASF-datapaket. Dessa datapaket utgör det sista ASF-dataobjektet för den nya filen.
Så här genererar du ljudströmsexempel
- Välj den första ljudströmmen på splittern genom att anropa IMFASFSplitter::SelectStreams.
- Läs mediedatablock med fast storlek från källbyteströmmen till en mediebuffert.
- Samla in strömexemplen som medieexempel från splittern genom att anropa IMFASFSplitter::GetNextSample i en loop så länge den tar emot flaggan ASF_STATUSFLAGS_INCOMPLETE i parametern pdwStatusFlags. Mer information finns i Generera exempel för ASF-datapaket" i Generera streamexempel från ett befintligt ASF-dataobjekt.
- För varje medieexempel anropar du IMFASFMultiplexer::P rocessSample för att skicka medieexemplet till multiplexern. Multiplexern genererar datapaketen för ASF-dataobjektet.
- Skriv datapaketet som genereras av multiplexern till databyteströmmen.
- När alla datapaket har genererats anropar du IMFASFMultiplexer::End för att uppdatera ContentInfo-utdataobjektet med information som samlats in under genereringen av ASF-datapaket.
Följande exempelkod genererar dataströmexempel från ASF-splittern och skickar dem till multiplexern. Multiplexern genererar ASF-datapaket och skriver det till en dataström.
//-------------------------------------------------------------------
// 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;
}
För att hämta paket från ASF-splittern anropar den tidigare koden funktionen GetPacketsFromSplitter
, som visas här:
//-------------------------------------------------------------------
// 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;
}
Funktionen GenerateDataPackets
hämtar datapaket från multiplexer. Mer information finns i Hämta ASF-datapaket.
//-------------------------------------------------------------------
// 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. Skriv ASF-objekten i den nya filen
Därefter skriver du innehållet i ContentInfo-objektet för utdata till en mediebuffert genom att anropa IMFASFContentInfo::GenerateHeader. Den här metoden konverterar data som lagras i ContentInfo-objektet till binära data i ASF-rubrikobjektformat. Mer information finns i Att generera ett nytt ASF-huvudobjekt.
När det nya ASF-huvudobjektet har genererats skriver du utdatafilen genom att först skriva rubrikobjektet till utdatabyteströmmen som skapades i steg 2 genom att anropa hjälpfunktionen WriteBufferToByteStream. Följ huvudobjektet med dataobjektet som finns i databyteströmmen. Exempelkoden visar en funktion som överför innehållet i databyteströmmen till utdatabyteströmmen.
//-------------------------------------------------------------------
// 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 Skriv funktionen Entry-Point
Nu kan du sätta ihop föregående steg i ett komplett program. Innan du använder något av Media Foundation-objekten initierar du Media Foundation-plattformen genom att anropa MFStartup. När du är klar anropar du MFShutdown. Mer information finns i Initializing Media Foundation.
Följande kod visar det fullständiga konsolprogrammet. Kommandoradsargumentet anger namnet på filen som ska konverteras och namnet på den nya ljudfilen.
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;
}
Relaterade ämnen