r=AVIStreamFormatSize(avistream, 0, &fmtlen);
TRACE("AVIStreamFormatSize: %sn", r==0 ? "OK" : "failed");
int formatsize=fmtlen+sizeof(RGBQUAD)*256;
if (srcfmt) delete [] srcfmt;
srcfmt = (LPBITMAPINFOHEADER)new BYTE[formatsize];
ZeroMemory(srcfmt, formatsize);
if (dstfmt) delete [] dstfmt;
dstfmt = (LPBITMAPINFOHEADER)new BYTE[formatsize];
ZeroMemory(dstfmt, formatsize);
r=AVIStreamReadFormat(avistream, 0, srcfmt, &fmtlen);
TRACE("AVIStreamReadFormat: %sn", r==0 ? "OK" : "failed");
TRACE(" --- %s ---n", filename);
TRACE(" biSize: %dn", srcfmt->biSize);
TRACE(" biWidth x biHeight: %dx%dn", srcfmt->biWidth, srcfmt->biHeight);
if (srcfmt->biPlanes != 1) TRACE(" - biPlanes: %dn", srcfmt->biPlanes);
TRACE(" biBitCount: %dn", srcfmt->biBitCount);
CString comp;
switch (srcfmt->biCompression) {
case BI_RGB:
comp="BI_RGB";
break;
case BI_RLE8:
comp="BI_RLE8";
break;
case BI_RLE4:
comp="BI_RLE4";
break;
case BI_BITFIELDS:
comp="BI_BITFIELDS";
break;
}
TRACE(" biCompression: %sn", comp);
TRACE(" biSizeImage: %dn", srcfmt->biSizeImage);
TRACE(" ------------------n");
memcpy(dstfmt, srcfmt, fmtlen);
dstfmt->biBitCount = 8;
dtfmt->biCompression = BI_RGB;
dstfmt->biSizeImage = dstfmt->biWidth * dstfmt->biHeight;
startframe = AVIStreamStart(avistream);
TRACE("stream start: %dn", startframe);
endframe = AVIStreamEnd(avistream);
TRACE("stream end: %dn", endframe);
r=AVIStreamInfo(avistream, &streaminfo, sizeof(streaminfo));
TRACE("AVIStreamInfo: %sn", r==0 ? "OK" : "failed" );
buflen = dstfmt->biSizeImage;
int finalbuflen=((dstfmt->biWidth+3) & ~3) * dstfmt->biHeight;
if (streaminfo.dwSuggestedBufferSize) if ((LONG)streaminfo.dwSuggestedBufferSize < buflen) {
TRACE("adjusting buflen to suggested sizen");
buflen = (LONG)streaminfo.dwSuggestedBufferSize;
}
if (decomp) ICClose(decomp);
decomp = ICDecompressOpen(ICTYPE_VIDEO, streaminfo.fccHandler, srcfmt, dstfmt);
TRACE("ICDecompressOpen: %sn", decomp ? "OK" : "failed");
if (rawdata) {
TRACE("delete [] rawdata...n");
delete [] rawdata;
}
rawdata = new BYTE[buflen];
if (finaldata) {
TRACE("delete [] finaldata...n");
delete [] finaldata;
}
finaldata = new BYTE[finalbuflen];
return TRUE;
}
В функции LoadAvi() используются функции VFW. Сначала LoadAvi() закрывает открытый ранее AVI-поток функцией AVIStreamRelease(), а затем открывает новый поток функцией AVIStreamOpenFromFile(), которой в числе прочих аргументов передается имя открываемого AVI-файла.
Обратите внимание — третьим аргументом функции AVIStreamOpenFromFile() является флаг, определяющий тип открываемого потока. В нашем случае использован флаг видеопотока streamtypeVIDEO, но с помощью трех оставшихся флагов (streamtypeAUDIO, streamtypeMIDI и streamtypeTEXT) можно открывать и потоки других типов.
Затем мы получаем данные о формате потока функцией AVIStreamReadFormat() (пользуясь при этом функцией AVIStreamFormatSize()). Я специально оставил в этом фрагменте отладочные макросы TRACE(), чтобы продемонстрировать, какую информацию можно получить об AVI-файле.
На этой стадии инициализируются некоторые важные переменные класса. Например, мы присваиваем значения переменным startframe и endframe, чтобы во время извлечения кадров были известны допустимые значения их индексов.
Затем мы получаем доступ к декомпрессору. Функция ICDecompressorOpen() по структуре, описывающей AVI-файл и желательный формат вывода, возвращает логический номер модуля декомпрессии. Позднее этот модуль используется для восстановления кадров. Наконец, мы выделяем память под два буфера: в одном хранятся необработанные (сжатые) данные, извлеченные из AVI-потока, а в другом — итоговый (восстановленный) кадр.
Функция CreateAviSurface()
Теперь у нашего приложения есть открытый AVI-поток и информация в объеме, достаточном для извлечения кадров. Но что же делать с кадром после того, как он будет прочитан и восстановлен? Нам понадобится поверхность для хранения полученных данных, и тогда воспроизведение видеоролика сведется к простому блиттингу содержимого этой поверхности во вторичный буфер приложения с последующим переключением страниц. Эта промежуточная поверхность создается функцией CreateAviSurface():
BOOL AviPlayWin::CreateAviSurface() {
if (avisurf) avisurf->Release(), avisurf=0;
avisurf=CreateSurface(srcfmt->biWidth, srcfmt->biHeight);
CRect displayrect=GetDisplayRect();
x=(displayrect.Width()-srcfmt->biWidth)/2;
y=0;
return TRUE;
}
После освобождения поверхности, созданной ранее, функция CreateAviSurface() с помощью функции CreateSurface() интерфейса DirectDraw создает поверхность, размеры которой совпадают с размерами кадра. Кроме того, функция CreateAviSurface() инициализирует переменные x и y, определяющие положение поверхности AVI на вторичном буфере. В нашем случае кадры будут выравниваться по центру экрана, поэтому в вычислениях применяется функция DirectDrawWin::GetDisplayRect() для определения размеров экрана.
Функция InstallPalette()
С помощью файлового формата AVI и VFW API можно получить палитру, оптимально подходящую для просмотра видеоролика. Функция InstallPalette() извлекает необходимые данные и использует их для конструирования палитры DirectDraw. Функция InstallPalette() выглядит так:
BOOL AviPlayWin::InstallPalette() {
ICDecompressGetPalette(decomp, srcfmt, dstfmt);
PALETTEENTRY pe[256];
LPBITMAPINFO info=(LPBITMAPINFO)dstfmt;
for (int i=0; i<256; i++) {
pe[i].peRed = info->bmiColors[i].rgbRed;
pe[i].peGreen = info->bmiColors[i].rgbGreen;
pe[i].peBlue = info->bmiColors[i].rgbBlue;
pe[i].peFlags = 0;
}
if (avipal) avipal->Release();
ddraw2->CreatePalette(DDPCAPS_8BIT, pe, &avipal, 0);
primsurf->SetPalette(avipal);
return TRUE;
}
Функция ICDecompressGetPalette() получает данные палитры и в цикле преобразует их в формат, который мы можем использовать. Полученный массив передается при вызове функции CreatePalette() интерфейса DirectDraw. Остается лишь присоединить созданную палитру к первичной поверхности.
Функция DrawScene()
Наконец, все готово к отображению кадров видеоролика. Для этого мы подготавливаем и выводим очередной кадр при каждом вызове функции DrawScene() классом DirectDrawWin. Функция DrawScene() выглядит так:
void AviPlayWin::DrawScene() {
long r;
r=AVIStreamRead(avistream, curframe, 1, rawdata, buflen, 0, 0);
if (r) {
TRACE("AVIStreamRead failed: ");
switch (r) {
case AVIERR_BUFFERTOOSMALL:
TRACE("BUFFERTOOSMALLn");
break;
case AVIERR_MEMORY:
TRACE("MEMORYn");
break;
case AVIERR_FILEREAD:
TRACE("FILEREADn");
break;
}
}
r=ICDecompress(decomp, 0, srcfmt, rawdata, dstfmt, finaldata);
UpdateAviSurface();
backsurf->BltFast(x, y, avisurf, 0, DDBLTFAST_WAIT);
curframe=(curframe<endframe) ? curframe+1 : startframe;
primsurf->Flip(0, DDFLIP_WAIT);
}
Функция DrawScene() с помощью функции AVIStreamRead() извлекает очередной кадр из AVI-потока, после чего сохраняет полученные данные в буфере rawdata. Я оставил в ней несколько макросов TRACE(), которые пригодились мне при отладке, но надеюсь, что вам они не понадобятся.
Затем мы вызываем функцию ICDecompress() и передаем ей логический номер декомпрессора, ранее полученный от функции LoadAvi(). Аргументами функции ICDecompress() являются два буфера — первый содержит необработанные (сжатые) данные, а второй — восстановленное изображение.
Функция UpdateAviSurface() копирует восстановленный кадр на поверхность AVI. Эта функция рассматривается ниже.
Подготовленная поверхность AVI копируется во вторичный буфер функцией BltFast() интерфейса DirectDrawSurface. После этого переменная curframe увеличивается или сбрасывается в зависимости от ее значения и количества кадров в ролике. Наконец, функция Flip() интерфейса DirectDrawSurface выводит кадр на экран.
Функция 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) {