windows-nt/Source/XPSP1/NT/multimedia/dshow/streams/writer/write.cpp
2020-09-26 16:20:57 +08:00

449 lines
14 KiB
C++

// Copyright (c) 1997 Microsoft Corporation. All Rights Reserved.
#include <windows.h>
#include <stdio.h>
#include "strmif.h"
#include "uuids.h"
#include "ddraw.h"
#include "mmstream.h"
#include "amstream.h"
#include "ddstream.h"
typedef HRESULT (STDAPICALLTYPE * PFNSAMPLECALLBACK) (IStreamSample *pSource,
IStreamSample *pDest,
void * pvContext);
#define RELEASE(x) if (x) { (x)->Release(); (x) = NULL; };
#define CHECK_ERROR(x) \
if (FAILED(hr = (x))) { \
printf(#x " failed with HRESULT(0x%8.8X)\n", hr); \
goto Exit; \
}
#define MAX_COPY_STREAMS 5
class CopyPair {
public:
IStreamSample *pSource;
IStreamSample *pDest;
PFNSAMPLECALLBACK pCallback;
void * pCallbackContext;
HRESULT hrLastStatus;
bool bReading;
};
class CCopyEngine
{
public:
CCopyEngine() : m_cNumPairs(0) {};
~CCopyEngine();
HRESULT CopyMediaStream(IMultiMediaStream *pSourceStream,
IMultiMediaStream *pDestStream,
REFMSPID PurposeId,
PFNSAMPLECALLBACK pCallback = NULL,
void * pContext = NULL);
HRESULT AddCopyPair(IStreamSample *pSource, IStreamSample *pDest,
PFNSAMPLECALLBACK pCallback = NULL, void * pvContext = NULL);
HRESULT CopyStreamData();
private:
CopyPair m_aPair[MAX_COPY_STREAMS];
HANDLE m_aEvent[MAX_COPY_STREAMS];
int m_cNumPairs;
};
HRESULT CCopyEngine::AddCopyPair(IStreamSample *pSource, IStreamSample *pDest,
PFNSAMPLECALLBACK pCallback, void * pContext)
{
if (m_cNumPairs >= MAX_COPY_STREAMS) {
return E_FAIL;
}
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent) {
return E_OUTOFMEMORY;
}
pSource->AddRef();
pDest->AddRef();
m_aEvent[m_cNumPairs] = hEvent;
m_aPair[m_cNumPairs].pSource = pSource;
m_aPair[m_cNumPairs].pDest = pDest;
m_aPair[m_cNumPairs].pCallback = pCallback;
m_aPair[m_cNumPairs].pCallbackContext = pContext;
m_cNumPairs++;
return NOERROR;
}
HRESULT CCopyEngine::CopyMediaStream(IMultiMediaStream *pSourceMMStream,
IMultiMediaStream *pDestMMStream,
REFMSPID PurposeId,
PFNSAMPLECALLBACK pCallback, void * pContext)
{
HRESULT hr = E_FAIL; // Assume it won't work.
IMediaStream *pSource;
if (pSourceMMStream->GetMediaStream(PurposeId, &pSource) == NOERROR) {
IMediaStream *pDest;
if (pDestMMStream->GetMediaStream(PurposeId, &pDest) == NOERROR) {
IStreamSample *pSourceSample;
hr = pSource->AllocateSample(0, &pSourceSample);
if (SUCCEEDED(hr)) {
IStreamSample *pDestSample;
hr = pDest->CreateSharedSample(pSourceSample, 0, &pDestSample);
if (SUCCEEDED(hr)) {
hr = AddCopyPair(pSourceSample, pDestSample, pCallback, pContext);
pDestSample->Release();
}
pSourceSample->Release();
}
pDest->Release();
}
pSource->Release();
}
return hr;
}
HRESULT CCopyEngine::CopyStreamData()
{
if (m_cNumPairs == 0) {
return S_FALSE;
}
int i;
for (i = 0; i < m_cNumPairs; i++) {
m_aPair[i].hrLastStatus = NOERROR;
m_aPair[i].bReading = true;
m_aPair[i].pSource->Update(0, m_aEvent[i], NULL, 0);
}
int NumRunning = i;
while (NumRunning > 0) {
DWORD dwWaitRet = WaitForMultipleObjects(m_cNumPairs, m_aEvent, FALSE, INFINITE);
if (dwWaitRet >= WAIT_OBJECT_0 && dwWaitRet < WAIT_OBJECT_0 + m_cNumPairs) {
int iCompleted = dwWaitRet - WAIT_OBJECT_0;
CopyPair *pPair = &m_aPair[iCompleted];
IStreamSample *pDone = pPair->bReading ? pPair->pSource : pPair->pDest;
pPair->hrLastStatus = pDone->CompletionStatus(0, 0);
if (pPair->hrLastStatus == NOERROR) {
if (pPair->bReading) {
STREAM_TIME stStart, stStop;
if (pPair->pCallback) {
pPair->pCallback(pPair->pSource, pPair->pDest, pPair->pCallbackContext);
}
pPair->pSource->GetSampleTimes(&stStart, &stStop, NULL);
pPair->pDest->SetSampleTimes(&stStart, &stStop);
pPair->pDest->Update(0, m_aEvent[iCompleted], NULL, 0);
pPair->bReading = false;
} else {
pPair->pSource->Update(0, m_aEvent[iCompleted], NULL, 0);
pPair->bReading = true;
}
} else {
if (pPair->bReading && pPair->hrLastStatus == MS_S_ENDOFSTREAM) {
IMediaStream *pStream;
pPair->pDest->GetMediaStream(&pStream);
pStream->SendEndOfStream(0);
pStream->Release();
ResetEvent(m_aEvent[iCompleted]);
}
NumRunning--;
}
}
}
return NOERROR;
}
CCopyEngine::~CCopyEngine()
{
int i;
for (i = 0; i < m_cNumPairs; i++) {
CloseHandle(m_aEvent[i]);
m_aPair[i].pSource->Release();
m_aPair[i].pDest->Release();
}
}
HRESULT STDAPICALLTYPE ArcEffect(IStreamSample *pSource, IStreamSample *pDest, void * pvPrimarySurface)
{
static int iFrame = 0;
IDirectDrawStreamSample *pSample;
if (pSource->QueryInterface(IID_IDirectDrawStreamSample, (void **)&pSample) == NOERROR) {
IDirectDrawSurface *pSurface;
IDirectDrawSurface *pPrimarySurface = (IDirectDrawSurface *)pvPrimarySurface;
RECT rect;
if (SUCCEEDED(pSample->GetSurface(&pSurface, &rect))) {
HDC hdc;
if (SUCCEEDED(pSurface->GetDC(&hdc))) {
Ellipse(hdc, 0, 0, (iFrame * 2) % rect.right, iFrame % rect.bottom);
pSurface->ReleaseDC(hdc);
}
pPrimarySurface->Blt(&rect, pSurface, &rect, DDBLT_WAIT, NULL);
pSurface->Release();
}
pSample->Release();
}
iFrame ++;
return NOERROR;
}
/*
HRESULT PolylineEffectToSample(IDirectDrawStreamSample *pSample)
{
IDirectDrawSurface *pSurface = NULL;
RECT rect;
HRESULT hr;
POINT pt[2] = {0};
HDC hdc;
CHECK_ERROR(pSample->GetSurface(&pSurface, &rect));
pt[1].x = iFrame % rect.right;
pt[1].y = rect.bottom;
CHECK_ERROR(pSurface->GetDC(&hdc));
Polyline(hdc, pt, 2);
pSurface->ReleaseDC(hdc);
iFrame ++;
Exit:
RELEASE(pSurface);
return hr;
}
*/
HRESULT FindCompressor(REFCLSID rcidCategory,
int Index,
IBaseFilter **ppFilter)
{
*ppFilter = NULL;
ICreateDevEnum *pCreateDevEnum = NULL;
IEnumMoniker *pEm = NULL;
IMoniker *pMoniker = NULL;
ULONG cFetched;
HRESULT hr;
CHECK_ERROR(CoCreateInstance(
CLSID_SystemDeviceEnum,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,
(void**)&pCreateDevEnum));
CHECK_ERROR(pCreateDevEnum->CreateClassEnumerator(rcidCategory, &pEm, 0));
if (Index) {
pEm->Skip(Index);
}
CHECK_ERROR(pEm->Next(1, &pMoniker, &cFetched));
if (cFetched == 1) {
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)ppFilter);
}
Exit:
RELEASE(pMoniker);
RELEASE(pCreateDevEnum);
RELEASE(pEm);
return hr;
}
HRESULT CreateStreamWithSameFormat(IAMMultiMediaStream *pAMStream,
IMultiMediaStream *pSourceMMStream,
REFMSPID PurposeId,
IMediaStream **ppNewMediaStream)
{
IMediaStream *pSource;
HRESULT hr = pSourceMMStream->GetMediaStream(PurposeId, &pSource);
if (SUCCEEDED(hr)) {
hr = pAMStream->AddMediaStream(pSource, &PurposeId, AMMSF_CREATEPEER, ppNewMediaStream);
pSource->Release();
}
return hr;
}
HRESULT CreateWriterStream(const char * pszOutputFileName,
IMultiMediaStream *pSourceMMStream,
IDirectDraw *pDD,
IMultiMediaStream **ppMMStream)
{
static LPWSTR szVideoRender = L"@device:sw:CLSID\\{083863F1-70DE-11D0-BD40-00A0C911CE86}\\Instance\\{70E102B0-5556-11CE-97C0-00AA0055595A}";
*ppMMStream = NULL;
IAMMultiMediaStream *pAMStream = NULL;
IMediaStream *pVideoStream = NULL;
IMediaStream *pAudioStream = NULL;
ICaptureGraphBuilder *pBuilder = NULL;
IGraphBuilder *pFilterGraph = NULL;
IFileSinkFilter *pFileSinkWriter = NULL;
IBaseFilter *pVideoCompressFilter = NULL;
IBaseFilter *pMuxFilter = NULL;
HRESULT hr;
WCHAR wPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pszOutputFileName, -1, wPath, sizeof(wPath)/sizeof(wPath[0]));
CHECK_ERROR(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
IID_IAMMultiMediaStream, (void **)&pAMStream));
CHECK_ERROR(pAMStream->Initialize(STREAMTYPE_WRITE, 0, NULL));
CHECK_ERROR(CreateStreamWithSameFormat(pAMStream, pSourceMMStream, MSPID_PrimaryVideo, &pVideoStream));
CHECK_ERROR(CreateStreamWithSameFormat(pAMStream, pSourceMMStream, MSPID_PrimaryAudio, &pAudioStream));
CHECK_ERROR(pAMStream->GetFilterGraph(&pFilterGraph));
// Create a new Bind Context (connects a file to an object type)
LPBC lpBC;
CreateBindCtx(0, &lpBC);
ULONG cchEaten;
IMoniker *pMoniker;
hr = MkParseDisplayName(lpBC, szVideoRender, &cchEaten, &pMoniker);
if( SUCCEEDED(hr) ) {
IBaseFilter *pVideoRenderFilter = 0;
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pVideoRenderFilter);
if( SUCCEEDED(hr) ) {
pFilterGraph->AddFilter(pVideoRenderFilter, L"Video Renderer");
pVideoRenderFilter->Release();
}
pMoniker->Release();
}
pAMStream->Render(0);
/*
CHECK_ERROR(CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder, (void **)&pBuilder));
CHECK_ERROR(pBuilder->SetFiltergraph(pFilterGraph));
CHECK_ERROR(pBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, wPath, &pMuxFilter, &pFileSinkWriter));
CHECK_ERROR(FindCompressor(CLSID_VideoCompressorCategory, 1, &pVideoCompressFilter));
CHECK_ERROR(pFilterGraph->AddFilter(pVideoCompressFilter, L"Video Compression filter"))
CHECK_ERROR(pBuilder->RenderCompressionStream(pVideoStream, pVideoCompressFilter, pMuxFilter));
CHECK_ERROR(pBuilder->RenderCompressionStream(pAudioStream, NULL, pMuxFilter));
*/
*ppMMStream = pAMStream;
pAMStream->AddRef();
Exit:
if (pAMStream == NULL) {
printf("Could not create a CLSID_MultiMediaStream object\n"
"Check you have run regsvr32 amstream.dll\n");
}
RELEASE(pAMStream);
RELEASE(pBuilder);
RELEASE(pFilterGraph);
RELEASE(pFileSinkWriter);
RELEASE(pVideoCompressFilter);
RELEASE(pMuxFilter);
RELEASE(pVideoStream);
RELEASE(pAudioStream);
return hr;
}
HRESULT OpenReadMMStream(const char * pszFileName, IDirectDraw *pDD, IMultiMediaStream **ppMMStream)
{
*ppMMStream = NULL;
IAMMultiMediaStream *pAMStream;
HRESULT hr;
CHECK_ERROR(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
IID_IAMMultiMediaStream, (void **)&pAMStream));
CHECK_ERROR(pAMStream->Initialize(STREAMTYPE_READ, 0, NULL));
CHECK_ERROR(pAMStream->AddMediaStream(pDD, &MSPID_PrimaryVideo, 0, NULL));
CHECK_ERROR(pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL));
WCHAR wPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, wPath, sizeof(wPath)/sizeof(wPath[0]));
CHECK_ERROR(pAMStream->OpenFile(wPath, 0));
*ppMMStream = pAMStream;
pAMStream->AddRef();
Exit:
if (pAMStream == NULL) {
printf("Could not create a CLSID_MultiMediaStream object\n"
"Check you have run regsvr32 amstream.dll\n");
}
RELEASE(pAMStream);
return hr;
}
HRESULT RenderStreamToSurface(IDirectDraw *pDD, IDirectDrawSurface *pPrimary,
IMultiMediaStream *pReadStream,
IMultiMediaStream *pWriteStream)
{
HRESULT hr;
CCopyEngine Engine;
CHECK_ERROR(Engine.CopyMediaStream(pReadStream, pWriteStream, MSPID_PrimaryVideo, ArcEffect, pPrimary));
CHECK_ERROR(Engine.CopyMediaStream(pReadStream, pWriteStream, MSPID_PrimaryAudio));
CHECK_ERROR(pReadStream->SetState(STREAMSTATE_RUN));
CHECK_ERROR(pWriteStream->SetState(STREAMSTATE_RUN));
Engine.CopyStreamData();
pReadStream->SetState(STREAMSTATE_STOP);
pWriteStream->SetState(STREAMSTATE_STOP);
Exit:
return hr;
}
int _CRTAPI1
main(
int argc,
char *argv[]
)
{
if (argc < 2) {
printf("Usage : writer movie.ext\n");
exit(0);
}
CoInitialize(NULL);
IDirectDraw *pDD;
HRESULT hr = DirectDrawCreate(NULL, &pDD, NULL);
if (SUCCEEDED(hr)) {
DDSURFACEDESC ddsd;
IDirectDrawSurface *pPrimarySurface;
pDD->SetCooperativeLevel(GetDesktopWindow(), DDSCL_NORMAL);
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
hr = pDD->CreateSurface(&ddsd, &pPrimarySurface, NULL);
if (SUCCEEDED(hr)) {
IMultiMediaStream *pReadStream;
hr = OpenReadMMStream(argv[1], pDD, &pReadStream);
if (SUCCEEDED(hr)) {
IMultiMediaStream *pWriteStream;
hr = CreateWriterStream("C:\\TEST.AVI", pReadStream, pDD, &pWriteStream);
if (SUCCEEDED(hr)) {
RenderStreamToSurface(pDD, pPrimarySurface, pReadStream, pWriteStream);
pWriteStream->Release();
}
pReadStream->Release();
}
pPrimarySurface->Release();
}
pDD->Release();
} else {
printf("Could not open DirectDraw - check it is installed\n");
}
CoUninitialize();
return 0;
}