Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————结合三个部分工作
前面三篇文章分别介绍了视频捕获、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℃