前面三篇文章分别介绍了视频捕获、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;
1if(pTempNode == NULL) { 2 // printf("Debug:LinkList root empty, inited \n"); 3 sQueuePrivateData.pLinkListRoot = malloc(sizeof(sLinkListNode)); 4 sQueuePrivateData.pLinkListRoot->pNext = NULL; 5 sQueuePrivateData.pLinkListRoot->pData = pData; 6 sQueuePrivateData.pLinkListRoot->Times = 0; 7 return 0; 8} 9 10while(pTempNode->pNext != NULL) { 11 pTempNode = pTempNode->pNext; 12} 13 14pTempNode->pNext = malloc(sizeof(sLinkListNode)); 15pTempNode->pNext->pNext = NULL; 16pTempNode->pNext->pData = pData; 17pTempNode->pNext->Times = 0; 18 19return 0;}
int LinkedListDel(void* pData) { sLinkListNode* pTempNode = NULL; sLinkListNode** ppPre = &sQueuePrivateData.pLinkListRoot;
1if(*ppPre == NULL) { 2 // printf("Error: LinkList empty\n"); 3 return -1; 4} 5 6while(*ppPre != NULL) { 7 if((*ppPre)->pData == pData) { 8 if((*ppPre)->Times == CONFIG_QUEUE_LIMIT - 1) { 9 pTempNode = (*ppPre)->pNext; 10 free(pData); 11 free(*ppPre); 12 *ppPre = pTempNode; 13 // printf("Debug: free buffer\n"); 14 break; 15 } else { 16 // printf("Debug: times not equ limit: %d times\n", (*ppPre)->Times); 17 (*ppPre)->Times++; 18 break; 19 } 20 } else { 21 ppPre = &(*ppPre)->pNext; 22 // printf("Debug: ppPre: %p\n", *ppPre); 23 // printf("Debug: next\n"); 24 } 25} 26 27return 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;
1pthread_mutex_init(&sQueuePrivateData.QueueMutex, NULL); 2pthread_mutex_init(&sQueuePrivateData.LinkListMutex, NULL); 3return 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;
1Index = (TailIndex + 1) % CONFIG_QUEUE_SIZE; 2if(Index == HeadIndex) { 3 // printf("Warn: queue full\n"); 4 pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); 5 return -1; 6} else { 7 memcpy(&pQueuePrivateData->Data[TailIndex], pData, sizeof(sQueueData)); 8 pQueuePrivateData->TailIndex = Index; 9 pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); 10 return 0; 11}}
int QueuePutData(sQueueData* pData) { int Ret = -1; pthread_mutex_lock(&sQueuePrivateData.QueueMutex);
1pthread_mutex_lock(&sQueuePrivateData.LinkListMutex); 2LinkedListAdd(pData->pData); 3pthread_mutex_unlock(&sQueuePrivateData.LinkListMutex); 4 5for(int i = 0; i < sQueuePrivateData.Cnt; i++) { 6 Ret = QueuePushBack(sQueuePrivateData.QueueList[i], pData); 7 if(Ret) { 8 QueueCallback(pData); 9 } 10} 11 12pthread_mutex_unlock(&sQueuePrivateData.QueueMutex); 13return 0;}
int QueuePopData(sQueue* pQueuePrivateData, sQueueData* pData) { int HeadIndex, TailIndex;
1pthread_mutex_lock(&pQueuePrivateData->QueueMutex); 2HeadIndex = pQueuePrivateData->HeadIndex; 3TailIndex = pQueuePrivateData->TailIndex; 4if(HeadIndex != TailIndex) { 5 memcpy(pData, &pQueuePrivateData->Data[HeadIndex], sizeof(sQueueData)); 6 pQueuePrivateData->HeadIndex = (HeadIndex + 1) % CONFIG_QUEUE_SIZE; 7 pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); 8 return 0; 9} else { 10 pthread_mutex_unlock(&pQueuePrivateData->QueueMutex); 11 return -1; 12}}
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
1## Hardware 21. Raspberry Pi 3 32. USB camera 4 5## Target 61. capture frame size: 640*480 72. display fps: >= 30fps 83. memory limit: <20M 9 10## Addtion 111. 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;
1signal(SIGINT, SignalHandle); 2 3Ret = CameraOpen(CONFIG_CAPTURE_DEVICE); 4if(Ret) { 5 printf("Camera open failed \n"); 6 return -1; 7} 8 9Ret = DisplayInit(CONFIG_DISPLAY_DEV); 10 11if(Ret) { 12 printf("Diaplay open failed \n"); 13 return -1; 14} 15 16CameraCaptureCallbackSet(EnQueueCallback); 17CameraCaptureStart(); 18DisplayStart(); 19EncodeStart("test.h264"); 20 21char KeyValue = getchar(); 22printf("You press [%c] button, now stop capture\n", KeyValue); 23SignalHandle(0); 24return 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℃