Использование Canon EDSDK в .NET разработке

Нашей задачей стало создание WPF-приложения Photobooth, которое должно работать с камерами Canon EOS: фотографировать и снимать видео. Для этих целей Canon предоставляет разработчикам EOS Digital SDK (EDSDK). Мы использовали версию 2.11. Ниже приведен полный перечень поддерживаемых камер:

  • EOS-1D Mark III
  • EOS 40D
  • EOS-1Ds Mark III
  • EOS DIGITAL REBEL Xsi/450D/ Kiss X2
  • EOS DIGITAL REBEL XS/ 1000D/ KISS F
  • EOS 50D
  • EOS 5D Mark II
  • EOS Kiss X3/EOS REBEL T1i /EOS 500D
  • EOS 7D
  • EOS-1D Mark IV
  • EOS Kiss X4/EOS REBEL T2i /EOS 550D
  • EOS 60D
  • EOS Kiss X5/EOS REBEL T3i /EOS 600D
  • EOS Kiss X50/EOS REBEL T3 /EOS 1100D
  • EOS 5D Mark III
  • EOS 1D X
  • EOS Kiss X6i/EOS 650D/EOS REBEL T4i

Canon EDSDK — библиотека, написанная на C/C++. Она предоставляет возможность управлять цифровой камерой, подключенной к ПК, загружать фото и видео с камеры на компьютер.

Так как мы используем C# и WPF, то взяли враппер EDSDK для .NET — https://edsdkwrapper.codeplex.com/. Он использует COM Interop. Мы только добавили поддержку видео. EDSDK содержит несколько нативных DLL-библиотек:

Список файлов

Чтобы использовать EDSDK в вашем .NET-проекте, поместите эти библиотеки и папку «icc» в папку Debug или Release и добавьте ссылку на EDSDKWrapper.Framework.dll. Основной класс этого враппера, FrameworkManager, расположен в пространстве имён EDSDKWrapper.Framework.Managers. Этот класс реализует функцию инициализации/завершения работы EDSDK и даёт доступ к списку камер. Чтобы получить доступ к камере, подключенной к ПК, используйте следующий код:

using EDSDKWrapper.Framework.Managers;
using EDSDKWrapper.Framework.Objects;
var frameworkManager = new FrameworkManager();
var camera = frameworkManager.GetCameras().FirstOrDefault();

Camera — второй важный класс в EDSDK. Она реализует доступ к таким настройкам камеры, как автоэкспозиция, чувствительность ISO, диафрагма, выдержка, баланс белого и др. Полный список настроек есть в руководстве к EDSDK API. Также этот класс реализует функцию предпросмотра, фото- и видео-съёмки. Все настройки камеры завёрнуты в перечисляемые типы в пространстве имён EDSDKWrapper.Framework.Enums. Чтобы задать настройки и начать запись видео с предпросмотром, используйте следующий код:

using EDSDKWrapper.Framework.Managers;
using EDSDKWrapper.Framework.Objects;
using EDSDKWrapper.Framework.Enums;
var frameworkManager = new FrameworkManager();
var camera = frameworkManager.GetCameras().FirstOrDefault();
camera.LiveViewEnabled = true;
camera.LiveViewOutputDevice = LiveViewOutputDevice.Computer;
camera.AEModeSelect = AEMode.Movie;
camera.SaveTo = SaveTo.Camera;
camera.VideoDownloaded += cameraVideoDownloaded;
camera.Record = Record.Start;
DateTime start = DateTime.Now;
while (camera.IsRecording && (DateTime.Now - start).Seconds < someDuration)
{
    var stream = this.Camera.GetLiveViewImage();
    // some actions with stream, e.g. displaying live view image in app window
}
camera.Record = Record.Stop;
camera.LiveViewEnabled = false;
camera.Dispose();
frameworkManager.Dispose();

Здесь мы включили режим предпросмотра и видеорежим, установили передачу картинки предпросмотра и записанного видео на ПК, а также добавили обработчик для события VideoDownloaded. Затем мы начали записывать в течение некоторого времени, после чего остановили запись и освободили неиспользуемые ресурсы.

Как уже было сказано, данный враппер не поддерживает видеорежим. Как же заставить его работать? Когда мы устанавливаем какой-либо параметр камеры, мы вызываем стандартную функцию EDSDK — EdsSetPropertyData:

EdsError EdsSetPropertyData(
    EdsBaseRef inRef,
    EdsPropertyID inPropertyID,
    EdsInt32  inParam,
    EdsUInt32 inPropertySize,
    const EdsVoid*  inPropertyData)

Описания параметров:

  • inRef — обозначает объект (EdsCameraRef или EdsImageRef), для которого устанавливаются свойства.
  • inPropertyID — объявляет идентификатор свойства.
  • inParam — объявляет дополнительную информацию о свойстве. Используйте дополнительную информацию о свойствах, если они могут быть установлены или возвращены для нескольких элементов, таких как стили изображения.
  • inPropertySize — устанавливает объём данных для свойства в байтах. Для каждого свойства это значение можно узнать при помощи функции EdsGetPropertySize.
  • inPropertyData — определяет данные свойства, которое устанавливается.

Параметр inPropertyID определяет, какие операции мы будем производить и какие настройки камеры будем менять. Для видеозаписи он равен 0x00000510. Таким образом, мы добавили следующую строку для перечисляемого типа PropertyId во враппере:

namespace EDSDKWrapper.Framework.Enums
{
   public enum PropertyId : uint
   {
       ...
       Record = 0x00000510,
       ...
   }
}

Далее мы можем создать новый перечисляемый тип Record с состояниями записи:

namespace EDSDKWrapper.Framework.Enums
{
   public enum Record : uint
   {
       Start = 4,
       Stop = 0
   }
}

Далее добавляем свойства классу Camera:

public Record Record
{
    get { return (Record) GetUInt32Property(PropertyId.Record); }
    set { SetUInt32Property(PropertyId.Record, (UInt32) value); }
}
public bool IsRecording
{
    get { return Record == Record.Start; }
}

Далее определяем наш делегат:

public static class UserDelegates
{
    public delegate void DownloadedVideoHandler(Stream videoStream, string filename);
}

После чего мы изменили метод objectEventHandler и добавили новый метод DownloadVideo в класс Camera:

if (inEvent == (uint)ObjectEvent.DirItemCreated)
{
    DownloadVideo(inRef);
}
public event UserDelegates.DownloadedVideoHandler VideoDownloaded;
public void DownloadVideo(IntPtr inRef)
{
    IntPtr streamPointer = IntPtr.Zero;
    DirectoryItemInformation info;
    uint returnValue = EDSDKInvokes.GetDirectoryItemInformation(inRef, out info);
    ReturnValueManager.HandleFunctionReturnValue(returnValue);
    try
    {
        //returnValue = EDSDKInvokes.CreateFileStream(info.FileName,
               //                       FileCreateDisposition.CreateAlways,
        //                       Access.ReadWrite,
        //                       out streamPointer);
        EDSDKInvokes.CreateMemoryStream(info.Size, out streamPointer);
        ReturnValueManager.HandleFunctionReturnValue(returnValue);
        IntPtr videoBlob = IntPtr.Zero;
        try
        {
            returnValue = EDSDKInvokes.Download(inRef,
                       info.Size, streamPointer);
            ReturnValueManager.HandleFunctionReturnValue(returnValue);
            returnValue = EDSDKInvokes.DownloadComplete(inRef);
            ReturnValueManager.HandleFunctionReturnValue(returnValue);
            returnValue = EDSDKInvokes.GetPointer(streamPointer,
                       out videoBlob);
            ReturnValueManager.HandleFunctionReturnValue(returnValue);
            uint videoLength;
            returnValue = EDSDKInvokes.GetLength(streamPointer,
                       out videoLength);
            ReturnValueManager.HandleFunctionReturnValue(returnValue);
            var buffer = new byte[videoLength];
            Marshal.Copy(videoBlob, buffer, 0, (int)videoLength);
            var stream = new MemoryStream(buffer);
            if (VideoDownloaded != null)
                VideoDownloaded(stream, info.FileName);
            Global.OnDownloadVideo(stream, info.FileName);
        }
        finally
        {
            EDSDKInvokes.Release(videoBlob);
        }
    }
    finally
    {
        EDSDKInvokes.Release(streamPointer);
    }
}

Этот метод передаёт записанный поток видео обработчику событий VideoDownloaded. Поток можно сохранить в файл следующим образом:

private void CameraVideoDownloaded(Stream videoStream, string filename)
{
    using (var fileStream = File.Create(filename))
    {
        videoStream.CopyTo(fileStream);
        fileStream.Flush();
        fileStream.Close();
   }
}

Таким образом, через EDSDK можно управлять многими функциями камер Canon EOS. Полный список параметров и команд содержится в руководстве к EDSDK API.