初始化固定数量的结点装入空闲队列,当相机回调产生数据后,从空闲队列头部取出一个结点将产生的每一帧图像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);
}
}
复制代码