Функция UpdateAviSurface()
Перед тем как рассматривать функцию UpdateAviSurface(), я хочу обратить ваше внимание на ее сходство с кодом класса DirectDrawWin, предназначенным для загрузки BMP-файлов на поверхность (см. главу 5). Функция UpdateAviSurface(), как и функции загрузки BMP-файлов DirectDrawWin, блокирует поверхность и затем копирует данные в ее память:
BOOL AviPlayWin::UpdateAviSurface() {
HRESULT r;
if (finaldata==0) return FALSE;
DWORD dwWidth = (srcfmt->biWidth+3) & ~3;
DWORD dwHeight = srcfmt->biHeight;
DDSURFACEDESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.dwSize = sizeof(desc);
r = avisurf->Lock(0, &desc, DDLOCK_WAIT, 0);
if (r==DD_OK) {
BYTE* src = finaldata + dwWidth * (dwHeight-1);
BYTE* dst = (BYTE *)desc.lpSurface;
for (DWORD y=0; y
memcpy(dst, src, dwWidth);
dst += desc.lPitch;
src -= dwWidth;
}
avisurf->Unlock(0);
}
return TRUE;
}
После блокировки поверхности функция UpdateAviSurface()в цикле копирует каждую строку пикселей AVI-данных в память поверхности. В формате AVI, как и в формате BMP, изображения хранятся в перевернутом виде, поэтому мы начинаем с последней строки буфера данных и двигаемся к его началу.
Функция RestoreSurfaces()
Все трудное осталось позади, дальше будет легко. Особенно просто реализуется функция RestoreSurfaces():
void AviPlayWin::RestoreSurfaces() {
avisurf->Restore();
}
Вспомните — функция RestoreSurfaces()вызывается только при восстановлении потерянных поверхностей, а класс DirectDrawWinавтоматически восстанавливает первичную поверхность со вторичным буфером. В программе AviPlay остается лишь восстановить поверхность AVI, а для этого достаточно вызвать функцию Restore()интерфейса DirectDrawSurface.
В некоторых программах функция RestoreSurfaces()восстанавливала не только область памяти, но и содержимое поверхности. В нашем случае можно ограничиться восстановлением памяти, потому что ее содержимое будет перезаписано следующим кадром. Если вы вдруг засомневаетесь, напомню — вызов функции Restore()для поверхности, которая не была потеряна (например, находящейся в системной памяти), не причинит никакого вреда.
Обработка пользовательского ввода
В программе AviPlay ввод не играет особой роли. Программа реагирует всего на три клавиши, причем одинаково. Ввод с клавиатуры обрабатывается функцией OnKeyDown():
void AviPlayWin::OnKeyDown(UINT key, UINT nRepCnt, UINT nFlags) {
switch (key) {
case VK_ESCAPE:
case VK_SPACE:
case VK_RETURN:
ShowDialog();
break;
}
DirectDrawWin::OnKeyDown(key, nRepCnt, nFlags);
}
Все три клавиши вызывают функцию ShowDialog(). Аналогично обрабатывается и ввод от мыши, это происходит в функции OnRButtonDown():
void AviPlayWin::OnRButtonDown(UINT nFlags, CPoint point) {
ShowDialog();
DirectDrawWin::OnRButtonDown(nFlags, point);
}
Когда пользователь закрывает диалоговое окно для выбора AVI-файла, функция ShowDialog()посылает сообщение WM_CLOSE, сигнализируя о завершении приложения.
Функция OnDestroy()
Остается лишь завершить приложение. Функция OnDestroy()занимается «уборкой мусора» — она закрывает открытые AVI-потоки, освобождает декомпрессор и буферы данных AVI:
void AviPlayWin::OnDestroy() {
DirectDrawWin::OnDestroy();
if (avistream) AVIStreamRelease(avistream), avistream=0;
if (decomp) ICClose(decomp), decomp=0;
if (srcfmt) delete [] srcfmt, srcfmt=0;
if (dstfmt) delete [] dstfmt, dstfmt=0;
if (rawdata) {
TRACE("delete [] rawdata...\n");
delete [] rawdata, rawdata=0;
}
if (finaldata) {
TRACE("delete [] finaldata...\n");
delete [] finaldata, finaldata=0;
}
if (avidialog) delete avidialog, avidialog=0;
AVIFileExit();
}
Обратите внимание на вызов функции AviFileExit()в конце OnDestroy(). Это завершает работу VFW и освобождает все используемые им ресурсы.
Заключение
Наше знакомство с воспроизведением видеороликов подходит к концу. Честно говоря, чтобы превратить программу AviPlay в полноценный проигрыватель AVI-файлов, вам придется еще немало потрудиться. Необходимо организовать поддержку звука и хронометраж, не говоря уже о том, что VFW обладает многими странностями и в работе с ним приходится много экспериментировать.
И последнее замечание. По неизвестным мне причинам VFW отказывается работать с AVI-файлами, сжатыми кодеками IR32 и IR42 (возможно, есть и другие, но я заметил эти два). С другой стороны, AVI-файлы, использующие кодеки MS-CRAM и Cinepak, работают нормально.
Читать дальше