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 
");
		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
");
		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
");
				break;
			} else {
				// printf("Debug: times not equ limit: %d times
", (*ppPre)->Times);
				(*ppPre)->Times++;
				break;
			}
		} else {
			ppPre = &(*ppPre)->pNext;
			// printf("Debug: ppPre: %p
", *ppPre);
			// printf("Debug: next
");
		}
	}

	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
");
		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
");
	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 
");
		return -1;
	}
	
	Ret = DisplayInit(CONFIG_DISPLAY_DEV);

	if(Ret) {
		printf("Diaplay open failed 
");
		return -1;
	}

	CameraCaptureCallbackSet(EnQueueCallback);
	CameraCaptureStart();
	DisplayStart();
	EncodeStart("test.h264");

	char KeyValue = getchar();
	printf("You press [%c] button, now stop capture
", 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