//------------------------------------------------------------------------------ // File: Sample.cpp // // Desc: DirectShow sample code - implementation of CSample class. // // Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include "stdafx.h" #define FIX_LOCK_NAME #include #include // _I64_MAX #include #include #include // DirectShow media type guids #include // VIDEOINFOHEADER definition #include "resource.h" #include "state.h" #include "Sample.h" #include "util.h" // Helpers #pragma warning(disable:4100) // Disable C4100: unreferenced formal parameter HRESULT CSample::InternalGetInputStreamInfo(DWORD dwInputStreamIndex, DWORD *pdwFlags) { // We can process data on any boundary *pdwFlags = 0; return S_OK; } HRESULT CSample::InternalGetOutputStreamInfo(DWORD dwOutputStreamIndex, DWORD *pdwFlags) { // We output single frames if (0 == dwOutputStreamIndex) { *pdwFlags = DMO_OUTPUT_STREAMF_WHOLE_SAMPLES | DMO_OUTPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER | DMO_OUTPUT_STREAMF_FIXED_SAMPLE_SIZE; } else { // Stream 1 // Just text, no special buffering but 1 sample per sample *pdwFlags = DMO_OUTPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER | DMO_OUTPUT_STREAMF_OPTIONAL; } return S_OK; } HRESULT CSample::InternalCheckInputType(DWORD dwInputStreamIndex, const DMO_MEDIA_TYPE *pmt) { // Check if the type is already set and if so reject any type that's not identical if (InputTypeSet(dwInputStreamIndex)) { if (!TypesMatch(pmt, InputType(dwInputStreamIndex))) { return DMO_E_INVALIDTYPE; } else { return S_OK; } } // We accept MEDIATYPE_Video, MEDIASUBTYPE_MPEG1Video // Check the format is defined if (pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_MPEG1Payload && pmt->formattype == FORMAT_MPEGVideo && pmt->pbFormat != NULL) { return S_OK; } else { return DMO_E_INVALIDTYPE; } } HRESULT CSample::InternalCheckOutputType(DWORD dwOutputStreamIndex, const DMO_MEDIA_TYPE *pmt) { // Check if the type is already set and if so reject any type that's not identical if (OutputTypeSet(dwOutputStreamIndex)) { if (!TypesMatch(pmt, OutputType(dwOutputStreamIndex))) { return DMO_E_INVALIDTYPE; } else { return S_OK; } } // We output frames on stream 1 if (dwOutputStreamIndex == 0) { if (!InputTypeSet(0)) { return DMO_E_INVALIDTYPE; } if (pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_RGB565 && pmt->formattype == FORMAT_VideoInfo && pmt->pbFormat != NULL) { const VIDEOINFOHEADER *pvihInput = (const VIDEOINFOHEADER *)InputType(0)->pbFormat; const VIDEOINFOHEADER *pvihOutput = (const VIDEOINFOHEADER *)pmt->pbFormat; LONG lWidth, lHeight; if (IsRectEmpty(&pvihOutput->rcTarget)) { lWidth = pvihOutput->bmiHeader.biWidth; lHeight = pvihOutput->bmiHeader.biHeight; } else { lWidth = pvihOutput->rcTarget.right - pvihOutput->rcTarget.left; lHeight = pvihOutput->rcTarget.bottom - pvihOutput->rcTarget.top; } if (pvihInput->bmiHeader.biWidth == lWidth && pvihInput->bmiHeader.biHeight == lHeight) { return S_OK; } } return DMO_E_INVALIDTYPE; } else { // Stream 1 if (pmt->majortype == MEDIATYPE_Text && pmt->subtype == GUID_NULL) { return S_OK; } else { return DMO_E_INVALIDTYPE; } } } HRESULT CSample::InternalGetInputType(DWORD dwInputStreamIndex, DWORD dwTypeIndex, DMO_MEDIA_TYPE *pmt) { // No types to all indices for dwTypeIndex are out of range. return DMO_E_NO_MORE_ITEMS; } HRESULT CSample::InternalGetOutputType(DWORD dwOutputStreamIndex, DWORD dwTypeIndex, DMO_MEDIA_TYPE *pmt) { if (!InputTypeSet(0)) { return DMO_E_TYPE_NOT_SET; } if (dwTypeIndex != 0) { return DMO_E_NO_MORE_ITEMS; } // If GetOutputType()'s pmt parameter is NULL, return S_OK if the type exists. // Return DMO_E_NO_MORE_ITEMS if the type does not exists. See the // documentation for IMediaObject::GetOutputType() for more information. if (NULL != pmt) { if (dwOutputStreamIndex == 0) { // Create our media type HRESULT hr = MoInitMediaType(pmt, FIELD_OFFSET(VIDEOINFO, dwBitMasks[3])); if (FAILED(hr)) { return hr; } const VIDEOINFOHEADER *pvihInput = (const VIDEOINFOHEADER *)InputType(0)->pbFormat; LONG lWidth = pvihInput->bmiHeader.biWidth; LONG lHeight = pvihInput->bmiHeader.biHeight; // Initialize the media type structure (MoInitMediaType initalized cbFormat // and pbFormat) pmt->majortype = MEDIATYPE_Video; pmt->subtype = MEDIASUBTYPE_RGB565; pmt->bFixedSizeSamples = TRUE; pmt->bTemporalCompression = FALSE; pmt->lSampleSize = lWidth * lHeight * 2; pmt->formattype = FORMAT_VideoInfo; pmt->pUnk = NULL; // Initialize the format VIDEOINFO *pviOutput = (VIDEOINFO *)pmt->pbFormat; ZeroMemory(pviOutput, FIELD_OFFSET(VIDEOINFO, dwBitMasks[3])); pviOutput->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pviOutput->bmiHeader.biCompression = BI_BITFIELDS; pviOutput->bmiHeader.biBitCount = 16; pviOutput->bmiHeader.biPlanes = 1; pviOutput->bmiHeader.biWidth = lWidth; pviOutput->bmiHeader.biHeight = lHeight; pviOutput->bmiHeader.biSizeImage = pmt->lSampleSize; pviOutput->TrueColorInfo.dwBitMasks[0] = 0xF800; pviOutput->TrueColorInfo.dwBitMasks[1] = 0x07E0; pviOutput->TrueColorInfo.dwBitMasks[2] = 0x001F; pviOutput->AvgTimePerFrame = pvihInput->AvgTimePerFrame; } else { ZeroMemory(pmt, sizeof(*pmt)); pmt->majortype = MEDIATYPE_Text; } } return S_OK; } HRESULT CSample::InternalGetInputSizeInfo(DWORD dwInputStreamIndex, DWORD *pcbSize, DWORD *pcbMaxLookahead, DWORD *pcbAlignment) { *pcbSize = 1; *pcbMaxLookahead = 0; *pcbAlignment = 1; return S_OK; } HRESULT CSample::InternalGetOutputSizeInfo(DWORD dwOutputStreamIndex, DWORD *pcbSize, DWORD *pcbAlignment) { *pcbAlignment = 1; if (dwOutputStreamIndex == 0) { *pcbSize = OutputType(0)->lSampleSize; return S_OK; } else { *pcbSize = sizeof(L"hh:mm:ss:ff"); // hh:mm:ss:ff return S_OK; } } HRESULT CSample::InternalGetInputMaxLatency(DWORD dwInputStreamIndex, REFERENCE_TIME *prtMaxLatency) { return E_NOTIMPL; } HRESULT CSample::InternalSetInputMaxLatency(DWORD dwInputStreamIndex, REFERENCE_TIME rtMaxLatency) { return E_NOTIMPL; } HRESULT CSample::InternalFlush() { InternalDiscontinuity(0); // Release buffer m_pBuffer = NULL; return S_OK; } HRESULT CSample::InternalDiscontinuity(DWORD dwInputStreamIndex) { // Zero our timestamp m_rtFrame = 0; // No pictures yet m_bPicture = false; // Reset state machine m_StreamState.Reset(); return S_OK; } HRESULT CSample::InternalAllocateStreamingResources() { // Reinitialize variables InternalDiscontinuity(0); // Allocate our bitmap return S_OK; } HRESULT CSample::InternalFreeStreamingResources() { return S_OK; } HRESULT CSample::InternalProcessInput(DWORD dwInputStreamIndex, IMediaBuffer *pBuffer, DWORD dwFlags, REFERENCE_TIME rtTimestamp, REFERENCE_TIME rtTimelength) { // Check parameters _ASSERTE(m_pBuffer == NULL); HRESULT hr = pBuffer->GetBufferAndLength(&m_pbData, &m_cbData); if (FAILED(hr)) { return hr; } m_pBuffer = pBuffer; if (0 == (dwFlags & DMO_INPUT_DATA_BUFFERF_TIME)) { rtTimestamp = INVALID_TIME; } m_StreamState.TimeStamp(rtTimestamp); // Process() returns S_FALSE if there is no output, S_OK otherwise hr = Process(); return hr; } HRESULT CSample::InternalProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, DMO_OUTPUT_DATA_BUFFER *pOutputBuffers, DWORD *pdwStatus) { // Check buffer PBYTE pbData; DWORD cbData; DWORD cbCurrent; // Do we have any output? if (!m_bPicture) { return S_FALSE; } HRESULT hr = pOutputBuffers[0].pBuffer->GetBufferAndLength( &pbData, &cbCurrent); if (FAILED(hr)) { return hr; } hr = pOutputBuffers[0].pBuffer->GetMaxLength(&cbData); if (FAILED(hr)) { return hr; } if (cbData < cbCurrent + (DWORD)OutputType(0)->lSampleSize) { return E_INVALIDARG; } // Say we've filled the buffer hr = pOutputBuffers[0].pBuffer->SetLength(cbCurrent + (DWORD)OutputType(0)->lSampleSize); cbData -= cbCurrent; pbData += cbCurrent; // Generate our data DWORD dwTimeCode; REFERENCE_TIME rt = m_StreamState.PictureTime(&dwTimeCode); TCHAR szBuffer[20]; wsprintf(szBuffer, TEXT("%2.2d:%2.2d:%2.2d:%2.2d"), (dwTimeCode >> 19) & 0x1F, (dwTimeCode >> 13) & 0x3F, (dwTimeCode >> 6) & 0x3F, dwTimeCode & 0x3F); // Update our bitmap with turquoise OurFillRect((const VIDEOINFOHEADER *)OutputType(0)->pbFormat, pbData, 0x03EF); // Draw our text DrawOurText((const VIDEOINFOHEADER *)OutputType(0)->pbFormat, pbData, szBuffer); pOutputBuffers[0].dwStatus = DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT; // Set the timestamp if (rt != INVALID_TIME) { pOutputBuffers[0].rtTimestamp = rt; } else { pOutputBuffers[0].rtTimestamp = m_rtFrame; } REFERENCE_TIME rtLength = ((const VIDEOINFOHEADER *)OutputType(0)->pbFormat)->AvgTimePerFrame; pOutputBuffers[0].rtTimelength = rtLength; m_rtFrame = pOutputBuffers[0].rtTimestamp + rtLength; // Uncompressed video must always have a timestamp pOutputBuffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME | DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH; // Update our state m_bPicture = false; // Is there any more data to output at this point? if (S_OK == Process()) { pOutputBuffers[0].dwStatus = DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE; } // Is there an output buffer for stream 1? if (pOutputBuffers[1].pBuffer) { PBYTE pbData; DWORD cbLength; DWORD cbMax; DWORD dwLen = lstrlen(szBuffer) * sizeof(WCHAR); if (S_OK == pOutputBuffers[1].pBuffer->GetBufferAndLength(&pbData, &cbLength) && S_OK == pOutputBuffers[1].pBuffer->GetMaxLength(&cbMax) && cbLength + dwLen <= cbMax) { // Convert to UNICODE! USES_CONVERSION; LPWSTR lpsz = T2W(szBuffer); CopyMemory(pbData + cbLength, lpsz, dwLen); pOutputBuffers[1].pBuffer->SetLength(cbLength + dwLen); pOutputBuffers[1].dwStatus = pOutputBuffers[0].dwStatus; pOutputBuffers[1].rtTimestamp = pOutputBuffers[0].rtTimestamp; pOutputBuffers[1].rtTimelength = rtLength; } } return S_OK; } HRESULT CSample::InternalAcceptingInput(DWORD dwInputStreamIndex) { return m_pBuffer == NULL ? S_OK : S_FALSE; } // Scan input data until either we're exhausted or we find // a picture start code // Note GOP time codes as we encounter them so we can // output time codes HRESULT CSample::Process() { // Process bytes and update our state machine while (m_cbData && !m_bPicture) { m_bPicture = m_StreamState.NextByte(*m_pbData); m_cbData--; m_pbData++; } // Release buffer if we're done with it if (m_cbData == 0) { m_pBuffer = NULL; } // assert that if have no picture to output then we ate all the data _ASSERTE(m_bPicture || m_cbData == 0); return m_bPicture ? S_OK : S_FALSE; }