omap3isp上层应用解析

omap3isp上层应用解析

代码仓库位置:
https://git.ideasonboard.org/

仓库包含几个项目:
media-ctl
media-enum
omap3-isp-dsp
omap3-isp-live

本文从omap3-isp-dsp入手分析上层应用中media的使用流程。

main入口函数

 omap3-isp-dsp/isp-dsp.c 
int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unused__)))
{
       struct omap3_dsp_device dsp;
       struct omap3_isp_device isp;
       struct timespec start, end;
       unsigned int count = 0;
       int exit_code = EXIT_FAILURE;
       float fps;
       int ret;

       /* Register a signal handler for SIGINT, received when the user presses
        * CTRL-C. This will allow the main loop to be interrupted, and resources
        * to be freed cleanly.
        */
       signal(SIGINT, sigint_handler);

       memset(&dsp, 0, sizeof dsp);
       memset(&isp, 0, sizeof isp);

       /* Open the media device and setup the capture pipeline. The pipeline
        * topology is hardcoded to sensor -> CCDC -> CCDC output.
        */
        /*
        原英文注释已经很清楚:sensor输出视频-》CCDC接收-》CCDC输出数据到内存(可能用于LCD显示等)。
        media是本文的重点分析。
        */
       isp.mdev = media_open(MEDIA_DEVICE, 0);
       if (isp.mdev == NULL) {
               printf("error: unable to open media device %s\n", MEDIA_DEVICE);
               goto cleanup;
       }
       ret = omap3isp_pipeline_setup(&isp);//建立pipeline,重点2
       if (ret < 0) {
               printf("error: unable to setup pipeline\n");
               goto cleanup;
       }

       /* Open the video capture device, setup the format and allocate the
        * buffers.
        */
       isp.vdev = v4l2_open(isp.video->devname);//V4L2的流程在此不关心
       if (isp.vdev == NULL) {
               printf("error: unable to open video capture device %s\n",
                       isp.video->devname);
               goto cleanup;
       }

       ret = omap3isp_video_setup(&isp);
       if (ret < 0) {
               printf("error: unable to setup video capture\n");
               goto cleanup;
       }

       /* Initialize the DSP. */
       ret = omap3dsp_setup(&dsp, isp.vdev->buffers);
       if (ret < 0) {
               printf("error: unable to setup DSP\n");
               goto cleanup;
       }

       /* Start the DSP and ISP. */
       ret = omap3dsp_start(&dsp);
       if (ret < 0)
               goto cleanup;
       ret = omap3isp_video_start(&isp);
       if (ret < 0)
               goto cleanup;

       clock_gettime(CLOCK_MONOTONIC, &start);

       /* Main capture loop. Wait for a video buffer using select() and process
        * it.
        */
       while (!done) {
               struct timeval timeout;
               fd_set rfds;

               timeout.tv_sec = SELECT_TIMEOUT / 1000;
               timeout.tv_usec = (SELECT_TIMEOUT % 1000) * 1000;
               rfds = isp.fds;

               ret = select(isp.vdev->fd + 1, &rfds, NULL, NULL, &timeout);
               if (ret < 0) {
                       /* EINTR means that a signal has been received, continue
                        * to the next iteration in that case.
                        */
                       if (errno == EINTR)
                               continue;

                       printf("error: select failed with %d\n", errno);
                       goto cleanup;
               }
               if (ret == 0) {
                       /* select() should never time out as the ISP is supposed
                        * to capture images continuously. A timeout is thus
                        * considered as a fatal error.
                        */
                       printf("error: select timeout\n");
                       goto cleanup;
               }

               process_image(&isp, &dsp);
               count++;
       }

       clock_gettime(CLOCK_MONOTONIC, &end);

       /* Stop the DSP and ISP. */
       omap3isp_video_stop(&isp);
       omap3dsp_stop(&dsp);

       /* Print some statistics. */
       end.tv_sec -= start.tv_sec;
       end.tv_nsec -= start.tv_nsec;
       if (end.tv_nsec < 0) {
               end.tv_sec--;
               end.tv_nsec += 1000000000;
       }

       fps = count / (end.tv_sec + end.tv_nsec / 1000000000.0);

       printf("%u images processed in %lu.%06lu seconds (%f fps)\n", count,
              end.tv_sec, end.tv_nsec / 1000, fps);

       exit_code = EXIT_SUCCESS;

cleanup:
       /* Cleanup the DSP and ISP resources. */
       omap3dsp_cleanup(&dsp);
       omap3isp_video_cleanup(&isp);
       if (isp.mdev)
               media_close(isp.mdev);

media_open获取media信息的流程

struct media_device *media_open(const char *name, int verbose)
{
        struct media_device *media;
        int ret;

        media = malloc(sizeof(*media));
        if (media == NULL) {
                printf("%s: unable to allocate memory\n", __func__);
                return NULL;
        }
        memset(media, 0, sizeof(*media));

        if (verbose)
                printf("Opening media device %s\n", name);
        media->fd = open(name, O_RDWR);
        if (media->fd < 0) {
                media_close(media);
                printf("%s: Can't open media device %s\n", __func__, name);
                return NULL;
        }

        //MEDIA_IOC_DEVICE_INFO所提取的信息后面稍用到。
        ret = ioctl(media->fd, MEDIA_IOC_DEVICE_INFO, &media->info);
        if (ret < 0) {
                printf("%s: Unable to retrieve media device information for "
                       "device %s (%s)\n", __func__, name, strerror(errno));
                media_close(media);
                return NULL;
        }

        if (verbose)
                printf("Enumerating entities\n");

        //枚举所有entity,并保存起来。关键。
        ret = media_enum_entities(media);
        if (ret < 0) {
                printf("%s: Unable to enumerate entities for device %s (%s)\n",
                        __func__, name, strerror(-ret));
                media_close(media);
                return NULL;
        }

        if (verbose) {
                printf("Found %u entities\n", media->entities_count);
                printf("Enumerating pads and links\n");
        }

        //枚举所有link,并保存起来。关键。
        ret = media_enum_links(media);
        if (ret < 0) {
                printf("%s: Unable to enumerate pads and linksfor device %s\n",
                        __func__, name);
                media_close(media);
                return NULL;
        }

        return media;
}
static int media_enum_entities(struct media_device *media)
{       
        struct media_entity *entity;
        struct stat devstat;
        unsigned int size;
        char devname[32];
        char sysname[32];
        char target[1024];
        char *p;        
        __u32 id;
        int ret;
        
        for (id = 0; ; id = entity->info.id) {
                size = (media->entities_count + 1) * sizeof(*media->entities);
                media->entities = realloc(media->entities, size);//entities存放所有entity
                
                entity = &media->entities[media->entities_count];
                memset(entity, 0, sizeof(*entity));
                entity->fd = -1;
                entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT;//希望获得NEXT id

                /*
                   entity->info结构体是内核标准结构体:
                   struct media_entity_desc info;
                   其中有两个成员容易误会,所以将这两个成员的驱动赋值贴出来,其意思一目了然:
                   u_ent.pads = ent->num_pads;//entity pad数
                   u_ent.links = ent->num_links - ent->num_backlinks;//entity的link数
                 */
                ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
                if (ret < 0) {
                        if (errno == EINVAL)
                                break;
                        return -errno;
                }

                /* Number of links (for outbound links) plus number of pads (for
                 * inbound links) is a good safe initial estimate of the total
                 * number of links.
                 */
                 //未能理解透彻?
                entity->max_links = entity->info.pads + entity->info.links;

                //以下的pads和links都是应用程序自定义的结构体,不是内核的。
                entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));//将会记录此entity所有pad
                entity->links = malloc(entity->max_links * sizeof(*entity->links));//将会记录此entity所有link
                if (entity->pads == NULL || entity->links == NULL)
                        return -ENOMEM;

                media->entities_count++;

                /* Find the corresponding device name. */
                //只查找videoX或者v4l-subdevX这些节点的entity,其他类型的entity不填充devname。
                if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE &&
                    media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
                        continue;

                sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
                        entity->info.v4l.minor);
                ret = readlink(sysname, target, sizeof(target));
                if (ret < 0)
                        continue;

                target[ret] = '\0';
                p = strrchr(target, '/');
                if (p == NULL)
                        continue;

                sprintf(devname, "/dev/%s", p + 1);
                ret = stat(devname, &devstat);
                if (ret < 0)
                        continue;

                /* Sanity check: udev might have reordered the device nodes.
                 * Make sure the major/minor match. We should really use
                 * libudev.
                 */
                if (major(devstat.st_rdev) == entity->info.v4l.major &&
                    minor(devstat.st_rdev) == entity->info.v4l.minor)
                        strcpy(entity->devname, devname);
                //最后devname应该是:videoX、v4l-subdevX这些名称。
        }

        return 0;
}
/*      
小结:  
1.填充应用自定义的entity->max_links,为entity->pads和entity->links分配足够内存;
2. media->entities_count记录entity总个数。
*/ 
static int media_enum_links(struct media_device *media)
{       
        __u32 id;
        int ret = 0;    
                
        for (id = 1; id <= media->entities_count; id++) {
                struct media_entity *entity = &media->entities[id - 1];
                struct media_links_enum links;//media_links_enum是内核定义的结构体,不是应用定义
                unsigned int i;
                
                links.entity = entity->info.id;//此entity的id号
                links.pads = calloc(entity->info.pads, sizeof(struct media_pad_desc));//为当前entity分配所有pad内存. media_pad_desc内核定义的结构体
                links.links = calloc(entity->info.links, sizeof(struct media_link_desc));//为当前entity分配所有link内存
        
                //ioctl会返回这个entity对应的所有pad和link。
                if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
                        printf("%s: Unable to enumerate pads and links (%s).\n",
                                __func__, strerror(errno));
                        free(links.pads);
                        free(links.links);
                        return -errno;
                }

                for (i = 0; i < entity->info.pads; ++i) {
                        //提取所有pad的参数,保存到自定义的结构体entity->pads中。
                        entity->pads[i].entity = entity;
                        entity->pads[i].index = links.pads[i].index;
                        entity->pads[i].flags = links.pads[i].flags;
                }

                for (i = 0; i < entity->info.links; ++i) {
                        //提取所有link的参数。
                        //以下除了source和sink,其他都是内核定义的结构体
                        struct media_link_desc *link = &links.links[i];
                        struct media_entity_link *fwdlink;
                        struct media_entity_link *backlink;
                        struct media_entity *source;
                        struct media_entity *sink;

                        source = media_get_entity_by_id(media, link->source.entity);
                        sink = media_get_entity_by_id(media, link->sink.entity);

                        if (source == NULL || sink == NULL) {
                                printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
                                        id, i, link->source.entity, link->source.index,
                                        link->sink.entity, link->sink.index);
                                ret = -EINVAL;
                        }

                        //在应用程序定义的source和sink entity各自增加一个link,将连接的信息保存好。
                        fwdlink = media_entity_add_link(source);
                        fwdlink->source = &source->pads[link->source.index];
                        fwdlink->sink = &sink->pads[link->sink.index];
                        fwdlink->flags = links.links[i].flags;

                        backlink = media_entity_add_link(sink);
                        backlink->source = &source->pads[link->source.index];
                        backlink->sink = &sink->pads[link->sink.index];
                        backlink->flags = links.links[i].flags;

                        fwdlink->reverse = backlink;
                        backlink->reverse = fwdlink;
                }

                free(links.pads);
                free(links.links);
        }

        return ret;
}

/*
回顾应用程序自定义的struct media_device,注意这是应用程序自定义的不是内核那个。
struct media_device {
        int fd;//打开/dev/media0的句柄
        int refcount;
        char *devnode;

        struct media_device_info info;//一些驱动信息,基本没啥用途
        struct media_entity *entities;//一个数组,记录整个驱动所有entity
        unsigned int entities_count;//entities的个数

        void (*debug_handler)(void *, ...);
        void *debug_priv;

        struct {
                struct media_entity *v4l;
                struct media_entity *fb;
                struct media_entity *alsa;
                struct media_entity *dvb;
        } def;
};
*/

omap3isp_pipeline_setup建立pipeline

static int omap3isp_pipeline_setup(struct omap3_isp_device *isp)
{       
        struct v4l2_mbus_framefmt format;
        struct media_entity *entity;
        unsigned int i;
        int ret;
        
        /* Reset all links to make sure we're in a consistent, known state. */
        //恢复所有link,其实就是设置disable所有非可动态改变的link
        ret = media_reset_links(isp->mdev);
        if (ret < 0) {
                printf("error: unable to reset links.\n");
                return ret;
        }

        /* Setup a Sensor -> CCDC -> memory pipeline.
         * 
         * Start by locating the three entities. The output video node is
         * located by looking for a devnode connected to the CCDC.
         */
         /*
         在所有entity数组里面寻找这个名字的entity,注意entity是应用自定义的,
         跟内核的struct media_entity有区别。

         MEDIA_IOC_DEVICE_INFO从驱动获取的信息,终于在这里派上用场,
         media_get_entity_by_name内部用到其中的name。
         */
         //找到CCDC entity。
        isp->ccdc = media_get_entity_by_name(isp->mdev, ENTITY_CCDC,
                                             strlen(ENTITY_CCDC));
        if (isp->ccdc == NULL) {
                printf("error: unable to locate CCDC.\n");
                return -ENOENT;
        }

        //找到SENSOR entity。
        isp->sensor = media_get_entity_by_name(isp->mdev, ENTITY_SENSOR,
                                               strlen(ENTITY_CCDC));
        if (isp->sensor == NULL) {
                printf("error: unable to locate sensor.\n");
                return -ENOENT;
        }

        for (i = 0; i < isp->ccdc->info.links; ++i) {
                entity = isp->ccdc->links[i].sink->entity;
                /*
                这里找的是CCDC video output的videoX节点对于的entity。
                也就是memory输出。
                */
                if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
                        break;
        }

        if (i == isp->ccdc->info.links) {
                printf("error: unable to locate CCDC output video node.\n");
                return -ENOENT;
        }

        isp->video = entity;

        /* Enable the Sensor -> CCDC and CCDC -> memory links. */
        //这里sensor->CCDC CCDC_PAD_SINK。
        ret = media_setup_link(isp->mdev, &isp->sensor->pads[0],
                               &isp->ccdc->pads[0], MEDIA_LNK_FL_ENABLED);
        if (ret < 0) {
                printf("error: unable to setup sensor -> CCDC link.\n");
                return ret;
        }

        //这里CCDC CCDC_PAD_SINK-> CCDC video_out SINK。
        ret = media_setup_link(isp->mdev, &isp->ccdc->pads[1],
                               &isp->video->pads[0], MEDIA_LNK_FL_ENABLED);
        if (ret < 0) {
                printf("error: unable to setup CCDC -> devnode link.\n");
                return ret;
        }

        /* Configure formats. Retrieve the default format at the sensor output
         * and propagate it through the pipeline. As the CCDC will not perform
         * any cropping we can just apply the same format on all pads.
         */
        ret = v4l2_subdev_get_format(isp->sensor, &format, 0,
                                     V4L2_SUBDEV_FORMAT_TRY);
        if (ret < 0) {
                printf("error: get format on sensor output failed.\n");
                return ret;
        }

        ret = v4l2_subdev_set_format(isp->sensor, &format, 0,
                                     V4L2_SUBDEV_FORMAT_ACTIVE);
        if (ret < 0) {
                printf("error: set format failed on %s:%u.\n",
                        isp->sensor->info.name, 0);
                return ret;
        }

        ret = v4l2_subdev_set_format(isp->ccdc, &format, 0,
                                     V4L2_SUBDEV_FORMAT_ACTIVE);
        if (ret < 0) {
                printf("error: set format failed on %s:%u.\n",
                        isp->ccdc->info.name, 1);
                return ret;
        }

        ret = v4l2_subdev_set_format(isp->ccdc, &format, 1,
                                     V4L2_SUBDEV_FORMAT_ACTIVE);
        if (ret < 0) {
                printf("error: set format failed on %s:%u.\n",
                        isp->ccdc->info.name, 1);
                return ret;
        }

        isp->format = format;

        return 0;
}
上一篇:-bash: /usr/bin/python: Too many levels of symbolic links 解决办法。


下一篇:从 PageRank Example 谈 Spark 应用程序调优