從現有的 ASF 資料對象產生資料流範例
ASF 分割器 物件是 WMContainer 層元件,可剖析進階系統格式 (ASF) 檔案的 ASF 資料物件。
將數據封包傳遞至分割器之前,應用程式必須先初始化、設定和選取分割器上的數據流,才能準備剖析程式。 如需詳細資訊,請參閱 建立 ASF 分割器物件 和 設定 ASF 分割器物件。
剖析 ASF 資料物件所需的方法如下:
- IMFASFSplitter::ParseData,將包含數據封包的緩衝區推送至分割器,以啟動解析過程。
- IMFASFSplitter::GetNextSample 收集從緩衝區傳遞至分割器後產生的流樣本。
尋找數據位移
開始剖析程式之前,應用程式必須在 ASF 檔案中找出資料物件。 有兩種方式可從檔案開頭取得數據物件的位移:
在初始化 ContentInfo 物件之前,您可以呼叫 IMFASFContentInfo::GetHeaderSize 方法。 此方法需要包含 ASF 標頭前 30 個字節的緩衝區。 它會傳回整個標頭的大小,指出第一個數據封包的位移。 這個值也包含 50 個字節的數據對象標頭大小。
初始化 ContentInfo 物件之後,您可以呼叫 IMFASFContentInfo::GeneratePresentationDescriptor來取得簡報描述元,然後查詢 MF_PD_ASF_DATA_START_OFFSET 屬性的簡報描述元。 這個屬性的值是標頭大小。
注意
簡報描述元上的 MF_PD_ASF_DATA_LENGTH 屬性會指定 ASF 資料對象的長度。
在這兩種情況下,傳回的值是 Header 物件的大小,加上 Data Object 的標頭區段大小。 因此,產生的值是 ASF 資料物件中數據封包開頭的位移。 當您開始將數據傳送至分割器時,數據必須從 ASF 檔案開頭的這個位移開始。
位移值會當做參數傳遞至啟動剖析程式的 ParseData。
數據物件分成數據封包。 每個數據封包都包含一個數據封包標頭,可提供封包剖析資訊和承載數據,也就是實際的數位媒體數據。 在搜尋案例中,應用程式可能會希望分割器開始在特定數據封包進行剖析。 若要這樣做,您可以使用 ASF 索引器來擷取位移。 索引器會傳回從封包界限開始的位移值。 如果您未使用索引器,請確定位移會在數據封包標頭的開頭開始。 如果傳遞無效的位移至分割器,例如值未指向封包界限,ParseHeader 和 GetNextSample 呼叫成功,但 GetNextSample 不會擷取任何範例,而且 NULL 會在 pSample 參數中接收。
如果分割器設定為反向剖析,則分割器一律會在傳遞至 parseData的媒體緩衝區結尾開始剖析。 因此,若要在呼叫 ParseData中反向剖析,請在 cbLength 參數中傳遞位移,以指定數據的長度,並將 cbBufferOffset cbBufferOffset 設定為零。
產生 ASF 數據封包的範例
應用程式會藉由將數據封包傳遞至分割器來啟動剖析程式。 分割器的輸入是一連串的媒體緩衝區,其中包含 Data Object 的整體或片段。 分割器輸出是包含封包數據的一系列媒體範例。
若要將輸入數據傳遞至分割器,請建立媒體緩衝區,並將 ASF 檔案的 Data Object 區段的數據填滿。 (如需媒體緩衝區的詳細資訊,請參閱 媒體緩衝區。然後,將媒體緩衝區傳遞至 IMFASFSplitter::P arseData 方法。 您也可以指定:
- 緩衝區中分割器應該開始剖析的位置偏移量。 如果位移為零,剖析會從緩衝區開頭開始。 如需設定數據偏移的相關信息,請參閱本主題中的「尋找數據偏移」一節。
- 要剖析的數據量。 如果此值為零,分割器會剖析直到到達緩衝區結尾為止,如 IMFMediaBuffer::GetCurrentLength方法所指定。
分割器會藉由參考媒體緩衝區中的數據來產生媒體樣本。 用戶端可以在迴圈中呼叫 IMFASFSplitter::GetNextSample,以擷取輸出範例,直到沒有其他數據可剖析為止。 如果 GetNextSample 傳回 pdwStatusFlags 參數中的ASF_STATUSFLAGS_INCOMPLETE旗標,則表示有更多要擷取的範例,而且應用程式可以再次呼叫 GetNextSample。 否則,請呼叫 ParseData,以將更多數據傳遞至分割器。 針對產生的範例,分割器會設定下列資訊:
- 分隔器會在它產生的所有範例上設定時間戳。 範例時間代表簡報時間,不包含預先註冊時間。 應用程式可以 呼叫 imfSample::GetSampleTime,以 100 奈秒為單位取得簡報時間。
- 如果在樣本產生期間發生中斷,分割器會在不連續之後的第一個樣本上設定 MFSampleExtension_Discontinuity 屬性。 中斷通常是因為網路連線上的封包中斷、檔案數據損毀或分割器從一個來源數據流切換到另一個來源數據流所造成。
- 在影片中,分割器會檢查片段是否包含關鍵影格。 如果這樣做,分割器會在範例上設定 MFSampleExtension_CleanPoint 屬性。
如果分割器正在剖析從媒體伺服器接收的數據封包,封包長度可能是可變的。 在此情況下,客戶端必須針對每個封包呼叫 ParseData,並在傳送至分割器的每個緩衝區上設定 MFASFSPLITTER_PACKET_BOUNDARY 屬性。 這個屬性會指示分隔器媒體緩衝區是否包含 ASF 封包的開頭。 如果緩衝區包含新封包的開頭,請將 屬性設定為 TRUE。 如果緩衝區包含上一個封包的接續,請將 屬性設定為 FALSE。 緩衝區無法跨越多個封包。
將新的媒體緩衝區傳遞至分割器之前,應用程式必須呼叫 IMFASFSplitter::Flush。 這個方法會重設分隔器,並清除任何等待完成的部分幀。 這在尋找位移位於不同位置的案例中很有用。
例
下列程式代碼範例示範如何剖析數據封包。 此範例會從數據物件開頭剖析至數據流結尾,並顯示包含主要畫面格之範例的相關信息。 如需使用此程式碼的完整範例,請參閱 教學課程:讀取 ASF 檔案。
// Parse the video stream and display information about the video samples.
//
// The current read position of the byte stream must be at the start of the ASF
// Data Object.
HRESULT DisplayKeyFrames(IMFByteStream *pStream, IMFASFSplitter *pSplitter)
{
const DWORD cbReadSize = 2048; // Read size (arbitrary value)
IMFMediaBuffer *pBuffer = NULL;
IMFSample *pSample = NULL;
HRESULT hr = S_OK;
while (SUCCEEDED(hr))
{
// The parser must get a newly allocated buffer each time.
hr = MFCreateMemoryBuffer(cbReadSize, &pBuffer);
if (FAILED(hr))
{
break;
}
// Read data into the buffer.
hr = ReadFromByteStream(pStream, pBuffer, cbReadSize);
if (FAILED(hr))
{
break;
}
// Get the amound of data that was read.
DWORD cbData;
hr = pBuffer->GetCurrentLength(&cbData);
if (FAILED(hr))
{
break;
}
if (cbData == 0)
{
break; // End of file.
}
// Send the data to the ASF splitter.
hr = pSplitter->ParseData(pBuffer, 0, 0);
SafeRelease(&pBuffer);
if (FAILED(hr))
{
break;
}
// Pull samples from the splitter.
DWORD parsingStatus = 0;
do
{
WORD streamID;
hr = pSplitter->GetNextSample(&parsingStatus, &streamID, &pSample);
if (FAILED(hr))
{
break;
}
if (pSample == NULL)
{
// No samples yet. Parse more data.
break;
}
if (IsRandomAccessPoint(pSample))
{
DisplayKeyFrame(pSample);
}
SafeRelease(&pSample);
} while (parsingStatus & ASF_STATUSFLAGS_INCOMPLETE);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
相關主題