初始化固定數量的結點裝入空閒隊列,當相機回調產生數據後,從空閒隊列頭部取出一個結點將產生的每一幀圖像buffer裝入,而後入隊到工做隊列的尾部,處理buffer的線程從工做隊列的頭部取出一個結點中的Buffer進行處理,處理完成後會將裝有次buffer的結點中data置空並從新放入空閒隊列的頭部以供下次使用。node
解析ios
typedef struct XDXCustomQueueNode {
void *data;
size_t size; // data size
long index;
struct XDXCustomQueueNode *next;
} XDXCustomQueueNode;
複製代碼
結點中使用void *類型的data存放咱們須要的sampleBuffer,使用index記錄當前裝入結點的sampleBuffer的索引,以便咱們在取出結點時比較是不是按照順序取出,結點中還裝着同類型下一個結點的元素。git
typedef struct XDXCustomQueue {
int size;
XDXCustomQueueType type;
XDXCustomQueueNode *front;
XDXCustomQueueNode *rear;
} XDXCustomQueue;
複製代碼
隊列中即爲咱們裝載的結點數量,由於咱們採用的是預先分配固定內存,因此工做隊列與空閒隊列的和始終不變(由於結點中的元素不在工做隊列就在空閒隊列)github
class XDXCustomQueueProcess {
private:
pthread_mutex_t free_queue_mutex;
pthread_mutex_t work_queue_mutex;
public:
XDXCustomQueue *m_free_queue;
XDXCustomQueue *m_work_queue;
XDXCustomQueueProcess();
~XDXCustomQueueProcess();
// Queue Operation
void InitQueue(XDXCustomQueue *queue,
XDXCustomQueueType type);
void EnQueue(XDXCustomQueue *queue,
XDXCustomQueueNode *node);
XDXCustomQueueNode *DeQueue(XDXCustomQueue *queue);
void ClearXDXCustomQueue(XDXCustomQueue *queue);
void FreeNode(XDXCustomQueueNode* node);
void ResetFreeQueue(XDXCustomQueue *workQueue, XDXCustomQueue *FreeQueue);
};
複製代碼
由於涉及到異步操做,因此須要對結點的操做加鎖,使用時須要先初始化隊列,而後定義了入隊,出隊,清除隊列中元素,釋放結點,重置空閒隊列等操做。算法
const int XDXCustomQueueSize = 3;
XDXCustomQueueProcess::XDXCustomQueueProcess() {
m_free_queue = (XDXCustomQueue *)malloc(sizeof(struct XDXCustomQueue));
m_work_queue = (XDXCustomQueue *)malloc(sizeof(struct XDXCustomQueue));
InitQueue(m_free_queue, XDXCustomFreeQueue);
InitQueue(m_work_queue, XDXCustomWorkQueue);
for (int i = 0; i < XDXCustomQueueSize; i++) {
XDXCustomQueueNode *node = (XDXCustomQueueNode *)malloc(sizeof(struct XDXCustomQueueNode));
node->data = NULL;
node->size = 0;
node->index= 0;
this->EnQueue(m_free_queue, node);
}
pthread_mutex_init(&free_queue_mutex, NULL);
pthread_mutex_init(&work_queue_mutex, NULL);
NSLog(@"XDXCustomQueueProcess Init finish !");
}
複製代碼
假設空閒隊列結點總數爲3.首先爲工做隊列與空閒隊列分配內存,其次對其分別進行初始化操做,具體過程可參考Demo,而後根據結點總數來爲每一個結點初始化分配內存,並將分配好內存的結點入隊到空閒隊列中。緩存
void XDXCustomQueueProcess::EnQueue(XDXCustomQueue *queue, XDXCustomQueueNode *node) {
if (queue == NULL) {
NSLog(@"XDXCustomQueueProcess Enqueue : current queue is NULL");
return;
}
if (node==NULL) {
NSLog(@"XDXCustomQueueProcess Enqueue : current node is NULL");
return;
}
node->next = NULL;
if (XDXCustomFreeQueue == queue->type) {
pthread_mutex_lock(&free_queue_mutex);
if (queue->front == NULL) {
queue->front = node;
queue->rear = node;
}else {
/*
// tail in,head out
freeQueue->rear->next = node;
freeQueue->rear = node;
*/
// head in,head out
node->next = queue->front;
queue->front = node;
}
queue->size += 1;
NSLog(@"XDXCustomQueueProcess Enqueue : free queue size=%d",queue->size);
pthread_mutex_unlock(&free_queue_mutex);
}
if (XDXCustomWorkQueue == queue->type) {
pthread_mutex_lock(&work_queue_mutex);
//TODO
static long nodeIndex = 0;
node->index=(++nodeIndex);
if (queue->front == NULL) {
queue->front = node;
queue->rear = node;
}else {
queue->rear->next = node;
queue->rear = node;
}
queue->size += 1;
NSLog(@"XDXCustomQueueProcess Enqueue : work queue size=%d",queue->size);
pthread_mutex_unlock(&work_queue_mutex);
}
}
複製代碼
如上所述,入隊操做若是是空閒隊列,則使用頭進的方式,即始終讓入隊的結點在隊列的頭部,具體代碼實現即讓當前結點的next指向空閒隊列的頭結點,而後將當前結點變爲空閒隊列的頭結點;若是入隊操做是工做隊列,則使用尾進的方式,並對結點的index賦值,以便咱們在取出結點時能夠打印Index是否連續,若是連續則說明入隊時始終保持順序入隊。bash
這裏使用了簡單的數據結構中的知識,若有不懂可上網進行簡單查閱數據結構
XDXCustomQueueNode* XDXCustomQueueProcess::DeQueue(XDXCustomQueue *queue) {
if (queue == NULL) {
NSLog(@"XDXCustomQueueProcess DeQueue : current queue is NULL");
return NULL;
}
const char *type = queue->type == XDXCustomWorkQueue ? "work queue" : "free queue";
pthread_mutex_t *queue_mutex = ((queue->type == XDXCustomWorkQueue) ? &work_queue_mutex : &free_queue_mutex);
XDXCustomQueueNode *element = NULL;
pthread_mutex_lock(queue_mutex);
element = queue->front;
if(element == NULL) {
pthread_mutex_unlock(queue_mutex);
NSLog(@"XDXCustomQueueProcess DeQueue : The node is NULL");
return NULL;
}
queue->front = queue->front->next;
queue->size -= 1;
pthread_mutex_unlock(queue_mutex);
NSLog(@"XDXCustomQueueProcess DeQueue : %s size=%d",type,queue->size);
return element;
}
複製代碼
出隊操做不管空閒隊列仍是工做隊列都是從頭出,即取出當前隊列頭結點中的數據。異步
void XDXCustomQueueProcess::ResetFreeQueue(XDXCustomQueue *workQueue, XDXCustomQueue *freeQueue) {
if (workQueue == NULL) {
NSLog(@"XDXCustomQueueProcess ResetFreeQueue : The WorkQueue is NULL");
return;
}
if (freeQueue == NULL) {
NSLog(@"XDXCustomQueueProcess ResetFreeQueue : The FreeQueue is NULL");
return;
}
int workQueueSize = workQueue->size;
if (workQueueSize > 0) {
for (int i = 0; i < workQueueSize; i++) {
XDXCustomQueueNode *node = DeQueue(workQueue);
CFRelease(node->data);
node->data = NULL;
EnQueue(freeQueue, node);
}
}
NSLog(@"XDXCustomQueueProcess ResetFreeQueue : The work queue size is %d, free queue size is %d",workQueue->size, freeQueue->size);
}
複製代碼
當咱們將執行一些中斷操做,例如從本View跳轉到其餘View,或進入後臺等操做,咱們須要將工做隊列中的結點均置空而後從新放回空閒隊列,這樣能夠保證咱們最初申請的結點還均有效可用,保證結點不會丟失。post
常規流程,Demo中有實現,在此不復述
設置相機代理後,在 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
方法中將samplebuffer裝入空閒隊列
- (void)addBufferToWorkQueueWithSampleBuffer:(CMSampleBufferRef)sampleBuffer {
XDXCustomQueueNode *node = _captureBufferQueue->DeQueue(_captureBufferQueue->m_free_queue);
if (node == NULL) {
NSLog(@"XDXCustomQueueProcess addBufferToWorkQueueWithSampleBuffer : Data in , the node is NULL !");
return;
}
CFRetain(sampleBuffer);
node->data = sampleBuffer;
_captureBufferQueue->EnQueue(_captureBufferQueue->m_work_queue, node);
NSLog(@"XDXCustomQueueProcess addBufferToWorkQueueWithSampleBuffer : Data in , work size = %d, free size = %d !",_captureBufferQueue->m_work_queue->size, _captureBufferQueue->m_free_queue->size);
}
複製代碼
注意:由於相機回調中捕捉的sampleBuffer是有生命週期的因此須要手動CFRetain一下使咱們隊列中的結點持有它。
使用pthread建立一條線程,每隔10ms取一次數據,咱們能夠在此對取到的數據進行咱們想要的操做,操做完成後再將清空釋放sampleBuffer再將其裝入空閒隊列供咱們循環使用。
- (void)handleCacheThread {
while (true) {
// 從隊列取出在相機回調中放入隊列的線程
XDXCustomQueueNode *node = _captureBufferQueue->DeQueue(_captureBufferQueue->m_work_queue);
if (node == NULL) {
NSLog(@"Crop handleCropThread : Data node is NULL");
usleep(10*1000);
continue;
}
CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)node->data;
// 打印結點的index,若是連續則說明在相機回調中放入的samplebuffer是連續的
NSLog(@"Test index : %ld",node->index);
/* 可在此處理從隊列中拿到的Buffer,用完後記得釋放內存並將結點從新放回空閒隊列
* ........
*/
CFRelease(sampleBuffer);
node->data = NULL;
_captureBufferQueue->EnQueue(_captureBufferQueue->m_free_queue, node);
}
}
複製代碼