• 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℃
  • 相关阅读:
    UICollectionView 应用
    关于UIWebView不能响应touchesBegan等四个方法的解决案例【可以检测 单击双击】
    IOS6 中新特性介绍
    KVO 使用
    IOS 学习资料汇总(^_^)
    [DEVDIV翻译] Core Animation中文翻译_第一章_什么是核心动画
    StoryBoard学习..(很详细.)
    Intent跳转到系统应用中的拨号界面、联系人界面、短信界面及其他
    sqlite语句主页
    Android的快速开发框架 afinal
  • 原文地址:https://www.cnblogs.com/rootming/p/10854063.html
Copyright © 2020-2023  润新知