從現有的 ASF 資料物件產生串流範例
ASF 分割器 物件是 WMContainer 層元件,可剖析進階系統格式的 ASF 資料物件 (ASF) 檔案。
將資料封包傳遞至分割器之前,應用程式必須先初始化、設定和選取分隔器上的資料流程,才能準備進行剖析程式。 如需詳細資訊,請參閱 建立 ASF 分隔器物件 和 設定 ASF 分割器物件。
剖析 ASF 資料物件所需的方法如下:
- IMFASFSplitter::P arseData ,藉由將包含資料封包的緩衝區推送至分割器,以啟動剖析程式。
- 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 Object 的大小加上 Data Object 的標頭區段大小。 因此,產生的值是 ASF 資料物件中資料封包開頭的位移。 當您開始將資料傳送至分割器時,資料必須從 ASF 檔案開頭的這個位移開始。
位移值會當做參數傳遞至 ParseData ,以啟動剖析程式。
資料物件分成資料封包。 每個資料封包都包含一個資料封包標頭,可提供封包剖析資訊和承載資料,也就是實際的數位媒體資料。 在搜尋案例中,應用程式可能會希望分割器開始在特定資料封包上剖析。 若要這樣做,您可以使用 ASF 索引子來擷取位移。 索引子會傳回從封包界限開始的位移值。 如果您未使用索引子,請確定位移從資料封包標頭的開頭開始。 如果傳遞不正確位移至分割器,例如值未指向封包界限,ParseHeader和GetNextSample呼叫會成功,但GetNextSample不會擷取任何樣本,而且pSample參數中會收到Null。
如果分割器設定為以反向方向剖析,則分割器一律會在傳遞至 ParseData的媒體緩衝區結尾開始剖析。 因此,若要在對 ParseData的呼叫中反向剖析,請在 cbLength 參數中傳遞位移,以指定資料的長度並將 cbBufferOffset 設定為零。
產生 ASF 資料封包的範例
應用程式會藉由將資料封包傳遞至分割器來啟動剖析程式。 分割器的輸入是一系列的媒體緩衝區,其中包含資料物件的整個或片段。 分割器的輸出是包含封包資料的一系列媒體範例。
若要將輸入資料傳遞至分割器,請建立媒體緩衝區,並將 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;
}
相關主題