Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————结合三个部分工作

Stella981
• 阅读 883

前面三篇文章分别介绍了视频捕获、h264视频压缩、帧缓冲显示的实现, 现在将他们结合起来

摄像头采集到的数据, 需要交给视频压缩线程、显示线程使用, 那么我采用的方法是使用队列及链表来实现:

  1. 摄像头采集到数据后, 分别放入两个处理线程队列中, 并将相关信息放入链表中

  2. 两个线程处理完成数据后, 调用回调函数, 从链表里找到对应的节点,然后释放前面申请的资源

    /* queue.h */ #ifndef QUEUE_H #define QUEUE_H

    #include <stdint.h> #include <pthread.h>

    typedef struct QueueData { void* pData; uint32_t Length; } sQueueData;

    typedef struct { sQueueData Data[CONFIG_QUEUE_SIZE]; int HeadIndex; int TailIndex; pthread_mutex_t QueueMutex; } sQueue;

    int QueueInit(sQueue* pQueuePrivateData); int QueuePutData(sQueueData* pData); // int QueuePushBack(sQueue* pQueuePrivateData, sQueueData* pData); int QueuePopData(sQueue* pQueuePrivateData, sQueueData* pData); int QueueCallback(sQueueData* pQueueData);

    #endif

    /* queue.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "config.h" #include "queue.h"

    typedef struct LinkListNode { struct LinkListNode* pNext; uint8_t Times; void* pData; } sLinkListNode;

    static struct { int Cnt; sQueue* QueueList[CONFIG_QUEUE_LIMIT]; pthread_mutex_t QueueMutex; pthread_mutex_t LinkListMutex; sLinkListNode* pLinkListRoot; } sQueuePrivateData;

    int LinkedListAdd(void* pData) { sLinkListNode* pTempNode = sQueuePrivateData.pLinkListRoot; if(pTempNode == NULL) { // printf("Debug:LinkList root empty, inited \n"); sQueuePrivateData.pLinkListRoot = malloc(sizeof(sLinkListNode)); sQueuePrivateData.pLinkListRoot->pNext = NULL; sQueuePrivateData.pLinkListRoot->pData = pData; sQueuePrivateData.pLinkListRoot->Times = 0; return 0; } while(pTempNode->pNext != NULL) { pTempNode = pTempNode->pNext; } pTempNode->pNext = malloc(sizeof(sLinkListNode)); pTempNode->pNext->pNext = NULL; pTempNode->pNext->pData = pData; pTempNode->pNext->Times = 0; return 0; }

    int LinkedListDel(void* pData) { sLinkListNode* pTempNode = NULL; sLinkListNode** ppPre = &sQueuePrivateData.pLinkListRoot; if(*ppPre == NULL) { // printf("Error: LinkList empty\n"); return -1; } while(*ppPre != NULL) { if((*ppPre)->pData == pData) { if((*ppPre)->Times == CONFIG_QUEUE_LIMIT - 1) { pTempNode = (*ppPre)->pNext; free(pData); free(*ppPre); *ppPre = pTempNode; // printf("Debug: free buffer\n"); break; } else { // printf("Debug: times not equ limit: %d times\n", (*ppPre)->Times); (*ppPre)->Times++; break; } } else { ppPre = &(*ppPre)->pNext; // printf("Debug: ppPre: %p\n", *ppPre); // printf("Debug: next\n"); } } return 0; }

    int BaseQueueInit(void) { sQueuePrivateData.Cnt = 0; for(int i = 0; i < CONFIG_QUEUE_LIMIT; i++) { sQueuePrivateData.QueueList[i] = NULL; } sLinkListNode* pTempRoot = sQueuePrivateData.pLinkListRoot; sLinkListNode* pTemp = NULL; while(pTempRoot != NULL) { pTemp = pTempRoot->pNext; free(pTempRoot); pTempRoot = pTemp; } sQueuePrivateData.pLinkListRoot = NULL; pthread_mutex_init(&sQueuePrivateData.QueueMutex, NULL); pthread_mutex_init(&sQueuePrivateData.LinkListMutex, NULL); return 0; }

    int QueueInit(sQueue* pQueuePrivateData) { if(sQueuePrivateData.Cnt > CONFIG_QUEUE_LIMIT) { printf("Queue register count over limit"); return -1; } pthread_mutex_init(&pQueuePrivateData->QueueMutex, NULL); pQueuePrivateData->HeadIndex = 0; pQueuePrivateData->TailIndex = 0; pthread_mutex_lock(&sQueuePrivateData.QueueMutex); sQueuePrivateData.QueueList[sQueuePrivateData.Cnt] = pQueuePrivateData; sQueuePrivateData.Cnt++; pthread_mutex_unlock(&sQueuePrivateData.QueueMutex); return 0; }

    static int QueuePushBack(sQueue* pQueuePrivateData, sQueueData* pData) { int HeadIndex, TailIndex, Index; pthread_mutex_lock(&pQueuePrivateData->QueueMutex); HeadIndex = pQueuePrivateData->HeadIndex; TailIndex = pQueuePrivateData->TailIndex; Index = (TailIndex + 1) % CONFIG_QUEUE_SIZE; if(Index == HeadIndex) { // printf("Warn: queue full\n"); pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); return -1; } else { memcpy(&pQueuePrivateData->Data[TailIndex], pData, sizeof(sQueueData)); pQueuePrivateData->TailIndex = Index; pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); return 0; } }

    int QueuePutData(sQueueData* pData) { int Ret = -1; pthread_mutex_lock(&sQueuePrivateData.QueueMutex); pthread_mutex_lock(&sQueuePrivateData.LinkListMutex); LinkedListAdd(pData->pData); pthread_mutex_unlock(&sQueuePrivateData.LinkListMutex); for(int i = 0; i < sQueuePrivateData.Cnt; i++) { Ret = QueuePushBack(sQueuePrivateData.QueueList[i], pData); if(Ret) { QueueCallback(pData); } } pthread_mutex_unlock(&sQueuePrivateData.QueueMutex); return 0; }

    int QueuePopData(sQueue* pQueuePrivateData, sQueueData* pData) { int HeadIndex, TailIndex; pthread_mutex_lock(&pQueuePrivateData->QueueMutex); HeadIndex = pQueuePrivateData->HeadIndex; TailIndex = pQueuePrivateData->TailIndex; if(HeadIndex != TailIndex) { memcpy(pData, &pQueuePrivateData->Data[HeadIndex], sizeof(sQueueData)); pQueuePrivateData->HeadIndex = (HeadIndex + 1) % CONFIG_QUEUE_SIZE; pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); return 0; } else { pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); return -1; } }

    int QueueCallback(sQueueData* pQueueData) { pthread_mutex_lock(&sQueuePrivateData.LinkListMutex); LinkedListDel(pQueueData->pData); pthread_mutex_unlock(&sQueuePrivateData.LinkListMutex); return 0; }

    /* main.c / /*

    • author: rootming
    • date: 2019.4
    • version: v1.0

    */

    /* # Video capture ## Basic note 1. use V4L2 interface open camera & capture video 2. use framebuffer driver for preview 3. text overlay video 4. use h264 algorithm compresse frame

    ## Hardware
    1. Raspberry Pi 3
    2. USB camera
    
    ## Target
    1. capture frame size: 640*480
    2. display fps: >= 30fps
    3. memory limit: <20M
    
    ## Addtion
    1. Maybe can add log library
    

    */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <signal.h> #include "config.h" #include "camera.h" #include "encode.h" #include "display.h" #include "queue.h"

    /* 入队列回调 / void EnQueueCallback(uint8_t pData, uint32_t Width, uint32_t Height, uint32_t Length) { sQueueData QueueData; QueueData.pData = malloc(Length); if(!QueueData.pData) { perror("Malloc failed"); return; } QueueData.Length = Length; memcpy(QueueData.pData, pData, Length); QueuePutData(&QueueData); }

    void SignalHandle(int SignalNumber) { printf("Now clean resource\n"); CameraCaptureStop(); CameraClose(); DisplayStop(); EncodeStop(); }

    int main(int Argc, char* pArgv[]) { int Ret = -1;

    signal(SIGINT, SignalHandle);
    
    Ret = CameraOpen(CONFIG_CAPTURE_DEVICE);
    if(Ret) {
        printf("Camera open failed \n");
        return -1;
    }
    
    Ret = DisplayInit(CONFIG_DISPLAY_DEV);
    
    if(Ret) {
        printf("Diaplay open failed \n");
        return -1;
    }
    
    CameraCaptureCallbackSet(EnQueueCallback);
    CameraCaptureStart();
    DisplayStart();
    EncodeStart("test.h264");
    
    char KeyValue = getchar();
    printf("You press [%c] button, now stop capture\n", KeyValue);
    SignalHandle(0);
    return 0;
    

    }

    Makefile

    TARGET = YTC100

    CFLAG = -Wall -Werror -std=c99

    #CFLAG = -Wall -std=c99 -O2 CFLAG = -Wall -O2 LIB = -pthread -lx264

    ${TARGET}: main.o camera.o encode.o queue.o display.o gcc main.o camera.o encode.o display.o queue.o ${LIB} ${CFLAG} -o ${TARGET}

    main.o: main.c config.h gcc main.c ${CFLAG} -c -o main.o

    camera.o: camera.c camera.h config.h gcc camera.c ${CFLAG} -c -o camera.o

    encode.o: encode.c encode.h config.h gcc encode.c ${CFLAG} -c -o encode.o

    queue.o: queue.c queue.h config.h gcc queue.c ${CFLAG} -c -o queue.o

    display.o: display.c display.h config.h gcc display.c ${CFLAG} -c -o display.o

    .PHONY: clean

    clean: rm -f *.o ${TARGET}

后面的话

  1. Makefile写的比较傻, 后面可以改改
  2. 经常malloc和free不是一个好的选择, 还可以优化一下
  3. V4L2捕获视频可以使用select实现
  4. 线程中的死循环可以加入usleep让出CPU时间,降低CPU占用率
  5. 树莓派上跑640*480 30fps时, 温度达到72℃
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
6个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
4、jstack查看线程栈信息
1、介绍利用jps、top、jstack命令找到进程中耗时最大的线程,以及线程状态等等,同时最后还可以显示出死锁的线程查找:FoundoneJavaleveldeadlock即可1、jps获得进程号!(https://oscimg.oschina.net/oscnet/da00a309fa6
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这