上網找了一個開源的庫,是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;
}