市面比较常用佳能单反,其他单反是否也提供SDK没有搜过。
佳能单反一般选用EOS500D,550D,600D,650D,750D这些都是被EDSDK所支持的。
截止2019年年初,佳能官方EDSDK需要在官网申请,不对中国提供。
我是使用的EDSDK的版本是3.5/3.6.1
单反侧面翻盖打开后,一般会看到两个接口,一个是micro usb,一个是mini hdmi。前者是本文章主要介绍的方式所使用的,即通过micro usb 转 usb线连接单反与主机
单反开机,把拨盘拨到M挡
如果觉得原配的usb线太短,可以加一根usb延长线,根据距离考虑是否要自带放大器的
EDSDK的dll和c#脚本导入之后如下
我们主要需要关注的是SDKHander.cs。它定义了变量、事件、连接设备方法和摄像机命令。
STAThread是一个线程管理脚本。
1 初始化
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EDSDKLib;
using UnityEngine.UI;
using EDSDK.NET;
public class Main : MonoBehaviour {
SDKHandler CameraHandler;
EDSDK.NET.Camera myCamera;
#region UI变量
public RawImage moviePlane;
private Texture2D tt;
#endregion
#region 设备可用变量
private string avValues = "";
private string tvValues = "";
private string isoValues = "";
#endregion
// Use this for initialization
void Start () {
//实例化单反操控类
CameraHandler = new SDKHandler();
print("EDSDK 初始化成功");
//找到设备
}
}
2 找到设备
//找到设备
List<EDSDK.NET.Camera> ll = CameraHandler.GetCameraList();
foreach (EDSDK.NET.Camera cc in ll)
print(cc.Info.szDeviceDescription);
if (ll.Count == 0)
{
print("没有发现佳能单反设备");
return;
}
myCamera = ll[0];
//设置设备
3 设置设备
//了解设备参数
//拍摄模式,就是主拨盘档位,没有意义,自己在相机上拨就好了
//我试的时候,需要拨到M档,如果需要获取视频流,必须是录像档位
//foreach (int ii in CameraHandler.GetSettingsList(EDSDK.PropID_AEModeSelect))
// print("AEMode = " + ii);
//光圈
foreach (int ii in CameraHandler.GetSettingsList(EDSDKLib.EDSDK.PropID_Av))
avValues += ii + "|";
print("Av = " + avValues);
//快门
foreach (int ii in CameraHandler.GetSettingsList(EDSDKLib.EDSDK.PropID_Tv))
tvValues += ii + "|";
print("Tv = " + tvValues);
//感光度
foreach (int ii in CameraHandler.GetSettingsList(EDSDKLib.EDSDK.PropID_ISOSpeed))
isoValues += ii + "|";
print("ISO = " + isoValues);
//以下不重要
//foreach (int ii in CameraHandler.GetSettingsList(EDSDK.PropID_MeteringMode))
// print("Metering = " + ii);
//foreach (int ii in CameraHandler.GetSettingsList(EDSDK.PropID_ExposureCompensation))
// print("Exposure = " + ii);
//设置设备
CameraHandler.SetSetting(EDSDKLib.EDSDK.PropID_Av, 80);//光圈
CameraHandler.SetSetting(EDSDKLib.EDSDK.PropID_Tv, 61);//快门速度
CameraHandler.SetSetting(EDSDKLib.EDSDK.PropID_ISOSpeed, 104);//ISO
//Save To有三种,相机存储卡、本地即电脑、两者都保存。这里选保存到主机,单反没配存储卡
CameraHandler.SetSetting(EDSDKLib.EDSDK.PropID_SaveTo, (uint)EDSDKLib.EDSDK.EdsSaveTo.Host);//图片保存位置
CameraHandler.SetCapacity();//这句必须加,曾经不明白这句的API解释而含累错过
CameraHandler.ImageSaveDirectory = Application.streamingAssetsPath;//把拍到的照片保存到streamingAsset下
//显示输出
数值对应表:
光圈
快门
4 预设实时视频流的显示
这个纯粹就是准备好一个RawImage组件,准备播放。
tt = new Texture2D(1920, 1080,TextureFormat.RGB24,true);
moviePlane.texture = tt;
5 控制与输出
private bool isLiveOn = false;
private byte[] tempBytes;
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.S))
{
//拍照
CameraHandler.TakePhoto();
}
if (Input.GetKeyDown(KeyCode.D))
{
//开直播
CameraHandler.StartLiveView();
isLiveOn = true;
}
if (Input.GetKeyDown(KeyCode.F))
{
//退出直播间~~
CameraHandler.StopLiveView();
isLiveOn = false;
}
if (isLiveOn)
{
//如果直播在开着,就一帧帧load显示。这里才是有坑的地方。
tempBytes = CameraHandler.GetImageByte();
tt.LoadImage(tempBytes);
}
}
6 录像视频流的API改造
佳能的EDSDK提供的cs脚本并没有针对Unity,Unity C#的开发和.net framework的开发最突出的问题就是:C#里面的Bitmap在Unity里面没有,怎么把Bitmap转换成Texture呢?
在SDKHandler.cs里面需要做一些改动
1 新增函数和变量
public byte[] TextureBytes;//byte数组,外部获取
public byte[] GetImageByte()
{
if (TextureBytes != null && TextureBytes.Length != 0)
return TextureBytes;
return null;
}
public Bitmap TextureBitmap;//Bitmap中转变量
private MemoryStream ms;//转换中的关键变量
2 找到DownloadEvf函数,在函数体内做如下修改
//run live view
while (IsLiveViewOn)
{
//download current live view image
err = EDSDKLib.EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);
if (err == EDSDKLib.EDSDK.EDS_ERR_OK) err = EDSDKLib.EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);
if (err == EDSDKLib.EDSDK.EDS_ERR_OBJECT_NOTREADY) { Thread.Sleep(4); continue; }
else Error = err;
/*
lock (STAThread.ExecLock)
{
//download current live view image
err = EDSDKLib.EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);
if (err == EDSDKLib.EDSDK.EDS_ERR_OK) err = EDSDKLib.EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);
if (err == EDSDKLib.EDSDK.EDS_ERR_OBJECT_NOTREADY) { Thread.Sleep(4); continue; }
else Error = err;
}
*/
//get pointer
Error = EDSDKLib.EDSDK.EdsGetPointer(stream, out jpgPointer);
Error = EDSDKLib.EDSDK.EdsGetLength(stream, out length);
//get some live view image metadata
if (!IsCoordSystemSet) { Evf_CoordinateSystem = GetEvfCoord(EvfImageRef); IsCoordSystemSet = true; }
Evf_ZoomRect = GetEvfZoomRect(EvfImageRef);
Evf_ZoomPosition = GetEvfPoints(EDSDKLib.EDSDK.PropID_Evf_ZoomPosition, EvfImageRef);
Evf_ImagePosition = GetEvfPoints(EDSDKLib.EDSDK.PropID_Evf_ImagePosition, EvfImageRef);
//release current evf image
if (EvfImageRef != IntPtr.Zero) { Error = EDSDKLib.EDSDK.EdsRelease(EvfImageRef); }
//create stream to image
unsafe { ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(), (long)length, (long)length, FileAccess.Read); }
//把unmanageedMemoryStream类型的ums转成Bitmap
TextureBitmap = new Bitmap(ums, true);
ms = new MemoryStream();
//用System.Drawing.Bitmap的方法将Bitmap转成MemoryStream
TextureBitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
//将MemoryStream转成Bytes
TextureBytes = ms.GetBuffer();
//fire the LiveViewUpdated event with the live view image stream
if (LiveViewUpdated != null) LiveViewUpdated(ums);
ums.Close();
}
主要注意这一段:
7 关闭会话
void OnApplicationQuit()
{
//CameraHandler.StopLiveView();
CameraHandler.Dispose();
}
在这个基础上可以实现拍照,照片会产生在StreamingAssets文件夹下,可以开启实时录像视频流,但是视频流观察下来有0.5秒-1秒的延迟。对于互动应用,如果需要实时画面让用户看到效果,不会影响需求实现,但是如果对实时性要求比较高,这个延迟是不行的,市面上我见过直接使用micro usb接口实现的单反拍照应用不延迟,我目前想应该不是通过unity实现的,还有一点,由于用到了STAThread脚本去管理一个线程,这个线程会在打开实时录像时使用,一旦使用,在exe状态下,关闭应用程序会导致卡死,后续我也会继续去研究怎么改进。
其他方案
视频采集卡
单反有mini hdmi接口
我们可以用mini hdmi 转hdmi线,连接单反与采集卡
视频采集卡,无理是pcie内置采集卡,还是外置采集卡都可以
如果是PCIE的内置采集卡,Unity可以通过WebCamDevice去获取到这个“摄像”设备
如果是外置采集卡,则用采集卡另一端的USB3.0接口与主机连接,同样,Unity也是通过WebCamDevice去获取。
这样的技术方案,没有延迟。
缺点是连接了mini hdmi 档位需要在摄像档位,此时连接micro usb无法通过程序进行控制。
这个需求从当初研究到现在已经很长时间,如果有些地方写的不对或者不够好,欢迎您的指正。
工程源码参考:
Unity控制佳能单反工程