佳能相机操作 EDSDK 教程 C# 版本

http://blog.csdn.net/zajin/article/details/17021339 
 

介绍

佳能EOS数码SDK是一个 可以用来远程控制其数码单反相机相当强大的SDK。不幸的是,想在互联网上找些很好的例子相当难,而且提供的文档也不是很齐全。因为我已经找到了很多能让其他人更容易掌握它的东西,我就想可以把我认为最重要的东西组织在一起,做成一个教程。 
本教程包括: 
  • 初始化和终止的SDK
  • 打开和关闭摄像头会话
  • 获取连接的像机列表
  • set和get相机设置
  • 获取可用的设置列表
  • 正常拍照和使用闪光灯模式
  • 处理相机事件
  • 将拍摄的照片下载到电脑上
  • 启动和实时查看
  • 记录实时查看
  • 锁定/解锁相机的用户界面
另外:其实我不从属于佳能公司,也不受其自助。 
还有:我不对这个软件做任何方式担保。使用时请您自己注意风险! (你可以在GPL许可证中找到有关此主题的更多信息。) 

背景

你必须有佳能EDSDK副本才能让这个运行起来。 (我认为)我不会被允许包含那些官方的DLL的到项目中,所以你必须自己通过申请去获取,它们在这里:

一旦你得到了那些DLL,就把它们放到项目中的 EDSDK文件夹里面,并确保再调试/发行目录中也要有相同的文件夹。 (或你认为的任何地方,同时据此调整 EDSDK.cs文件中的DLLPath变量(在右上部)。 

使用代码

我使用了三个简单的类,SDKHandler,Camera和CameraValues,还有来自 佳能SDK的EDSDK。 

在SDKHandler中有几个变量:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// The used camera  
  3. /// </summary>  
  4. public Camera MainCamera { get; private set; }  
  5.   
  6. /// <summary>  
  7. /// States if a session with the MainCamera is opened  
  8. /// </summary>  
  9. public bool CameraSessionOpen { get; private set; }  
  10.   
  11. /// <summary>  
  12. /// States if the LiveView is on or not  
  13. /// </summary>  
  14. public bool IsLiveViewOn { get; private set; }  
  15.   
  16. /// <summary>  
  17. /// States if LiveView is recorded or not  
  18. /// </summary>  
  19. public bool IsEvfFilming { get; private set; }  
  20.   
  21. /// <summary>  
  22. /// Directory to where photos will be saved  
  23. /// </summary>  
  24. public string ImageSaveDirectory { get; set; }  
  25.   
  26. /// <summary>  
  27. /// Handles errors that happen with the SDK  
  28. /// </summary>  
  29. public uint Error  
  30. {  
  31.     get { return EDSDK.EDS_ERR_OK; }  
  32.     set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: " + value); }  
  33. }  
  34.   
  35. /// <summary>  
  36. /// Frame buffer for LiveView recording  
  37. /// </summary>  
  38. private Queue<byte[]> FrameBuffer = new Queue<byte[]>(1000);  
还有一些来自SDK的以及我自己添加的一些事件:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #region SDK Events  
  2.   
  3. public event EDSDK.EdsCameraAddedHandler SDKCameraAddedEvent;  
  4. public event EDSDK.EdsObjectEventHandler SDKObjectEvent;  
  5. public event EDSDK.EdsProgressCallback SDKProgressCallbackEvent;  
  6. public event EDSDK.EdsPropertyEventHandler SDKPropertyEvent;  
  7. public event EDSDK.EdsStateEventHandler SDKStateEvent;  
  8.  
  9. #endregion  
  10.  
  11. #region Custom Events  
  12.   
  13. public delegate void CameraAddedHandler();  
  14. public delegate void ProgressHandler(int Progress);  
  15. public delegate void ImageUpdate(Image img);  
  16. public delegate void FloatUpdate(float Value);  
  17.   
  18. /// <summary>  
  19. /// Fires if a camera is added  
  20. /// </summary>  
  21. public event CameraAddedHandler CameraAdded;  
  22.   
  23. /// <summary>  
  24. /// Fires if any process reports progress  
  25. /// </summary>  
  26. public event ProgressHandler ProgressChanged;  
  27.   
  28. /// <summary>  
  29. /// Fires if the LiveView image is updated  
  30. /// </summary>  
  31. public event ImageUpdate LiveViewUpdated;  
  32.   
  33. /// <summary>  
  34. /// Fires if a new framerate is calculated  
  35. /// </summary>  
  36. public event FloatUpdate FrameRateUpdated;  
  37.  
  38. #endregion    
这个类的方法将在稍后讨论。

Camera类相当简单,工作起来就像一个相机指针和有关相机的一些信息的容器:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. public class Camera  
  2. {  
  3.     internal IntPtr Ref;  
  4.     public EDSDK.EdsDeviceInfo Info { get; private set; }  
  5.     public uint Error  
  6.     {  
  7.         get { return EDSDK.EDS_ERR_OK; }  
  8.         set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: " + value); }  
  9.     }  
  10.   
  11.     public Camera(IntPtr Reference)  
  12.     {  
  13.         this.Ref = Reference;          
  14.         EDSDK.EdsDeviceInfo dinfo;  
  15.         Error = EDSDK.EdsGetDeviceInfo(Reference, out dinfo);  
  16.         this.Info = dinfo;  
  17.     }  
  18. }   
CameraValues是一个拥有所有单元ID值以及存储从Av、Tv到ISO字符串值的静态类。

初始化和终止SDK

初始化和终止是最容易做的事情。当您一启动程序,就创建了一个SDKHandler的新实例。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Initialises the SDK and adds events  
  3. /// </summary>  
  4. public SDKHandler()  
  5. {  
  6.     //this is the important part of initialisation  
  7.     Error = EDSDK.EdsInitializeSDK();   
  8.   
  9.     //here we subscribe to the CameraAddedEvent and tell the SDK we did so  
  10.     CameraAddedEvent += new EDSDK.EdsCameraAddedHandler(SDKHandler_CameraAddedEvent);  
  11.     EDSDK.EdsSetCameraAddedHandler(CameraAddedEvent, IntPtr.Zero);  
  12.       
  13.     //here we subscribe to the rest of the camera events  
  14.     SDKStateEvent += new EDSDK.EdsStateEventHandler(Camera_SDKStateEvent);  
  15.     SDKPropertyEvent += new EDSDK.EdsPropertyEventHandler(Camera_SDKPropertyEvent);  
  16.     SDKProgressCallbackEvent += new EDSDK.EdsProgressCallback(Camera_SDKProgressCallbackEvent);  
  17.     SDKObjectEvent += new EDSDK.EdsObjectEventHandler(Camera_SDKObjectEvent);  
  18. }  
而当你关闭程序时,就会调用:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Closes open session and terminates the SDK  
  3. /// </summary>  
  4. public void Dispose()  
  5. {  
  6.     if (CameraSessionOpen) Error = EDSDK.EdsCloseSession(MainCamera.Ref);  
  7.     Error = EDSDK.EdsTerminateSDK();  
  8. }  

获取连接的像机列表

要打开一个会话,你必须选择一个相机。如果要获得所有连接的像机的列表,那就调用这个:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Get a list of all connected cameras  
  3. /// </summary>  
  4. /// <returns>The camera list</returns>  
  5. public List<Camera> GetCameraList()  
  6. {  
  7.     IntPtr camlist;  
  8.     //Get cameralist  
  9.     Error = EDSDK.EdsGetCameraList(out camlist);  
  10.     //Get each camera from camlist  
  11.     int c;  
  12.     Error = EDSDK.EdsGetChildCount(camlist, out c);  
  13.     List<Camera> OutCamList = new List<Camera>();  
  14.     for (int i = 0; i < c; i++)  
  15.     {  
  16.         IntPtr cptr;  
  17.         Error = EDSDK.EdsGetChildAtIndex(camlist, i, out cptr);  
  18.         OutCamList.Add(new Camera(cptr));  
  19.     {  
  20.     return OutCamList;  
  21. }  

打开和关闭相机会话

从以前收到的像机列表中选择一个,打开一个使用它的会话:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Opens a session with given camera  
  3. /// </summary>  
  4. /// <param name="NewCamera">The camera which will be used</param>  
  5. public void OpenSession(Camera NewCamera)  
  6. {  
  7.     //make sure the previous camera session is closed  
  8.     if (CameraSessionOpen) Error = EDSDK.EdsCloseSession(MainCamera.Ref);  
  9.     if (NewCamera != null)  
  10.     {  
  11.         MainCamera = NewCamera;  
  12.         //open a session  
  13.         Error = EDSDK.EdsOpenSession(MainCamera.Ref);  
  14.         //subscribe to the camera events (this time, in-Camera)  
  15.         EDSDK.EdsSetCameraStateEventHandler(MainCamera.Ref,   
  16.              EDSDK.StateEvent_All, SDKStateEvent, IntPtr.Zero);  
  17.         EDSDK.EdsSetObjectEventHandler(MainCamera.Ref,   
  18.              EDSDK.ObjectEvent_All, SDKObjectEvent, IntPtr.Zero);  
  19.         EDSDK.EdsSetPropertyEventHandler(MainCamera.Ref,   
  20.              EDSDK.PropertyEvent_All, SDKPropertyEvent, IntPtr.Zero);  
  21.         CameraSessionOpen = true;  
  22.     }  
  23. }  
如果你完成了对相机的使用,就使用这个方法关闭会话:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Closes the session with the current camera  
  3. /// </summary>  
  4. public void CloseSession()  
  5. {  
  6.     if (CameraSessionOpen)  
  7.     {  
  8.         Error = EDSDK.EdsCloseSession(MainCamera.Ref);  
  9.         CameraSessionOpen = false;  
  10.     }  
  11. }  

Set 和 Get 相机设置

通过ID去设置和获取相机的设置是非常简单的,但是一些有难度的结构值(这里还没有介绍).下面这个例子你可以在这个方法中获取到Tv,Av和ISO的设置。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Gets the current setting of given property ID  
  3. /// </summary>  
  4. /// <param name="PropID">The property ID</param>  
  5. /// <returns>The current setting of the camera</returns>  
  6. public uint GetSetting(uint PropID)  
  7. {  
  8.     if (MainCamera.Ref != IntPtr.Zero)  
  9.     {  
  10.         unsafe  
  11.         {  
  12.             uint property = 0;  
  13.             EDSDK.EdsDataType dataType;  
  14.             int dataSize;  
  15.             IntPtr ptr = new IntPtr(&property);  
  16.             //get the size of this property  
  17.             Error = EDSDK.EdsGetPropertySize(MainCamera.Ref, PropID, 0, out dataType, out dataSize);  
  18.             //get the data for this property  
  19.             Error = EDSDK.EdsGetPropertyData(MainCamera.Ref, PropID, 0, dataSize, ptr);  
  20.             return property;  
  21.         }  
  22.     }  
  23.     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }  
  24. }  
Setting方法(这里的参数一般是ID,从Camera类中获取这个string值):
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Sets a value for the given property ID  
  3. /// </summary>  
  4. /// <param name="PropID">The property ID</param>  
  5. /// <param name="Value">The value which will be set</param>  
  6. public void SetSetting(uint PropID, uint Value)  
  7. {  
  8.     if (MainCamera.Ref != IntPtr.Zero)  
  9.     {  
  10.         int propsize;  
  11.         EDSDK.EdsDataType proptype;  
  12.         //get the size of this property  
  13.         Error = EDSDK.EdsGetPropertySize(MainCamera.Ref, PropID, 0, out proptype, out propsize);  
  14.         //set the property  
  15.         Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, PropID, 0, propsize, Value);  
  16.     }  
  17.     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }  
  18. }  
可获取的设置值清单:

特定的相机没有特定的支持设置。这就是为什么你需要去获取所有可支持的设置值清单。这些只支持"AEModeSelect", "ISO", "Av", "Tv", "MeteringMode" 和"ExposureCompensation"。传给特定的ID你可以获取到对应的返回值。在Camera类中可以找到和Av,Tv和ISO的对应值。查看PDF格式的SDK文档可以获取其他的值。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Gets the list of possible values for the current camera to set.  
  3. /// Only the PropertyIDs "AEModeSelect", "ISO", "Av", "Tv", "MeteringMode"   
  4. /// and "ExposureCompensation" are allowed.  
  5. /// </summary>  
  6. /// <param name="PropID">The property ID</param>  
  7. /// <returns>A list of available values for the given property ID</returns>  
  8. public List<int> GetSettingsList(uint PropID)  
  9. {  
  10.     if (MainCamera.Ref != IntPtr.Zero)  
  11.     {  
  12.         if (PropID == EDSDK.PropID_AEModeSelect || PropID == EDSDK.PropID_ISOSpeed ||   
  13.             PropID == EDSDK.PropID_Av  
  14.             || PropID == EDSDK.PropID_Tv || PropID == EDSDK.PropID_MeteringMode ||   
  15.             PropID == EDSDK.PropID_ExposureCompensation)  
  16.         {  
  17.             EDSDK.EdsPropertyDesc des;  
  18.             Error = EDSDK.EdsGetPropertyDesc(MainCamera.Ref, PropID, out des);  
  19.             return des.PropDesc.Take(des.NumElements).ToList();  
  20.         }  
  21.         else throw new ArgumentException("Method cannot be used with this Property ID");  
  22.     }  
  23.     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }  
  24. }  

在bulb mode(灯泡模式)下正常拍照

用当前设置拍照,调用TakePhoto方法。有三点需要特别注意:

1、新线程启动了所以主线程没有被挂起。 
2、之所有这里用while 循环,是因为由相机有时不会立即就绪,需要稍后再试 
3、如果你将pc作为外设,那么请到下一个章节学习如何获取图片。

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Takes a photo with the current camera settings  
  3. /// </summary>  
  4. public void TakePhoto()  
  5. {  
  6.     new Thread(delegate()  
  7.     {  
  8.         int BusyCount = 0;  
  9.         uint err = EDSDK.EDS_ERR_OK;  
  10.         while (BusyCount < 20)  
  11.         {  
  12.             //try to take a photo  
  13.             err = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_TakePicture, 0);  
  14.             //if the camer is currently busy, wait and try again.   
  15.             //If successful or an error happened, break the loop  
  16.             if (err == EDSDK.EDS_ERR_DEVICE_BUSY) { BusyCount++; Thread.Sleep(50); }  
  17.             else { break; }  
  18.         }  
  19.         Error = err;  
  20.     }).Start();  
  21. }  
在bulb 模式下拍照,调用带有时间参数的takePhoto方法
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Takes a photo in bulb mode with the current camera settings  
  3. /// </summary>  
  4. /// <param name="BulbTime">The time in milliseconds for how long the shutter will be open</param>  
  5. public void TakePhoto(uint BulbTime)  
  6. {  
  7.     new Thread(delegate()  
  8.     {  
  9.         if (BulbTime < 1000)   
  10.         { throw new ArgumentException("Bulbtime has to be bigger than 1000ms"); }  
  11.         int BusyCount = 0;  
  12.         uint err = EDSDK.EDS_ERR_OK;  
  13.         while (BusyCount < 20)  
  14.         {  
  15.             //open the shutter  
  16.             err = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbStart, 0);  
  17.             if (err == EDSDK.EDS_ERR_DEVICE_BUSY) { BusyCount++; Thread.Sleep(50); }  
  18.             else { break; }  
  19.         }  
  20.   
  21.         Error = err;  
  22.         //Wait for the specified time  
  23.         Thread.Sleep((int)BulbTime);  
  24.         //close the shutter  
  25.         Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbEnd, 0);  
  26.     }).Start();  
  27. }  

将拍摄的图片上传到电脑中

想要把拍摄的照片直接传到电脑上,代替相机存储,请调用SetSetting方法进行设置:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. SetSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Host);  
每拍摄一张照片,EDSDK.ObjectEvent_DirItemRequestTransfer 类型的SDKObjectEvent都会被触发
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// An Objectevent fired  
  3. /// </summary>  
  4. /// <param name="inEvent">The ObjectEvent id</param>  
  5. /// <param name="inRef">Pointer to the object</param>  
  6. /// <param name="inContext"></param>  
  7. /// <returns>An EDSDK errorcode</returns>  
  8. private uint Camera_SDKObjectEvent(uint inEvent, IntPtr inRef, IntPtr inContext)  
  9. {  
  10.     if(inEvent == EDSDK.ObjectEvent_DirItemRequestTransfer)   
  11.         DownloadImage(inRef, @"Images");  
  12.     return EDSDK.EDS_ERR_OK;  
  13. }  
DownloadImage方法如下:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Downloads an image to given directory  
  3. /// </summary>  
  4. /// <param name="Info">Pointer to the object.   
  5. /// Get it from the SDKObjectEvent.</param>  
  6. /// <param name="directory"></param>  
  7. public void DownloadImage(IntPtr ObjectPointer, string directory)  
  8. {  
  9.     EDSDK.EdsDirectoryItemInfo dirInfo;  
  10.     IntPtr streamRef;  
  11.     //get information about the image  
  12.     Error = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);  
  13.     string CurrentPhoto = Path.Combine(directory, dirInfo.szFileName);  
  14.     //create a filestream for the image  
  15.     Error = EDSDK.EdsCreateFileStream(CurrentPhoto,   
  16.     EDSDK.EdsFileCreateDisposition.CreateAlways, EDSDK.EdsAccess.ReadWrite, out streamRef);  
  17.     uint blockSize = 1024 * 1024;  
  18.     uint remainingBytes = dirInfo.Size;  
  19.     //download the image data in blocks  
  20.     do  
  21.     {  
  22.         if (remainingBytes < blockSize) { blockSize = (uint)(remainingBytes / 512) * 512; }  
  23.         remainingBytes -= blockSize;  
  24.         Error = EDSDK.EdsDownload(ObjectPointer, blockSize, streamRef);  
  25.     } while (remainingBytes > 512);  
  26.     //download the last bit of the image  
  27.     Error = EDSDK.EdsDownload(ObjectPointer, remainingBytes, streamRef);  
  28.     //tell the camera that the download is done  
  29.     Error = EDSDK.EdsDownloadComplete(ObjectPointer);  
  30.     //release image and stream  
  31.     Error = EDSDK.EdsRelease(ObjectPointer);  
  32.     Error = EDSDK.EdsRelease(streamRef);  
  33. }  

打开并查看视频

视频是最难处理的事情之一,尤其是要求高性能的情况下。 首先我们这样打开视频:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Starts the LiveView  
  3. /// </summary>  
  4. public void StartLiveView()  
  5. {  
  6.     //make sure it's not already on  
  7.     if (!IsLiveViewOn)  
  8.     {  
  9.         //set the LiveView output to be the PC  
  10.         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);  
  11.         IsLiveViewOn = true;  
  12.     }  
  13. }  
完成之后, SDKPropertyEvent这个事件的 inPropertyID参数就被设置成了 EDSDK.PropID_Evf_OutputDevice:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// A property changed  
  3. /// </summary>  
  4. /// <param name="inEvent">The PropetyEvent ID</param>  
  5. /// <param name="inPropertyID">The Property ID</param>  
  6. /// <param name="inParameter">Event Parameter</param>  
  7. /// <param name="inContext">...</param>  
  8. /// <returns>An EDSDK errorcode</returns>  
  9. private uint Camera_SDKPropertyEvent  
  10. (uint inEvent, uint inPropertyID, uint inParameter, IntPtr inContext)  
  11. {  
  12.     if (inPropertyID == EDSDK.PropID_Evf_OutputDevice)  
  13.     {  
  14.         if (IsEvfFilming == true) DownloadEvfFilm();  
  15.         else if (IsLiveViewOn == true) DownloadEvf();  
  16.     }  
  17.     return EDSDK.EDS_ERR_OK;  
  18. }  
DownloadEvf方法如下:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Downloads the LiveView image  
  3. /// </summary>  
  4. private void DownloadEvf()  
  5. {  
  6.     new Thread(delegate()  
  7.     {  
  8.         //To give the camera time to switch the mirror  
  9.         Thread.Sleep(1500);  
  10.   
  11.         IntPtr jpgPointer;  
  12.         IntPtr stream = IntPtr.Zero;  
  13.         IntPtr EvfImageRef = IntPtr.Zero;  
  14.         UnmanagedMemoryStream ums;  
  15.         uint err;  
  16.         uint length;  
  17.         //create streams  
  18.         err = EDSDK.EdsCreateMemoryStream(0, out stream);  
  19.         err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);  
  20.   
  21.         Stopwatch watch = new Stopwatch();    //stopwatch for FPS calculation  
  22.         float lastfr = 24; //last actual FPS  
  23.   
  24.         //Run LiveView  
  25.         while (IsLiveViewOn)  
  26.         {  
  27.             watch.Restart();  
  28.             //download current LiveView image  
  29.             err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);  
  30.   
  31.             unsafe  
  32.             {  
  33.                 //get pointer and create stream  
  34.                 Error = EDSDK.EdsGetPointer(stream, out jpgPointer);  
  35.                 Error = EDSDK.EdsGetLength(stream, out length);  
  36.                 ums = new UnmanagedMemoryStream  
  37.                 ((byte*)jpgPointer.ToPointer(), length, length, FileAccess.Read);  
  38.                 //fire the LiveViewUpdated event with   
  39.                 //the LiveView image created from the stream  
  40.                 if (LiveViewUpdated != null) LiveViewUpdated(Image.FromStream(ums));  
  41.                 ums.Close();  
  42.             }  
  43.             //calculate the framerate and fire the FrameRateUpdated event  
  44.             lastfr = lastfr * 0.9f + (100f / watch.ElapsedMilliseconds);  
  45.             if (FrameRateUpdated != null) FrameRateUpdated(lastfr);  
  46.         }  
  47.   
  48.         //Release and finish  
  49.         if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }  
  50.         if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }  
  51.         //stop the LiveView  
  52.         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_TFT);  
  53.     }).Start();  
  54. }  

虽然这样下载视频图像不是最简单的,但可以说是最快的。

调用StopLiveView方法就能停止实物取景,实质上它的目的是让DownloadEvf方法跳出while循环:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Stops the LiveView  
  3. /// </summary>  
  4. public void StopLiveView()  
  5. {  
  6.     IsLiveViewOn = false;  
  7. }  

记录播放窗口

记录视频的工作跟播放视频的方式很像。

开始方法如下:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Starts LiveView and records it  
  3. /// </summary>  
  4. public void StartEvfFilming()  
  5. {  
  6.     if (!IsLiveViewOn)  
  7.     {  
  8.         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);  
  9.         IsLiveViewOn = true;  
  10.         IsEvfFilming = true;  
  11.     }  
  12. }  
捕获SDKPropertyEvent事件:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// A property changed  
  3. /// </summary>  
  4. /// <param name="inEvent">The PropetyEvent ID</param>  
  5. /// <param name="inPropertyID">The Property ID</param>  
  6. /// <param name="inParameter">Event Parameter</param>  
  7. /// <param name="inContext">...</param>  
  8. /// <returns>An EDSDK errorcode</returns>  
  9. private uint Camera_SDKPropertyEvent  
  10. (uint inEvent, uint inPropertyID, uint inParameter, IntPtr inContext)  
  11. {  
  12.     if (inPropertyID == EDSDK.PropID_Evf_OutputDevice)  
  13.     {  
  14.         if (IsEvfFilming == true) DownloadEvfFilm();  
  15.         else if (IsLiveViewOn == true) DownloadEvf();  
  16.     }  
  17.     return EDSDK.EDS_ERR_OK;  
  18. }  
DownloadEvfFilmmethod和DownloadEvfmethod比较相似,但有以下不同:
  • 在开始while循环之前,已经下载了一个边框,并启动了 StartEvfVideoWriter方法。
  • 为了更好的性能,LiveViewUpdatedevent只在每次第四个边框中调用(让实时取景稍显缓慢,但视频很流畅)
  • 实时取景图像作为byte array放入队列中,供StartEvfVideoWriter方法处理
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Records the LiveView image  
  3. /// </summary>  
  4. private void DownloadEvfFilm()  
  5. {  
  6.     new Thread(delegate()  
  7.     {  
  8.         //To give the camera time to switch the mirror  
  9.         Thread.Sleep(1500);  
  10.   
  11.         IntPtr jpgPointer;  
  12.         IntPtr stream = IntPtr.Zero;  
  13.         IntPtr EvfImageRef = IntPtr.Zero;  
  14.         UnmanagedMemoryStream ums;  
  15.         uint err;  
  16.         uint length;  
  17.   
  18.         err = EDSDK.EdsCreateMemoryStream(0, out stream);  
  19.         err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);  
  20.   
  21.         //Download one frame to init the video size  
  22.         err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);  
  23.         unsafe  
  24.         {  
  25.             Error = EDSDK.EdsGetPointer(stream, out jpgPointer);  
  26.             Error = EDSDK.EdsGetLength(stream, out length);  
  27.             ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(),   
  28.                     length, length, FileAccess.Read);  
  29.             Bitmap bmp = new Bitmap(ums);  
  30.             StartEvfVideoWriter(bmp.Width, bmp.Height);  
  31.             bmp.Dispose();  
  32.             ums.Close();  
  33.         }  
  34.   
  35.         Stopwatch watch = new Stopwatch();  
  36.         byte[] barr; //bitmap byte array  
  37.         const long ft = 41; //Frametime at 24FPS   
  38.                     //(actually 41.66, but there is a bit of calculation overhead)  
  39.         float lastfr = 24; //last actual FPS  
  40.         int LVUpdateBreak1 = 0;  
  41.   
  42.         //Run LiveView  
  43.         while (IsEvfFilming)  
  44.         {  
  45.             watch.Restart();  
  46.             err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);  
  47.   
  48.             unsafe  
  49.             {  
  50.                 Error = EDSDK.EdsGetPointer(stream, out jpgPointer);  
  51.                 Error = EDSDK.EdsGetLength(stream, out length);  
  52.                 ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(),   
  53.                         length, length, FileAccess.Read);  
  54.                 barr = new byte[length];  
  55.                 ums.Read(barr, 0, (int)length);  
  56.   
  57.                 //For better performance the LiveView is only updated with every 4th frame  
  58.                 if (LVUpdateBreak1 == 0 && LiveViewUpdated != null)   
  59.                     { LiveViewUpdated(Image.FromStream(ums)); LVUpdateBreak1 = 4; }  
  60.                 LVUpdateBreak1--;  
  61.                 FrameBuffer.Enqueue(barr);  
  62.                 ums.Close();  
  63.             }  
  64.   
  65.             //To get a steady framerate:  
  66.             while (true) if (watch.ElapsedMilliseconds >= ft) break;  
  67.             lastfr = lastfr * 0.9f + (100f / watch.ElapsedMilliseconds);  
  68.             if (FrameRateUpdated != null) FrameRateUpdated(lastfr);  
  69.         }  
  70.   
  71.         //Release and finish  
  72.         if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }  
  73.         if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }  
  74.         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_TFT);  
  75.     }).Start();  
  76. }  
由于写硬件驱动转换图片对象很慢,并且这也不需要实时处理,所以有了下面的StartEvfVideoWriter 方法 。这个方法将边框从队列中取出来保存,直到队列为空并且电影处理已关闭。我这里没有包含实际的视频保存功能,你可以用你偏好的类库去完成。
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Writes video frames from the buffer to a file  
  3. /// </summary>  
  4. /// <param name="Width">Width of the video</param>  
  5. /// <param name="Height">Height of the video</param>  
  6. private void StartEvfVideoWriter(int Width, int Height)  
  7. {  
  8.     new Thread(delegate()  
  9.     {  
  10.         byte[] byteArray;  
  11.         ImageConverter ic = new ImageConverter();  
  12.         Image img;  
  13.         while (IsEvfFilming)  
  14.         {  
  15.             while (FrameBuffer.Count > 0)  
  16.             {  
  17.                 //get byte array from queue  
  18.                 byteArray = FrameBuffer.Dequeue();  
  19.                 //convert it to an image object  
  20.                 img = (Image)ic.ConvertFrom(byteArray);  
  21.                 //Save video frame here. e.g. with the VideoFileWriter from the AForge library.  
  22.             }  
  23.             //if saving is faster than the LiveView, wait a bit for new frames and start over  
  24.             if (IsEvfFilming) Thread.Sleep(10);  
  25.         }  
  26.     }).Start();  
  27. }  
下面是如何利用 AForgelibrary的一个例子 (请注意correct DLLs,它们没被包含在这个项目中)
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. private void StartVideoWriter(int Width, int Height)  
  2. {  
  3.     new Thread(delegate()  
  4.     {  
  5.         VideoFileWriter writer = new VideoFileWriter();  
  6.         writer.Open("LiveViewVideo.avi", Width, Height, 24, VideoCodec.MPEG4);  
  7.         byte[] byteArray;  
  8.         ImageConverter ic = new ImageConverter();  
  9.         Image img;  
  10.         while (IsEvfFilming)  
  11.         {  
  12.             while (FrameBuffer.Count > 0)  
  13.             {  
  14.                 byteArray = FrameBuffer.Dequeue();  
  15.                 img = (Image)ic.ConvertFrom(byteArray);  
  16.                 writer.WriteVideoFrame(new Bitmap(img));  
  17.             }  
  18.             if (IsEvfFilming) Thread.Sleep(10);  
  19.         }  
  20.         writer.Close();  
  21.     }).Start();  
  22. }  
关闭电影功能跟关闭实时取景方法一样:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Stops LiveView and filming  
  3. /// </summary>  
  4. public void StopEvfFilming()  
  5. {  
  6.     IsLiveViewOn = false;  
  7.     IsEvfFilming = false;  
  8. }   

关闭/打开相机的接口

为了避免或允许用户在相机上改变设置,你可以这样关闭或者打开相机的接口:

[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /// <summary>  
  2. /// Locks or unlocks the cameras UI  
  3. /// </summary>  
  4. /// <param name="LockState">True for locked, false to unlock</param>  
  5. public void UILock(bool LockState)  
  6. {  
  7.     if (LockState == true) Error =   
  8.     EDSDK.EdsSendStatusCommand(MainCamera.Ref, EDSDK.CameraState_UILock, 0);  
  9.     else Error = EDSDK.EdsSendStatusCommand  
  10.         (MainCamera.Ref, EDSDK.CameraState_UIUnLock, 0);  
  11. }  

利用图形化界面

在图像化界面向导代码中,你可以看到如何将以上所有的代码运用到一个真实可用的软件中。你也可以设置 Av,Tv,ISO和白平衡,实时取景和拍照等模式.

插入相机,打开图形化界面就可以开始你的设置啦。

题外话

我用EOS 40D测试了以上代码:

如果你尝试了不同的方法,请告诉我,我会把它添加到这篇文章中。

如果你发现一些bug,对方法有改进或者有一些新的想法,非常希望你能告诉我。

源码下载:

原文地址:https://www.cnblogs.com/carl2380/p/4730571.html