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

前面三篇文章分别介绍了视频捕获、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℃

转载于:https://www.cnblogs.com/rootming/p/10854063.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值