Статьи

Использование веб-камеры в приложении WPF

Есть много разработчиков, которые пытаются интегрировать возможности взаимодействия с веб-камерой в свои приложения. Во время моей работы в качестве администратора в онлайн-сообществе разработчиков я столкнулся со многими вопросами по этой теме. Разнообразие реализаций огромно — некоторые люди рекомендуют использовать SDK, предоставляемые производителем камеры, а некоторые советуют использовать слой WIA (Windows Image Acquisition). Он может работать в определенных ситуациях, однако это никоим образом не универсальные решения. По своему опыту могу сказать, что не каждый производитель камер предоставляет SDK. Кроме того, многие новые камеры (особенно те, которые встроены в ноутбуки) не совместимы с WIA, так что это тоже не выбор.

С этого момента я признаю, что разработчик, который собирается использовать приведенный ниже код, установил соответствующие драйверы камеры. Это означает, что камера успешно обнаружена Windows и способна сделать снимок.

Для начала создайте новое приложение WPF. В этой конкретной статье я не буду фокусироваться на коде XAML, а скорее на функциональности кода. Я буду сильно полагаться на Win32 API для этого, на библиотеку avicap32, если быть точным.

Создайте стандартное приложение C # WPF. Вы можете переключиться в режим просмотра кода прямо при запуске, так как пока я вообще не буду касаться интерфейса. Прежде всего, требуется объявление для System.Runtime.InteropServices — я буду использовать DllImport для доступа к функциям, предоставляемым avicap32 .
Теперь нужны дополнительные объявления. Вот что я положил в свой класс:

IntPtr deviceHandle;public const uint WM_CAP = 0x400;public const uint WM_CAP_DRIVER_CONNECT = 0x40a;public const uint WM_CAP_DRIVER_DISCONNECT = 0x40b;public const uint WM_CAP_EDIT_COPY = 0x41e;public const uint WM_CAP_SET_PREVIEW = 0x432;public const uint WM_CAP_SET_OVERLAY = 0x433;public const uint WM_CAP_SET_PREVIEWRATE = 0x434;public const uint WM_CAP_SET_SCALE = 0x435;public const uint WS_CHILD = 0x40000000;public const uint WS_VISIBLE = 0x10000000;

 

IntPtr представляет указатель — целочисленный размер в зависимости от системы (будь то 32-разрядный или 64-разрядный). Используется для хранения дескриптора устройства. Следующий набор констант используется для установки параметров камеры для захвата. Вы можете использовать значения напрямую, но я настоятельно рекомендую сохранить константы.

Теперь есть несколько объявлений функций, которые необходимо ввести. С помощью этих функций приложение будет взаимодействовать с WinAPI для получения изображения через системный уровень. Декларации следующие:

[DllImport("avicap32.dll")]public extern static IntPtr capGetDriverDescription(ushort index, StringBuilder name, int nameCapacity, StringBuilder description,            int descriptionCapacity);[DllImport("avicap32.dll")]public extern static IntPtr capCreateCaptureWindow(string title, uint style, int x, int y, int width, int height, IntPtr window,            int id);[DllImport("user32.dll")]public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);[DllImport("user32.dll")]public static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

Как видите, там также задействован user32.dll. SendMessage отправит сообщение захвата в кадр захвата (также называемый окном). Я буду использовать SetWindowPos, чтобы установить поля, где будет показано изображение.

Мне также нужно создать функцию Attach () , которая будет получать изображение с подключенного устройства. Функция выглядит так:

public void Attach(){    deviceHandle = capCreateCaptureWindow(string.Empty, WS_VISIBLE | WS_CHILD, 0, 0, (int)this.ActualWidth -150, (int)this.ActualHeight, new WindowInteropHelper(this).Handle, 0);    if (SendMessage(deviceHandle, WM_CAP_DRIVER_CONNECT, (IntPtr)0, (IntPtr)0).ToInt32() > 0)    {        SendMessage(deviceHandle, WM_CAP_SET_SCALE, (IntPtr)(-1), (IntPtr)0);        SendMessage(deviceHandle, WM_CAP_SET_PREVIEWRATE, (IntPtr)0x42, (IntPtr)0);        SendMessage(deviceHandle, WM_CAP_SET_PREVIEW, (IntPtr)(-1), (IntPtr)0);        SetWindowPos(deviceHandle, new IntPtr(0), 0, 0, (int)this.ActualWidth-150, (int)this.ActualHeight, 6);    }}

 

Обратите внимание, что я использую WindowInteropHelper (this) .Handle,   чтобы получить текущий дескриптор окна. WPF назначает дескриптор только окну, поэтому попытка заставить его работать с определенным элементом управления будет слишком сложной в данный момент. Поэтому я просто собираюсь использовать текущее окно.

Приведенная выше функция устанавливает все необходимые параметры захвата, и вы можете видеть, что я использую константы, объявленные до фактической функции.

И это все. Теперь в обработчике событий Window_Loaded просто напишите Attach ();   затем создайте и запустите приложение. Вы увидите изображение веб-камеры, показанное на экране.

Как видите, поля устанавливаются с помощью (int) this.ActualWidth -150 и (int) this.ActualHeight при вызове capCreateCaptureWindow . Вы можете настроить поля по своему усмотрению, но учтите, что изображение будет терять пропорции, поэтому вам также придется настроить параметры масштабирования и учитывать их при изменении размера. Установка правильных полей также позволяет размещать дополнительные элементы управления в окне, чтобы захват не охватывал всю рабочую область.