MaWenge's Blog

android ptp 编程(1)

最近公司用到一个项目,android手机通过OTG与单反相机实现连接,实现相机中有新增图片的时候自动保存到手机当中,然后手机自动上传到自己的服务器上。后面一步是比较常规的操作,一般都可以实现,主要是前一步。这个系列将介绍我如何一步步实现这些功能以及踩到的一些坑(文中很多链接需要自备梯子)

以下介绍相关概念

PTP协议(picture transfer protocol)

Picture Transfer Protocol (PTP) is a protocol developed by the International Imaging Industry Association to allow the transfer of images from digital cameras to computers and other peripheral devices without the need of additional device drivers. The protocol has been standardised as ISO 15740.

It is further standardized for USB by the USB Implementers Forum as the still image capture device class. USB is the default network transport media for PTP devices. USB PTP is a common alternative to the USB mass-storage device class (USB MSC), as a digital camera connection protocol. Some cameras support both modes.

这是维基百科对ptp的描述。可见主要是数码相机与外部设备通信的一种协议。

PTP是最早由柯达与微软协商制定的一种标准,符合这种标准的图像设备在接入Windows XP系统之后可以更好地被系统和应用程序所共享,尤其在网络传输方面,系统可以直接访问这些设备用于建立网络相册时图片的上传、网上聊天时图片的传送等。

MTP媒体传输协议(Media Transfer Protocol,MTP)

媒体传输协议(Media Transfer Protocol,MTP)是一个基于图片传输协议(Picture Transfer Protocol,PTP)的自定义扩展协议。该协议允许用户在移动设备上线性访问媒体文件。PTP只是被设计用于从数码相机下载照片,而MTP可以支持数字音频播放器上的音乐文件和便携式媒体播放器上的媒体文件,以及个人数字助理的个人信息的传输。MTP是WMDRM10-PD的一个关键部分,而WMDRM10-PD是Windows Media的一项数字版权管理(DRM)服务。

媒体传输协议(即通常所说的MTP)是“Windows Media”框架的一部分,从而与Windows Media Player密切相关。Windows系统从Windows XP SP2开始支持MTP。Windows XP需要安装Windows Media Player 10或更高版本来获得对MTP的支持。在这之后的系统则原生支持MTP。微软同时向Windows98之后的旧有操作系统提供MTP驱动包。OS X 和 Linux 各自拥有可支持MTP的升级软件包. (维基百科)

mtp明显是ptp的升级版,从仅传输图片文件到传输媒体文件,包括音频视频文件。对于ptp协议和mtp协议的具体文件我也是都有看过一遍,mtp当中很多指令等都于ptp完全相同,在我看来就是新增了一些东西,新增的部分我也没有多看,项目进度原因,也没有深究。

PIMA15740:2000

文档下载
那么这个pima 15740又是什么呢

The technical content of this PIMA standard is closely related to ISO 15740, which is currently
in the working draft stage while work on multiple transports is being completed. The main difference is that PIMA 15740 ncludes an informative annex describing a USB implementation of ISO 15740. This information is not included in ISO 15740, hich instead references the USB still device class document developed by the Device Working Group of the USB Implementers Forum. The USB annex in PIMA 15740 provides the same technical approach as the USB still device class specification. The eason for developing PIMA 15740 is to immediately provide a complete, fully documented, stable, publicly available pecification or USB implementations of the Picture Transfer Protocol defined in ISO 15740. This will enable hardware and software nufacturers to immediately produce product implementations, without waiting for the ISO and USB documents to complete the approval process. This PIMA 15740 standard may be withdrawn once the ISO 15740 and USB still device class documents have een approved and are publicly available.

上面这段话清楚地写出,pima15740 是usb实现的pima15740,就是为了提供一个完整的,稳定的,文档化的基于usb通信协议的iso15740,也就是基于usb的ptp协议。看到这里大家应该差不多明白了,我的需求基本上就是这个了。

实现分析

android.hardware.usb

首先查看安卓官方文档会发现有一个android.hardware.usb这样一个包,安卓本身提供了这样一套api用于获取连接在android手机上的usb设备,并且进行相应的通信。连接usb设备一共有两种模式,一种Host模式,一种Accessary模式,前者相当于android手机是老大,连接的设备是奴隶,手机可以进行任何操作;后一种是相当于连接的设备是老大,android手机是奴隶。具体可以看看这篇文章,百度也可以搜出来一大堆文章,基本上学习这个结合官方文档一般人都不会有问题,只要静下心来都不会有问题。

android.hardware.usb
不管如何跟相机通信,必须首先拿到连接到手机上的UsbDevice对象,才能进行下一步的操作。

android.mtp

接着会发现还有一个android.mtp的包,这个也是android自身实现的一个跟mtp设备通信的api。用法也十分简单,文档上面介绍的也是非常的详细,这里我简单介绍一下:
android.hardware.usb

1、获取UsbDevice设备 和UsbDeviceConnection,获取看上一小节的链接
2、获取MtpDevice对象然后建立连接

1
2
MtpDevice mtpDevice = new MtpDevice(mUsbDevice);
mtpDevice.open(mDeviceConnection)

3、然后就可以用MtpDevice对象获取各种有用的信息,MtpDevice的方法也不多,直接点进去就可以看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
close()
deleteObject(int objectHandle)
getDeviceId()
getDeviceInfo()
getDeviceName()
getObject(int objectHandle, int objectSize)
getObjectHandles(int storageId, int format, int objectHandle)
getObjectInfo(int objectHandle)
getParent(int objectHandle)
getStorageId(int objectHandle)
getStorageIds()
getStorageInfo(int)
getStorageInfo(int storageId)
getThumbnail(int objectHandle)
importFile(int objectHandle, String destPath)
importFile(int objectHandle, ParcelFileDescriptor descriptor)
open(UsbDeviceConnection connection)
readEvent(CancellationSignal signal)
sendObject(int objectHandle, long size, ParcelFileDescriptor descriptor)
sendObjectInfo(MtpObjectInfo info)
.
.
.

是不是很方便,看方法的字面意思基本上就知道用法了,用起来真的没难度,so easy,而且这里面的好多方法都是我需要的,你以为到这里就能解决我的问题了吗,一开始我也是这样人为的,后来才发现还是太年轻。。。

mtp这个包其实就是google自身基于mtp和usb通信协议实现的,具体实现还是调用native方法实现的,所以我还看不到每个方法的具体实现细节。我用这一套api确实可以读取到相机中的所有文件,并且可以手动复制相机中的图片到手机中(注意是手动,和我的需求还有一些不同)。我需要的是自动复制,大家肯定看到了还有一个

1
readEvent(CancellationSignal signal)

这个方法可以接收相机的事件,当有新的图片的时候就会发送一个事件编号位ObjectAdded的事件,然后我就可以主动拉取这个照片了,是不是很完美。可是我用的时候又发现只有api24才能用。。。所以这个方案也不行了。除了这一点,还有一个致命的问题,那就是我有些手机通过这种方式连接到相机之后相机被锁定了,不能继续拍照了,关于这个问题我还和阿拉神农,做了简单的交流,虽然没有解决问题,但是还是感谢他。这个问题后来我大致明白了,应该是不同手机在连接相机的时候对相机的mode进行了设置,所以会导致相机不能工作。

结语

通过这些坑,我明白了使用android现成的mtp包里面的接口暂时是无法实现我的需求了,一切有都得从长计议了。