购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 视频采集设备

我们是最后一代婴儿时期没有被人用手机拍过照片的人。

——轶话

照相机诞生至今,已有近200年的历史。

1826年,法国的尼普斯(Nicéphore Niépce,1765—1833)用沥青加上薰衣草油涂在铅锡合金板上,曝光约8小时,拍摄了自家二楼阳台的窗外,这是人类保留下来的最早的照片,如图2-8所示。

图2-8 人类最早的照片

1861年,第一张彩色图像在电磁学之父麦克斯韦的演讲中诞生。

1878年,柯达(Kodak)创始人乔治·伊士曼(George Eastman,美国,1854—1932)成功研制了胶片,开启了传统相机时代。

1975年,柯达的史蒂文·赛尚(Steven J.Sasson,美国,1950—)研制出第一台数码相机,光电逐步取代光化学。

2007年,随着苹果智能手机的发布,拍摄成为触手可及的日常。

本节将介绍获取视频采集设备信息的方法,包括采集设备枚举、参数选择、插拔通知。

2.2.1 采集设备枚举

视频采集的第一步,是枚举出系统上有哪些采集设备。

在Windows上,我们使用DirectShow技术来实现设备枚举,这基于C++的组件对象模型技术。

在macOS和iOS上,我们使用AVFoundation的AVCaptureDevice来实现枚举,这基于Objective-C的OC类。

在Android上,我们使用android.hardware.Camera2来实现枚举,这基于Java的对象包。

不论在哪个平台上,我们最终都需要将数据转换到C++层,以便后续统一处理。这涉及COM编程、OC混编、JNI编程等基础知识,有关内容可以查阅附录B和F。

Windows采集设备枚举

使用DirectShow需要如下头文件和库文件。

img

我们创建CLSID_SystemDeviceEnum对象,并获取ICreateDevEnum接口。

img

ICreateDevEnum接口只有一个方法,就是获取一个IEnumMoniker的接口指针。

img

通过IEnumMoniker,我们可以循环枚举到IMoniker接口,其中Moniker是名字的意思。

img

通过IMoniker的BindToStorage方法,获取设备属性接口IPropertyBag,从而获得设备属性。

img
img

通过IMoniker的BindToObject方法,获取一个IBaseFilter的指针,后续可用它操纵设备。

img

对于每个设备,我们都定义一个C++类,用来保存这些信息。

img

m_device_name用作设备名称的展示;m_device_path用作设备ID的获取;m_device_filter用来查询设备的能力,显示设备的设置对话框,或者后续采集时使用。

在作者的Windows系统上,有一个内置摄像头和一个USB外接摄像头,获取的信息如下。

img

macOS和iOS采集设备枚举

相比Windows,在macOS下的采集设备枚举要容易得多,Apple公司已经封装好了AV CaptureDevice类。

img

对于返回的AVCaptureDevice数组,使用count可访问其总数,使用objectAtIndex可访问其元素。这个设备对象有30多个属性,我们感兴趣的是下面这些。

img

作者的macOS笔记本上有一个内置的摄像头,其信息如下。

img

OC文件的后缀名通常是.m。当OC文件需要包含C++头文件时,需要将其后缀名改为.mm。

对于iOS,这里的枚举方法是完全一样的,作者的iPhone手机有前后两个摄像头。

img

Android设备枚举

Android系统提供了android.hardware.Camera包,可以轻松枚举相机。

img
img

上述代码在作者的Android手机上的运行结果如下。

img

Camera1在API level 21时被废弃了。推荐使用Camera2来完成相机的枚举。

img

上述代码在作者的Android手机上显示如下。

img
img

这里后置摄像头为编号0,旋转角为90 ° 。前置摄像头为编号1,旋转角为270 °

2.2.2 图像采集参数

俗话说,摄影是用光的艺术。对于相机而言,如何捕获与控制光的留存是核心问题。将拍照过程想象成给黑暗的密室开一扇窗,透一缕光,则关键的控制部件有以下4个。

· 镜头 (Lens)调整焦距控制进光的范围,即窗的取景范围。

· 光圈 (Aperture)控制窗户的大小。

· 快门 (Shutter)控制开窗的时长。

· 感光度 (ISO)调整底片/感应器对光的敏感程度。

(1)镜头焦距决定了我们要拍的物体范围。焦距越小,则视角越广。拍风景时,焦距通常在24~35mm,手机上的广角主摄为25~28mm。拍人像时,焦距通常在35~70mm,这属于标准镜头,其与肉眼看到的内容大致相符,50mm拍人像时效果最佳。

特殊镜头,如长焦镜头、微焦镜头、超广角镜头等,提供了更宽泛的焦距和视角。通过镜头的物理变焦也称为 光学变焦

(2)光圈用来控制进光量,常用 f 值表示。 f 值越小,光圈越大,如图2-9所示。

图2-9 不同光圈大小

光圈越大,进的光线越多,图像越亮,但同时会使景深(Depth of Field,DOF)变浅,带来背景虚化模糊。反之,光圈小,进光少,则景深大,背景清晰。

(3)快门用来控制曝光时间,常见值为1秒、1/2秒、1/4秒……至1/2500秒。该值应与光圈大小配合,光圈越大,快门时间应越短,使得进入的光线总量保持恒定。通常快门时间越短,能捕捉到的运动细节越多。

(4)感光度的概念来自传统相机,常见值为100、200和400,数值越高,表明对光线越敏感。感光度过高会产生噪点,应尽量使用低感光度进行拍摄。只有在小光圈高速快门时,为了提高拍摄成功率,才稍微增大感光度。

上述4个物理层面的拍照参数,会影响最终的成像效果。

对RTC而言,我们关心的采集参数为分辨率、帧率、像素格式。

·分辨率,就是图像大小,用长和宽表示,单位是像素。如640×480、1280×720等,常限制于4096之内。普通屏幕使用4∶3,宽屏使用16∶9,常见的视频分辨率规格如表2-3所示。

表2-3 常见的视频分辨率规格

·帧率,就是每秒采样的图像数,如15 fps、30 fps等,常限制在120 fps以内。帧率会受曝光时间长短的影响。在开启自动曝光(Automatic Exposure)模式后,设光圈的 f 值为 A ,曝光时间为 T ,环境亮度为 B ,感光度为 S ,则它们之间的关系如式(2.7)所示。

img

因此环境的亮度通常会影响摄像头的输出帧率,越亮的环境,曝光时间越短,帧率往往会越高。

·像素格式,为硬件输出的帧格式,有BGRA、I420、NV12等。

下面我们来看各个平台是如何枚举这些能力的。

Windows平台

在上一节,我们获得了每个设备的IBaseFilter指针,它有一个EnumPins()方法,可以用来枚举Filter上的端口(Pin)。每个Filter都会有若干输入Pin和输出Pin。通过枚举,可以找到Filter上所有的输出端口(Output Pin)。

img

在输出端口上,可以查询IAMStreamConfig接口,使用它可以遍历其支持的分辨率、像素格式及平均帧率。

img

对于给定的像素格式和分辨率,支持的帧率可能有多种。可以使用Filter上的IAMVideo Control来获取帧率列表。

img

当要选择某种格式和分辨率进行采集时,使用IAMStreamConfig的SetFormat函数。

img

CaptureDevice

CaptureDevice是一个简单的设备枚举示例程序,用来在Windows上枚举视频采集设备,打印其所有能力。其界面如图2-10所示。

这里对IAMStreamConfig获取的结构体的每一个值都进行了dump输出。

在VIDEOINFOHEADER结构中,有一个BITMAPINFO结构,它与位图文件头中的定义是一致的。

“Show Window”按钮显示了一个基于IBaseFilter的设备属性框。

具体的实现细节,读者可以直接查看示例工程的代码。

图2-10 CaptureDevice界面

macOS和iOS平台

macOS和iOS平台的格式遍历仍使用AVCaptureDevice,它有一个formats方法,返回该设备支持的格式。

img

这里420v表示kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange。

对应420f表示kCVPixelFormatType_420YpCbCr8BiPlanarFullRange。

yuvs表示kCVPixelFormatType_422YpCbCr8_yuvs,以Y0 Cb Y1 Cr排列。

若要设置此格式进行采集,需要先锁住设备,然后修改。

img

若要修改输出像素格式,需给AVCaptureSession添加AVCaptureVideoDataOutput,设置其videoSettings属性的kCVPixelBufferPixelFormatTypeKey字段,参看下一节的示例工程。

Android平台

Camera1的Camera.Parameters类提供了大量的相机参数信息。

img

Camera2可从设备ID获取的CameraCharacteristics继续获得更多属性。

img

给定分辨率下的最大帧率,还可以通过如下方法获得。

img
img

Android平台支持NV21和YV12格式,默认会使用NV21格式的视频帧。

2.2.3 设备的热插拔通知

现代PC都支持外接设备的热插拔。如果在采集过程中,设备发生了断开,或者有新的采集设备连接,我们希望得到相应的通知处理。

本节将介绍各个平台上的设备插拔与程序之间是如何交互的。

Windows平台

Windows平台上的设备插拔通过WM_DEVICECHANGE事件发送到对应的窗口上。一个窗口想要系统通知这个事件,需要调用下列API进行注册。

img

当事件跟随WM_DEVICECHANGE到来时,WPARAM是事件的类型,我们关注的两类事件是DBT_DEVICEARRIVAL和DBT_DEVICEREMOVECOMPLETE。

LPARAM是一个DEV_BROADCAST_DEVICEINTERFACE*的指针。

img

当我们不再需要监听变更设备时,注销之前的通知句柄即可。

img

macOS平台

macOS平台使用NSNotificationCenter来注册设备的添加与删除事件。

img

当不需要再关注通知时,可以移除观察者。

img

移动端采集中断

在手机上,摄像头不会轻易被插拔,但是由于手机场景的特殊性,采集过程经常会被以下事件打断。

·App退到后台:VideoDeviceNotAvailableInBackground。

·相机被其他App抢占:VideoDeviceInUseByAnotherClient。

·多个前台App:VideoDeviceNotAvailableWithMultipleForegroundApps。

·系统压力:VideoDeviceNotAvailableDueToSystemPressure。

在iOS平台上,相关的通知通过AVCaptureSessionWasInterruptedNotification的AVCaptureSessionInterruptionReason获得。

img

在Android平台上,相关的错误通知可以通过RuntimeException等异常抛出来,也可以通过android.hardware.Camera的setErrorCallback接口来设置回调通知。

img
总结

本节我们介绍了各个平台上的视频采集设备枚举方法,对设备支持的采样参数做了枚举,并学习了如何得到设备插拔的通知。下一节我们将介绍如何获取视频帧数据。

参考阅读

1.照相机的发展史,可以通过网络在线查阅

2.微软音视频技术有MSDN在线文档,其中有关于视频采集的专题。

3.微软为DirectShow提供了一些基类,更好地封装了使用步骤。

4.《DirectShow开发指南》(2003年),陆其明著,是DirectShow非常好的入门图书。

5.《AV Foundation开发秘籍》(2015年)介绍了AVFoundation的用法。

6.《Android应用开发攻略》(2013年)介绍了Android应用的开发调试过程。

练习题

1.[1人天](设备的详细信息)尝试打印iOS视频采集设备的全部属性。

2.[2人天](Linux设备枚举)在Linux上,我们对文件设备/dev/videoX( X =0,1,···,63)进行open()操作,获得对应的文件描述符(File Descriptor,FD),来实现设备的枚举。试着写一个枚举程序。

3.[1人天](高拍仪)高拍仪是一种全新的采集设备,主要用来拍摄文本纸张,用作在线教学使用。试枚举出高拍仪的各种采集能力。

4.[1人天](闪光与变焦)手机上的采集往往可以进行闪光灯与变焦的调整。试使用相关API。

5.[1人天](iOS高级参数)iOS有许多高级采集参数,包括缩放因子(zoom factor)、曝光模式(exposure mode)、曝光点(set_exposure_point)、曝光补偿(set_exposure_compensation)、人脸检测(face detection)、对焦模式(focus Mode)、白平衡模式(whiteBalanceMode)、防抖配置(anti-shake),试了解其含义,并使用相关API。

6.[30分钟](Android相机检测)写一个函数,检查Android手机上是否有相机。 06hPTdgnL+WR7A38AcXKL4IiJtoJufDtBXQ16kJeiGSurUjM1k5eSxxfwiKVY68m

点击中间区域
呼出菜单
上一章
目录
下一章
×