一、imx6ull平台视频相关-ipu的EOF中断响应过程


ipu_csi_enc.c中的csi_enc_enabling_tasks注册EOF中断,EOF中断说如下:(在手册中摘阅)

该中断的回调函数实现也在ipu_csi_enc.c文件中-csi_enc_callback函数。

static irqreturn_t csi_enc_callback(int irq, void *dev_id)
{cam_data *cam = (cam_data *) dev_id;if (cam->enc_callback == NULL)return IRQ_HANDLED;cam->enc_callback(irq, dev_id);return IRQ_HANDLED;
}

cam->enc_callback(irq, dev_id);调用钩子函数,这个函数的注册在mxc_v4l2_capture.c的init_camera_struct函数中(贴出部分语句)。

 cam->enc_callback = camera_callback;init_waitqueue_head(&cam->power_queue);spin_lock_init(&cam->queue_int_lock);spin_lock_init(&cam->dqueue_int_lock);

camera_callback函数就是钩子的实体,也在mxc_v4l2_capture.c中。

static void camera_callback(u32 mask, void *dev)
{struct mxc_v4l_frame *done_frame;struct mxc_v4l_frame *ready_frame;struct timeval cur_time;cam_data *cam = (cam_data *) dev;if (cam == NULL)return;pr_debug("In MVC:camera_callback\n");spin_lock(&cam->queue_int_lock);spin_lock(&cam->dqueue_int_lock);if (!list_empty(&cam->working_q)) {do_gettimeofday(&cur_time);done_frame = list_entry(cam->working_q.next,struct mxc_v4l_frame,queue);if (done_frame->ipu_buf_num != cam->local_buf_num)goto next;/** Set the current time to done frame buffer's* timestamp. Users can use this information to judge* the frame's usage.*/done_frame->buffer.timestamp = cur_time;if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;/* Added to the done queue */list_del(cam->working_q.next);list_add_tail(&done_frame->queue, &cam->done_q);/* Wake up the queue */cam->enc_counter++;wake_up_interruptible(&cam->enc_queue);} elsepr_err("ERROR: v4l2 capture: camera_callback: ""buffer not queued\n");}next:if (!list_empty(&cam->ready_q)) {ready_frame = list_entry(cam->ready_q.next,struct mxc_v4l_frame,queue);if (cam->enc_update_eba)if (cam->enc_update_eba(cam,ready_frame->buffer.m.offset) == 0) {list_del(cam->ready_q.next);list_add_tail(&ready_frame->queue,&cam->working_q);ready_frame->ipu_buf_num = cam->local_buf_num;}} else {if (cam->enc_update_eba)cam->enc_update_eba(cam, cam->dummy_frame.buffer.m.offset);}cam->local_buf_num = (cam->local_buf_num == 0) ? 1 : 0;spin_unlock(&cam->dqueue_int_lock);spin_unlock(&cam->queue_int_lock);return;
}

一种方式是唤醒wake_up_interruptible(&cam->enc_queue);一种方式更新数据cam->enc_update_eba(cam, buffer.m.offset) 。第一种情况响应的位置在mxc_v4l_dqueue中wait_event_interruptible_timeout(cam->enc_queue,cam->enc_counter != 0,10 * HZ)。第二种情况是调用了钩子cam->enc_update_eba,这个钩子的注册在ipu_csi_enc.c中的csi_enc_select函数中。

cam->enc_update_eba = csi_enc_eba_update;

csi_enc_eba_update函数的实现:

static int csi_enc_eba_update(void *private, dma_addr_t eba)
{int err = 0;cam_data *cam = (cam_data *) private;struct ipu_soc *ipu = cam->ipu;int *buffer_num = &cam->ping_pong_csi;ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;pr_debug("eba %x\n", eba);err = ipu_update_channel_buffer(ipu, chan, IPU_OUTPUT_BUFFER,*buffer_num, eba);if (err != 0) {ipu_clear_buffer_ready(ipu, chan, IPU_OUTPUT_BUFFER,*buffer_num);err = ipu_update_channel_buffer(ipu, chan,IPU_OUTPUT_BUFFER, *buffer_num, eba);if (err != 0) {pr_err("ERROR: v4l2 capture: fail to update ""buf%d\n", *buffer_num);return err;}}ipu_select_buffer(ipu, chan, IPU_OUTPUT_BUFFER, *buffer_num);*buffer_num = (*buffer_num == 0) ? 1 : 0;return 0;
}

从ipu的通道中将数据搬到eba中,eba是上一步中的buffer.m.offset。而buffer.m.offset地址实际是被用户空间进行了map。

二、用户空间map视频存储过程


map的过程我分为了三步,第一步提出申请,第二步获取到内核层申请的内存空间并做映射,第三步获取视频数据。

我使用的上层应用代码是ffmpeg源码,其他源码也会进行相应的操作。

第一步:

在v4l2.c的mmap_init函数中,调用了v4l2_ioctl(s->fd, VIDIOC_REQBUFS, &req)。
VIDIOC_REQBUFS的实现在mxc_v4l2_capture.c。(为什么调用v4l2_ioctl会到case VIDIOC_REQBUFS, 如果不懂,请参考字符设备的ioctl函数的相关知识与实现)

 case VIDIOC_REQBUFS: {struct v4l2_requestbuffers *req = arg;pr_debug("   case VIDIOC_REQBUFS\n");if (req->count > FRAME_NUM) {pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: ""not enough buffers\n");req->count = FRAME_NUM;}if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: ""wrong buffer type\n");retval = -EINVAL;break;}mxc_streamoff(cam);if (req->memory & V4L2_MEMORY_MMAP) {mxc_free_frame_buf(cam);retval = mxc_allocate_frame_buf(cam, req->count);}break;}

这里在最后会判断是不是map(除了map还有read的方式等),如果是进入到mxc_allocate_frame_buf函数。

static int mxc_allocate_frame_buf(cam_data *cam, int count)
{int i;pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n",cam->v2f.fmt.pix.sizeimage);for (i = 0; i < count; i++) {cam->frame[i].vaddress =dma_alloc_coherent(0,PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),&cam->frame[i].paddress,GFP_DMA | GFP_KERNEL);if (cam->frame[i].vaddress == 0) {pr_err("ERROR: v4l2 capture: ""mxc_allocate_frame_buf failed.\n");mxc_free_frame_buf(cam);return -ENOBUFS;}cam->frame[i].buffer.index = i;cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;cam->frame[i].buffer.length =PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;cam->frame[i].buffer.m.offset = cam->frame[i].paddress;cam->frame[i].index = i;}return 0;
}

mxc_allocate_frame_buf函数中调用了dma_alloc_coherent函数,我认为这个的作用是从dma的内存中申请一块固定大小的内存,返回虚拟地址,物理地址存在cam->frame[i].paddress里,并且这句话cam->frame[i].buffer.m.offset = cam->frame[i].paddress;,将cam->frame[i].paddress存在了cam->frame[i].buffer.m.offset中(buffer.m.offset就是函数csi_enc_eba_update最后搬移数据的目的地址)。

第二步:

还是在v4l2.c的mmap_init函数中,继续往下,调用了v4l2_ioctl(s->fd, VIDIOC_QUERYBUF, &buf),
VIDIOC_QUERYBUF的实现在mxc_v4l2_capture.c。

 case VIDIOC_QUERYBUF: {struct v4l2_buffer *buf = arg;int index = buf->index;pr_debug("   case VIDIOC_QUERYBUF\n");if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {pr_err("ERROR: v4l2 capture: ""VIDIOC_QUERYBUFS: ""wrong buffer type\n");retval = -EINVAL;break;}if (buf->memory & V4L2_MEMORY_MMAP) {memset(buf, 0, sizeof(buf));buf->index = index;}down(&cam->param_lock);if (buf->memory & V4L2_MEMORY_USERPTR) {mxc_v4l2_release_bufs(cam);retval = mxc_v4l2_prepare_bufs(cam, buf);}if (buf->memory & V4L2_MEMORY_MMAP)retval = mxc_v4l2_buffer_status(cam, buf);up(&cam->param_lock);break;}

调用了mxc_v4l2_buffer_status函数。


```c
static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
{pr_debug("In MVC:mxc_v4l2_buffer_status\n");if (buf->index < 0 || buf->index >= FRAME_NUM) {pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers ""not allocated\n");return -EINVAL;}memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));return 0;
}
这个函数主要是做了信息的copy返回给用户空间(memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));)。回到v4l2.c的mmap_init函数中,继续往下,```cs->buf_start[i] = v4l2_mmap(NULL, buf.length,PROT_READ | PROT_WRITE, MAP_SHARED,s->fd, buf.m.offset);

这句话做了遍映射,使用的物理地址就是上边提到的从dma中申请出来的buf.m.offset。

第三步:

第三步是读数据,在v4l2.c的mmap_read_frame中调用了v4l2_ioctl(s->fd, VIDIOC_DQBUF, &buf),
VIDIOC_DQBUF的实现在mxc_v4l2_capture.c。

 case VIDIOC_DQBUF: {struct v4l2_buffer *buf = arg;pr_debug("   case VIDIOC_DQBUF\n");if ((cam->enc_counter == 0) &&(file->f_flags & O_NONBLOCK)) {retval = -EAGAIN;break;}retval = mxc_v4l_dqueue(cam, buf);break;}

进入mxc_v4l_dqueue函数:

static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
{int retval = 0;struct mxc_v4l_frame *frame;unsigned long lock_flags;pr_debug("In MVC:mxc_v4l_dqueue\n");if (!wait_event_interruptible_timeout(cam->enc_queue,cam->enc_counter != 0,10 * HZ)) {pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout ""enc_counter %x\n",cam->enc_counter);return -ETIME;} else if (signal_pending(current)) {pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() ""interrupt received\n");return -ERESTARTSYS;}if (down_interruptible(&cam->busy_lock))return -EBUSY;spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);cam->enc_counter--;frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);list_del(cam->done_q.next);if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;} else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: ""Buffer not filled.\n");frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;retval = -EINVAL;} else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: ""Buffer not queued.\n");retval = -EINVAL;}cam->frame[frame->index].buffer.field = cam->device_type ?V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;buf->bytesused = cam->v2f.fmt.pix.sizeimage;buf->index = frame->index;buf->flags = frame->buffer.flags;buf->m = cam->frame[frame->index].buffer.m;buf->timestamp = cam->frame[frame->index].buffer.timestamp;buf->field = cam->frame[frame->index].buffer.field;spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);up(&cam->busy_lock);return retval;
}

可以看到上面提到的中断唤醒在此时就发挥作用了,如果不唤醒,则不会获取到数据。唤醒之后下面会从done_q队列中获取信息并返回给用户空间(队列还没太看懂,一共有三个队列done_q、ready_q、working_q)。

再回到v4l2.c的mmap_read_frame中。此时数据就在s->buf_start[buf.index]中,s->buf_start[buf.index]就是上面提到的映射的内存,根据获取到的信息,就可以读取出视频数据了。

个人分析,如果有错误,还望指正。

imx6ull平台视频相关-ipu的EOF中断响应过程及用户空间map视频存储过程相关推荐

  1. Android平台视频相关的多媒体技术理解笔记

    做了几年的视频相关项目,一直没有系统的对所涉技术进行整理,趁着最近有点事情,整理归纳一下.多媒体技术还是一个相当大而全的领域,我是做视频的,只谈一下视频所涉的一些技术点的理解,平台是基于Android ...

  2. 转: 视频相关的协议族介绍(rtsp, hls, rtmp)

    转自: http://www.zhihu.com/question/20621558 作者:杨华 链接:http://www.zhihu.com/question/20621558/answer/15 ...

  3. 【HDR学习】HDR视频相关知识讲解(一)

    [背景] 由来:HDR首先作为静态摄影的一种技术而闻名于世.在静态摄影中,这种进行多次曝光并堆栈成一张暗部和亮部都有细节的图像的方法就是HDR摄影. 随着科技的进步,人们观看影视的体验越来越好,这不仅 ...

  4. Android 平台camera相关梳理

    其实这个题目起得有点大,不过,我实在找不到好的描述. 本文主要的作用是梳理思路用的. 一.usb 免驱camera. 其实就是UVC camera,免驱其实是免除手动安装,实际上是camera和系统都 ...

  5. uboot移植到IMX6ULL平台详细过程

    uboot移植到IMX6ULL平台详细过程 文章目录 uboot移植到IMX6ULL平台详细过程 1.解压 2 编译 NXP 官方开发板对应的 uboot 3 烧写验证与驱动测试 1.SD 卡和 EM ...

  6. 音视频面试必备:一文搞懂视频相关的基础概念

    1.引言 随着移动互联网的普及,实时音视频技术已经在越来越多的场景下发挥重要作用,已经不再局限于IM中的实时视频聊天.实时视频会议这种功能,在远程医疗.远程教育.智能家居等等场景也司空见惯. 虽然实时 ...

  7. 自媒体短视频中视频相关的问题总结,涉及视频类型、质量、时长和原创等方面

    自媒体短视频中视频相关的问题总结,涉及视频类型.质量.时长和原创等方面 大家好,我是我赢助手,专注于自媒体短视频去水印.去重和文案提取运营! 前几天跟大家分享了我最近在做的一个短视频账号,也就是刚刚开 ...

  8. ffmpeg音视频相关资料

    ffmpeg相关资料 FFMPEG官网 FFMPEG教学视频 夏曹俊老师的FFMPEG视频教程 雷霄骅的音视频博客和开源项目 An ffmpeg and SDL Tutorial SRS开源视频服务器 ...

  9. 如何实现一个直播平台的相关基础及开源软件推荐

    如何实现一个直播平台的相关基础及开源软件推荐 首先我们要大致知道一个直播系统核心的构成要件主要是三部分: 采集--主播使用 OBS 之类的软件来录制视频,并能够将数据发向服务器 转播--服务器使用 S ...

最新文章

  1. SQLite基本操作
  2. [转]使用fragments
  3. 为没有源码的DLL文件添加强名称
  4. 云小课|三大灵魂拷问GaussDB(DWS)数据落盘安全问题
  5. Web前端工作笔记006---各种弹框框架
  6. python进阶学习笔记(四)--多线程thread
  7. 从零基础入门Tensorflow2.0 ----三、10. 近似求导
  8. JavaScript继承方式详解[转]
  9. 软件测试实例:登录功能怎么设计测试用例
  10. table表单的制作
  11. 星梦邮轮世界梦号推出深圳母港特别航次
  12. 计算机考研909考试大纲,山东大学2019年909数据结构考研大纲
  13. 都是古人抓紧时间发奋苦读的典范
  14. 关于linux重启后磁盘分区消失的情况复现与修复
  15. 计算机应用基础二作业,计算机应用基础(二)作业2 (精选可编辑)
  16. LORA模块 无线模式433 2.4G模块分析
  17. 快速获取Windows系统上的国家和地区信息
  18. Jenkins+Gradle+Python进行Android自动化打包
  19. php 汉王云名片_超级云名片,让你的客户不与你说“考虑一下”!
  20. Kali Linux中前十名的Wifi攻击工具(附官网链接、使用教程)

热门文章

  1. Matlab使用Camera Calibrator工具箱标定相机
  2. Unity场景中的双相机设置,3D相机和2D相机设置
  3. 微信小程序 云数据库使用(上)
  4. PMP考试时间是什么时候?
  5. 普通人如何做到“我命由我不由天”
  6. linux qq java_Java实现QQ登陆界面
  7. 《潜能成功学》----如何建立自信
  8. [趣谈网络协议学习] 03 DHCP与PXE:IP是怎么来的, 又是怎么没的?
  9. 数据库原理--数据库管理系统的功能与组成
  10. 中学教学01教学概述02教学过程03教学原则与方法