达芬奇TI DVSDK之视频数据流过程分析
博客: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视频服务器。
达芬奇TI DVSDK之视频数据流过程分析的更多相关文章
- 基于TI Davinci架构的多核/双核开发高速扫盲(以OMAP L138为例),dm8168多核开发參考以及达芬奇系列资料user guide整理
基于TI Davinci架构的双核嵌入式应用处理器OMAPL138开发入门 原文转自http://blog.csdn.net/wangpengqi/article/details/8115614 感谢 ...
- TI DaVinci(达芬奇)入门
(转载来自 德州仪器半导体技术(上海)有限公司 通用DSP 技术应用工程师 崔晶 德州仪器(TI)的第一颗达芬奇(DaVinci)芯片(处理器)DM6446已经问世快三年了.继DM644x之后,TI又 ...
- 【DSP开发】德州仪器达芬奇五年之路七宗罪,嵌入式处理器架构之争决战2012
芯片是产业链上游重要的一个环节,一颗小小的芯片具有极高的技术含量和价值,半导体行业每年都会有一个各大厂商营业额的排名,除去2009年,常年盘踞在前三名位置的分别是英特尔,三星半导体和德州仪器,英特尔凭 ...
- 达芬奇架构NPU
达芬奇架构NPU 达芬奇架构的核心优势是什么?如何更好地赋能麒麟990? 达芬奇架构,是华为自研的面向AI计算特征的全新计算架构,具备高算力.高能效.灵活可裁剪的特性,是实现万物智能的重要基础.具体来 ...
- buu 达芬奇 && ROT
一.达芬奇 百度了下电影简介,发现了斐波那契数列,同时发现密文是由斐波那契数列移动而来的,有点像base64变种 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 ...
- Mac 达芬奇【Davinci Resolve】 无法添加媒体
参考 : https://zhidao.baidu.com/question/182613491787331404.html 打开软件,点击默认的未命名项目: 点击左上角图中箭头位置: 选中系统-&g ...
- 动态获取爱奇艺上传视频mp4格式url地址
有时候,在工作中有些客户需要用到视频,我们大家都知道视频是非常的耗费流量的,因此,如果因为项目要求客户单独买台视频服务器是非常划不来的.那么将视频上传到优酷,爱奇艺等视频网站来托管那是一件很好的解决方 ...
- 用Flask实现视频数据流传输
Flask 是一个 Python 实现的 Web 开发微框架.这篇文章是一个讲述如何用它实现传送视频数据流的详细教程. 我敢肯定,现在你已经知道我在O’Reilly Media上发布了有关Flask的 ...
- 基于H5的摄像头视频数据流采集
最近,为了支持部门团队的项目,通过H5实现摄像头的视频流数据的捕获,抓取到视频流后,传输到视频识别服务器进行后续的逻辑处理. 视频数据的采集过程,其实是比较没有谱的过程,因为之前没有研究过HTML5操 ...
随机推荐
- Elasticsearch 邻近查询示例
Elasticsearch 邻近查询示例(全切分分词) JAVA API方式: SpanNearQueryBuilder span = QueryBuilders.spanNearQuery(); s ...
- DevExpress 去除皮肤的方法
我从不用皮肤,方法如下:
- HDU 1669 Jamie's Contact Groups(多重匹配+二分枚举)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1669 题目大意: 给你各个人可以属于的组,把这些人分组,使这些组中人数最多的组人数最少,并输出这个人数 ...
- MySQL学习笔记:exists和in的区别
一.exists函数 表示存在,常常与子查询配合使用. 用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False. 当子查询返回为真时,则外层查询语句将进行 ...
- 10 Best jQuery and HTML5 WYSIWYG Plugins
https://www.sitepoint.com/10-best-html-wysiwyg-plugins/
- Spring Boot 结合 Redis 缓存
Redis官网: 中:http://www.redis.cn/ 外:https://redis.io/ redis下载和安装 Redis官方并没有提供Redis的Windows版本,这里使用微软提供的 ...
- [leetcode DP]72. Edit Distance
计算最少用多少不把word1变为word2, 思路:建立一个dp表,行为word1的长度,宽为word2的长度 1.边界条件,dp[i][0] = i,dp[0][j]=j 2.最优子问题,考虑已经知 ...
- iOS 9应用开发教程之多行读写文本ios9文本视图
iOS 9应用开发教程之多行读写文本ios9文本视图 多行读写文本——ios9文本视图 文本视图也是输入控件,与文本框不同的是,文本视图可以让用户输入多行,如图2.23所示.在此图中字符串“说点什么吧 ...
- JS (function (window, document, undefined) {})(window, document)的真正含义
原文地址:What (function (window, document, undefined) {})(window, document); really means 按原文翻译 在这篇文章中,我 ...
- [BZOJ4668]冷战(并查集)
比较自然的思路是,由于需要记录连通块合并时的信息,所以需要建出Kruskal重构树. 需要用LCT维护,支持加点和在线LCA操作. 不妨考虑在并查集合并的同时记录信息,pre[x]表示x与它的父亲相连 ...