typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
Некоторые поля структуры (например, biXPelsPerMeter и biYPelsPerMeter) не имеют отношения к AVI-файлам, но таких полей немного. Размеры, глубина пикселей, алгоритм сжатия и количество цветов — все это можно взять из структуры BITMAPINFOHEADER после успешного вызова функции AVIStreamReadFormat().
Более подробные сведения о потоке можно получить с помощью функции AVIStreamInfo(). Функция AVIStreamInfo(), как и функция AVIStreamReadFormat(), получает в качестве аргумента логический номер и заполняет структуру данными о потоке. Для передачи информации AVIStreamInfo() использует структуру AVISTREAMINFO. Ее определение в файле vfw.h выглядит так:
typedef struct _AVISTREAMINFOW {
DWORD fccType;
DWORD fccHandler;
DWORD dwFlags;
DWORD dwCaps;
WORD wPriority;
WORD wLanguage;
DWORD dwScale;
DWORD dwRate;
DWORD dwStart;
DWORD dwLength;
DWORD dwInitialFrames;
DWORD dwSuggestedBufferSize;
DWORD dwQuality;
DWORD dwSampleSize;
RECT rcFrame;
DWORD dwEditCount;
DWORD dwFormatChangeCount;
WCHAR szName[64];
} AVISTREAMINFOW, FAR * LPAVISTREAMINFOW;
Давайте рассмотрим некоторые ключевые поля этой структуры. Первое поле, fccType, определяет тип потока. В файлах формата AVI поддерживаются четыре типа потоковых данных: видео, аудио, MIDI (музыка) и текст. В этой книге будут рассматриваться только видеопотоки.
Второе поле структуры AVISTREAMINFO, fccHandler, определяет алгоритм сжатия, примененный при сохранении видеопотока. Как вы вскоре увидите, в Video For Windows это поле можно использовать для создания «декомпрессоров», которые способны восстановить каждый сжатый кадр из потока.
Поле dwStart определяет индекс первого кадра потока (это важно, потому что первый кадр видеоролика может иметь индекс 0 или 1). Эту же величину можно непосредственно получить функцией AVIStreamStart().В поле dwLength хранится общее количество кадров видеоролика. Его можно либо взять из структуры AVISTREAMINFO, либо получить с помощью функции AVIStreamLength().
Теперь мы знаем, как открыть видеопоток и получить сведения о нем. Можно переходить к процессу чтения и отображения видеокадров. Чтение кадров выполняется функцией AVIStreamRead(). Эта функция, получив логический номер потока и индекс интересующего нас кадра, помещает данные кадра в буфер, который мы также должны ей передать. Данные кадра хранятся в сжатом виде, поэтому перед отображением их необходимо восстановить.
Для восстановления кадров применяется функция ICDecompress(). Она получает два буфера: в одном находятся сжатые данные, а в другой будет помещен восстановленный кадр. Функции ICDecompress() также следует передать логический номер декомпрессора, используемого функцией ICDecompress() для обработки сжатых данных.
Логический номер декомпрессора можно получить функцией ICDecompressOpen(). По описанию видеоданных функция ICDecompressOpen() ищет декомпрессор с поддержкой нужного алгоритма восстановления.
После завершения работы с потоком необходимо закрыть его функцией AVIStreamRelease(). После вызова функции AVIStreamRelease() логический номер потока становится недействительным (до повторного открытия потока). Наконец, перед завершением программы необходимо вызвать функцию AVIFileExit(), которая освобождает программные модули VFW.
Пора браться за программирование. Программу AviPlay, как и все остальные программы, рассматриваемые в этой книге, можно найти на CD-ROM.
Программа AviPlay использует Video For Windows для открытия и воспроизведения AVI-файлов на поверхностях DirectDraw. Она позволяет выбрать любой AVI-файл и задать видеорежим для воспроизведения ролика. Диалоговое окно для выбора файла изображено на рис. 8.1.
Рис. 8.1. Диалоговое окно для выбора AVI-файла в программе AviPlay
Если вы еще не успели опробовать программу AviPlay и не обнаружили этого сами, я хочу указать на несколько вещей, которые эта программа не умеет делать. Например, она не воспроизводит аудиопотоки из выбранного AVI-файла, а лишь извлекает и воспроизводит графическую часть файла.
Кроме того, программа не пытается управлять частотой вывода кадров; все кадры извлекаются и воспроизводятся с максимальной скоростью. В каждом AVI-файле присутствуют хронометрические данные, которые используются всеми полноценными программами для воспроизведения AVI-файлов.
Еще одна полезная возможность, которая бы пригодилась в программе AviPlay, — поддержка 16- и 24-битных видеорежимов. В представленном варианте всегда применяются 8-битные режимы независимо от глубины пикселей воспроизводимого ролика.
Класс AviPlayWin
Большинство возможностей программы AviPlay обеспечивается классом AviPlayWin, который наследует поддержку DirectDraw от класса DirectDrawWin. В отличие от других программ этой книги класс AviPlayWin использует диалоговое окно для выбора файла. Вместо того чтобы создавать поверхности при запуске, программа AviPlay (как и программа BmpView из главы 5) ожидает, пока пользователь выберет файл. Затем программа создает поверхности и настраивает их в соответствии с содержимым выбранного файла. Определение класса AviPlayWin приведено в листинге 8.1.
Листинг 8.1. Класс AviPlayWin
class AviPlayWin : public DirectDrawWin {
public:
AviPlayWin();
protected:
//{{AFX_MSG(AviPlayWin)
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
int SelectInitialDisplayMode();
BOOL CreateCustomSurfaces() {
return TRUE;
}
void DrawScene();
void RestoreSurfaces();
void GetSystemPalette();
void ShowDialog();
BOOL LoadAvi();
BOOL CreateAviSurface();
BOOL UpdateAviSurface();
BOOL InstallPalette();
private:
AviDialog* avidialog;
CString fullfilename;
CString filename;
CString pathname;
CRect displayrect;
LPDIRECTDRAWSURFACE avisurf;
CRect avirect;
int x,y;
DisplayModeArray displaymode;
LPDIRECTDRAWPALETTE syspal;
LPDIRECTDRAWPALETTE avipal;
PAVISTREAM avistream;
AVISTREAMINFO streaminfo;
HIC decomp;
long fmtlen, buflen;
long startframe, endframe;
long curframe;
LPBITMAPINFOHEADER srcfmt;
LPBITMAPINFOHEADER dstfmt;
BYTE* rawdata;
BYTE* finaldata;
};
Сначала мы объявляем конструктор класса AviPlayWin, предназначенный только для инициализации переменных класса.
В классе определены четыре обработчика сообщений: OnKeyDown(), OnRButtonDown(), OnCreate() и OnDestroy(). Функция OnKeyDown() следит за нажатием клавиш Escape и пробела во время воспроизведения, прерывает ролик и отображает диалоговое окно для выбора AVI-файла (мы могли воспользоваться DirectInput, но программа AviPlay не стоит подобных хлопот). Функция OnRButtonDown() тоже вызывает диалоговое окно для выбора AVI-файла, но по щелчку правой кнопки мыши. Функция OnCreate() инициализирует DirectDraw и AVI, а функция OnDestroy() завершает их работу.
Затем мы объявляем 10 закрытых (private) функций. Первой идет функция SelectInitialDisplayMode(), которая выполняет три задачи: выбор исходного видеорежима (то, для чего предназначена сама функция), построение списка 8-битных режимов для диалогового окна и захват системной палитры. Вскоре мы рассмотрим эту функцию. Функция GetSystemPalette() вызывается функцией SelectInitialDisplayMode(); мы увидим, как она работает, при знакомстве с последней.
Функция CreateCustomSurfaces() объявлена встроенной (inline). Она всего лишь возвращает TRUE, потому что при запуске приложения поверхности не создаются.
Следующая функция, ShowDialog(), отображает диалоговое окно и в случае выбора допустимого AVI-файла загружает его функцией LoadAvi(). Основная часть функциональных возможностей программы обеспечивается этими двумя функциями, поэтому мы рассмотрим их подробно.
За функций LoadAvi() объявляется функция DrawScene(). Мы воспользуется ею для вывода кадров видеоролика. Помимо извлечения и восстановления кадров видеопотока DrawScene() осуществляет блиттинг и переключение страниц, необходимые для отображения кадра.