1466 lines
40 KiB
C++
1466 lines
40 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ASFManager.cpp : CASFManager class implementation.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include <new>
|
|
#include "ASFManager.h"
|
|
|
|
// ----- Static Methods -----------------------------------------------
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: CreateInstance
|
|
// Description: Instantiates the class statically
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::CreateInstance(CASFManager **ppASFManager)
|
|
{
|
|
|
|
// Note: CASFManager constructor sets the ref count to zero.
|
|
// Create method calls AddRef.
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CASFManager *pASFManager = new (std::nothrow) CASFManager(&hr);
|
|
|
|
if (!pASFManager)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppASFManager = pASFManager;
|
|
(*ppASFManager)->AddRef();
|
|
|
|
TRACE((L"CASFManager created.\n"));
|
|
}
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager creation failed.\n", hr);
|
|
|
|
SAFE_RELEASE (pASFManager);
|
|
return hr;
|
|
}
|
|
|
|
// ----- Public Methods -----------------------------------------------
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: CASFManager
|
|
// Description: Constructor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
CASFManager::CASFManager(HRESULT* hr)
|
|
: m_nRefCount(1),
|
|
m_CurrentStreamID (0),
|
|
m_guidCurrentMediaType (GUID_NULL),
|
|
m_fileinfo(NULL),
|
|
m_pDecoder (NULL),
|
|
m_pContentInfo (NULL),
|
|
m_pIndexer (NULL),
|
|
m_pSplitter (NULL),
|
|
m_pDataBuffer (NULL),
|
|
m_pByteStream(NULL),
|
|
m_cbDataOffset(0),
|
|
m_cbDataLength(0)
|
|
{
|
|
//Initialize Media Foundation
|
|
*hr = MFStartup(MF_VERSION);
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: ~ASFManager
|
|
// Description: Destructor
|
|
//
|
|
// -Calls Shutdown
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
CASFManager::~CASFManager()
|
|
{
|
|
|
|
//Release memory
|
|
Reset();
|
|
|
|
// Shutdown the Media Foundation platform
|
|
(void)MFShutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: OpenASFFile
|
|
//
|
|
// Opens a file and returns a byte stream.
|
|
//
|
|
// sFileName: Path name of the file
|
|
// ppStream: Receives a pointer to the byte stream.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::OpenASFFile(const WCHAR *sFileName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFByteStream* pStream = NULL;
|
|
|
|
// Open a byte stream for the file.
|
|
CHECK_HR(hr = MFCreateFile(
|
|
MF_ACCESSMODE_READ,
|
|
MF_OPENMODE_FAIL_IF_NOT_EXIST,
|
|
MF_FILEFLAGS_NONE,
|
|
sFileName,
|
|
&pStream
|
|
));
|
|
|
|
|
|
TRACE((L"Opened ASF File\n"));
|
|
|
|
//Reset the ASF components.
|
|
Reset();
|
|
|
|
// Create the Media Foundation ASF objects.
|
|
CHECK_HR(hr = CreateASFContentInfo(pStream, &m_pContentInfo));
|
|
|
|
CHECK_HR(hr = CreateASFSplitter(pStream, &m_pSplitter));
|
|
|
|
CHECK_HR(hr = CreateASFIndexer(pStream, &m_pIndexer));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::OpenASFFile failed.\n", hr);
|
|
|
|
SAFE_RELEASE(pStream);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// ----- Private Methods -----------------------------------------------
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: CreateASFContentInfo
|
|
//
|
|
// Reads the ASF Header Object from a byte stream and returns a
|
|
// pointer to the ASF content information object.
|
|
//
|
|
// pStream: Pointer to the byte stream. The byte stream's
|
|
// current read position must be 0 that indicates the start of the
|
|
// ASF Header Object.
|
|
// ppContentInfo: Receives a pointer to the ASF content information
|
|
// object.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::CreateASFContentInfo (IMFByteStream *pContentByteStream,
|
|
IMFASFContentInfo **ppContentInfo)
|
|
{
|
|
if (!pContentByteStream || !ppContentInfo)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
QWORD cbHeader = 0;
|
|
|
|
|
|
IMFASFContentInfo *pContentInfo = NULL;
|
|
IMFMediaBuffer *pBuffer = NULL;
|
|
|
|
// Create the ASF content information object.
|
|
CHECK_HR(hr = MFCreateASFContentInfo(&pContentInfo));
|
|
|
|
// Read the first 30 bytes to find the total header size.
|
|
CHECK_HR(hr = ReadDataIntoBuffer(
|
|
pContentByteStream, 0, MIN_ASF_HEADER_SIZE, &pBuffer));
|
|
|
|
CHECK_HR(hr = pContentInfo->GetHeaderSize(pBuffer, &cbHeader));
|
|
|
|
SAFE_RELEASE(pBuffer);
|
|
|
|
//Read the header into a buffer
|
|
CHECK_HR(hr = ReadDataIntoBuffer(
|
|
pContentByteStream, 0, (DWORD)cbHeader, &pBuffer));
|
|
|
|
// Pass the buffer for the header object.
|
|
CHECK_HR(hr = pContentInfo->ParseHeader(pBuffer, 0));
|
|
|
|
|
|
// Return the pointer to the caller.
|
|
*ppContentInfo = pContentInfo;
|
|
(*ppContentInfo)->AddRef();
|
|
|
|
TRACE((L"Created ContentInfo object.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::CreateASFContentInfo failed.\n", hr);
|
|
|
|
SAFE_RELEASE(pBuffer);
|
|
SAFE_RELEASE(pContentInfo);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: CreateASFSplitter
|
|
//
|
|
// Creates the ASF splitter.
|
|
//
|
|
// pContentByteStream: Pointer to the byte stream that contains the ASF Data Object.
|
|
// ppSplitter: Receives a pointer to the ASF splitter.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::CreateASFSplitter (IMFByteStream *pContentByteStream,
|
|
IMFASFSplitter **ppSplitter)
|
|
{
|
|
if (!pContentByteStream || !ppSplitter)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!m_pContentInfo)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFASFSplitter *pSplitter = NULL;
|
|
IMFPresentationDescriptor* pPD = NULL;
|
|
|
|
UINT64 cbDataOffset = 0, cbDataLength = 0;
|
|
|
|
CHECK_HR(hr = MFCreateASFSplitter(&pSplitter));
|
|
|
|
CHECK_HR(hr = pSplitter->Initialize(m_pContentInfo));
|
|
|
|
//Generate the presentation descriptor
|
|
CHECK_HR(hr = m_pContentInfo->GeneratePresentationDescriptor(&pPD));
|
|
|
|
//Get the offset to the start of the Data Object
|
|
CHECK_HR(hr = pPD->GetUINT64(MF_PD_ASF_DATA_START_OFFSET, &cbDataOffset));
|
|
|
|
//Get the length of the Data Object
|
|
CHECK_HR(hr = pPD->GetUINT64(MF_PD_ASF_DATA_LENGTH, &cbDataLength));
|
|
|
|
m_pByteStream = pContentByteStream;
|
|
m_pByteStream->AddRef();
|
|
|
|
m_cbDataOffset = cbDataOffset;
|
|
m_cbDataLength = cbDataLength;
|
|
|
|
// Return the pointer to the caller.
|
|
*ppSplitter = pSplitter;
|
|
(*ppSplitter)->AddRef();
|
|
|
|
TRACE((L"Created Splitter object.\n"));
|
|
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::CreateASFSplitter failed.\n", hr);
|
|
|
|
SAFE_RELEASE(pSplitter);
|
|
SAFE_RELEASE(pPD);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: CreateASFIndexer
|
|
//
|
|
// Creates the ASF Indexer.
|
|
//
|
|
// pContentByteStream: Pointer to the content byte stream
|
|
//
|
|
// ppIndexer: Receives a pointer to the ASF Indexer.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::CreateASFIndexer (IMFByteStream *pContentByteStream,
|
|
IMFASFIndexer **ppIndexer)
|
|
{
|
|
if (!pContentByteStream || !ppIndexer)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!m_pContentInfo)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFASFIndexer *pIndexer = NULL;
|
|
IMFByteStream *pIndexerByteStream = NULL;
|
|
|
|
QWORD qwLength = 0, qwIndexOffset = 0, qwBytestreamLength = 0;
|
|
DWORD dwByteStreamsNeeded = 0;
|
|
|
|
// Create the indexer.
|
|
CHECK_HR(hr = MFCreateASFIndexer(&pIndexer));
|
|
|
|
//Initialize the indexer to work with this ASF library
|
|
CHECK_HR(hr = pIndexer->Initialize(m_pContentInfo));
|
|
|
|
|
|
//Check if the index exists.
|
|
//you can only do this after creating the indexer
|
|
|
|
//Get byte stream length
|
|
CHECK_HR(hr = pContentByteStream->GetLength(&qwLength));
|
|
|
|
//Get index offset
|
|
CHECK_HR(hr = pIndexer->GetIndexPosition( m_pContentInfo, &qwIndexOffset ));
|
|
|
|
if ( qwIndexOffset >= qwLength)
|
|
{
|
|
//index object does not exist, release the indexer
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
// initialize the indexer
|
|
// Create a byte stream that the Indexer will use to read in
|
|
// and parse the indexers.
|
|
CHECK_HR(hr = MFCreateASFIndexerByteStream( pContentByteStream,
|
|
qwIndexOffset,
|
|
&pIndexerByteStream ));
|
|
}
|
|
|
|
CHECK_HR(hr = pIndexer->GetIndexByteStreamCount( &dwByteStreamsNeeded ));
|
|
|
|
if (dwByteStreamsNeeded == 1)
|
|
{
|
|
CHECK_HR(hr = pIndexer->SetIndexByteStreams( &pIndexerByteStream, dwByteStreamsNeeded ));
|
|
}
|
|
|
|
// Return the pointer to the caller.
|
|
*ppIndexer = pIndexer;
|
|
(*ppIndexer)->AddRef();
|
|
|
|
TRACE((L"Created Indexer object.\n"));
|
|
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::CreateASFIndexer failed.\n", hr);
|
|
SAFE_RELEASE(pIndexer);
|
|
SAFE_RELEASE(pIndexerByteStream);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: EnumerateStreams
|
|
//
|
|
// Enumerates the streams in the ASF file.
|
|
//
|
|
// ppwStreamNumbers: Receives the stream identifiers in an array.
|
|
// The caller must release the allocated memory.
|
|
//
|
|
// ppguidMajorType: Receives the major media type GUIDs in an array.
|
|
// The caller must release the allocated memory.
|
|
//
|
|
// cbTotalStreams: Receives total number of elements in the array.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::EnumerateStreams (WORD** ppwStreamNumbers,
|
|
GUID** ppguidMajorType,
|
|
DWORD* pcbTotalStreams)
|
|
{
|
|
if (!ppwStreamNumbers || !ppguidMajorType || !pcbTotalStreams)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!m_pContentInfo)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFASFStreamConfig* pStream = NULL;
|
|
IMFASFProfile* pProfile = NULL;
|
|
|
|
*pcbTotalStreams =0;
|
|
|
|
WORD* pwStreamNumbers;
|
|
GUID* pguidMajorType;
|
|
|
|
CHECK_HR(hr = m_pContentInfo->GetProfile(&pProfile));
|
|
|
|
CHECK_HR(hr = pProfile->GetStreamCount(pcbTotalStreams));
|
|
|
|
if (*pcbTotalStreams == 0)
|
|
{
|
|
SAFE_RELEASE(pProfile);
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
//create an array of stream numbers and initialize elements swith 0
|
|
pwStreamNumbers = new WORD[*pcbTotalStreams*sizeof(WORD)];
|
|
|
|
if (!pwStreamNumbers)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
ZeroMemory (pwStreamNumbers, *pcbTotalStreams*sizeof(WORD));
|
|
|
|
//create an array of guids and initialize elements with GUID_NULL.
|
|
pguidMajorType = new GUID[*pcbTotalStreams*sizeof(GUID)];
|
|
|
|
if (!pguidMajorType)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
ZeroMemory (pguidMajorType, *pcbTotalStreams*sizeof(GUID));
|
|
|
|
//populate the arrays with stream numbers and guids from the profile object
|
|
for (unsigned index = 0; index < *pcbTotalStreams; index++)
|
|
{
|
|
CHECK_HR(hr = pProfile->GetStream(index, &pwStreamNumbers[index], &pStream));
|
|
|
|
CHECK_HR(hr = pStream->GetStreamType(&pguidMajorType[index]));
|
|
|
|
SAFE_RELEASE(pStream);
|
|
}
|
|
|
|
|
|
*ppwStreamNumbers = pwStreamNumbers;
|
|
*ppguidMajorType = pguidMajorType;
|
|
|
|
TRACE((L"Enumerated streams.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::EnumerateStreams failed.\n", hr);
|
|
|
|
SAFE_RELEASE(pProfile);
|
|
SAFE_RELEASE(pStream);
|
|
|
|
if (FAILED (hr))
|
|
{
|
|
SAFE_ARRAY_DELETE(pwStreamNumbers);
|
|
SAFE_ARRAY_DELETE(pguidMajorType);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: SelectStream
|
|
//
|
|
// Selects the streams in the ASF file.
|
|
//
|
|
// pwStreamNumber: Specifies the identifier of the stream to be selected
|
|
// on the splitter.
|
|
//
|
|
// pguidCurrentMediaType: Receives the major media type GUID of the
|
|
// currently selected stream.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::SelectStream (WORD wStreamNumber,
|
|
GUID* pguidCurrentMediaType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (wStreamNumber == 0 || !pguidCurrentMediaType)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (! m_pSplitter || ! m_pContentInfo)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
//Select the stream you want to parse. This sample allows you to select only one stream at a time
|
|
CHECK_HR(hr = m_pSplitter->SelectStreams(&wStreamNumber, 1));
|
|
|
|
//Load the appropriate stream decoder
|
|
CHECK_HR(hr = SetupStreamDecoder(wStreamNumber, pguidCurrentMediaType));
|
|
|
|
m_CurrentStreamID = wStreamNumber;
|
|
|
|
m_guidCurrentMediaType = *pguidCurrentMediaType;
|
|
|
|
TRACE((L"Stream selected.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::SelectStreams failed.\n", hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: SetupStreamDecoder
|
|
//
|
|
// Loads the appropriate decoder for stream. The decoder is implemented as
|
|
// a Media Foundation Transform (MFT). The class CDecoder provides a wrapper for
|
|
// the MFT. The CASFManager::GenerateSamples feeds compressed samples to
|
|
// the decoder. The MFT decodes the samples and sends them to the CMediaController
|
|
// object, which plays 10 seconds of uncompressed audio samples or displays the
|
|
// key frame for the video stream
|
|
//
|
|
// wStreamNumber: Specifies the identifier of the stream.
|
|
//
|
|
// pguidCurrentMediaType: Receives the major media type GUID of the
|
|
// currently selected stream.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::SetupStreamDecoder (WORD wStreamNumber,
|
|
GUID* pguidCurrentMediaType)
|
|
{
|
|
if (! m_pContentInfo)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
if (wStreamNumber == 0)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
IMFASFProfile* pProfile = NULL;
|
|
IMFMediaType* pMediaType = NULL;
|
|
IMFASFStreamConfig *pStream = NULL;
|
|
|
|
GUID guidMajorType = GUID_NULL;
|
|
GUID guidSubType = GUID_NULL;
|
|
GUID guidDecoderCategory = GUID_NULL;
|
|
|
|
BOOL fIsCompressed = TRUE;
|
|
|
|
CLSID *pDecoderCLSIDs = NULL; // Pointer to an array of CLISDs.
|
|
UINT32 cDecoderCLSIDs = 0; // Size of the array.
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//Get the profile object that stores stream information
|
|
CHECK_HR(hr = m_pContentInfo->GetProfile(&pProfile));
|
|
|
|
//Get stream configuration object from the profile
|
|
CHECK_HR(hr = pProfile->GetStreamByNumber(wStreamNumber, &pStream));
|
|
|
|
//Get the media type
|
|
CHECK_HR(hr = pStream->GetMediaType(&pMediaType));
|
|
|
|
//Get the major media type
|
|
CHECK_HR(hr = pMediaType->GetMajorType(&guidMajorType));
|
|
|
|
//Get the sub media type
|
|
CHECK_HR(hr = pMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubType));
|
|
|
|
//find out if the media type is compressed
|
|
CHECK_HR(hr = pMediaType->IsCompressedFormat(&fIsCompressed));
|
|
|
|
if (fIsCompressed)
|
|
{
|
|
//get decoder category
|
|
if (guidMajorType == MFMediaType_Video)
|
|
{
|
|
guidDecoderCategory = MFT_CATEGORY_VIDEO_DECODER;
|
|
}
|
|
else if (guidMajorType == MFMediaType_Audio)
|
|
{
|
|
guidDecoderCategory = MFT_CATEGORY_AUDIO_DECODER;
|
|
}
|
|
else
|
|
{
|
|
CHECK_HR(hr = MF_E_INVALIDMEDIATYPE);
|
|
}
|
|
|
|
// Look for a decoder.
|
|
MFT_REGISTER_TYPE_INFO tinfo;
|
|
tinfo.guidMajorType = guidMajorType;
|
|
tinfo.guidSubtype = guidSubType;
|
|
|
|
CHECK_HR(hr = MFTEnum(
|
|
guidDecoderCategory,
|
|
0, // Reserved
|
|
&tinfo, // Input type to match. (Encoded type.)
|
|
NULL, // Output type to match. (Don't care.)
|
|
NULL, // Attributes to match. (None.)
|
|
&pDecoderCLSIDs, // Receives a pointer to an array of CLSIDs.
|
|
&cDecoderCLSIDs // Receives the size of the array.
|
|
));
|
|
|
|
// MFTEnum can return zero matches.
|
|
if (cDecoderCLSIDs == 0)
|
|
{
|
|
hr = MF_E_TOPO_CODEC_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
//if the CDecoder instance does not exist, create one.
|
|
if (!m_pDecoder)
|
|
{
|
|
CHECK_HR(hr = CDecoder::CreateInstance(&m_pDecoder));
|
|
}
|
|
|
|
//Load the first MFT in the array for the current media type
|
|
CHECK_HR(hr = m_pDecoder->Initialize(pDecoderCLSIDs[0], pMediaType));
|
|
}
|
|
*pguidCurrentMediaType = guidMajorType;
|
|
}
|
|
else
|
|
{
|
|
// Not compressed. Don't need a decoder.
|
|
CHECK_HR(hr = MF_E_INVALIDREQUEST);
|
|
}
|
|
|
|
|
|
TRACE((L"Stream decoder loaded.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::SetupStreamDecoder failed.\n", hr);
|
|
|
|
SAFE_RELEASE(pProfile);
|
|
SAFE_RELEASE(pMediaType);
|
|
SAFE_RELEASE(pStream);
|
|
|
|
CoTaskMemFree(pDecoderCLSIDs);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GetSeekPosition
|
|
//
|
|
// Gets the offset from the start of the ASF Data Object corresponding
|
|
// to the specified time that is seeked by the caller.
|
|
//
|
|
// hnsSeekTime: [In/out]Seek time in hns. This includes the preroll time. The received
|
|
// value is the actual seek time wth preroll adjustment.
|
|
//
|
|
// cbDataOffset: Receives the offset in bytes.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::GetSeekPosition (MFTIME* hnsSeekTime,
|
|
QWORD *pcbDataOffset,
|
|
MFTIME* phnsApproxSeekTime)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
//if the media type is audio, or doesn't have an indexed data
|
|
//calculate the offset manually
|
|
if (( m_guidCurrentMediaType == MFMediaType_Audio) || (!m_pIndexer))
|
|
{
|
|
CHECK_HR(hr = GetSeekPositionManually(*hnsSeekTime, pcbDataOffset));
|
|
}
|
|
|
|
//if the type is video, get the position with the indexer
|
|
if (( m_guidCurrentMediaType == MFMediaType_Video))
|
|
{
|
|
CHECK_HR(hr = GetSeekPositionWithIndexer(*hnsSeekTime, pcbDataOffset, phnsApproxSeekTime));
|
|
}
|
|
|
|
|
|
TRACE((L"Offset calculated.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::GetSeekPosition failed.\n", hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GetSeekPositionManually
|
|
//
|
|
// Gets the offset for audio media types or ones that do not have ASF Index Objects defined.
|
|
//Offset is calculated as fraction with respect to time
|
|
//
|
|
// hnsSeekTime: Presentation time in hns.
|
|
//
|
|
// cbDataOffset: Receives the offset in bytes.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::GetSeekPositionManually(MFTIME hnsSeekTime,
|
|
QWORD *cbDataOffset)
|
|
{
|
|
//Get average packet size
|
|
UINT32 averagepacketsize = ( m_fileinfo->cbMaxPacketSize+ m_fileinfo->cbMinPacketSize)/2;
|
|
|
|
DWORD dwFlags = 0;
|
|
|
|
double fraction = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//Check if the reverse flag is set, if so, offset is calculated from the end of the presentation
|
|
CHECK_HR(hr = this->m_pSplitter->GetFlags(&dwFlags));
|
|
|
|
if (dwFlags & MFASF_SPLITTER_REVERSE)
|
|
{
|
|
fraction = ((double) (m_fileinfo->cbPresentationDuration) - (double) (hnsSeekTime))/(double) (m_fileinfo->cbPresentationDuration);
|
|
}
|
|
else
|
|
{
|
|
fraction = (double)(hnsSeekTime)/(double) (m_fileinfo->cbPresentationDuration);
|
|
}
|
|
|
|
//calculate the number of packets passed
|
|
int seeked_packets = (int)( m_fileinfo->cbPackets * fraction);
|
|
|
|
//get the offset
|
|
*cbDataOffset = (QWORD)averagepacketsize * seeked_packets;
|
|
|
|
|
|
|
|
if (*cbDataOffset >= 0)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
TRACE((L"Offset calculated through fraction.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::GetSeekPositionManually failed.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GetSeekPositionWithIndexer
|
|
//
|
|
// Gets the offset for video media types that have ASF Index Objects defined.
|
|
//
|
|
// hnsSeekTime: Presentation time in hns.
|
|
//
|
|
// cbDataOffset: Receives the offset in bytes.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::GetSeekPositionWithIndexer (
|
|
MFTIME hnsSeekTime,
|
|
QWORD *cbDataOffset,
|
|
MFTIME* hnsApproxSeekTime)
|
|
{
|
|
if (! m_pIndexer)
|
|
{
|
|
return MF_E_ASF_NOINDEX;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
PROPVARIANT var;
|
|
PropVariantInit(&var);
|
|
|
|
var.vt = VT_I8;
|
|
var.hVal.QuadPart = hnsSeekTime;
|
|
|
|
ASF_INDEX_IDENTIFIER IndexIdentifier;
|
|
|
|
BOOL fIsIndexed = FALSE;
|
|
DWORD cbIndexDescriptor = 0;
|
|
|
|
DWORD dwFlags = 0;
|
|
|
|
//currently only time index is supported, set to GUID_NULL
|
|
IndexIdentifier.guidIndexType = GUID_NULL;
|
|
IndexIdentifier.wStreamNumber = m_CurrentStreamID;
|
|
|
|
//Is the stream indexed? Get the value of cbIndexDescriptor
|
|
hr = m_pIndexer->GetIndexStatus( &IndexIdentifier,
|
|
&fIsIndexed,
|
|
NULL,
|
|
&cbIndexDescriptor );
|
|
|
|
if (hr == MF_E_BUFFERTOOSMALL)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
CHECK_HR(hr);
|
|
|
|
//check if the reverse flag needs to be set on the indexer
|
|
CHECK_HR (hr = m_pSplitter->GetFlags(&dwFlags));
|
|
|
|
if (dwFlags & MFASF_SPLITTER_REVERSE)
|
|
{
|
|
CHECK_HR (hr = m_pIndexer->SetFlags(MFASF_INDEXER_READ_FOR_REVERSEPLAYBACK));
|
|
}
|
|
|
|
//Get the offset from the indexer
|
|
if (fIsIndexed)
|
|
{
|
|
CHECK_HR(hr = m_pIndexer->GetSeekPositionForValue(
|
|
&var,
|
|
&IndexIdentifier,
|
|
cbDataOffset,
|
|
hnsApproxSeekTime,
|
|
0 ));
|
|
}
|
|
else
|
|
{
|
|
hr = MF_E_ASF_NOINDEX;
|
|
}
|
|
|
|
TRACE((L"Offset calculated through the Indexer.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::GetSeekPositionWithIndexer failed.\n", hr);
|
|
|
|
//release memory
|
|
PropVariantClear(&var);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GetTestDuration
|
|
//
|
|
//For audio stream, this method retrieves the duration of the test sample.
|
|
//
|
|
// hnsSeekTime: Presentation time in hns.
|
|
// hnsTestDuration: Presentation time in hns that represents the end time.
|
|
// dwFlags: Specifies splitter configuration, generate samples in reverse
|
|
// or generate samples for protected content.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
void CASFManager::GetTestDuration(const MFTIME& hnsSeekTime, BOOL bReverse, MFTIME* phnsTestDuration)
|
|
{
|
|
MFTIME hnsMaxSeekableTime = m_fileinfo->cbPlayDuration - m_fileinfo->cbPreroll;
|
|
|
|
if (bReverse)
|
|
{
|
|
// Reverse playback: The stop time should not go beyond the start of the
|
|
// ASF Data Object (reading backwards in the file).
|
|
if (hnsSeekTime - TEST_AUDIO_DURATION < 0)
|
|
{
|
|
*phnsTestDuration = 0 ;
|
|
}
|
|
else
|
|
{
|
|
*phnsTestDuration = hnsSeekTime - TEST_AUDIO_DURATION;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// Forward playback: The stop time should not exceed the end of the presentation.
|
|
if (hnsSeekTime + TEST_AUDIO_DURATION > hnsMaxSeekableTime)
|
|
{
|
|
*phnsTestDuration = hnsMaxSeekableTime ;
|
|
}
|
|
else
|
|
{
|
|
*phnsTestDuration = hnsSeekTime + TEST_AUDIO_DURATION;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GenerateSamples
|
|
//
|
|
//Gets data offset for the seektime and prepares buffer for parsing.
|
|
//
|
|
// hnsSeekTime: Presentation time in hns.
|
|
// dwFlags: Specifies splitter configuration, generate samples in
|
|
// reverse or generate samples for protected content.
|
|
// pSampleInfo: Pointer to SAMPLE_INFO structure that stores sample
|
|
// information.
|
|
// FuncPtrToDisplaySampleInfo: Callback defined by the caller that
|
|
// will display the sample information
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::GenerateSamples(
|
|
MFTIME hnsSeekTime,
|
|
DWORD dwFlags,
|
|
SAMPLE_INFO* pSampleInfo,
|
|
void (*FuncPtrToDisplaySampleInfo)(SAMPLE_INFO*)
|
|
)
|
|
{
|
|
if (! m_pSplitter)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
QWORD cbStartOffset = 0;
|
|
DWORD cbReadLen = 0;
|
|
MFTIME hnsApproxTime =0;
|
|
MFTIME hnsTestSampleDuration =0;
|
|
BOOL bReverse = FALSE;
|
|
|
|
// Flush the splitter to remove any samples that were delivered
|
|
// to the ASF splitter during a previous call to this method.
|
|
CHECK_HR(hr = m_pSplitter->Flush());
|
|
|
|
//set the reverse flag if applicable
|
|
hr = m_pSplitter->SetFlags(dwFlags);
|
|
|
|
if (FAILED (hr))
|
|
{
|
|
dwFlags = 0;
|
|
hr = S_OK;
|
|
}
|
|
|
|
bReverse = ((dwFlags & MFASF_SPLITTER_REVERSE) == MFASF_SPLITTER_REVERSE);
|
|
|
|
// Get the offset from the start of the ASF Data Object to the desired seek time.
|
|
CHECK_HR(hr = GetSeekPosition(&hnsSeekTime, &cbStartOffset, &hnsApproxTime));
|
|
|
|
// Get the audio playback duration. (The duration is TEST_AUDIO_DURATION or up to
|
|
// the end of the file, whichever is shorter.)
|
|
if (m_guidCurrentMediaType == MFMediaType_Audio)
|
|
{
|
|
GetTestDuration(hnsSeekTime, bReverse, &hnsTestSampleDuration);
|
|
}
|
|
|
|
// Notify the MFT we are about to start.
|
|
if (m_pDecoder)
|
|
{
|
|
if ( m_pDecoder->GetDecoderStatus() != STREAMING)
|
|
{
|
|
hr = m_pDecoder->StartDecoding();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
SAFE_RELEASE(m_pDecoder);
|
|
}
|
|
}
|
|
|
|
cbReadLen = (DWORD)(m_cbDataLength - cbStartOffset);
|
|
|
|
if (bReverse)
|
|
{
|
|
// Reverse playback: Read from the offset back to zero.
|
|
|
|
CHECK_HR(hr = GenerateSamplesLoop(
|
|
hnsSeekTime,
|
|
hnsTestSampleDuration,
|
|
bReverse,
|
|
(DWORD)(m_cbDataLength + m_cbDataOffset - cbStartOffset), //DWORD cbDataOffset
|
|
cbReadLen, //DWORD cbDataLen
|
|
pSampleInfo,
|
|
FuncPtrToDisplaySampleInfo
|
|
));
|
|
|
|
}
|
|
else
|
|
{
|
|
// Forward playback: Read from the offset to the end.
|
|
|
|
CHECK_HR(hr = GenerateSamplesLoop(
|
|
hnsSeekTime,
|
|
hnsTestSampleDuration,
|
|
bReverse,
|
|
(DWORD)(m_cbDataOffset + cbStartOffset), //DWORD cbDataOffset,
|
|
cbReadLen, //DWORD cbDataLen
|
|
pSampleInfo,
|
|
FuncPtrToDisplaySampleInfo
|
|
));
|
|
}
|
|
|
|
// Note: cbStartOffset is relative to the start of the data object.
|
|
// GenerateSamplesLoop expects the offset relative to the start of the file.
|
|
|
|
|
|
done:
|
|
LOG_MSG_IF_FAILED(L"CASFManager::GenerateSamples failed.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: GenerateSamplesLoop
|
|
//
|
|
//Reads 1024 * 4 byte chunks of media data from a byte stream and
|
|
//parses the ASF Data Object starting at the specified offset.
|
|
//Collects 5seconds audio samples and sends to the MFT to decode.
|
|
//Gets the first key frame for the video stream and sends to the MFT
|
|
//
|
|
// hnsSeekTime: Presentation time in hns.
|
|
// hnsTestSampleDuration: Presentation time at which the parsing should end.
|
|
// bReverse: Specifies if the splitter configured to parse in reverse.
|
|
// cbDataOffset: Offset relative to the start of the data object.
|
|
// cbDataLen: Length of data to parse
|
|
// pSampleInfo: Pointer to SAMPLE_INFO structure that stores sample
|
|
// information.
|
|
// FuncPtrToDisplaySampleInfo: Callback defined by the caller that
|
|
// will display the sample information
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::GenerateSamplesLoop(
|
|
const MFTIME& hnsSeekTime,
|
|
const MFTIME& hnsTestSampleDuration,
|
|
BOOL bReverse,
|
|
DWORD cbDataOffset,
|
|
DWORD cbDataLen,
|
|
SAMPLE_INFO* pSampleInfo,
|
|
void (*FuncPtrToDisplaySampleInfo)(SAMPLE_INFO*)
|
|
)
|
|
{
|
|
const DWORD READ_SIZE = 1024 * 4;
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD cbRead = 0;
|
|
DWORD dwStatusFlags = 0;
|
|
WORD wStreamNumber = 0;
|
|
BOOL fComplete = FALSE;
|
|
|
|
IMFSample *pSample = NULL;
|
|
IMFMediaBuffer *pBuffer = NULL;
|
|
|
|
MFTIME hnsCurrentSampleTime = 0;
|
|
|
|
while (!fComplete && (cbDataLen > 0))
|
|
{
|
|
cbRead = min(READ_SIZE, cbDataLen);
|
|
|
|
if (bReverse)
|
|
{
|
|
// Reverse playback: Read data chunks going backward from cbDataOffset.
|
|
CHECK_HR(hr = ReadDataIntoBuffer(m_pByteStream, cbDataOffset - cbRead, cbRead, &pBuffer));
|
|
cbDataOffset -= cbRead;
|
|
cbDataLen -= cbRead;
|
|
}
|
|
else
|
|
{
|
|
// Forward playback: Read data chunks going forward from cbDataOffset.
|
|
CHECK_HR(hr = ReadDataIntoBuffer(m_pByteStream, cbDataOffset, cbRead, &pBuffer));
|
|
cbDataOffset += cbRead;
|
|
cbDataLen -= cbRead;
|
|
}
|
|
|
|
// Push data on the splitter
|
|
CHECK_HR(hr = m_pSplitter->ParseData(pBuffer, 0, 0));
|
|
|
|
// Start getting samples from the splitter as long as it returns ASF_STATUSFLAGS_INCOMPLETE
|
|
do
|
|
{
|
|
CHECK_HR(hr = m_pSplitter->GetNextSample(&dwStatusFlags, &wStreamNumber, &pSample));
|
|
|
|
if (pSample)
|
|
{
|
|
// Get sample information
|
|
pSampleInfo->wStreamNumber = wStreamNumber;
|
|
|
|
//if decoder is initialized, collect test data
|
|
if (m_pDecoder)
|
|
{
|
|
if (m_guidCurrentMediaType == MFMediaType_Audio)
|
|
{
|
|
// Send audio data to the decoder.
|
|
(void)SendAudioSampleToDecoder(pSample, hnsTestSampleDuration, bReverse, &fComplete, pSampleInfo, FuncPtrToDisplaySampleInfo);
|
|
}
|
|
else if (m_guidCurrentMediaType == MFMediaType_Video)
|
|
{
|
|
// Send video data to the decoder.
|
|
(void)SendKeyFrameToDecoder(pSample, hnsSeekTime, bReverse, &fComplete, pSampleInfo, FuncPtrToDisplaySampleInfo);
|
|
|
|
}
|
|
|
|
if (fComplete)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SAFE_RELEASE(pSample);
|
|
|
|
} while (dwStatusFlags & ASF_STATUSFLAGS_INCOMPLETE);
|
|
|
|
SAFE_RELEASE(pBuffer);
|
|
}
|
|
|
|
done:
|
|
SAFE_RELEASE(pBuffer);
|
|
SAFE_RELEASE(pSample);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: SendAudioSampleToDecoder
|
|
//
|
|
// For audio, collect test samples and send it to the decoder
|
|
//
|
|
// pSample: Uncompressed sample that needs to be decoded
|
|
// hnsTestSampleEndTime: Presenation time at which to stop decoding.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::SendAudioSampleToDecoder(
|
|
IMFSample* pSample,
|
|
const MFTIME& hnsTestSampleEndTime,
|
|
BOOL bReverse,
|
|
BOOL *pbComplete,
|
|
SAMPLE_INFO* pSampleInfo,
|
|
void (*FuncPtrToDisplaySampleInfo)(SAMPLE_INFO*))
|
|
{
|
|
if (!pSample)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
MFTIME hnsCurrentSampleTime = 0;
|
|
BOOL bShouldDecode = FALSE;
|
|
|
|
// Get the time stamp on the sample.
|
|
CHECK_HR(hr = pSample->GetSampleTime(&hnsCurrentSampleTime));
|
|
|
|
if (bReverse)
|
|
{
|
|
bShouldDecode = (hnsCurrentSampleTime > hnsTestSampleEndTime);
|
|
}
|
|
else
|
|
{
|
|
bShouldDecode = (hnsCurrentSampleTime < hnsTestSampleEndTime);
|
|
}
|
|
|
|
if (bShouldDecode)
|
|
{
|
|
|
|
// If the decoder is not streaming, start it.
|
|
if ( m_pDecoder->GetDecoderStatus() != STREAMING)
|
|
{
|
|
CHECK_HR (hr = m_pDecoder->StartDecoding());
|
|
}
|
|
|
|
CHECK_HR (hr = m_pDecoder->ProcessAudio (pSample));
|
|
|
|
//Get sample information
|
|
(void)GetSampleInfo(pSample, pSampleInfo);
|
|
|
|
//Send it to callback to display
|
|
FuncPtrToDisplaySampleInfo(pSampleInfo);
|
|
|
|
}
|
|
else
|
|
{
|
|
//all samples have been decoded. Inform the decoder.
|
|
CHECK_HR (hr = m_pDecoder->StopDecoding());
|
|
}
|
|
|
|
*pbComplete = !bShouldDecode;
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: SendKeyFrameToDecoder
|
|
//
|
|
//For Video, get the key frame closest to the time seeked by the caller
|
|
//
|
|
// pSample: Uncompressed sample that needs to be decoded
|
|
// hnsTestSampleEndTime: Presenation time at which to stop decoding.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::SendKeyFrameToDecoder(
|
|
IMFSample* pSample,
|
|
const MFTIME& hnsSeekTime,
|
|
BOOL bReverse,
|
|
BOOL* fDecodedKeyFrame,
|
|
SAMPLE_INFO* pSampleInfo,
|
|
void (*FuncPtrToDisplaySampleInfo)(SAMPLE_INFO*))
|
|
{
|
|
if (!pSample)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
MFTIME hnsCurrentSampleTime =0;
|
|
|
|
BOOL fShouldDecode = FALSE;
|
|
UINT32 fIsKeyFrame = 0;
|
|
|
|
IMFSample* pKeyFrameSample = NULL;
|
|
|
|
// Get the time stamp on the sample
|
|
CHECK_HR (hr = pSample->GetSampleTime (&hnsCurrentSampleTime));
|
|
|
|
if ((UINT64)hnsCurrentSampleTime > m_fileinfo->cbPreroll)
|
|
{
|
|
hnsCurrentSampleTime -= m_fileinfo->cbPreroll;
|
|
}
|
|
|
|
// Check if the key-frame attribute is set on the sample
|
|
fIsKeyFrame = MFGetAttributeUINT32(pSample, MFSampleExtension_CleanPoint, FALSE);
|
|
|
|
if (!fIsKeyFrame)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Should we decode this sample?
|
|
if (bReverse)
|
|
{
|
|
// Reverse playback:
|
|
// Is the sample *prior* to the seek time, and a key frame?
|
|
fShouldDecode = (hnsCurrentSampleTime <= hnsSeekTime) ;
|
|
}
|
|
else
|
|
{
|
|
// Forward playback:
|
|
// Is the sample *after* the seek time, and a key frame?
|
|
fShouldDecode = (hnsCurrentSampleTime >= hnsSeekTime);
|
|
}
|
|
|
|
if (fShouldDecode)
|
|
{
|
|
// We found the key frame closest to the seek time.
|
|
// Start the decoder if not already started.
|
|
if ( m_pDecoder->GetDecoderStatus() != STREAMING)
|
|
{
|
|
CHECK_HR (hr = m_pDecoder->StartDecoding());
|
|
}
|
|
|
|
// Set the discontinity attribute.
|
|
CHECK_HR (hr = pSample->SetUINT32(MFSampleExtension_Discontinuity, TRUE));
|
|
|
|
//Send the sample to the decoder.
|
|
CHECK_HR (hr = m_pDecoder->ProcessVideo(pSample));
|
|
|
|
*fDecodedKeyFrame = TRUE;
|
|
|
|
//Get sample information
|
|
(void)GetSampleInfo(pSample, pSampleInfo);
|
|
pSampleInfo->fSeekedKeyFrame = *fDecodedKeyFrame;
|
|
|
|
//Send it to callback to display
|
|
FuncPtrToDisplaySampleInfo(pSampleInfo);
|
|
|
|
CHECK_HR (hr = m_pDecoder->StopDecoding());
|
|
}
|
|
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Name: ReadDataIntoBuffer
|
|
//
|
|
// Reads data from a byte stream and returns a media buffer that
|
|
// contains the data.
|
|
//
|
|
// pStream: Pointer to the byte stream
|
|
// cbOffset: Offset at which to start reading
|
|
// cbToRead: Number of bytes to read
|
|
// ppBuffer: Receives a pointer to the buffer.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::ReadDataIntoBuffer(
|
|
IMFByteStream *pStream, // Pointer to the byte stream.
|
|
DWORD cbOffset, // Offset at which to start reading
|
|
DWORD cbToRead, // Number of bytes to read
|
|
IMFMediaBuffer **ppBuffer // Receives a pointer to the 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.
|
|
CHECK_HR(hr = MFCreateMemoryBuffer(cbToRead, &pBuffer));
|
|
|
|
// Access the buffer.
|
|
CHECK_HR(hr = pBuffer->Lock(&pData, NULL, NULL));
|
|
|
|
//Set the offset
|
|
CHECK_HR(hr = pStream->SetCurrentPosition(cbOffset));
|
|
|
|
// Read the data from the byte stream.
|
|
CHECK_HR(hr = pStream->Read(pData, cbToRead, &cbRead));
|
|
|
|
CHECK_HR(hr = pBuffer->Unlock());
|
|
pData = NULL;
|
|
|
|
// Update the size of the valid data.
|
|
CHECK_HR(hr = pBuffer->SetCurrentLength(cbRead));
|
|
|
|
// Return the pointer to the caller.
|
|
*ppBuffer = pBuffer;
|
|
(*ppBuffer)->AddRef();
|
|
|
|
TRACE((L"Read data from the ASF file into a media buffer.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::ReadDataIntoBuffer failed.\n", hr);
|
|
|
|
if (pData)
|
|
{
|
|
pBuffer->Unlock();
|
|
}
|
|
SAFE_RELEASE(pBuffer);
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: SetFilePropertiesObject
|
|
// Description: Retrieves ASF File Object information through attributes on
|
|
// the presentation descriptor that is generated from Content Info
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::SetFilePropertiesObject(FILE_PROPERTIES_OBJECT* fileinfo)
|
|
{
|
|
if (! m_pContentInfo)
|
|
{
|
|
return MF_E_NOT_INITIALIZED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFPresentationDescriptor *pPD = NULL;
|
|
|
|
UINT32 cbBlobSize = 0;
|
|
|
|
CHECK_HR( hr = m_pContentInfo->GeneratePresentationDescriptor(&pPD));
|
|
|
|
//get File ID
|
|
hr = pPD->GetGUID(MF_PD_ASF_FILEPROPERTIES_FILE_ID, &fileinfo->guidFileID);
|
|
|
|
// get Creation Time
|
|
hr = pPD->GetBlob(MF_PD_ASF_FILEPROPERTIES_CREATION_TIME, (BYTE *)&fileinfo->ftCreationTime, sizeof(FILETIME), &cbBlobSize);
|
|
|
|
//get Data Packets Count
|
|
hr = pPD->GetUINT32(MF_PD_ASF_FILEPROPERTIES_PACKETS, &fileinfo->cbPackets);
|
|
|
|
//get Play Duration
|
|
hr = pPD->GetUINT64(MF_PD_ASF_FILEPROPERTIES_PLAY_DURATION, &fileinfo->cbPlayDuration);
|
|
|
|
//get presentation duration
|
|
hr = pPD->GetUINT64(MF_PD_DURATION, &fileinfo->cbPresentationDuration);
|
|
|
|
//get Send Duration
|
|
hr = pPD->GetUINT64(MF_PD_ASF_FILEPROPERTIES_SEND_DURATION, &fileinfo->cbSendDuration);
|
|
|
|
//get preroll
|
|
UINT64 msecspreroll = 0, hnspreroll =0;
|
|
hr = pPD->GetUINT64(MF_PD_ASF_FILEPROPERTIES_PREROLL, &msecspreroll);
|
|
hnspreroll = msecspreroll*10000;
|
|
fileinfo->cbPreroll = hnspreroll;
|
|
|
|
//get Flags
|
|
hr = pPD->GetUINT32(MF_PD_ASF_FILEPROPERTIES_FLAGS, &fileinfo->cbFlags);
|
|
|
|
//get Maximum Data Packet Size
|
|
hr = pPD->GetUINT32(MF_PD_ASF_FILEPROPERTIES_MAX_PACKET_SIZE, &fileinfo->cbMaxPacketSize);
|
|
|
|
//get Minimum Data Packet Size
|
|
hr = pPD->GetUINT32(MF_PD_ASF_FILEPROPERTIES_MIN_PACKET_SIZE, &fileinfo->cbMinPacketSize);
|
|
|
|
// get Maximum Bit rate
|
|
hr = pPD->GetUINT32(MF_PD_ASF_FILEPROPERTIES_MAX_BITRATE, &fileinfo->cbMaxBitRate);
|
|
|
|
|
|
this->m_fileinfo = fileinfo;
|
|
|
|
TRACE((L"Read File Properties Object from the ASF Header Object.\n"));
|
|
|
|
done:
|
|
|
|
LOG_MSG_IF_FAILED(L"CASFManager::SetFilePropertiesObject failed.\n", hr);
|
|
|
|
SAFE_RELEASE(pPD);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: GetSampleInfo
|
|
// Description: Retrieves sample information from the sample generated by the splitter
|
|
//
|
|
// pSample: Pointer to the sample object
|
|
// pSampleInfo: Pointer to the SAMPLE_INFO structure tha stores the sample information.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CASFManager::GetSampleInfo(IMFSample *pSample, SAMPLE_INFO* pSampleInfo)
|
|
{
|
|
if (!pSampleInfo || !pSample)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//Number of buffers in the sample
|
|
CHECK_HR(hr = pSample->GetBufferCount(&pSampleInfo->cBufferCount));
|
|
|
|
//Total buffer length
|
|
CHECK_HR(hr = pSample->GetTotalLength(&pSampleInfo->cbTotalLength));
|
|
|
|
//Sample time
|
|
hr = pSample->GetSampleTime(&pSampleInfo->hnsSampleTime);
|
|
|
|
if (hr == MF_E_NO_SAMPLE_TIMESTAMP)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Name: Reset
|
|
// Description: Releases the existing ASF objects for the current file
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void CASFManager::Reset()
|
|
{
|
|
SAFE_RELEASE( m_pContentInfo);
|
|
SAFE_RELEASE( m_pDataBuffer);
|
|
SAFE_RELEASE( m_pIndexer);
|
|
SAFE_RELEASE( m_pSplitter);
|
|
|
|
// TEST
|
|
SAFE_RELEASE(m_pByteStream);
|
|
m_cbDataOffset = 0;
|
|
m_cbDataLength = 0;
|
|
|
|
if (m_pDecoder)
|
|
{
|
|
m_pDecoder->Reset();
|
|
SAFE_RELEASE (m_pDecoder);
|
|
}
|
|
|
|
if( m_fileinfo)
|
|
{
|
|
m_fileinfo = NULL;
|
|
}
|
|
|
|
TRACE((L"CASFManager reset.\n"));
|
|
|
|
} |