The article will be synchronized to personal wechat official account: Android blog

1. Project requirements

The overall requirement of the project is that the Android box supports up, down, left and right control of the PTZ camera, as well as relative and absolute control of the camera position. Relative control, which means holding down the left arrow key, the camera keeps turning to the left until it reaches the maximum value, and vice versa; Absolute control, which means every time you press the D-pad, you turn an Angle and stop.

2. Requirement realization

Finally, the uVC code of the Android kernel layer was customized to compile the kernel, package the firmware, brush the phone and write the upper App, so as to get through the control process from top to bottom.

3. Source of inspiration

To verify whether the Android box supports the control of the pansy camera, you only need to connect the camera to the Ubuntu VIRTUAL machine and control the camera rotation through the tools on Ubuntu, which can also support the corresponding functions through the transformation of the Android kernel.

The previous article has been mentioned, using uvcdynCtrl tool, input the corresponding command on the line, here is a look at his source code is how to achieve:

Github.com/llmike/v4l2…

struct v4l2_control v4l2_ctrl = {
    .id		= control->v4l2_control,
    .value	= value->value
};
if(ioctl(v4l2_dev, VIDIOC_S_CTRL, &v4l2_ctrl)) {
    ret = C_V4L2_ERROR;
    set_last_error(hDevice, errno);
}

struct v4l2_control v4l2_ctrl = { .id = control->v4l2_control };
if(ioctl(v4l2_dev, VIDIOC_G_CTRL, &v4l2_ctrl)) {
	ret = C_V4L2_ERROR;
	set_last_error(hDevice, errno);
	goto done;
}
value->value	= v4l2_ctrl.value;
Copy the code

VIDIOC_S_CTRL and VIDIOC_G_CTRL are explained in the following links, one for setting parameters and one for getting parameters.

www.linuxtv.org/downloads/l…

V4l2_control is the medium between the two operations. New data can be passed to this structure via VIDIOC_S_CTRL. Once the ID is set, VIDIOC_G_CTRL is used to return the required data.

So what are the parameters for this ID? The value is struct uvc_control_mapping uvc_CTRl_mappings ID, which is the id of the parameter supported by a specific camera. Before setting the parameter, query the parameter supported by the camera and set it only after it is supported.

3.1 Upper-level JNI code writing

Obtain the control parameters supported by the camera:

jboolean queryControls(a){
	jint canControl = 0;
	__android_log_print(ANDROID_LOG_ERROR , TAG , "Device number =%d" , fd);

	struct v4l2_queryctrl qctrl;
	qctrl.id = V4L2_CTRL_CLASS_CAMERA | V4L2_CTRL_FLAG_NEXT_CTRL;
	int i = ioctl(fd, VIDIOC_QUERYCTRL, &qctrl);
	while (0 == i){
		__android_log_print(ANDROID_LOG_ERROR , TAG , "Start the search");
		if(V4L2_CTRL_ID2CLASS(qctrl.id) ! = V4L2_CTRL_CLASS_CAMERA)continue;

		if(strcmp(qctrl.name , CONTROL_FLAG_PAN) == 0 || strcmp(qctrl.name , CONTROL_FLAG_TILT) == 0
							|| strcmp(qctrl.name , CONTROL_FLAG_ZOOM) == 0){
			++canControl;
		}

		__android_log_print(ANDROID_LOG_ERROR , TAG , "The control function found is %s." , qctrl.name);
		__android_log_print(ANDROID_LOG_ERROR , TAG , "Keep looking.");
		__android_log_print(ANDROID_LOG_ERROR , TAG , "id = %d" , qctrl.id);
		__android_log_print(ANDROID_LOG_ERROR , TAG , "Next_Ctrl = %x" , V4L2_CTRL_FLAG_NEXT_CTRL);
		__android_log_print(ANDROID_LOG_ERROR , TAG , "Camera_Class = %x" , V4L2_CTRL_CLASS_CAMERA);

		qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;

		__android_log_print(ANDROID_LOG_ERROR , TAG , "id+ = %x" , qctrl.id);

		i = ioctl(fd, VIDIOC_QUERYCTRL, &qctrl);
		if(i ! =0){
			__android_log_print(ANDROID_LOG_ERROR, TAG,"uvcioc ctrl add error: errno=%d (reason=%s)\n", errno,strerror(errno)); }}// If PTZ controls exist, Pan,Tilt,Zoom strings should be added three times
	return canControl == 3;
}
Copy the code

Get the current value of an id:

int getControlValue(int controlId){
	//an array of v4l2_ext_control
	struct v4l2_ext_control clist[1].
	struct v4l2_ext_controls ctrls;

	memset(&clist, 0.sizeof(clist));
	memset(&ctrls, 0.sizeof(ctrls));

	clist[0].id    = controlId;
	clist[0].value = 0;

	//v4l2_ext_controls with list of v4l2_ext_control
	ctrls.ctrl_class = V4L2_CTRL_CLASS_CAMERA;
	ctrls.count = 1;
	ctrls.controls = clist;

	//read back the value
	if (- 1 == xioctl (fd, VIDIOC_G_EXT_CTRLS, &ctrls))
	{
		__android_log_print(ANDROID_LOG_ERROR,TAG,"get current value failed fd = %d,reason=%s" , fd,strerror(errno));
		return - 1;
	}
	__android_log_print(ANDROID_LOG_ERROR,TAG,"get before value success , %d" , clist[0].value);
	return clist[0].value;
}
Copy the code

Set the value of a parameter, that is, start to control the camera rotation left, right, up and down:

int startControl(int controlId , int value){
    //an array of v4l2_ext_control
    struct v4l2_ext_control clist[1].
    struct v4l2_ext_controls ctrls;
    CLEAR(clist);
    CLEAR(ctrls);
    
    clist[0].id    = controlId;
    clist[0].value = value;
    
    //v4l2_ext_controls with list of v4l2_ext_control
    ctrls.ctrl_class = V4L2_CTRL_CLASS_CAMERA;
    ctrls.count = 1;
    ctrls.controls = clist;
    
    int result = xioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
}
Copy the code

The value of v4L2_ext_control should be passed according to the definition of the value in the protocol document. For example, the value of left and right absolute control corresponds to the rotation Angle. The left and right relative control is divided into four bits. Each bit represents a different control mode, and different values need to be transferred according to different ids.

V4l2_ext_controls can under an id to control multiple parameters at the same time, specific see: www.linuxtv.org/downloads/l… .

4. Low-level customization

Once the above code is written, you can first select an ID that does not control the left and right rotation up and down to see if it is properly controlled, and then debug pan, tilt.

Generally, it is necessary to coordinate with the camera manufacturer, because the firmware of the camera manufacturer also needs to adapt to the UVC protocol. The UVC version is a big problem. Check the UVC version in the Android kernel at:

goldfish\drivers\media\usb\uvc\uvcvideo.h

#define DRIVER_VERSION		1.1.1 ""
Copy the code

If the UVC version in the kernel is inconsistent with the UVC version of the camera firmware, the control bit does not match, and the control fails to return.

From here, you can know the firmware version of the camera and the control parameters supported, so as to know the customization direction of the underlying kernel of the box Android. The current direction of customization for the project is to add pan and TILT relative control capabilities. The customization process is as follows:

goldfish\include\uapi\linux\v4l2-controls.h

Relative control speed in this file:

#define V4L2_CID_PAN_SPEED (V4L2_CID_CAMERA_CLASS_BASE+32)
#define V4L2_CID_TILT_SPEED (V4L2_CID_CAMERA_CLASS_BASE+33)
Copy the code

goldfish\drivers\media\v4l2-core\v4l2-ctrls.c

Add a description of relative control speed to the file:

const char *v4l2_ctrl_get_name(u32 id)
{
    case V4L2_CID_PAN_SPEED: return "Pan, Speed";
    case V4L2_CID_TILT_SPEED: return "Tilt, Speed";
}
Copy the code

goldfish\drivers\media\usb\uvc\uvc_ctrl.c

This file is the core control file, which contains setting and obtaining methods, and is finally implemented in this file. Here we need to add relative control methods:

#define UVC_CTRL_RELATIVE_PAN 10094852
#define UVC_CTRL_RELATIVE_TILT 10094853
#define UVC_CTRL_RELATIVE_ZOOM 10094863

static struct uvc_control_info uvc_ctrls[] = {
    static struct uvc_control_mapping uvc_ctrl_mappings[] = {
    {
		 .id = V4L2_CID_PAN_RELATIVE,
		 .name = "Pan (Relative)",
		 .entity = UVC_GUID_UVC_CAMERA,
		 .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
		 .size = 16,
		 .offset = 0,
		 .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
		 .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
		 .get = uvc_ctrl_get_rel_speed,
		 .set = uvc_ctrl_set_rel_speed,
	 },
	 {
 		.id = V4L2_CID_TILT_RELATIVE,
	 	.name = "Tilt (Relative)",
	 	.entity = UVC_GUID_UVC_CAMERA,
		.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
		.size = 16,
		.offset = 16,
		.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
		.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
		.get = uvc_ctrl_get_rel_speed,
		.set = uvc_ctrl_set_rel_speed,
	 },
    }
}

static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
 __u8 query, const __u8 *data)
{
    int first = mapping->offset / 8;
    __s8 rel = (__s8)data[first];

    switch (query) {
     case UVC_GET_CUR:
     return (rel == 0)?0 : (rel > 0 ? data[first+1]
     : -data[first+1]);
     case UVC_GET_MIN:
     return -data[first+1];
     case UVC_GET_MAX:
     case UVC_GET_RES:
     case UVC_GET_DEF:
     default:
     return data[first+1]; }}static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping, __s32 value, __u8 *data)
{
    int first = mapping->offset / 8;

    data[first] = value == 0 ? 0 : (value > 0)?1 : 0xff;
    data[first+1] = min_t(int.abs(value), 0xff);
}
Copy the code

Add relative control parameters to the mapping set, and also add methods to control and get speed.

Here the upper level of writing and the basic completion of the bottom customization.