博客:http://www.cnblogs.com/tinz
/******************************************************************************
* Capture_create
******************************************************************************/
Capture_Handle Capture_create(BufTab_Handle hBufTab, Capture_Attrs *attrs)
{
struct v4l2_capability cap;
struct v4l2_cropcap cropCap;
struct v4l2_crop crop;
struct v4l2_format fmt;
enum v4l2_buf_type type;
Capture_Handle hCapture;
VideoStd_Type videoStd;
Int32 width, height;
Uint32 pixelFormat; assert(attrs);
Dmai_clear(fmt); /* Allocate space for state object */
hCapture = calloc(, sizeof(Capture_Object)); if (hCapture == NULL) {
Dmai_err0("Failed to allocate space for Capture Object\n");
return NULL;
} /* User allocated buffers by default */
hCapture->userAlloc = TRUE; /* Open video capture device */
/* 打开V4L2视频输入设备 */
hCapture->fd = open(attrs->captureDevice, O_RDWR, ); if (hCapture->fd == -) {
Dmai_err2("Cannot open %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
} /* See if an input is connected, and if so which standard */
/* 检测V4L2设备当前是否有视频信号输入,输入信号的标准 */
if (Capture_detectVideoStd(hCapture, &videoStd, attrs) < ) {
cleanup(hCapture);
return NULL;
} hCapture->videoStd = videoStd; if (VideoStd_getResolution(videoStd, &width, &height) < ) {
cleanup(hCapture);
Dmai_err0("Failed to get resolution of capture video standard\n");
return NULL;
} /* Query for capture device capabilities */
/* 这里就是调用ioctl直接操作v4l2设备了,查询设备的特性*/
if (ioctl(hCapture->fd, VIDIOC_QUERYCAP, &cap) == -) {
cleanup(hCapture);
if (errno == EINVAL) {
Dmai_err1("%s is no V4L2 device\n", attrs->captureDevice);
cleanup(hCapture);
return NULL;
}
Dmai_err2("Failed VIDIOC_QUERYCAP on %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
} if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
Dmai_err1("%s is not a video capture device\n", attrs->captureDevice);
cleanup(hCapture);
return NULL;
} if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
Dmai_err1("%s does not support streaming i/o\n", attrs->captureDevice);
cleanup(hCapture);
return NULL;
} fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* 获取V4L2设备的帧格式 */
if (ioctl(hCapture->fd, VIDIOC_G_FMT, &fmt) == -) {
Dmai_err2("Failed VIDIOC_G_FMT on %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
} fmt.fmt.pix.width = width;
fmt.fmt.pix.height = height;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; switch(attrs->colorSpace) {
case ColorSpace_UYVY:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
break;
case ColorSpace_YUV420PSEMI:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
break;
case ColorSpace_YUV422PSEMI:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV16;
break;
default:
Dmai_err1("Unsupported color format %g\n", attrs->colorSpace);
cleanup(hCapture);
return NULL;
} if ((videoStd == VideoStd_BAYER_CIF) || (videoStd == VideoStd_BAYER_VGA) ||
(videoStd == VideoStd_BAYER_1280)) {
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
} fmt.fmt.pix.bytesperline = BufferGfx_calcLineLength(fmt.fmt.pix.width,
attrs->colorSpace);
fmt.fmt.pix.sizeimage = BufferGfx_calcSize(attrs->videoStd, attrs->colorSpace); //printf("DMAI: pix.bytesperline= %d, pix.sizeimage= %d\r\n",fmt.fmt.pix.bytesperline,fmt.fmt.pix.sizeimage);
pixelFormat = fmt.fmt.pix.pixelformat; if ((videoStd == VideoStd_CIF) || (videoStd == VideoStd_SIF_PAL) ||
(videoStd == VideoStd_SIF_NTSC) || (videoStd == VideoStd_D1_PAL) ||
(videoStd == VideoStd_D1_NTSC) || (videoStd == VideoStd_1080I_30) ||
(videoStd == VideoStd_1080I_25)) {
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
} else {
fmt.fmt.pix.field = V4L2_FIELD_NONE;
} /* 设置V4L2输入设备的帧格式 */
if (ioctl(hCapture->fd, VIDIOC_S_FMT, &fmt) == -) {
printf("Failed VIDIOC_S_FMT on %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
} if ((fmt.fmt.pix.width != width) || (fmt.fmt.pix.height != height)) {
Dmai_err4("Failed to set resolution %d x %d (%d x %d)\n", width,
height, fmt.fmt.pix.width, fmt.fmt.pix.height);
cleanup(hCapture);
return NULL;
} if (pixelFormat != fmt.fmt.pix.pixelformat) {
Dmai_err2("Pixel format 0x%x not supported. Received 0x%x\n",
pixelFormat, fmt.fmt.pix.pixelformat);
cleanup(hCapture);
return NULL;
} Dmai_dbg3("Video input connected size %dx%d pitch %d\n",
fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.bytesperline); /* Query for video input cropping capability */ if (attrs->cropWidth > && attrs->cropHeight > ) { cropCap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(hCapture->fd, VIDIOC_CROPCAP, &cropCap) == -) {
Dmai_err2("VIDIOC_CROPCAP failed on %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
} if (attrs->cropX & 0x1) {
Dmai_err1("Crop width (%ld) needs to be even\n", attrs->cropX);
cleanup(hCapture);
return NULL;
} crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c.left = attrs->cropX;
crop.c.top = attrs->cropY;
crop.c.width = attrs->cropWidth;
crop.c.height = hCapture->topOffset ? attrs->cropHeight + + :
attrs->cropHeight; Dmai_dbg4("Setting capture cropping at %dx%d size %dx%d\n",
crop.c.left, crop.c.top, crop.c.width, crop.c.height); /* Crop the image depending on requested image size */
if (ioctl(hCapture->fd, VIDIOC_S_CROP, &crop) == -) {
Dmai_err2("VIDIOC_S_CROP failed on %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
}
} if (hBufTab == NULL) {
hCapture->userAlloc = FALSE; /* The driver allocates the buffers */
/* 调用CMEM创建要用到的视频缓冲区 */
if (_Dmai_v4l2DriverAlloc(hCapture->fd,
attrs->numBufs,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
&hCapture->bufDescs,
&hBufTab,
hCapture->topOffset,
attrs->colorSpace) < ) {
Dmai_err1("Failed to allocate capture driver buffers on %s\n",
attrs->captureDevice);
cleanup(hCapture);
return NULL;
}
}
else {
/* Make the driver use the user supplied buffers */
/* 如果调用者已经创建好缓冲区,那只需要加入相应的队列管理就可以 */
if (_Dmai_v4l2UserAlloc(hCapture->fd,
attrs->numBufs,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
&hCapture->bufDescs,
hBufTab,
, attrs->colorSpace) < ) {
Dmai_err1("Failed to intialize capture driver buffers on %s\n",
attrs->captureDevice);
cleanup(hCapture);
return NULL;
}
} hCapture->hBufTab = hBufTab; /* Start the video streaming */
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* 配置完后,启动V4L2设备 */
if (ioctl(hCapture->fd, VIDIOC_STREAMON, &type) == -) {
Dmai_err2("VIDIOC_STREAMON failed on %s (%s)\n", attrs->captureDevice,
strerror(errno));
cleanup(hCapture);
return NULL;
} hCapture->started = TRUE; return hCapture;
}
从上面的代码可以看出,DMAI的函数将大量的对V4L2的控制操作都封装起来了,使用者只需要传入缓冲区列表和视频采集参数就可以创建一个视频采集设备。设备采集到
数据后,会将数据填充到使用者提供的缓冲区,使用者只需要取出相应的缓冲区就可以得到视频数据。在上面的函数中,调用了一个_Dmai_v4l2DriverAlloc()函数,这个函数
的作用是分配视频缓冲区,它最终是调用CMEM进行内存分配的,下面一起看看这个函数的具体实现。
2.DMAI与CMEM的交互。
查看dmai_2_20_00_15/packages/ti/sdo/dmai/linux/dm6467/_VideoBuf.c
/******************************************************************************
* _Dmai_v4l2DriverAlloc
******************************************************************************/
Int _Dmai_v4l2DriverAlloc(Int fd, Int numBufs, enum v4l2_buf_type type,
struct _VideoBufDesc **bufDescsPtr,
BufTab_Handle *hBufTabPtr, Int topOffset,
ColorSpace_Type colorSpace)
{
BufferGfx_Attrs gfxAttrs = BufferGfx_Attrs_DEFAULT;
struct v4l2_requestbuffers req;
struct v4l2_format fmt;
_VideoBufDesc *bufDesc;
Buffer_Handle hBuf;
Int bufIdx;
Int8 *virtPtr; Dmai_clear(fmt);
fmt.type = type; if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -) {
Dmai_err1("VIDIOC_G_FMT failed (%s)\n", strerror(errno));
return Dmai_EFAIL;
} Dmai_clear(req);
req.count = numBufs;
req.type = type;
req.memory = V4L2_MEMORY_MMAP; /* Allocate buffers in the capture device driver */
/* 申请建立V4L2的视频缓冲区管理队列*/
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -) {
Dmai_err1("VIDIOC_REQBUFS failed (%s)\n", strerror(errno));
return Dmai_ENOMEM;
} if (req.count < numBufs || !req.count) {
Dmai_err0("Insufficient device driver buffer memory\n");
return Dmai_ENOMEM;
} /* Allocate space for buffer descriptors */
*bufDescsPtr = calloc(numBufs, sizeof(_VideoBufDesc)); if (*bufDescsPtr == NULL) {
Dmai_err0("Failed to allocate space for buffer descriptors\n");
return Dmai_ENOMEM;
} gfxAttrs.dim.width = fmt.fmt.pix.width;
gfxAttrs.dim.height = fmt.fmt.pix.height;
gfxAttrs.dim.lineLength = fmt.fmt.pix.bytesperline;
gfxAttrs.colorSpace = colorSpace;
gfxAttrs.bAttrs.reference = TRUE; /* 调用CMEM建立缓冲区 */
*hBufTabPtr = BufTab_create(numBufs, fmt.fmt.pix.sizeimage,
BufferGfx_getBufferAttrs(&gfxAttrs)); if (*hBufTabPtr == NULL) {
return Dmai_ENOMEM;
} /* 将建立好的缓冲区放到队列中并且配置好相关属性 */
for (bufIdx = ; bufIdx < numBufs; bufIdx++) {
bufDesc = &(*bufDescsPtr)[bufIdx]; /* Ask for information about the driver buffer */
Dmai_clear(bufDesc->v4l2buf);
bufDesc->v4l2buf.type = type;
bufDesc->v4l2buf.memory = V4L2_MEMORY_MMAP;
bufDesc->v4l2buf.index = bufIdx; /* 查找队列中的缓冲区 */
if (ioctl(fd, VIDIOC_QUERYBUF, &bufDesc->v4l2buf) == -) {
Dmai_err1("Failed VIDIOC_QUERYBUF (%s)\n", strerror(errno));
return Dmai_EFAIL;
} /* 修改缓冲区的属性 */ /* Map the driver buffer to user space */
virtPtr = mmap(NULL,
bufDesc->v4l2buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
bufDesc->v4l2buf.m.offset) + topOffset; if (virtPtr == MAP_FAILED) {
Dmai_err1("Failed to mmap buffer (%s)\n", strerror(errno));
return Dmai_EFAIL;
} /* Initialize the Buffer with driver buffer information */
hBuf = BufTab_getBuf(*hBufTabPtr, bufIdx); Buffer_setNumBytesUsed(hBuf, fmt.fmt.pix.bytesperline *
fmt.fmt.pix.height);
Buffer_setUseMask(hBuf, gfxAttrs.bAttrs.useMask);
Buffer_setUserPtr(hBuf, virtPtr); /* Initialize buffer to black */
_Dmai_blackFill(hBuf); Dmai_dbg3("Driver buffer %d mapped to %#x has physical address "
"%#lx\n", bufIdx, (Int) virtPtr, Buffer_getPhysicalPtr(hBuf)); bufDesc->hBuf = hBuf; /* Queue buffer in device driver */
/* 将缓冲区放回队列中 */
if (ioctl(fd, VIDIOC_QBUF, &bufDesc->v4l2buf) == -) {
Dmai_err1("VIODIOC_QBUF failed (%s)\n", strerror(errno));
return Dmai_EFAIL;
}
} return Dmai_EOK;
}
继续分析Buffer_create()函数
查看dmai_2_20_00_15/packages/ti/sdo/dmai/Buffer.c。
/******************************************************************************
* Buffer_create
******************************************************************************/
Buffer_Handle Buffer_create(Int32 size, Buffer_Attrs *attrs)
{
Buffer_Handle hBuf;
UInt32 objSize; if (attrs == NULL) {
Dmai_err0("Must provide attrs\n");
return NULL;
} if (attrs->type != Buffer_Type_BASIC &&
attrs->type != Buffer_Type_GRAPHICS) { Dmai_err1("Unknown Buffer type (%d)\n", attrs->type);
return NULL;
} objSize = attrs->type == Buffer_Type_GRAPHICS ? sizeof(_BufferGfx_Object) :
sizeof(_Buffer_Object); hBuf = (Buffer_Handle) calloc(, objSize); if (hBuf == NULL) {
Dmai_err0("Failed to allocate space for Buffer Object\n");
return NULL;
} _Buffer_init(hBuf, size, attrs); if (!attrs->reference) { /* 这里就是调用了CMEM的接口进行缓冲区的创建 */
hBuf->userPtr = (Int8*)Memory_alloc(size, &attrs->memParams); if (hBuf->userPtr == NULL) {
printf("Failed to allocate memory.\n");
free(hBuf);
return NULL;
} /* 获取缓冲区的物理地址 */
hBuf->physPtr = Memory_getBufferPhysicalAddress(hBuf->userPtr,
size, NULL); Dmai_dbg3("Alloc Buffer of size %u at 0x%x (0x%x phys)\n",
(Uns) size, (Uns) hBuf->userPtr, (Uns) hBuf->physPtr);
} hBuf->reference = attrs->reference; return hBuf;
}
数据已经得到了,它们就放在通过CMEM创建的缓冲区里,那么验证或使用这些数据的一种最简单又有效的方式就是将它们直接
显示出来。再来看一下DVSDK的框图:
框图的两条红线表示数据的流向,需要将数据显示出来只需要两部,第一步创建V4L2的显示出输出设备,第二创建显示缓冲区并将采集到的数据放到缓冲区内。
创建V4L2的显示设备和创建VL42的采集设备很相似,请查看dmai_2_20_00_15/packages/ti/sdo/dmai/linux/dm6467/Display_v4l2.c
/******************************************************************************
* Display_v4l2_create
******************************************************************************/
Display_Handle Display_v4l2_create(BufTab_Handle hBufTab, Display_Attrs *attrs)
{
struct v4l2_format fmt;
enum v4l2_buf_type type;
Display_Handle hDisplay; assert(attrs); Dmai_clear(fmt); /* delayStreamon not supported for this platform */
if (attrs->delayStreamon == TRUE) {
Dmai_err0("Support for delayed VIDIOC_STREAMON not implemented\n");
return NULL;
} /* Allocate space for state object */
hDisplay = calloc(, sizeof(Display_Object)); if (hDisplay == NULL) {
Dmai_err0("Failed to allocate space for Display Object\n");
return NULL;
} hDisplay->userAlloc = TRUE; /* Open video capture device */
hDisplay->fd = open(attrs->displayDevice, O_RDWR, ); if (hDisplay->fd == -) {
Dmai_err2("Cannot open %s (%s)\n",
attrs->displayDevice, strerror(errno));
cleanup(hDisplay);
return NULL;
} if(Display_detectVideoStd(hDisplay, attrs) != Dmai_EOK) {
Dmai_err0("Display_detectVideoStd Failed\n");
cleanup(hDisplay);
return NULL;
}
/* Determine the video image dimensions */
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; if (ioctl(hDisplay->fd, VIDIOC_G_FMT, &fmt) == -) {
Dmai_err0("Failed to determine video display format\n");
cleanup(hDisplay);
return NULL;
} fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
switch(attrs->colorSpace) {
case ColorSpace_UYVY:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
break;
case ColorSpace_YUV420PSEMI:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
break;
case ColorSpace_YUV422PSEMI:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV16;
break;
default:
Dmai_err1("Unsupported color format %g\n", attrs->colorSpace);
cleanup(hDisplay);
return NULL;
}; if (hBufTab == NULL) {
fmt.fmt.pix.bytesperline = Dmai_roundUp(BufferGfx_calcLineLength(fmt.fmt.pix.width,
attrs->colorSpace), );
fmt.fmt.pix.sizeimage = BufferGfx_calcSize(attrs->videoStd, attrs->colorSpace);
#if 1
} else {
/* This will help user to pass lineLength to display driver. */
Buffer_Handle hBuf;
BufferGfx_Dimensions dim; hBuf = BufTab_getBuf(hBufTab, );
BufferGfx_getDimensions(hBuf, &dim);
if((dim.height > fmt.fmt.pix.height) ||
(dim.width > fmt.fmt.pix.width)) {
Dmai_err2("User buffer size check failed %dx%d\n",
dim.height, dim.width);
cleanup(hDisplay);
return NULL;
}
fmt.fmt.pix.bytesperline = dim.lineLength;
fmt.fmt.pix.sizeimage = Buffer_getSize(hBuf);
}
#endif
Dmai_dbg4("Video output set to size %dx%d pitch %d imageSize %d\n",
fmt.fmt.pix.width, fmt.fmt.pix.height,
fmt.fmt.pix.bytesperline, fmt.fmt.pix.sizeimage); if ((attrs->videoStd == VideoStd_CIF) || (attrs->videoStd == VideoStd_SIF_PAL) ||
(attrs->videoStd == VideoStd_SIF_NTSC) || (attrs->videoStd == VideoStd_D1_PAL) ||
(attrs->videoStd == VideoStd_D1_NTSC) || (attrs->videoStd == VideoStd_1080I_30) ||
(attrs->videoStd == VideoStd_1080I_25)) {
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
} else {
fmt.fmt.pix.field = V4L2_FIELD_NONE;
} if (ioctl(hDisplay->fd, VIDIOC_S_FMT, &fmt) == -) {
Dmai_err2("Failed VIDIOC_S_FMT on %s (%s)\n", attrs->displayDevice,
strerror(errno));
cleanup(hDisplay);
return NULL;
} /* Should the device driver allocate the display buffers? */
if (hBufTab == NULL) {
hDisplay->userAlloc = FALSE; if (_Dmai_v4l2DriverAlloc(hDisplay->fd,
attrs->numBufs,
V4L2_BUF_TYPE_VIDEO_OUTPUT,
&hDisplay->bufDescs,
&hBufTab,
, attrs->colorSpace) < ) {
Dmai_err1("Failed to allocate display driver buffers on %s\n",
attrs->displayDevice);
cleanup(hDisplay);
return NULL;
}
}
else {
hDisplay->userAlloc = TRUE; if (_Dmai_v4l2UserAlloc(hDisplay->fd,
attrs->numBufs,
V4L2_BUF_TYPE_VIDEO_OUTPUT,
&hDisplay->bufDescs,
hBufTab,
, attrs->colorSpace) < ) {
Dmai_err1("Failed to intialize display driver buffers on %s\n",
attrs->displayDevice);
cleanup(hDisplay);
return NULL;
}
} /* Start the video streaming */
type = V4L2_BUF_TYPE_VIDEO_OUTPUT; if (ioctl(hDisplay->fd, VIDIOC_STREAMON, &type) == -) {
Dmai_err2("VIDIOC_STREAMON failed on %s (%s)\n", attrs->displayDevice,
strerror(errno));
cleanup(hDisplay);
return NULL;
} hDisplay->started = TRUE;
hDisplay->hBufTab = hBufTab;
hDisplay->displayStd = Display_Std_V4L2; return hDisplay;
}
创建显示设备不需要检查视频标准,因为输出的标准是有调用者决定的。
显示设备创建好了,那怎样将采集到的数据显示出来呢?TI提供了一个叫encode的demo程序,大家可以在dvsdk_demos_3_10_00_16中找到。
这个demo中很巧妙的将视频采集缓冲区和视频显示缓冲区管理起来,接下来一下分析一下。
因为单个显示缓冲和采集缓冲区的大小是一样的,所以可以直接进行交互。
下面截取demo中的代码片段,看一下这个缓冲区的交互的具体实现。
/* 采集及显示线程中的循环 */
while (!gblGetQuit()) { /* 获取采集缓冲区 */
if (Capture_get(hCapture, &hCapBuf) < ) {
ERR("Failed to get capture buffer\n");
cleanup(THREAD_FAILURE);
} /* 获取显示缓冲区 */
if (Display_get(hDisplay, &hDisBuf) < ) {
ERR("Failed to get display buffer\n");
cleanup(THREAD_FAILURE);
} /* 叠加字幕 */
if (envp->osd) {
/* Get the current transparency */
trans = UI_getTransparency(envp->hUI); if (trans != oldTrans) {
/* Change the transparency in the palette */
for (i = ; i < ; i++) {
bConfigParams.palette[i][] = trans;
} /* Reconfigure the blending job if transparency has changed */
if (Blend_config(hBlend, NULL, hBmpBuf, hCapBuf, hCapBuf,
&bConfigParams) < ) {
ERR("Failed to configure blending job\n");
cleanup(THREAD_FAILURE);
}
} /*
* Because the whole screen is shown even if -r is used,
* reset the dimensions while Blending to make sure the OSD
* always ends up in the same place. After blending, restore
* the real dimensions.
*/
BufferGfx_getDimensions(hCapBuf, &srcDim);
BufferGfx_resetDimensions(hCapBuf); /*
* Lock the screen making sure no changes are done to
* the bitmap while we render it.
*/
hBmpBuf = UI_lockScreen(envp->hUI); /* Execute the blending job to draw the OSD */
/* 直接叠加在采集缓冲区的数据上 */
if (Blend_execute(hBlend, hBmpBuf, hCapBuf, hCapBuf) < ) {
ERR("Failed to execute blending job\n");
cleanup(THREAD_FAILURE);
} UI_unlockScreen(envp->hUI); BufferGfx_setDimensions(hCapBuf, &srcDim);
} /* Color convert the captured buffer from 422Psemi to 420Psemi */
/* 在进行H264编码前需要进行 422 到 420的颜色空间转换,hDstBuf是从视频编码缓冲区队列中得到的一个缓冲区 */
if (Ccv_execute(hCcv, hCapBuf, hDstBuf) < ) {
ERR("Failed to execute color conversion job\n");
cleanup(THREAD_FAILURE);
} /* Send color converted buffer to video thread for encoding */
/* 转换后将这个缓冲区放回视频编码缓冲区队列中 */
if (Fifo_put(envp->hOutFifo, hDstBuf) < ) {
ERR("Failed to send buffer to display thread\n");
cleanup(THREAD_FAILURE);
} BufferGfx_resetDimensions(hCapBuf); /* Send the preview to the display device driver */
/* 将采集缓冲区放到显示缓冲区队列中 */
if (Display_put(hDisplay, hCapBuf) < ) {
ERR("Failed to put display buffer\n");
cleanup(THREAD_FAILURE);
} BufferGfx_resetDimensions(hDisBuf); /* Return a buffer to the capture driver */
/* 将显示缓冲区放到采集缓冲区队列中 */
if (Capture_put(hCapture, hDisBuf) < ) {
ERR("Failed to put capture buffer\n");
cleanup(THREAD_FAILURE);
} /* Incremement statistics for the user interface */
/* 帧计数加1 */
gblIncFrames(); /* Get a buffer from the video thread */
/* 从视频编码缓冲区队列中得到一个缓冲区,用于下一次颜色空间转换使用 */
fifoRet = Fifo_get(envp->hInFifo, &hDstBuf); if (fifoRet < ) {
ERR("Failed to get buffer from video thread\n");
cleanup(THREAD_FAILURE);
} /* Did the video thread flush the fifo? */
if (fifoRet == Dmai_EFLUSH) {
cleanup(THREAD_SUCCESS);
}
}
到此为止,已经可以知道了数据是从哪里来到哪里去了,但是数据来了,肯定没那么容易就放它走,下一章将会讲到将采集到的数据如何编码并且保存。再加点预告,后面会讲到将编码后的数据通过live555发送出去,实现rtsp视频服务器。