//==========================================================================; // // 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) 1994 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; // // PROGRAM: VidClip.c - Based upon Generic.C // // PURPOSE: Illustrates the 'minimum' functionality of a well-behaved Win32 application. // // PLATFORMS: Windows 95, Windows NT 4.0 and up // // FUNCTIONS: // WinMain() - calls initialization function, processes message loop // InitApplication() - Initializes window data nd registers window // InitInstance() -saves instance handle and creates main window // WindProc() Processes messages // About() - Process menssages for "About" dialog box // CenterWindow() - Centers one window over another // // SPECIAL INSTRUCTIONS: N/A // #include "vcproj.h" #define APPNAME "VidClip" #include #include #include #include #include #define _WIN32_WINNT 0x0400 #define _ATL_APARTMENT_THREADED #include "atlbase.h" CComModule _Module; #include "atlcom.h" #include // Global Variables: HINSTANCE hInst; // current instance char szAppName[100]; // Name of the app char szTitle[100]; // The title bar text IDirectDraw *g_pDD = NULL; TCHAR g_szStart[100]; TCHAR g_szEnd[100]; TCHAR g_szAll[100]; const DDPIXELFORMAT g_aPixelFormats[] = { /// {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_PALETTEINDEXED8, 0, 8, 0, 0, 0, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0} }; CDocument g_Document; // Foward declarations of functions included in this code module: BOOL InitApplication(HINSTANCE); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); LPTSTR GetStringRes (int id); HRESULT CreateWriterStream(LPOLESTR szOutputFileName, LPOLESTR szVideoCodec, DDSURFACEDESC& ddsdVideoFormat, LPOLESTR szAudioCodec, WAVEFORMATEX& wfexAudioFormat, IMultiMediaStream **ppMMStream); void DoMakeMovie(HINSTANCE, HWND, CDocument *); // // FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) // // PURPOSE: Entry point for the application. // // COMMENTS: // // This function initializes the application and processes the // message loop. // int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; CoInitialize(NULL); // Initialize global strings lstrcpy (szAppName, APPNAME); LoadString (hInstance, IDS_APP_TITLE, szTitle, 100); LoadString (hInstance, IDS_START, g_szStart, sizeof(g_szStart)); LoadString (hInstance, IDS_END, g_szEnd, sizeof(g_szEnd)); LoadString (hInstance, IDS_ALL, g_szAll, sizeof(g_szAll)); if (!hPrevInstance) { // Perform instance initialization: if (!InitApplication(hInstance)) { return (FALSE); } } // Perform application initialization: if (!InitInstance(hInstance, nCmdShow)) { return (FALSE); } hAccelTable = LoadAccelerators (hInstance, szAppName); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } CoUninitialize(); return (msg.wParam); lpCmdLine; // This will prevent 'unused formal parameter' warnings } // // FUNCTION: InitApplication(HANDLE) // // PURPOSE: Initializes window data and registers window class // // COMMENTS: // // In this function, we initialize a window class by filling out a data // structure of type WNDCLASS. // BOOL InitApplication(HINSTANCE hInstance) { WNDCLASSEX wcex; HWND hwnd; // Win32 will always set hPrevInstance to NULL, so lets check // things a little closer. This is because we only want a single // version of this app to run at a time hwnd = FindWindow (szAppName, szTitle); if (hwnd) { // We found another version of ourself. Lets defer to it: if (IsIconic(hwnd)) { ShowWindow(hwnd, SW_RESTORE); } SetForegroundWindow (hwnd); // If this app actually had any functionality, we would // also want to communicate any action that our 'twin' // should now perform based on how the user tried to // execute us. return FALSE; } else { // Fill in window class structure with parameters that describe // the main window. // Added elements for Windows 95: wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon (hInstance, szAppName); wcex.hIconSm = LoadIcon(hInstance, "SMALL"); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = szAppName; wcex.lpszClassName = szAppName; // Register the window class and return success/failure code. return RegisterClassEx(&wcex); } } BOOL AddListViewItem(HWND hLV, TCHAR * pszItem, void * pThingie) { LV_ITEM lvi; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = 0x7FFF; lvi.iSubItem = 0; lvi.pszText = pszItem; lvi.lParam = (LPARAM)pThingie; lvi.cchTextMax = 0; ListView_InsertItem(hLV, &lvi); return TRUE; } // // FUNCTION: InitInstance(HANDLE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // Store instance handle in our global variable hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return (FALSE); } InitCommonControls(); /* HWND hListView = CreateWindowEx(0, WC_LISTVIEW, "Fred", WS_CHILDWINDOW | WS_CLIPSIBLINGS | LVS_REPORT, 0, 0, 0, 0, hWnd, (HMENU)1, hInstance, NULL); InitListView(hListView); AddListViewItem(hListView, "This is a test", NULL); AddListViewItem(hListView, "And another test", NULL); AddListViewItem(hListView, "And yet another test", NULL); */ g_Document.Initialize(hWnd); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); MessageBox (GetFocus(), GetStringRes(IDS_LIMITEDFUNCTION), szAppName, MB_OK); //ShowWindow(hListView, nCmdShow); //UpdateWindow(hListView); // BUGBUG -- There is still a bug in ddstream with 8bpp palettes & ddreaw // Make sure to make this work with a NULL g_pDD!!! CComPtr pFactory; HRESULT hr = CoCreateInstance(CLSID_DirectDrawFactory, NULL, CLSCTX_INPROC_SERVER, IID_IDirectDrawFactory, (void **)&pFactory); if (SUCCEEDED(hr)) { hr = pFactory->CreateDirectDraw(NULL, NULL, DDSCL_NORMAL,0, NULL, &g_pDD); } return (TRUE); } void UpdateMenus(HWND hWnd, CDocument * pDoc) { BOOL bEnableSave = FALSE; BOOL bEnableEdit = FALSE; if (pDoc->m_ClipList.NumClips() > 0) { bEnableSave = (pDoc->m_TargetFileName != NULL); bEnableEdit = (pDoc->m_ClipList.CurSelClipIndex() >= 0); } HMENU hMenu = GetMenu(hWnd); UINT uEditSetting = bEnableEdit ? MF_ENABLED : MF_GRAYED; UINT uSaveSetting = bEnableSave ? MF_ENABLED : MF_GRAYED; EnableMenuItem(hMenu, IDM_VIDEO_EDITCLIP, uEditSetting); EnableMenuItem(hMenu, IDM_VIDEO_DELETECLIP, uEditSetting); EnableMenuItem(hMenu, IDM_VIDEO_MAKEMOVIE, uSaveSetting); EnableMenuItem(hMenu, IDM_SAVE, uSaveSetting); EnableMenuItem(hMenu, IDM_SAVEAS, uSaveSetting); } // // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // PURPOSE: Processes messages for the main window. // // MESSAGES: // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // WM_DISPLAYCHANGE - message sent to Plug & Play systems when the display changes // WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate // WM_NCRBUTTONUP - User has clicked the right button on the application's system menu // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; POINT pnt; HMENU hMenu; BOOL bGotHelp; CDocument *pDoc = &g_Document; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); // Remember, these are... wmEvent = HIWORD(wParam); // ...different for Win32! //Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About); break; case IDM_SETTINGS: DoSettingsDialog(hInst, hWnd, pDoc); UpdateMenus(hWnd, pDoc); break; case IDM_EXIT: DestroyWindow (hWnd); break; case IDM_HELPTOPICS: // Only called in Windows 95 bGotHelp = WinHelp (hWnd, APPNAME".HLP", HELP_FINDER,(DWORD)0); if (!bGotHelp) { MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), szAppName, MB_OK|MB_ICONHAND); } break; case IDM_HELPCONTENTS: // Not called in Windows 95 bGotHelp = WinHelp (hWnd, APPNAME".HLP", HELP_CONTENTS,(DWORD)0); if (!bGotHelp) { MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), szAppName, MB_OK|MB_ICONHAND); } break; case IDM_HELPSEARCH: // Not called in Windows 95 if (!WinHelp(hWnd, APPNAME".HLP", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) { MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), szAppName, MB_OK|MB_ICONHAND); } break; case IDM_HELPHELP: // Not called in Windows 95 if(!WinHelp(hWnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) { MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), szAppName, MB_OK|MB_ICONHAND); } break; case IDM_VIDEO_ADDCLIP: pDoc->NewClip(); break; case IDM_VIDEO_EDITCLIP: pDoc->EditClip(); break; case IDM_VIDEO_DELETECLIP: pDoc->DeleteClip(); break; case IDM_VIDEO_MAKEMOVIE: DoMakeMovie(hInst, hWnd, pDoc); break; // Here are all the other possible menu options, // all of these are currently disabled: case IDM_NEW: pDoc->ResetContents(); break; case IDM_OPEN: return pDoc->OpenFile(); case IDM_SAVE: case IDM_SAVEAS: return pDoc->SaveAsFile(wmId == IDM_SAVEAS); case IDM_UNDO: case IDM_CUT: case IDM_COPY: case IDM_PASTE: case IDM_LINK: case IDM_LINKS: default: return (DefWindowProc(hWnd, message, wParam, lParam)); } break; case WM_NCRBUTTONUP: // RightClick on windows non-client area... if (SendMessage(hWnd, WM_NCHITTEST, 0, lParam) == HTSYSMENU) { // The user has clicked the right button on the applications // 'System Menu'. Here is where you would alter the default // system menu to reflect your application. Notice how the // explorer deals with this. For this app, we aren't doing // anything return (DefWindowProc(hWnd, message, wParam, lParam)); } else { // Nothing we are interested in, allow default handling... return (DefWindowProc(hWnd, message, wParam, lParam)); } break; case WM_RBUTTONDOWN: // RightClick in windows client area... pnt.x = LOWORD(lParam); pnt.y = HIWORD(lParam); ClientToScreen(hWnd, (LPPOINT) &pnt); // This is where you would determine the appropriate 'context' // menu to bring up. Since this app has no real functionality, // we will just bring up the 'Help' menu: hMenu = GetSubMenu (GetMenu (hWnd), 2); if (hMenu) { TrackPopupMenu (hMenu, 0, pnt.x, pnt.y, 0, hWnd, NULL); } else { // Couldn't find the menu... MessageBeep(0); } break; case WM_PAINT: hdc = BeginPaint (hWnd, &ps); // Add any drawing code here... EndPaint (hWnd, &ps); break; case WM_DESTROY: // Tell WinHelp we don't need it any more... WinHelp (hWnd, APPNAME".HLP", HELP_QUIT,(DWORD)0); PostQuitMessage(0); break; case WM_SIZE: pDoc->m_ClipList.SetSize(LOWORD(lParam), HIWORD(lParam)); break; case WM_NOTIFY: { LPNMHDR pnmhdr = (LPNMHDR)lParam; if (pnmhdr->hwndFrom == pDoc->m_ClipList.m_hLV) { if (pnmhdr->code == NM_DBLCLK) { pDoc->EditClip(); } UpdateMenus(hWnd, pDoc); } } break; case WM_ACTIVATE: UpdateMenus(hWnd, pDoc); // Fall through... default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (0); } // // FUNCTION: About(HWND, unsigned, WORD, LONG) // // PURPOSE: Processes messages for "About" dialog box // This version allows greater flexibility over the contents of the 'About' box, // by pulling out values from the 'Version' resource. // // MESSAGES: // // WM_INITDIALOG - initialize dialog box // WM_COMMAND - Input received // // LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HFONT hfontDlg; // Font for dialog text static HFONT hFinePrint; // Font for 'fine print' in dialog DWORD dwVerInfoSize; // Size of version information block LPSTR lpVersion; // String pointer to 'version' text DWORD dwVerHnd=0; // An 'ignored' parameter, always '0' UINT uVersionLen; WORD wRootLen; BOOL bRetCode; int i; char szFullPath[256]; char szResult[256]; char szGetName[256]; DWORD dwVersion; char szVersion[40]; DWORD dwResult; switch (message) { case WM_INITDIALOG: ShowWindow (hDlg, SW_HIDE); if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE) { hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0, VARIABLE_PITCH | FF_DONTCARE, ""); hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0, VARIABLE_PITCH | FF_DONTCARE, ""); } else { hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VARIABLE_PITCH | FF_SWISS, ""); hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VARIABLE_PITCH | FF_SWISS, ""); } CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER)); GetModuleFileName (hInst, szFullPath, sizeof(szFullPath)); // Now lets dive in and pull out the version information: dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd); if (dwVerInfoSize) { LPSTR lpstrVffInfo; HANDLE hMem; hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize); lpstrVffInfo = (char *)GlobalLock(hMem); GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo); // The below 'hex' value looks a little confusing, but // essentially what it is, is the hexidecimal representation // of a couple different values that represent the language // and character set that we are wanting string values for. // 040904E4 is a very common one, because it means: // US English, Windows MultiLingual characterset // Or to pull it all apart: // 04------ = SUBLANG_ENGLISH_USA // --09---- = LANG_ENGLISH // --11---- = LANG_JAPANESE // ----04E4 = 1252 = Codepage for Windows:Multilingual lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG)); wRootLen = lstrlen(szGetName); // Save this position // Set the title of the dialog: lstrcat (szGetName, "ProductName"); bRetCode = VerQueryValue((LPVOID)lpstrVffInfo, (LPSTR)szGetName, (LPVOID *)&lpVersion, (UINT *)&uVersionLen); // Notice order of version and string... if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE) { lstrcpy(szResult, lpVersion); lstrcat(szResult, " のバージョン情報"); } else { lstrcpy(szResult, "About "); lstrcat(szResult, lpVersion); } // ----------------------------------------------------- SetWindowText (hDlg, szResult); // Walk through the dialog items that we want to replace: for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) { GetDlgItemText(hDlg, i, szResult, sizeof(szResult)); szGetName[wRootLen] = (char)0; lstrcat (szGetName, szResult); uVersionLen = 0; lpVersion = NULL; bRetCode = VerQueryValue((LPVOID)lpstrVffInfo, (LPSTR)szGetName, (LPVOID *)&lpVersion, (UINT *)&uVersionLen); if ( bRetCode && uVersionLen && lpVersion) { // Replace dialog item text with version info lstrcpy(szResult, lpVersion); SetDlgItemText(hDlg, i, szResult); } else { dwResult = GetLastError(); wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult); SetDlgItemText (hDlg, i, szResult); } SendMessage (GetDlgItem (hDlg, i), WM_SETFONT, (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg), TRUE); } // for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) GlobalUnlock(hMem); GlobalFree(hMem); } else { // No version information available. } // if (dwVerInfoSize) SendMessage (GetDlgItem (hDlg, IDC_LABEL), WM_SETFONT, (WPARAM)hfontDlg,(LPARAM)TRUE); // We are using GetVersion rather then GetVersionEx // because earlier versions of Windows NT and Win32s // didn't include GetVersionEx: dwVersion = GetVersion(); if (dwVersion < 0x80000000) { // Windows NT wsprintf (szVersion, "Microsoft Windows NT %u.%u (Build: %u)", (DWORD)(LOBYTE(LOWORD(dwVersion))), (DWORD)(HIBYTE(LOWORD(dwVersion))), (DWORD)(HIWORD(dwVersion)) ); } else if (LOBYTE(LOWORD(dwVersion))<4) { // Win32s wsprintf (szVersion, "Microsoft Win32s %u.%u (Build: %u)", (DWORD)(LOBYTE(LOWORD(dwVersion))), (DWORD)(HIBYTE(LOWORD(dwVersion))), (DWORD)(HIWORD(dwVersion) & ~0x8000) ); } else { // Windows 95 wsprintf (szVersion, "Microsoft Windows 95 %u.%u", (DWORD)(LOBYTE(LOWORD(dwVersion))), (DWORD)(HIBYTE(LOWORD(dwVersion))) ); } SetWindowText (GetDlgItem(hDlg, IDC_OSVERSION), szVersion); ShowWindow (hDlg, SW_SHOW); return (TRUE); case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, TRUE); DeleteObject (hfontDlg); DeleteObject (hFinePrint); return (TRUE); } break; } return FALSE; } // // FUNCTION: CenterWindow(HWND, HWND) // // PURPOSE: Centers one window over another. // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // // This functionwill center one window over another ensuring that // the placement of the window is within the 'working area', meaning // that it is both within the display limits of the screen, and not // obscured by the tray or other framing elements of the desktop. BOOL CenterWindow (HWND hwndChild, HWND hwndParent) { RECT rChild, rParent, rWorkArea; int wChild, hChild, wParent, hParent; int xNew, yNew; BOOL bResult; // Get the Height and Width of the child window GetWindowRect (hwndChild, &rChild); wChild = rChild.right - rChild.left; hChild = rChild.bottom - rChild.top; // Get the Height and Width of the parent window GetWindowRect (hwndParent, &rParent); wParent = rParent.right - rParent.left; hParent = rParent.bottom - rParent.top; // Get the limits of the 'workarea' bResult = SystemParametersInfo( SPI_GETWORKAREA, // system parameter to query or set sizeof(RECT), &rWorkArea, 0); if (!bResult) { rWorkArea.left = rWorkArea.top = 0; rWorkArea.right = GetSystemMetrics(SM_CXSCREEN); rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN); } // Calculate new X position, then adjust for workarea xNew = rParent.left + ((wParent - wChild) /2); if (xNew < rWorkArea.left) { xNew = rWorkArea.left; } else if ((xNew+wChild) > rWorkArea.right) { xNew = rWorkArea.right - wChild; } // Calculate new Y position, then adjust for workarea yNew = rParent.top + ((hParent - hChild) /2); if (yNew < rWorkArea.top) { yNew = rWorkArea.top; } else if ((yNew+hChild) > rWorkArea.bottom) { yNew = rWorkArea.bottom - hChild; } // Set it, and return return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } //--------------------------------------------------------------------------- // // FUNCTION: GetStringRes (int id INPUT ONLY) // // COMMENTS: Load the resource string with the ID given, and return a // pointer to it. Notice that the buffer is common memory so // the string must be used before this call is made a second time. // //--------------------------------------------------------------------------- LPTSTR GetStringRes (int id) { static TCHAR buffer[MAX_PATH]; buffer[0]=0; LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH); return buffer; } //////////////////////////////////////////////////////////////////// typedef HRESULT (STDAPICALLTYPE * PFNSAMPLECALLBACK) (IStreamSample *pSource, IStreamSample *pDest, void * pvContext); #define MAX_COPY_STREAMS 5 class CopyPair { public: CComPtr pSource; CComPtr pDest; MSPID PurposeId; PFNSAMPLECALLBACK pCallback; void * pCallbackContext; HRESULT hrLastStatus; bool bReading; bool bGotFirstSample; STREAM_TIME stActualStart; STREAM_TIME stStartBias; STREAM_TIME stLastEndTime; }; class CCopyEngine { public: CCopyEngine(IMultiMediaStream *pDestMMStream); ~CCopyEngine(); HRESULT InitStream(REFMSPID PurposeId, PFNSAMPLECALLBACK pCallback = NULL, void * pContext = NULL, bool bSharedFormat = false); HRESULT CopyStreamData(IMultiMediaStream *pSourceMMStream, STREAM_TIME stStart, STREAM_TIME stEnd); private: CComPtr m_pDestMMStream; CopyPair m_aPair[MAX_COPY_STREAMS]; HANDLE m_aEvent[MAX_COPY_STREAMS]; int m_cNumPairs; bool m_bStarted; }; /////// CCopyEngine::CCopyEngine(IMultiMediaStream *pDestMMStream) : m_cNumPairs(0), m_pDestMMStream(pDestMMStream), m_bStarted(false) {}; HRESULT CCopyEngine::InitStream(REFMSPID PurposeId, PFNSAMPLECALLBACK pCallback, void * pContext, bool bSharedFormat) { HRESULT hr = E_FAIL; // Assume it won't work. if (m_cNumPairs < MAX_COPY_STREAMS) { CComPtr pDest; if (m_pDestMMStream->GetMediaStream(PurposeId, &pDest) == NOERROR) { CComPtr pDestSample; hr = pDest->AllocateSample(0, &pDestSample); if (SUCCEEDED(hr)) { m_aEvent[m_cNumPairs] = CreateEvent(NULL, TRUE, FALSE, NULL); if (!m_aEvent[m_cNumPairs]) { hr = E_OUTOFMEMORY; } else { m_aPair[m_cNumPairs].pDest = pDestSample; m_aPair[m_cNumPairs].pCallback = pCallback; m_aPair[m_cNumPairs].pCallbackContext = pContext; m_aPair[m_cNumPairs].PurposeId = PurposeId; m_aPair[m_cNumPairs].stStartBias = 0; m_cNumPairs++; } } } } return hr; } #ifdef USE_APCS VOID APIENTRY OurAPC(DWORD dwParam) { SetEvent((HANDLE)dwParam); } #endif HRESULT CCopyEngine::CopyStreamData(IMultiMediaStream *pSourceMMStream, STREAM_TIME stClipStart, STREAM_TIME stClipEnd) { HRESULT hr; if (m_cNumPairs == 0) { return S_FALSE; } int i; for (i = 0; i < m_cNumPairs; i++) { m_aPair[i].bGotFirstSample = false; CComPtr pSource; if (pSourceMMStream->GetMediaStream(m_aPair[i].PurposeId, &pSource) == NOERROR) { m_aPair[i].pSource = NULL; hr = pSource->AllocateSample(0, &m_aPair[i].pSource); } else { hr = E_FAIL; } if (FAILED(hr)) { return hr; } } pSourceMMStream->SetState(STREAMSTATE_RUN); if (stClipStart) { pSourceMMStream->Seek(stClipStart); } for (i = 0; i < m_cNumPairs; i++) { m_aPair[i].hrLastStatus = NOERROR; m_aPair[i].bReading = true; #ifdef USE_APCS m_aPair[i].pSource->Update(0, NULL, OurAPC, (DWORD)m_aEvent[i]); #else m_aPair[i].pSource->Update(0, m_aEvent[i], NULL, 0); #endif } if (!m_bStarted) { m_pDestMMStream->SetState(STREAMSTATE_RUN); m_bStarted = true; } int NumRunning = m_cNumPairs; while (NumRunning > 0) { #ifdef USE_APCS DWORD dwWaitRet; for (; ; ) { dwWaitRet = WaitForMultipleObjectsEx(m_cNumPairs, m_aEvent, FALSE, INFINITE, TRUE); if (dwWaitRet != WAIT_IO_COMPLETION) { break; } } #else DWORD dwWaitRet = WaitForMultipleObjects(m_cNumPairs, m_aEvent, FALSE, INFINITE); #endif 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; pPair->pSource->GetSampleTimes(&stStart, &stStop, NULL); if (stClipEnd > 0 && stStart > stClipEnd) { if (pPair->bGotFirstSample) { pPair->stStartBias += pPair->stLastEndTime - pPair->stActualStart; } NumRunning--; ResetEvent(m_aEvent[iCompleted]); } else { if (pPair->pCallback) { pPair->pCallback(pPair->pSource, pPair->pDest, pPair->pCallbackContext); } if (!pPair->bGotFirstSample) { pPair->stActualStart = stStart; pPair->bGotFirstSample = true; } pPair->stLastEndTime = stStop; stStart += pPair->stStartBias - pPair->stActualStart; stStop += pPair->stStartBias - pPair->stActualStart; pPair->pDest->SetSampleTimes(&stStart, &stStop); pPair->bReading = false; pPair->pDest->Update(0, m_aEvent[iCompleted], NULL, 0); } } else { pPair->pSource->Update(0, m_aEvent[iCompleted], NULL, 0); pPair->bReading = true; } } else { if (pPair->bGotFirstSample) { pPair->stStartBias += pPair->stLastEndTime - pPair->stActualStart; } ResetEvent(m_aEvent[iCompleted]); NumRunning--; } } } #ifdef USE_APCS // Flush out any APCs SleepEx(0, TRUE); #endif pSourceMMStream->SetState(STREAMSTATE_STOP); for (i = 0; i < m_cNumPairs; i++) { m_aPair[i].pSource = NULL; // Release the source sample } return NOERROR; } CCopyEngine::~CCopyEngine() { int i; for (i = 0; i < m_cNumPairs; i++) { CloseHandle(m_aEvent[i]); if (m_bStarted && m_aPair[i].pDest) { CComPtr pMS; m_aPair[i].pDest->GetMediaStream(&pMS); pMS->SendEndOfStream(0); } } if (m_bStarted) { m_pDestMMStream->SetState(STREAMSTATE_STOP); } } /////////////////////////////////////////////////////////////////// void ErrorMessage(int StringId) { MessageBox (GetFocus(), GetStringRes(StringId), szAppName, MB_OK|MB_ICONHAND); } #define CHECK_ERROR(x, idFailMsg) if (FAILED(hr = (x))) { if (idFailMsg) ErrorMessage(idFailMsg); goto Exit; } #define RECTHEIGHT(r) ((r).bottom - (r).top) #define RECTWIDTH(r) ((r).right - (r).left) HRESULT STDAPICALLTYPE VideoCallback(IStreamSample *pSource, IStreamSample *pDest, void * pvhWnd) { CComQIPtr pSrcSample(pSource); CComPtr pSrcSurface; RECT rectSrc; if (SUCCEEDED(pSrcSample->GetSurface(&pSrcSurface, &rectSrc))) { HDC hdcSurface; if (SUCCEEDED(pSrcSurface->GetDC(&hdcSurface))) { CComQIPtr pDestSample(pDest); CComPtr pDestSurface; RECT rectDest; HRESULT hr = pDestSample->GetSurface(&pDestSurface, &rectDest); HDC hdcDest = GetDC((HWND)pvhWnd); BOOL fred = StretchBlt(hdcDest, 0, 0, rectDest.right-rectDest.left, rectDest.bottom-rectDest.top, hdcSurface, rectSrc.left, rectSrc.top, RECTWIDTH(rectSrc), RECTHEIGHT(rectSrc), SRCCOPY); ReleaseDC((HWND)pvhWnd, hdcDest); hr = pDestSurface->GetDC(&hdcDest); StretchBlt(hdcDest, 0, 0, rectDest.right-rectDest.left, rectDest.bottom-rectDest.top, hdcSurface, rectSrc.left, rectSrc.top, RECTWIDTH(rectSrc), RECTHEIGHT(rectSrc), SRCCOPY); TextOut(hdcDest, 20, 20, "Easy to do effects!", 19); pDestSurface->ReleaseDC(hdcDest); } // Release this ALWAYS to bypass NT4.0 DDraw bug pSrcSurface->ReleaseDC(hdcSurface); } return NOERROR; } HRESULT STDAPICALLTYPE AudioCallback(IStreamSample *pSource, IStreamSample *pDest, void * pvhWnd) { // HACKHACK - this code ASSUMES the audio samples allocated in the // source and dest streams have the same size. Really it would // be best to just share the same audio sample. // Just copy the data across IAudioStreamSample *pAudioSource, *pAudioDest; IAudioData *pAudioDataSource, *pAudioDataDest; if (SUCCEEDED(pSource->QueryInterface(IID_IAudioStreamSample, (void **)&pAudioSource))) { if (SUCCEEDED(pAudioSource->GetAudioData(&pAudioDataSource))) { if (SUCCEEDED(pDest->QueryInterface(IID_IAudioStreamSample, (void **)&pAudioDest))) { if (SUCCEEDED(pAudioDest->GetAudioData(&pAudioDataDest))) { DWORD dwLenSource, dwActualSource; DWORD dwLenDest, dwActualDest; PBYTE pbSource, pbDest; pAudioDataSource->GetInfo(&dwLenSource, &pbSource, &dwActualSource); pAudioDataDest->GetInfo(&dwLenDest, &pbDest, &dwActualDest); CopyMemory(pbDest, pbSource, dwActualSource); pAudioDataDest->SetActual(dwLenSource); } pAudioDest->Release(); } } pAudioSource->Release(); } return NOERROR; } HRESULT OpenReadMMStream(LPOLESTR pszFileName, DDSURFACEDESC & ddsd, IMultiMediaStream **ppMMStream, WAVEFORMATEX& wfexAudioFormat) { *ppMMStream = NULL; CComPtr pAMStream; CComPtr pVideoStream; CComPtr pAudioStream; CComQIPtr pDDStream; CComQIPtr pAudioSpecificStream; HRESULT hr; CHECK_ERROR(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&pAMStream), IDS_UNABLETOINITREAD); CHECK_ERROR(pAMStream->Initialize(STREAMTYPE_READ, 0, NULL), IDS_UNABLETOINITREAD); CHECK_ERROR(pAMStream->AddMediaStream(g_pDD, &MSPID_PrimaryVideo, 0, &pVideoStream), IDS_UNABLETOINITREAD); CHECK_ERROR(pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, &pAudioStream), IDS_UNABLETOINITREAD); pDDStream = pVideoStream; CHECK_ERROR(pDDStream->SetFormat(&ddsd, NULL), IDS_UNABLETOINITREAD); pAudioSpecificStream = pAudioStream; CHECK_ERROR(pAudioSpecificStream->SetFormat(&wfexAudioFormat), IDS_UNABLETOINITREAD); CHECK_ERROR(pAMStream->OpenFile(pszFileName, AMMSF_NOCLOCK), IDS_UNABLETOOPENREAD); *ppMMStream = pAMStream; (*ppMMStream)->AddRef(); Exit: return hr; } void DoMakeMovie(HINSTANCE hInst, HWND hWndPreview, CDocument * pDocument) { if (!pDocument->m_TargetFileName) { ErrorMessage(IDS_NOFILENAME); return; } if (pDocument->m_ClipList.NumClips() == 0) { ErrorMessage(IDS_NOCLIPS); return; } DDSURFACEDESC ddsd; ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd.dwHeight = pDocument->m_Height; ddsd.dwWidth = pDocument->m_Width; memcpy(&ddsd.ddpfPixelFormat, &g_aPixelFormats[pDocument->m_PixelDepth], sizeof(ddsd.ddpfPixelFormat)); WAVEFORMATEX wfex; /* Choose a random wave format */ wfex.wFormatTag = WAVE_FORMAT_PCM; wfex.nChannels = 2; wfex.nSamplesPerSec = 22050; wfex.wBitsPerSample = 16; wfex.nBlockAlign = 4; wfex.nAvgBytesPerSec = 4 * 22050; wfex.cbSize = 0; ShowWindow(pDocument->m_ClipList.m_hLV, SW_HIDE); CComPtr pWriterStream; if (SUCCEEDED(CreateWriterStream(pDocument->m_TargetFileName, // If this fails, it will display an appropriate message pDocument->m_VideoCodecDisplayName, ddsd, pDocument->m_AudioCodecDisplayName, wfex, &pWriterStream))) { CCopyEngine Engine(pWriterStream); Engine.InitStream(MSPID_PrimaryVideo, VideoCallback, hWndPreview, false); Engine.InitStream(MSPID_PrimaryAudio, AudioCallback, hWndPreview, false); int NumClips = pDocument->m_ClipList.NumClips(); for (int i = 0; i < NumClips; i++) { CClip *pClip = pDocument->m_ClipList.GetClip(i); CComPtr pReadStream; ddsd.dwFlags = DDSD_PIXELFORMAT; // Only set pixel format on source streams if (SUCCEEDED(OpenReadMMStream(pClip->m_FileName, ddsd, &pReadStream, wfex))) { Engine.CopyStreamData(pReadStream, pClip->m_stStart, pClip->m_stEnd); } } } ShowWindow(pDocument->m_ClipList.m_hLV, SW_SHOW); } HRESULT AddAndRenderCompressor(LPOLESTR pszCodec, LPOLESTR pszFilterName, ICaptureGraphBuilder *pBuilder, IMediaStream *pStream, IBaseFilter *pMuxFilter) { HRESULT hr; CComPtr pDeviceMoniker; CComPtr pBindCtx; CComPtr pCodecFilter; CComPtr pFilterGraph; if (pszCodec) { unsigned long ccEaten; CHECK_ERROR(CreateBindCtx(0, &pBindCtx), IDS_NOCOMPRESSOR); CHECK_ERROR(MkParseDisplayName(pBindCtx, pszCodec, &ccEaten, &pDeviceMoniker), IDS_NOCOMPRESSOR); CHECK_ERROR(pDeviceMoniker->BindToObject(pBindCtx, NULL, IID_IBaseFilter, (void **)&pCodecFilter), IDS_NOCOMPRESSOR); CHECK_ERROR(pBuilder->GetFiltergraph(&pFilterGraph), IDS_INTERNALERROR); CHECK_ERROR(pFilterGraph->AddFilter(pCodecFilter, pszFilterName), IDS_INTERNALERROR); hr = pBuilder->RenderStream(NULL, pStream, pCodecFilter, pMuxFilter); if (FAILED(hr)) { ErrorMessage(IDS_CANTCOMPRESS); } } else { hr = pBuilder->RenderStream(NULL, pStream, NULL, pMuxFilter); if (FAILED(hr)) { ErrorMessage(IDS_CANTCONNECTTOMUX); } } Exit: return hr; } HRESULT CreateWriterStream(LPOLESTR szOutputFileName, LPOLESTR szVideoCodec, DDSURFACEDESC& ddsdVideoFormat, LPOLESTR szAudioCodec, WAVEFORMATEX& wfexAudioFormat, IMultiMediaStream **ppMMStream) { *ppMMStream = NULL; CComPtr pAMStream; CComPtr pVideoStream; CComPtr pAudioStream; CComPtr pBuilder; CComPtr pFilterGraph; CComPtr pFileSinkWriter; CComPtr pMuxFilter; CComQIPtr pDDStream; CComQIPtr pAudioSpecificStream; HRESULT hr; CHECK_ERROR(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&pAMStream), IDS_UNABLETOINITWRITE); CHECK_ERROR(pAMStream->Initialize(STREAMTYPE_WRITE, 0, NULL), IDS_UNABLETOINITWRITE); CHECK_ERROR(pAMStream->AddMediaStream(g_pDD, &MSPID_PrimaryVideo, 0, &pVideoStream), IDS_UNABLETOINITWRITE); CHECK_ERROR(pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, &pAudioStream), IDS_UNABLETOINITWRITE); pDDStream = pVideoStream; pAudioSpecificStream = pAudioStream; CHECK_ERROR(pDDStream->SetFormat(&ddsdVideoFormat, NULL), IDS_CANTSETFORMAT); CHECK_ERROR(pAudioSpecificStream->SetFormat(&wfexAudioFormat), IDS_CANTSETFORMAT); CHECK_ERROR(CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder, (void **)&pBuilder), IDS_UNABLETOINITWRITE); CHECK_ERROR(pAMStream->GetFilterGraph(&pFilterGraph), IDS_INTERNALERROR); CHECK_ERROR(pBuilder->SetFiltergraph(pFilterGraph), IDS_INTERNALERROR); CHECK_ERROR(pBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, szOutputFileName, &pMuxFilter, &pFileSinkWriter), IDS_UNABLETOSETOUTPUTNAME); CHECK_ERROR(AddAndRenderCompressor(szVideoCodec, L"Video compressor", pBuilder, pVideoStream, pMuxFilter), 0); // This function displays its own message CHECK_ERROR(AddAndRenderCompressor(szAudioCodec, L"Audio compressor", pBuilder, pAudioStream, pMuxFilter), 0); *ppMMStream = pAMStream; (*ppMMStream)->AddRef(); Exit: return hr; }