天天看点

Camkit 树莓派视频传输,使用分析

上网找了一个开源的库,是Camkit 一个开源的C语言库,支持单个摄像头发送RTP包视频流。

具体可以参考github文档:https://git.oschina.net/andyspider/Camkit

自己的好奇,希望在树莓派上使用两个摄像头,参考了库里面的simple_demo代码后,自己写了传输两个摄像头视频流的demo

先讲讲思路,比较简单,首先理由v4l2打开pi上的两个摄像头(需要注意的是,usb摄像头的设备位置会经常变动,有时候是video0 和video1 有时候又是video0 和video1,怎么回事,我也还没弄明白,但是先这样用着,可以用 ls /dev | grep video* 查询一下他们的名字,然后对应修改就可以了)

接着,打开两个udp socket的连接

后面打开convert handle,encode handle,pack handle;

接着设置他们的参数。

到这里,所有准备工作已经做完

接着就是一个loop 开始捕获图片,把捕获的图片转换为YUV420,获取h264 头,打包,发送头,编码转换为YUV420的图片,打包,发送。done

因为这里是两个摄像头,因此就每个摄像头交替完成上述的loop的过程!

下面附上代码(写的菜,不要介意!)

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/videodev2.h>
#include <pthread.h>
#include "camkit.h"

#define WIDTH 352
#define HEIGHT 288
#define FRAMERATE 10


// set paraments
U32 vfmt = V4L2_PIX_FMT_YUYV;
U32 ofmt = V4L2_PIX_FMT_YUV420;

char *deviceOne = "/dev/video2";
char *deviceTwo = "/dev/video3";

char *oneIp = "192.168.199.219";
int onePort = 8888;
int twoPort = 8000;

struct cvt_handle *cvthandle = NULL;
struct enc_handle *enchandle = NULL;
struct pac_handle *pachandle = NULL;
struct tms_handle *tmshandle = NULL;




struct net_handle * getNetParam(char* ip_address,int port)
{
	struct net_handle *nethandle = NULL;
	struct net_param netp;


	netp.type = UDP;
    netp.serip = ip_address;
    netp.serport = port;

    nethandle = net_open(netp);
    if (!nethandle)
    {
        printf("--- Open network failed\n");
    	return NULL;
    }
	return nethandle;
}

int  initParams()
{


	struct cvt_param cvtp;
	struct enc_param encp;
	struct pac_param pacp;

	struct tms_param tmsp;


    cvtp.inwidth = WIDTH;
	cvtp.inheight = HEIGHT;
	cvtp.inpixfmt = vfmt;
	cvtp.outwidth = WIDTH;
	cvtp.outheight = HEIGHT;
	cvtp.outpixfmt = ofmt;

	encp.src_picwidth = WIDTH;
	encp.src_picheight = HEIGHT;
	encp.enc_picwidth = WIDTH;
	encp.enc_picheight = HEIGHT;
	encp.chroma_interleave = 0;
	encp.fps = FRAMERATE;
	encp.gop = 12;
	encp.bitrate = 800;

	pacp.max_pkt_len = 1400;
	pacp.ssrc = 1234;


    tmsp.startx = 10;
	tmsp.starty = 10;
	tmsp.video_width = WIDTH;
	tmsp.factor = 0;

	cvthandle = convert_open(cvtp);
	if (!cvthandle)
	{
		printf("--- Open convert failed\n");
		return -1;
	}

	enchandle = encode_open(encp);
	if (!enchandle)
	{
		printf("--- Open encode failed\n");
		return -1;
	}

	pachandle = pack_open(pacp);
	if (!pachandle)
	{
		printf("--- Open pack failed\n");
		return -1;
	}

	tmshandle = timestamp_open(tmsp);
	if(!tmshandle)
	{
		printf("--- Open timestamp failde \n");
		return -1;
	}
	return 0;
}

int openOne()
{
	/* using net handle*/
	struct net_handle *nethandle = NULL;
	/* using capture handle*/
	struct cap_handle *caphandle = NULL;

	struct cap_handle *caphandleOne = NULL; // first captrue
	struct cap_handle *caphandleTwo = NULL; // second captrue

	struct net_handle *nethandleOne = getNetParam(oneIp,onePort); // first ip
	struct net_handle *nethandleTwo = getNetParam(oneIp,twoPort); // seconde ip

	/*set first captrue handle*/
    struct cap_param cappOne;
    cappOne.dev_name = deviceOne;
	cappOne.width = WIDTH;
	cappOne.height = HEIGHT;
	cappOne.pixfmt = vfmt;
	cappOne.rate = FRAMERATE;

	/*set second captrue handle*/
	struct cap_param cappTwo;
	cappTwo.dev_name = deviceTwo;
	cappTwo.width = WIDTH;
	cappTwo.height = HEIGHT;
	cappTwo.pixfmt = vfmt;
	cappTwo.rate = FRAMERATE;

	caphandleOne = capture_open(cappOne);
	caphandleTwo = capture_open(cappTwo);

	if (!caphandleOne || !caphandleTwo)
	{
		printf("--- Open capture failed\n");
		return -1;
	}


	initParams();


	// start stream loop
	int flag = 1;
	int ret;
	void *cap_buf, *cvt_buf, *hd_buf, *enc_buf, *pac_buf;
	int cap_len, cvt_len, hd_len, enc_len, pac_len;
	enum pic_t ptype;
	struct timeval  ltime;

	capture_start(caphandleOne);		// !!! need to start capture stream!
	capture_start(caphandleTwo);

//	caphandle = caphandleOne;
//	nethandle = nethandleOne;
    gettimeofday(<ime,NULL);
	while (1)
	{
		if (flag)
		{
			caphandle = caphandleOne;
			nethandle = nethandleOne;
			flag = 0;
	//	printf("0 start \n");
		}
		else
		{
			caphandle = caphandleTwo;
			nethandle = nethandleTwo;
			flag = 1;
		//	printf("1  start \n");
		}
		// 获取一帧图像
		ret = capture_get_data(caphandle, &cap_buf, &cap_len);
		if (ret != 0)
		{
			if (ret < 0)		// error
			{
				printf("--- capture_get_data failed\n");
				break;
			}
			else	// again
			{
				usleep(10000);
				continue;
			}
		}
		if (cap_len <= 0)
		{
			printf("!!! No capture data  : %d \n", flag);
			continue;
		}
		// else

		//转换,YUV422=>YUV420, 如果你的摄像头直接支持采集YUV420数据则不需要这一步
		ret = convert_do(cvthandle, cap_buf, cap_len, &cvt_buf, &cvt_len);
		if (ret < 0)
		{
			printf("--- convert_do failed\n");
			break;
		}
		if (cvt_len <= 0)
		{
			printf("!!! No convert data\n");
			continue;
		}
		// else
		//加时间戳
		timestamp_draw(tmshandle,cvt_buf);

		// fetch h264 headers first!
		// 第一次取得头 获取h264头,PPS/SPS
		while ((ret = encode_get_headers(enchandle, &hd_buf, &hd_len, &ptype))!= 0)
		{
            		//fwrite(hd_buf, 1, hd_len, dumpfile);
			pack_put(pachandle, hd_buf, hd_len);
			while (pack_get(pachandle, &pac_buf, &pac_len) == 1)
			{
				//发送头
               	ret = net_send(nethandle, pac_buf, pac_len);
				if (ret != pac_len)
				{
					printf("send pack data failed, size: %d, err: %s\n", pac_len,
							strerror(errno));
				}
			}
		}
		 编码一帧图像
		//这个type 
		ret = encode_do(enchandle, cvt_buf, cvt_len, &enc_buf, &enc_len,
				&ptype);
		if (ret < 0)
		{
			printf(" --- encode_do failed  %d \n", flag);
			break;
		}
		if (enc_len <= 0)
		{
			printf("!!! No encode data\n");
			continue;
		}
		// else

        //fwrite(enc_buf, 1, enc_len, dumpfile);
		// RTP pack and send
		// // 将编码后的图像送给打包器
		pack_put(pachandle, enc_buf, enc_len);
		while (pack_get(pachandle, &pac_buf, &pac_len) == 1)  // 获取一个打包后的RTP包
		{
          		ret = net_send(nethandle, pac_buf, pac_len);
			if (ret != pac_len)
			{
				printf("send pack failed %d , size: %d, err: %s\n", flag, pac_len,
						strerror(errno));
			}
		}

	}

	capture_stop(caphandleOne);
	capture_stop(caphandleTwo);

	pack_close(pachandle);
	encode_close(enchandle);
	convert_close(cvthandle);
	timestamp_close(tmshandle);

	net_close(nethandleOne);
	net_close(nethandleTwo);
	return 0;
}


int main()
{
	openOne();
	return 0;
}