前面三篇文章分别介绍了视频捕获、h264视频压缩、帧缓冲显示的实现, 现在将他们结合起来
摄像头采集到的数据, 需要交给视频压缩线程、显示线程使用, 那么我采用的方法是使用队列及链表来实现:
摄像头采集到数据后, 分别放入两个处理线程队列中, 并将相关信息放入链表中
两个线程处理完成数据后, 调用回调函数, 从链表里找到对应的节点,然后释放前面申请的资源
/* 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}
后面的话
- Makefile写的比较傻, 后面可以改改
- 经常malloc和free不是一个好的选择, 还可以优化一下
- V4L2捕获视频可以使用select实现
- 线程中的死循环可以加入usleep让出CPU时间,降低CPU占用率
- 树莓派上跑640*480 30fps时, 温度达到72℃