Основы практического программирования оверлейного видео в Windows
В этой статье автор делится своим богатым практическим опытом по обработке видео сигнала от внешнего, по отношению к компьютеру, источника сигнала. Статья содержит пример программы, демонстрирующей стандартные возможности драйверов видео карт. Кроме описания принципов работы этой программы, автор приводит результаты тестирования этой программы на различных видео картах.
Данная статья целиком и полностью посвящена выводу видеоизображения из внешнего источника на монитор компьютера под управлением операционной системы Windows. Разумеется, для достижения этой цели компьютер должен быть снабжен видео карточкой с видео входом (или отдельной платой для ввода видео). С каждой такой видео карточкой поставляется программное обеспечение для воспроизведения видео из внешнего источника, но как это сделать в своей программе - вот вопрос, который часто задают, и ответ на который я, в свое время, не смог найти ни в одном русскоязычном источнике. А справка к сожалению, дает лишь представление о каждой функции по отдельности, но не о принципах их совокупного использования.
Модуль vfw.pas
Пример, рассматриваемый в этой статье, создавался в среде Delphi. Следует отметить, что в среде C/C++ ситуация, возможно, будет еще проще. В C/С++ нет необходимости писать интерфейсный модуль. Он, как правило, уже существует ( традиционное имя - vfw.h). Справедливости ради надо отметить, что и для Delphi подобных модулей в Internet сосредоточено внушительное количество, но наглядного описания процесса создания программы все же нет. Поэтому я все-таки решил привести пример маленького интерфейсного модуля с минимумом поддерживаемых функций.
Приведенный ниже пример позволяет воспроизвести видео из внешнего источника (телевизор, видеомегнитофон или видеокамера) на экране монитора компьютера в определенном окне, обеспечить настройку видео (яркость, контрастность, PAL/SECAM и прочее), при необходимости сделать "фотографию" с видеоизображения и сохранить его. Для упрощения задачи настройку будем проводить из стандартных диалогов Windows, а "фотографию" просто помещать в буфер обмена Windows.
Проект начнем с создания формы (дадим ей необычное, а главное редкое имя - Form1), а так же нового модуля в проекте - назовем его vfw.pas. Этот модуль будут описаны основные функции, используемые в программе, а так же в нем обеспечивается связь с API- функциями видеообработки. Исходный код этого модуля приведен в Листинге 1.
Листинг 1. Исходный код модуля vfw.pas, ответственного за взаимодействие с AVICAP32.DLL
unit vfw;
//зацепление API функций из AVICAP32.DLL
//а так же отправка сообщений для работы с видеосигналом
interface
uses Messages,Windows;
//создание окна, куда будем кино показывать
//находиться в avicap32.dll
function capCreateCaptureWindowA(
lpszWindowName: pChar;
dwStyle: DWORD;
x,y,nWidth,nHeight: Integer;
hwndParent: hWnd;
nID: Integer
): HWnd; stdcall;external 'AVICAP32';
function capDriverConnect( hwnd:HWnd): BOOL;
function capDriverDisconnect(hwnd:HWnd): BOOL;
function capPreviewScale( hwnd:HWnd; f:BOOL ): BOOL;
function capOverlay( hwnd:HWnd; f:BOOL ): BOOL;
function capDlgVideoSource(hwnd:Hwnd): BOOL;
function capDlgVideoFormat(hwnd:HWND):BOOL;
function capEditCopy(hwnd:HWND):BOOL;
implementation
const
//номера сообщений были взяты из подобного, но большого (на все
//функции для видео) файла из инета
WM_CAP_DRIVER_CONNECT =WM_USER+10;
WM_CAP_DRIVER_DISCONNECT =WM_USER+11;
WM_CAP_SET_OVERLAY =WM_USER+51;
WM_CAP_SET_SCALE =WM_USER+53;
WM_CAP_DLG_VIDEOFORMAT =WM_USER+41;
WM_CAP_DLG_VIDEOSOURCE =WM_USER+42;
WM_CAP_EDIT_COPY =WM_USER+30;
//далее идут функции для работы с видео
//все они реализуются путем отправки сообщений Windows
//активизация видео
function capDriverConnect( hwnd:HWnd): BOOL;
begin
Result := bool(SendMessage( hwnd, WM_CAP_DRIVER_CONNECT, 0, 0 ));
end;
//выключить видео
function capDriverDisconnect(hwnd:HWnd): BOOL;
begin
Result:=bool(SendMessage(hwnd,WM_CAP_DRIVER_DISCONNECT,0,0 ));
end;
//Масштабировать видеоизображение по размерам окна
function capPreviewScale( hwnd:HWnd; f:BOOL ): BOOL;
begin
Result:=bool(SendMessage( hwnd,WM_CAP_SET_SCALE, LongWord(f), 0 ));
end;
//включить оверлейное видео
function capOverlay( hwnd:HWnd; f:BOOL ): BOOL;
begin
Result:=bool(SendMessage(Hwnd,WM_CAP_SET_OVERLAY,LongWord(f),0));
end;
//вызывает окно настройки источника видео
function capDlgVideoSource(hwnd:Hwnd): BOOL;
begin
Result:=bool(SendMessage(Hwnd,WM_CAP_DLG_VIDEOSOURCE,0,0));
end;
//вызывает окно формата видео
function capDlgVideoFormat(hwnd:HWND):BOOL;
begin
Result:=bool(SendMessage(Hwnd,WM_CAP_DLG_VIDEOFORMAT,0,0));
end;
//делает "фотографию" и помещает ее в буфер обмена
function capEditCopy(hwnd:HWND):BOOL;
begin
Result:=bool(SendMessage(Hwnd,WM_CAP_EDIT_COPY,0,0));
end;
end.
Справедливости ради хочу отметить, что пользоваться лучше полным модулем vfw.pas, который можно скачать в Сети. Это лишь пример с минимально необходимым набором функций! Причем этот пример актуален лишь для Windows 95/98/Me. Ходят слухи, что в серии Windows NT он не работает. Таким образом приходится искать и качать другой vfw.pas, предназначенный именно для этой серии операционных систем.
Обратите внимание, что функция создания окна для видео именуется capCreateCaptureWindowA, хотя в справочной документации указано capCreateCaptureWindow. В AVICAP32.DLL имеется именно функция с буковкой A и никаких намеков на присутствие "честно" описанной в справке функции в этой DLL я не обнаружил. Переименование функции можно сделать и в самом интерфейсном модуле, что сделано в vfw.h для С/C++ в "честных" vfw.pas. Остальные функции для работы с видео реализуются посылкой системе соответствующих сообщений. Даже вывод окон настройки видеосигнала.
Для воспроизведения видео вначале необходимо создать специальное видео окно (capture window) функцией capCreateCaptureWindow, этой же функцией задается соединение вашего видео окна с окном, отображаемым на мониторе. Далее следует вызвать функцию capDriverConnect для присоединения к видео окну драйвера. Затем воспользуемся функцией capPreviewScale для масштабирования видео окна в форме Form1, отображаемой на мониторе. Далее включаем оверлейное видео с помощью функции capOverlay.
Выводим видео на экран
Теперь приступим к реализации основной программы. Не забудьте в uses указать vfm.pas. Так же в Form1 надо накидать пяток кнопок, дать им соответствующее название и на OnClick завязать приведенные ниже реакции. Показывать видео будем в форме Form1:
procedure TForm1.CreateVideoClick(Sender: TObject);
begin
//Создание окна оверлейного видео и помещения
//его в клиетскую часть Form1
CaptureHandle:= capCreateCaptureWindowA(
'SB_VFW_WINDOW', //имя окна
WS_CHILD or WS_CLIPSIBLINGS or WS_VISIBLE,
0,0, {координаты (X,Y) верхнего левого угла}
Form1.ClientWidth,{ширина}
Form1.ClientHeight,{высота}
//Куда выводим окно с видео - в Form1.Handle
Form1.Handle,-1);
//конектимся
capDriverConnect(CaptureHandle);
//вытягивание (сжатие) видео в окно
capPreviewScale(CaptureHandle,true);
//включаем оверлейное видео
capOverlay(CaptureHandle,true);
end;
CaptureHandle - переменная типа HWND, представляет собой ссылку на окно, где прокручивается видео. Она должна быть описана в глобальных переменных модуля или как свойство Form1 - лишь бы ее "видели" процедуры Form1. Флаги окна WS_CHILD и WS_VISIBLE должны быть обязательно установлены. Флаг WS_CLIPSIBLINGS - по необходимости. Этот флаг делает окно вывода изображения "прозрачным". То есть через окно видео будут видны элементы интерфейса, расположенные на форме (например наши кнопки управления).
Обратите внимание на координаты рабочего окна. С помощью манипулирования координатами можно, например, оставить часть формы для элементов интерфейса или даже осуществить вывод увеличенной части видеоизображения. Попробуйте ввести координаты X = -400, Y = -400, Ширина = Form1.ClientWidth+400 и Высота = Form1.ClientHeigth+400 и вы получите в окне увеличенное изображение центральной части воспроизводимого видеосигнала (работает не на всех видео картах, но об этом позже).
Следующая по значимости функция - закрытие окна видеоизображения. Происходит это следующим образом:
procedure TForm1.CloseVideoClick(Sender: TObject);
begin
capDriverDisConnect(CaptureHandle);
DestroyWindow(CaptureHandle);
end;
Закрытие необходимо. Если Вы вышли из программы и не закрыли окно видеоизображения приведенным выше способом, то с вероятностью 90% можно утверждать, что в следующий раз попытка воспроизведения не удастся. Рекомендую по событию OnClose для Form1 производить проверку на наличие окна видеоизображения и после этого закрывать его.
Остается три функции:
- настройка видео формата
- настройка параметров видеоисточника
- "фотографирование" и помещение снимка в буфер обмена
Рассмотрим их более детально.
Настройка видео формата
За настройку видео формата в нашем приложении ответственен следующий метод:
procedure TForm1.FormatVideoClick(Sender: TObject);
begin
CapDlgVideoFormat(CaptureHandle);
end;
Инициирует вывод стандартного диалога Windows для выбора параметров оцифровки видеоизображения (см. рисунок 1).

Рисунок 1. Стандартный диалог Windows для выбора параметров оцифровки видеоизображения
Параметр Image Format понадобится в первую очередь если видео сохраняется в AVI-файл (данная операция выходит за рамки статьи). Кроме того, от значения Image Format зависят наборы размеров в Image Dimension. А вот на этот параметр стоит обратить внимание, ибо это и есть размер оцифровки видеосигнала. Чем он больше, тем лучше качество картинки на экране. Функции createCaptureWindow и capPreviewScale лишь масштабируют уже оцифрованное видео на экран монитора. Если этот параметр будет слишком мал, то на экране вы получите набор больших пикселей. С другой стороны, если этот параметр слишком большой, а производительность вашего компьютера и/или видео карты не велики, то периодические подвисания изображения вам гарантированы.
Настройка параметров видеоисточника
За настройку параметров видеоисточника в нашем приложении ответственен следующий метод:
procedure TForm1.SourceVideoClick(Sender: TObject);
begin
capDlgVideoSource(CaptureHandle);
end;
При этом, собственно, вызывается диалог настройки параметров видеоисточника, показанный на рисунке 2.

Рисунок 2. Диалог настройки параметров видеоисточника
Если у Вас картинка выглядит сильно поврежденной, то рекомендую обращаться в первую очередь к этому диалогу. С его помощью можно установить тип источника сигнала - Composite или S-Video, тип кодировки сигнала - PAL/SECAM (разумеется, список поддерживаемых стандартов значительно больше) и каким вы хотите его видеть: яркость, контрастность и цветность. Все произведенные изменения доблесно сохраняются в недрах системы. ВНИМАНИЕ!!! Внешний вид этого диалога и параметры, указанные в элементах интерфейса, напрямую зависят от драйвера видеоустройства!
"Фотографирование" и помещение снимка в буфер обмена
Функциональность "фотографирования" и размещения снимка в буфере обмена реализуется следующим методом:
procedure TForm1.CaptureVideoClick(Sender: TObject);
begin
capEditCopy(CaptureHandle);
end;
Очень полезная функция. После ее выполнения в буфер обмена будет помещена "фотография" видео экрана, которую можно просмотреть в отдельном окне или сохранить на диске. Прошу обратить внимание ни то, что размер помещенной в буфер картинки будет таким, каким Вы указали в диалоге, вызываемом функцией CapDlgVideoFormat.
Аппаратная сторона вопроса
В заключении хотелось бы поделиться наблюдениями, как все это хозяйство работает на разных видео картах. Приведенную в статье программу (во всяком случае, ее ядро) я протестировал на шести типах видео карт:
- старенький Matrox Millenium c платой видео ввода RainbowRunner
- Riva TNT PCI
- Riva TNT2 PCI
- Riva TNT2 AGP
- ATI Rage Fury VIVO
- ATI Radeon VIVO
Все Riva производства ASUS. Из них Matrox, ATI и RIVA TNT2 PCI работали без проблем. Riva TNT PCI и Riva TNT2 AGP страдали одной болезнью: окно вывода видео должно по размеру совпадать с параметрами указанными в диалоге настройки формата видеоизображения. Пиксель больше - пиксель меньше и начинаются проблемы: то темный экран вместо изображения, то "фотография" не идет, то изображение поврежденное. По моему у Riva полностью отсутствует блок масштабирования видеоизображения (TNT2 PCI была исключением по непонятным причинам). По некоторым данным, эта же проблема свойственна и GeForce, но утверждать это я не берусь, поскольку сам ее не проверял. У ATI проблема другая. При быстром изменении картинки на экране явно прослеживается оцифровка (идут не плавные переходы, а как бы состоящие из множества горизонтальных линий). При простом просмотре глаз этого практически не улавливает. Но стоит Вам сделать снимок и поглядеть на него… ужас. Но опять же данный эффект заметен лишь при быстро меняющейся картинке и далеко не всегда. В Radeone беда другая. Напрочь не работает API-функция capEditCopy. Смена драйверов с сайта ATI результатов не принесла (во всяком случае на момент написания статьи). Matrox Millenium работал без сбоев и нарицаний, но скорость у него по современным меркам конечно же неприемлемо низкая. Есть также опыт работы PCI-устройствами для видео ввода, отделенными от видео карты. При этом была замечена следующая проблема: никакого capEditCopy в режиме Overlay. Помогает переключение в режим Preview, в котором можно "схватить" снимок и затем опять переключиться в Overlay.
Вывод, в общем то, не утешительный. У меня сложилось стойкое ощущение, что все устройства видео ввода работают, мягко говоря, не совсем честно. Так что, любая конкретная реализация - это, как правило, головная боль конкретного программиста.
Павел Мамонов
pasha676@newmail.ru
|