/**************************************************************************** * * AVIOPTS.C * * routine for bringing up the compression options dialog * * AVISaveOptions() * * Copyright (c) 1992 - 1995 Microsoft Corporation. All Rights Reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. * ***************************************************************************/ #include #include #include #include #include "aviopts.h" #include "avifile.rc" #if !defined NUMELMS #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0])) #endif /**************************************************************************** ***************************************************************************/ extern HINSTANCE ghMod; INT_PTR FAR PASCAL _export AVICompressOptionsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); /**************************************************************************** ***************************************************************************/ int gnNumStreams = 0; // how many streams in array int gnCurStream = 0; // which stream's options we're setting PAVISTREAM FAR *gapAVI; // array of stream pointers LPAVICOMPRESSOPTIONS FAR *gapOpt; // array of option structures to fill UINT guiFlags; COMPVARS gCompVars; // for ICCompressorChoose /**************************************************************************** ***************************************************************************/ /************************************************************* * @doc EXTERNAL AVISaveOptions * * @api BOOL | AVISaveOptions | This function gets the save options for * a file and returns them in a buffer. * * @parm HWND | hwnd | Specifies the parent window handle for the Compression Options * dialog box. * * @parm UINT | uiFlags | Specifies the flags for displaying the * Compression Options dialog box. The following flags are defined: * * @flag ICMF_CHOOSE_KEYFRAME | Displays a "Key frame every" box for * the video options. This is the same flag used in . * * @flag ICMF_CHOOSE_DATARATE | Displays a "Data rate" box for the video * options. This is the same flag used in . * * @flag ICMF_CHOOSE_PREVIEW | Displays a "Preview" button for * the video options. This button previews the compression * using a frame from the stream. This is the same flag * used in . * * @parm int | nStreams | Specifies the number of streams * that will have their options set by the dialog box. * * @parm PAVISTREAM FAR * | ppavi | Specifies a pointer to an * array of stream interface pointers. The

* parameter indicates the number of pointers in the array. * * @parm LPAVICOMPRESSOPTIONS FAR * | plpOptions | Specifies a pointer * to an array of pointers * to hold the compression options set by the dialog box. The *

parameter indicates the number of * pointers in the array. * * @rdesc Returns TRUE if the user pressed OK, FALSE for CANCEL or an error. * * @comm This function presents a standard Compression Options dialog * box using

as the parent window handle. When the * user is finished selecting the compression options for * each stream, the options are returned in the * structures in the array referenced by

. The caller * must pass the interface pointers for the streams * in the array referenced by

. * ******************************************************************/ STDAPI_(INT_PTR) AVISaveOptions(HWND hwnd, UINT uiFlags, int nStreams, PAVISTREAM FAR *ppavi, LPAVICOMPRESSOPTIONS FAR *plpOptions) { INT_PTR f; AVICOMPRESSOPTIONS FAR *aOptions; int i; /* Save the stream pointer */ gnNumStreams = nStreams; gnCurStream = -1; gapAVI = ppavi; gapOpt = plpOptions; guiFlags = uiFlags; // // Remember the old compression options in case we cancel and need to // restore them // aOptions = (AVICOMPRESSOPTIONS FAR *)GlobalAllocPtr(GMEM_MOVEABLE, nStreams * sizeof(AVICOMPRESSOPTIONS)); if (!aOptions) return FALSE; #if 0 //def _WIN32 CopyMemory((PVOID)aOptions, (PVOID)*plpOptions, nStreams * sizeof(AVICOMPRESSOPTIONS)); #else // We really ought to use memcpy on this... for (i = 0; i < nStreams; i++) aOptions[i] = *plpOptions[i]; #endif f = DialogBox (ghMod, MAKEINTRESOURCE(IDD_COMPRESSOPTIONS), hwnd, AVICompressOptionsDlgProc); // // The user cancelled... put the old compression options back. // if (f == 0) { #if 0 //def _WIN32 CopyMemory((PVOID)*plpOptions, (PVOID)aOptions, nStreams * sizeof(AVICOMPRESSOPTIONS)); #else // We really ought to use memcpy on this... for (i = 0; i < nStreams; i++) *plpOptions[i] = aOptions[i]; #endif } // Couldn't bring up the dialog if (f == -1) f = 0; GlobalFreePtr(aOptions); // !!! Returning TRUE doesn't guarantee something actually changed... return f; } /************************************************************* * @doc EXTERNAL AVISaveOptionsFree * * @api LONG | AVISaveOptionsFree | This function frees the resources allocated * by . * * @parm int | nStreams | Specifies the number of * structures in the array passed in as the next parameter. * * @parm LPAVICOMPRESSOPTIONS FAR * | plpOptions | Specifies a pointer * to an array of pointers * to hold the compression options set by the dialog box. The * resources in each of these structures that were allocated by * will be freed. * * @rdesc This function always returns AVIERR_OK (zero) * * @comm This function frees the resources allocated by . **************************************************************/ STDAPI AVISaveOptionsFree(int nStreams, LPAVICOMPRESSOPTIONS FAR *plpOptions) { for (; nStreams > 0; nStreams--) { if (plpOptions[nStreams-1]->lpParms) GlobalFreePtr(plpOptions[nStreams-1]->lpParms); plpOptions[nStreams-1]->lpParms = NULL; if (plpOptions[nStreams-1]->lpFormat) GlobalFreePtr(plpOptions[nStreams-1]->lpFormat); plpOptions[nStreams-1]->lpFormat = NULL; } return AVIERR_OK; } /**************************************************************************** Bring up the compression options for the current stream ***************************************************************************/ BOOL StreamOptions(HWND hwnd) { AVISTREAMINFO avis; BOOL f = FALSE; LONG lTemp; UINT w; // Get the stream type if (AVIStreamInfo(gapAVI[gnCurStream], &avis, sizeof(avis)) != 0) return FALSE; // // Video stream -- bring up the video compression dlg // if (avis.fccType == streamtypeVIDEO) { // The structure we have now is not filled in ... init it if (!(gapOpt[gnCurStream]->dwFlags & AVICOMPRESSF_VALID)) { _fmemset(gapOpt[gnCurStream], 0, sizeof(AVICOMPRESSOPTIONS)); gapOpt[gnCurStream]->fccHandler = comptypeDIB; gapOpt[gnCurStream]->dwQuality = (DWORD)ICQUALITY_DEFAULT; } _fmemset(&gCompVars, 0, sizeof(gCompVars)); gCompVars.cbSize = sizeof(gCompVars); gCompVars.dwFlags = ICMF_COMPVARS_VALID; gCompVars.fccHandler = gapOpt[gnCurStream]->fccHandler; gCompVars.lQ = gapOpt[gnCurStream]->dwQuality; gCompVars.lpState = gapOpt[gnCurStream]->lpParms; gCompVars.cbState = gapOpt[gnCurStream]->cbParms; gCompVars.lKey = (gapOpt[gnCurStream]->dwFlags & AVICOMPRESSF_KEYFRAMES)? (gapOpt[gnCurStream]->dwKeyFrameEvery) : 0; gCompVars.lDataRate = (gapOpt[gnCurStream]->dwFlags & AVICOMPRESSF_DATARATE) ? (gapOpt[gnCurStream]->dwBytesPerSecond / 1024) : 0; // !!! Don't pass flags verbatim if others are defined!!! f = ICCompressorChoose(hwnd, guiFlags, NULL, gapAVI[gnCurStream], &gCompVars, NULL); /* Set the options to our new values */ gapOpt[gnCurStream]->lpParms = gCompVars.lpState; gapOpt[gnCurStream]->cbParms = gCompVars.cbState; gCompVars.lpState = NULL; // so it won't be freed gapOpt[gnCurStream]->fccHandler = gCompVars.fccHandler; gapOpt[gnCurStream]->dwQuality = gCompVars.lQ; gapOpt[gnCurStream]->dwKeyFrameEvery = gCompVars.lKey; gapOpt[gnCurStream]->dwBytesPerSecond = gCompVars.lDataRate * 1024; if (gCompVars.lKey) gapOpt[gnCurStream]->dwFlags |= AVICOMPRESSF_KEYFRAMES; else gapOpt[gnCurStream]->dwFlags &=~AVICOMPRESSF_KEYFRAMES; if (gCompVars.lDataRate) gapOpt[gnCurStream]->dwFlags |= AVICOMPRESSF_DATARATE; else gapOpt[gnCurStream]->dwFlags &=~AVICOMPRESSF_DATARATE; // If they pressed OK, we have valid stuff in here now. if (f) gapOpt[gnCurStream]->dwFlags |= AVICOMPRESSF_VALID; // Close the stuff opened by ICCompressorChoose ICCompressorFree(&gCompVars); // // Bring up the ACM format dialog and stuff it in our // compression options structure // } else if (avis.fccType == streamtypeAUDIO) { ACMFORMATCHOOSE acf; LONG lsizeF = 0; if (acmGetVersion() < 0x02000000L) { TCHAR achACM[160]; TCHAR achACMV[40]; LoadString(ghMod, IDS_BADACM, achACM, sizeof(achACM)/sizeof(TCHAR)); LoadString(ghMod, IDS_BADACMV, achACMV, sizeof(achACMV)/sizeof(TCHAR)); MessageBox(hwnd, achACM, achACMV, MB_OK | MB_ICONHAND); return FALSE; } _fmemset(&acf, 0, sizeof(acf)); // or ACM blows up acf.cbStruct = sizeof(ACMFORMATCHOOSE); // If our options struct has valid data, use it to init // the acm dialog with, otherwise pick a default. acf.fdwStyle = (gapOpt[gnCurStream]->dwFlags & AVICOMPRESSF_VALID) ? ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT : 0; acf.hwndOwner = hwnd; // Make sure the AVICOMPRESSOPTIONS has a big enough lpFormat acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, (LPVOID)&lTemp); if ((gapOpt[gnCurStream]->cbFormat == 0 || gapOpt[gnCurStream]->lpFormat == NULL) && lTemp) { gapOpt[gnCurStream]->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, lTemp); gapOpt[gnCurStream]->cbFormat = lTemp; } else if (gapOpt[gnCurStream]->cbFormat < (DWORD)lTemp && lTemp) { gapOpt[gnCurStream]->lpFormat = GlobalReAllocPtr(gapOpt[gnCurStream]->lpFormat, lTemp, GMEM_MOVEABLE); gapOpt[gnCurStream]->cbFormat = lTemp; } if (!gapOpt[gnCurStream]->lpFormat) return FALSE; acf.pwfx = gapOpt[gnCurStream]->lpFormat; acf.cbwfx = gapOpt[gnCurStream]->cbFormat; // // Only ask for choices that we can actually convert to // AVIStreamReadFormat(gapAVI[gnCurStream], AVIStreamStart(gapAVI[gnCurStream]), NULL, &lsizeF); // !!! Work around ACM bug by making sure our format is big enough lsizeF = max(lsizeF, sizeof(WAVEFORMATEX)); acf.pwfxEnum = (LPWAVEFORMATEX) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, lsizeF); if (acf.pwfxEnum) { AVIStreamReadFormat(gapAVI[gnCurStream], AVIStreamStart(gapAVI[gnCurStream]), acf.pwfxEnum, &lsizeF); acf.fdwEnum |= ACM_FORMATENUMF_CONVERT; } // If they pressed OK, we now have valid stuff in here! w = acmFormatChoose(&acf); if (w == MMSYSERR_NOERROR) gapOpt[gnCurStream]->dwFlags |= AVICOMPRESSF_VALID; else if (w != ACMERR_CANCELED) { MessageBeep(0); // !!! Should really be a message box! } if (acf.pwfxEnum) GlobalFreePtr(acf.pwfxEnum); f = (w == MMSYSERR_NOERROR); } return f; } #if defined _WIN32 && !defined UNICODE // Chicago only /* * convert a UNICODE string to 'normal' */ LPTSTR WINAPI aviWideToText (LPTSTR lpszOut, LPWSTR lpwIn, UINT cch) { if (sizeof(TCHAR) != sizeof(WCHAR)) WideCharToMultiByte(CP_ACP, 0, lpwIn, cch, lpszOut, cch, NULL, NULL); else lstrcpyn (lpszOut, (LPTSTR)lpwIn, cch); return lpszOut; } #else #define aviWideToText(lpszOut,lpwIn,cch) lstrcpyn(lpszOut,lpwIn,cch) #endif // _WIN32 on CHICAGO STATICDT SZCODE aszXbyXbyX[] = TEXT("%ldx%ldx%d\n"); STATICDT SZCODE aszBlahSpaceBlah[] = TEXT("%s %s"); // // Somebody chose a new stream. Do we need to grey InterleaveOpts? // Set the current stream. // void NEAR PASCAL NewStreamChosen(HWND hwnd) { AVISTREAMINFO avis; HIC hic; ICINFO icinfo; ACMFORMATDETAILS acmfmt; ACMFORMATTAGDETAILS aftd; LONG lsizeF; LPBITMAPINFOHEADER lp = NULL; TCHAR szFFDesc[80]; TCHAR szDesc[120]; gnCurStream = (int)SendDlgItemMessage(hwnd, IDC_intCHOOSESTREAM, CB_GETCURSEL, 0, 0L); if (gnCurStream < 0) return; if (AVIStreamInfo(gapAVI[gnCurStream], &avis, sizeof(avis)) != 0) return; // // Show a string describing the current format // szDesc[0] = TEXT('\0'); lsizeF = 0; AVIStreamReadFormat(gapAVI[gnCurStream], AVIStreamStart(gapAVI[gnCurStream]), NULL, &lsizeF); if (lsizeF) { lp = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, lsizeF); if (lp) { if (AVIStreamReadFormat(gapAVI[gnCurStream], AVIStreamStart(gapAVI[gnCurStream]), lp, &lsizeF) == AVIERR_OK) { if (avis.fccType == streamtypeVIDEO) { wsprintf(szDesc, aszXbyXbyX, lp->biWidth, lp->biHeight, lp->biBitCount); if (lp->biCompression == BI_RGB) { LoadString(ghMod, IDS_FFDESC, szFFDesc, sizeof(szFFDesc)/sizeof(TCHAR)); lstrcat(szDesc, szFFDesc); } else { hic = ICDecompressOpen(ICTYPE_VIDEO,avis.fccHandler, lp, NULL); if (hic) { if (ICGetInfo(hic, &icinfo,sizeof(icinfo)) != 0) { UINT cb = lstrlen(szDesc); aviWideToText (szDesc + cb, icinfo.szDescription, NUMELMS(szDesc) - cb); } ICClose(hic); } } } else if (avis.fccType == streamtypeAUDIO) { _fmemset(&acmfmt, 0, sizeof(acmfmt)); acmfmt.pwfx = (LPWAVEFORMATEX) lp; acmfmt.cbStruct = sizeof(ACMFORMATDETAILS); acmfmt.dwFormatTag = acmfmt.pwfx->wFormatTag; acmfmt.cbwfx = lsizeF; aftd.cbStruct = sizeof(aftd); aftd.dwFormatTag = acmfmt.pwfx->wFormatTag; aftd.fdwSupport = 0; if ((acmFormatTagDetails(NULL, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG) == 0) && (acmFormatDetails(NULL, &acmfmt, ACM_FORMATDETAILSF_FORMAT) == 0)) { wsprintf(szDesc, aszBlahSpaceBlah, (LPTSTR) acmfmt.szFormat, (LPTSTR) aftd.szFormatTag); } } } GlobalFreePtr(lp); } } SetDlgItemText(hwnd, IDC_intFORMAT, szDesc); // // AUDIO and VIDEO streams have a compression dialog // if (avis.fccType == streamtypeAUDIO || avis.fccType == streamtypeVIDEO) EnableWindow(GetDlgItem(hwnd, IDC_intOPTIONS), TRUE); else EnableWindow(GetDlgItem(hwnd, IDC_intOPTIONS), FALSE); } /*--------------------------------------------------------------+ * Dialog Proc for the main compression options dialog * +--------------------------------------------------------------*/ INT_PTR FAR PASCAL _export AVICompressOptionsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { int i, nVal; AVISTREAMINFO avis; DWORD dw; BOOL f; switch(msg){ case WM_INITDIALOG: // // If we've only got one stream to set the options for, it seems // strange to bring up a box to let you choose which stream you want. // Let's skip straight to the proper options dlg box. // if (gnNumStreams == 1) { gnCurStream = 0; EndDialog(hwnd, StreamOptions(hwnd)); return TRUE; } /* Add the list of streams to the drop-down box */ for (nVal = 0; nVal < gnNumStreams; nVal++) { // Get the name of this stream AVIStreamInfo(gapAVI[nVal], &avis, sizeof(avis)); SendDlgItemMessage(hwnd, IDC_intCHOOSESTREAM, CB_ADDSTRING, 0, (LONG_PTR) (LPTSTR)avis.szName); } // Set our initial selection to the first item SendDlgItemMessage(hwnd, IDC_intCHOOSESTREAM, CB_SETCURSEL, 0, 0L); // Make sure we see it SendMessage(hwnd, WM_COMMAND, GET_WM_COMMAND_MPS(IDC_intCHOOSESTREAM, hwnd, CBN_SELCHANGE)); // Set the interleave boxes for these streams. Every stream should // be interleaved the same way, so just look at the first guy. // !!! Admittedly, some app might only set the interleaving for // the audio stream, and we won't get his intended default here. // Default to interleave every 1 if we're uninitialized if (gapOpt[0]->dwFlags & AVICOMPRESSF_VALID) { f = (gapOpt[0]->dwFlags & AVICOMPRESSF_INTERLEAVE) != 0; dw = gapOpt[0]->dwInterleaveEvery; } else { dw = 1; f = FALSE; } CheckDlgButton(hwnd, IDC_intINTERLEAVE, f); SetDlgItemInt(hwnd, IDC_intINTERLEAVEEDIT, (int)dw, FALSE); EnableWindow(GetDlgItem(hwnd, IDC_intINTERLEAVEEDIT), f); return TRUE; case WM_COMMAND: switch(GET_WM_COMMAND_ID(wParam, lParam)){ case IDOK: // Set the interleaving for every stream to be the same, // whatever we have chosen. AVIFile doesn't support // interleaving different streams in different ways. f = IsDlgButtonChecked(hwnd, IDC_intINTERLEAVE); dw = (DWORD)GetDlgItemInt(hwnd, IDC_intINTERLEAVEEDIT, NULL, FALSE); for (i = 0; i < gnNumStreams; i++) { gapOpt[i]->dwInterleaveEvery = dw; if (f) gapOpt[i]->dwFlags |= AVICOMPRESSF_INTERLEAVE; else gapOpt[i]->dwFlags &=~AVICOMPRESSF_INTERLEAVE; } // fall through (AAAAaaaahhhhh.....) case IDCANCEL: EndDialog(hwnd, wParam == IDOK); break; case IDC_intOPTIONS: StreamOptions(hwnd); break; // // Somebody chose a new stream. // case IDC_intCHOOSESTREAM: if (GET_WM_COMMAND_CMD(wParam, lParam) != CBN_SELCHANGE) break; NewStreamChosen(hwnd); break; case IDC_intINTERLEAVE: // Enable the "interleave every" edit box iff we've checked it f = IsDlgButtonChecked(hwnd, IDC_intINTERLEAVE); EnableWindow(GetDlgItem(hwnd, IDC_intINTERLEAVEEDIT), f); break; default: break; } break; default: return FALSE; } return FALSE; }