天天看點

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;
}