IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> ZBar源码分析——video.c(二) | 2021SC@SDUSC -> 正文阅读

[C++知识库]ZBar源码分析——video.c(二) | 2021SC@SDUSC

2021SC@SDUSC

目录

一、Video模块

二、代码分析

ZBar对视频流的创建和销毁

获取相机设备状态

视频初始化(预处理)

视频流的图像迭代

三、总结


一、Video模块

我们知道,扫描时提供给ZBar的不都是静态的图片,也有可能是动态的视频。例如我们日常生活中调用的微信扫码,所提供的也是视频video,这时便需要Zbar对视频进行分析,动态扫码。

Video模块是ZBar实现对读入视频进行扫描分析的功能模块。核心代码由video.h和video.c组成,video.h包括对一些关键变量的声明和结构体的定义,同时也是其他部件对Video模块的调用接口(头文件),而具体功能代码则是在video.c中实现。


二、代码分析

上篇博客对ZBar关于图像和视频之间的转换和处理部分的代码进行了简要分析,这篇博客将继上篇博客往下分析。

ZBar对视频流的创建和销毁

zbar_video_t *zbar_video_create ()
{
    zbar_video_t *vdo = calloc(1, sizeof(zbar_video_t));
    int i;
    if(!vdo)
        return(NULL);
    err_init(&vdo->err, ZBAR_MOD_VIDEO);
    vdo->fd = -1;

    (void)_zbar_mutex_init(&vdo->qlock);

    /* pre-allocate images */
    vdo->num_images = ZBAR_VIDEO_IMAGES_MAX;
    vdo->images = calloc(ZBAR_VIDEO_IMAGES_MAX, sizeof(zbar_image_t*));
    if(!vdo->images) {
        zbar_video_destroy(vdo);
        return(NULL);
    }

    for(i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) {
        zbar_image_t *img = vdo->images[i] = zbar_image_create();
        if(!img) {
            zbar_video_destroy(vdo);
            return(NULL);
        }
        img->refcnt = 0;
        img->cleanup = _zbar_video_recycle_image;
        img->srcidx = i;
        img->src = vdo;
    }

    return(vdo);
}

首先为视频对象zbar_video_t *vdo申请内存,大小为zbar_video_所需内存。

若申请成功,则将相机状态置为打开(属性fd决定),若未申请成功,则返回NULL。

打开相机后,则代表着相机资源已被占用,其他视频对象不可使用相机资源。

这一功能由函数_zbar_mutex_init(&vdo->qlock)来实现,该函数在上篇博客中简单提到过,它实现了视频对象的上锁,即当前状态下不可产生新的视频对象(调用该函数),以免产生资源互斥。

接下来对图像进行预分配。

Zbar采用的处理方式是设定一个图像数量的最大值,然后将视频的图像数量置为该值并申请内存。

接下来根据这个最大值对视频对象进行遍历,对视频中包含的所有扫描得到的图像进行保存和创建,申请内存。并对每一张图像的进行计数、索引,以便调用和分析。

注意到,如果某一张图像出现异常、损坏,ZBar则直接销毁整个视频对象。

void zbar_video_destroy (zbar_video_t *vdo)
{
    if(vdo->intf != VIDEO_INVALID)
        zbar_video_open(vdo, NULL);
    if(vdo->images) {
        int i;
        for(i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++)
            if(vdo->images[i])
                _zbar_image_free(vdo->images[i]);
        free(vdo->images);
    }
    while(vdo->shadow_image) {
        zbar_image_t *img = vdo->shadow_image;
        vdo->shadow_image = img->next;
        free((void*)img->data);
        img->data = NULL;
        free(img);
    }
    if(vdo->buf)
        free(vdo->buf);
    if(vdo->formats)
        free(vdo->formats);
    err_cleanup(&vdo->err);
    _zbar_mutex_destroy(&vdo->qlock);

#ifdef HAVE_LIBJPEG
    if(vdo->jpeg_img) {
        zbar_image_destroy(vdo->jpeg_img);
        vdo->jpeg_img = NULL;
    }
    if(vdo->jpeg) {
        _zbar_jpeg_decomp_destroy(vdo->jpeg);
        vdo->jpeg = NULL;
    }
#endif
    free(vdo);
}

当ZBar需要销毁一个视频对象时,同样不能直接调用free函数释放内存,需要对将该视频对象占用的资源(包括占用的图像数组等)进行销毁后,才能释放视频对象。

这个函数的代码较为浅显,做的工作就是遍历视频对象调用过的图像和阴影部分图像,将其占用内存逐一释放后,最后free(vdo)释放视频对象。

获取相机设备状态

int zbar_video_get_fd (const zbar_video_t *vdo)
{
    if(vdo->intf == VIDEO_INVALID)
        return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
                           "video device not opened"));
    if(vdo->intf != VIDEO_V4L2)
        return(err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__,
                           "video driver does not support polling"));
    return(vdo->fd);
}

该函数对相机状态的两种异常情况进行了异常处理:视频设备未打开和视频驱动程序不支持轮询。

视频初始化(预处理)

int zbar_video_init (zbar_video_t *vdo,
                     unsigned long fmt)
{
#ifdef HAVE_LIBJPEG
    const zbar_format_def_t *vidfmt;
#endif
    if(vdo->initialized)
        /* FIXME re-init different format? */
        return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
                           "already initialized, re-init unimplemented"));

    if(vdo->init(vdo, fmt))
        return(-1);
    vdo->format = fmt;
    if(video_init_images(vdo))
        return(-1);
#ifdef HAVE_LIBJPEG
    vidfmt = _zbar_format_lookup(fmt);
    if(vidfmt && vidfmt->group == ZBAR_FMT_JPEG) {
        zbar_image_t *img;
        /* prepare for decoding */
        if(!vdo->jpeg)
            vdo->jpeg = _zbar_jpeg_decomp_create();
        if(vdo->jpeg_img)
            zbar_image_destroy(vdo->jpeg_img);

        /* create intermediate image for decoder to use*/
        img = vdo->jpeg_img = zbar_image_create();
        img->format = fourcc('Y','8','0','0');
        zbar_image_set_size(img, vdo->width, vdo->height);
        img->datalen = vdo->width * vdo->height;
    }
#endif
    vdo->initialized = 1;
    return(0);
}

在对视频进行初始化时,ZBar首先判断了该视频是否被初始化过,如果被初始化过,则判定一种异常情况,需要对该视频格式进行重新修正。这一点在我们写代码的过程中容易被忽略,值得学习。

接下来则是对视频进行预处理(创建解码器所需要的视频/图像格式),用于下一步的解码操作。

这里ZBar设定了编码格式为Y800。

对于单色的图像,Y800格式仅包含一个8位Y平面。它的FourCC编码中包含重复的为Y8和灰色对应的编码。这部分将在解码部分的代码中进行分析。

视频流的图像迭代

zbar_image_t *zbar_video_next_image (zbar_video_t *vdo)
{
    unsigned frame;
    zbar_image_t *img;

    if(video_lock(vdo))
        return(NULL);
    if(!vdo->active) {
        video_unlock(vdo);
        return(NULL);
    }

    frame = vdo->frame++;
    img = vdo->dq(vdo);
    if(img) {
        img->seq = frame;
        if(vdo->num_images < 2) {
            /* return a *copy* of the video image and immediately recycle
             * the driver's buffer to avoid deadlocking the resources
             */
            zbar_image_t *tmp = img;
            video_lock(vdo);
            img = vdo->shadow_image;
            vdo->shadow_image = (img) ? img->next : NULL;
            video_unlock(vdo);
                
            if(!img) {
                img = zbar_image_create();
                assert(img);
                img->refcnt = 0;
                img->src = vdo;
                /* recycle the shadow images */

                img->format = vdo->format;
                zbar_image_set_size(img, vdo->width, vdo->height);
                img->datalen = vdo->datalen;
                img->data = malloc(vdo->datalen);
            }
            img->cleanup = _zbar_video_recycle_shadow;
            img->seq = frame;
            memcpy((void*)img->data, tmp->data, img->datalen);
            _zbar_video_recycle_image(tmp);
        }
        else
            img->cleanup = _zbar_video_recycle_image;
        _zbar_image_refcnt(img, 1);
    }
    return(img);
}

这部分功能实现的核心在于迭代过程中需要返回视频图像的副本并立即回收驱动程序的缓冲区,以避免资源死锁。

这部分代码与操作系统中的信号量机制类似,对于每一张图像的循环,必须保证缓冲区的有序使用,这就需要对缓冲区进行上锁和解锁。

对于缓冲区的资源,图像之间需要进行递交,在递交过程中,需要借助到tmp暂时图像,在递交结束之后,需要对tmp进行回收,否则会产生相当大的资源浪费。


三、总结

本次博客对ZBar扫描器的Video模块的几个关键函数进行了分析。如有不足,敬请指正。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 15:39:53  更:2021-11-15 15:40:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:54:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码