FFMPEG は、USB ウェブカメラからデータをキャプチャすることを学びます
序文
FFMPEGを学習する過程で、直接ダウンロードできる画像データを取得する必要がありますが、処理の過程で、さまざまな形式のデータが必要です.たまたまUSBカメラがあり、USBから画像を取得するにはどうすればよいですかカメラ。Android では、基本的なデバイス管理は一般に v4l2 ドライバー フレームワークを介して管理されます. ここでは、v4l2 を使用して USB カメラを制御し、データ収集を実現しようとします.
1. v4l2 とは?
V4L2 は、Video for linux2 の略で、Linux のビデオ デバイス用のカーネル ドライバーです。Linux では、ビデオ デバイスはデバイス ファイルであり、通常のファイルのように読み書きできます. カメラは /dev/video* の下にあり、ビデオ デバイスが 1 つしかない場合は、通常は /dev/video0 です。一連のコールバック関数を使用してこれらの関数を実現する場合。カメラ周波数、フレームレート、ビデオ圧縮形式、画像パラメータなどを設定するのと同様に、プロセスは次のとおりです。
2. 具体的な実践
1. カメラの初期化
コードは次のとおりです (例)。
/**
* @brief init_camera Initialize camera device properties
* @param dev Equipment name
* @return
*/
int init_camera(const char* dev)
{
fd = open(dev, O_RDWR);
if(fd < 0){
printf("open \"%s\" error\n", dev);
return -1;
}
/**
* Query device properties
*/
struct v4l2_capability cap;
int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret < 0) {
printf("VIDIOC_QUERYCAP error\n");
return -1;
}
printf("Driver name : %s\n",cap.driver);
printf("Device name : %s\n",cap.card);
printf("Bus information : %s\n",cap.bus_info);
printf("Drive version number : 0x%x\n",cap.version);
if(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE){
printf("Video capture device:\n");
if(cap.capabilities & V4L2_CAP_STREAMING){
printf("\tSupport video stream capture\n");
}else{
printf("Video stream capture is not supported\n");
}
}else {
printf("Non video stream capture device\n");
return -1;
}
#if 1
printf("\tQuery supported image formats:\n");
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf("\t\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
#endif
/*Format set*/
//struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//Camera buffer
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//Pixel format (this parameter needs to be set according to the actual equipment situation)
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; //OK
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; //OK YU12
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
{
printf("Format: %d failed\n",fmt.fmt.pix.pixelformat);
return -1;
}
printf("Set the format of the acquired image V4L2_PIX_FMT_YUV420 success!\n");
return 0;
}
2. メモリ マッピング
コードは次のとおりです (例)。
/**
* @brief mmap_buffer Allocate user buffer memory and establish memory mapping
* @return
*/
int mmap_buffer()
{
printf("Allocate user space buffer\n");
//struct BUFTYPE *usr_buf;
usr_buf = (struct BUFTYPE *)calloc(buf_num, sizeof(struct BUFTYPE));
if (!usr_buf) {
printf("calloc \"frame buffer\" error : Out of memory\n");
return -1;
}
printf("Allocate kernel buffers (including the number of frame buffers)\n");
struct v4l2_requestbuffers req;
req.count = buf_num; //num of frame buf
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //buf type of videocap
req.memory = V4L2_MEMORY_MMAP; //mem mmap type
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
printf("VIDIOC_REQBUFS assignt kernel buffers failed \n");
return -1;
}
/*mmap kernel buffers to user buffers*/
printf("Mapping kernel buffer to user buffer\n");
unsigned int i = 0;
for(i = 0; i < buf_num; ++i)
{
/*query kernel buffers*/
struct v4l2_buffer v4l2_buf;
memset(&v4l2_buf, 0, sizeof(v4l2_buf));
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
v4l2_buf.index = i;
if(ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf) < 0)
{
printf("VIDIOC_QUERYBUF failed\n");
return -1;
}
/* establish mmaping
* keep v4l2_buf.index and usr_buf.index the same
* when kernel buffer DQbug to queue,get kernel buffer index by user buffers index,
* then get the index data user want
*/
usr_buf[i].length = v4l2_buf.length;
usr_buf[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset);
if (MAP_FAILED == usr_buf[i].start)
{
//mmap failed
printf("mmap failed: %d\n",i);
return -1;
}else
{
if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0)
{
// mmap success,qbuf to queue
printf("VIDIOC_QBUF failed\n");
return -1;
}
}
}
printf("Mapping kernel buffer to user buffer success\n");
return 0;
}
3.収集を開始する
コードは次のとおりです (例)。
/**
* @brief stream_on
* @return
*/
int stream_on()
{
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
{
printf("VIDIOC_STREAMON failed\n");
return -1;
}
printf("stream on success!\n");
return 0;
}
4. ファイルに書き込む
コードは次のとおりです (例)。
/**
* @brief write_frame read a frame
* @return return frame index,error for -1
*/
int write_frame(int index)
{
int writenum = 0;
struct v4l2_buffer v4l2_buf;
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
v4l2_buf.index = index;
if(ioctl(fd, VIDIOC_DQBUF, &v4l2_buf) < 0) // Kernel buffer out of queue
{
printf("VIDIOC_DQBUF failed, dropped frame\n");
return -1;
}
printf("VIDIOC_DQBUF size[%d]\n",v4l2_buf.bytesused);
/*
* mmp kernel buffer to user buffer,get data by user buffer
*/
char buffer[256];
sprintf(buffer,"/data/V4L2_PIX_FMT_YVYU_%x-%d.yuv",fmt.fmt.pix.pixelformat,v4l2_buf.index); //defined by fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
int file_fd = open(buffer,O_RDWR | O_CREAT); // didnot save this frame
printf("open or creat a pic success\n");
if(file_fd > 0)
{
//printf("data [%d] start:%s\n",v4l2_buf.index,usr_buf[v4l2_buf.index].start);
//printf("saving the %d frame image , size:%d\n",v4l2_buf.index,v4l2_buf.bytesused);
//itenum = write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused);
printf("saving the [%d] frame image , size[%d]\n",v4l2_buf.index,v4l2_buf.bytesused);
writenum = write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused);
printf("write %d byte\n",writenum);
if (0 < writenum )
{
printf("Write successful %d byte\n",writenum);
}
else
{
printf("Write failed%d\n",writenum);
}
close(file_fd);
}
else
{
printf("file_fd <= 0");
return -1;
}
if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0) //Buffer rejoining to queue
{
printf("VIDIOC_QBUF failed, dropped frame\n");
return -1;
}
return v4l2_buf.index;
}
5. コード ストリームを閉じる
コードは次のとおりです (例)。
/**
* @brief stream_off
* @return
*/
int stream_off()
{
printf("Turn off video streaming\n");
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd,VIDIOC_STREAMOFF,&type) == -1)
{
printf("Fail to ioctl 'VIDIOC_STREAMOFF'");
return -1;
}
return 0;
}
6. アンマップ
コードは次のとおりです (例)。
/**
* @brief unmap_buffer
* @return
*/
int unmap_buffer()
{
printf("Unmap kernel buffer to user buffer\n");
unsigned int i = 0;
for(i = 0; i < buf_num; i++)
{
int ret = munmap(usr_buf[i].start, usr_buf[i].length);
if (ret < 0)
{
printf("munmap failed\n");
return -1;
}
}
free(usr_buf);
return 0;
}
7. カメラの電源を切る
コードは次のとおりです (例)。
/**
* @brief release_camera
*/
void release_camera()
{
printf("Turn off the device\n");
close(fd);
}
8. メインプログラム
コードは次のとおりです (例)。
/**
* @brief release_camera
*/
void release_camera()
{
printf("Turn off the device\n");
close(fd);
}
int main(void)
{
int ret = init_camera("/dev/video1");
if(ret < 0){
printf("init_camera error\n");
return -1;
}
#if 1
ret = mmap_buffer();
if(ret < 0){
printf("mmap_buffer error\n");
return -1;
}
ret = stream_on();
if(ret < 0){
printf("stream_on error\n");
return -1;
}
int i=0;
for(i=0;i<5;i++)
{
write_frame(i);
encode_aframe(i);
}
ret = stream_off();
if(ret < 0){
printf("stream_off error\n");
return -1;
}
ret = unmap_buffer();
if(ret < 0){
printf("unmap_buffer error\n");
return -1;
}
#endif
release_camera();
return 0;
}
要約する
これは主に、v4l2 を使用して USB カメラをキャプチャする方法を記録するためのものです. 途中で、エンコードに libx264 を使用する方法が散在していましたが、カードの所有者はまだデバッグ中であるため、後で記録することを忘れないでください.