/*************************************************  
  File name     : av_ringbuffer.h
  Module        : audio and video library functions
  Author        : amir  
  Created on    : 2022-01-27    
  Description   :
      主要是每个"caller"可以带自己的index向一份共享的av_buffer取自己预期的内容(有的caller快,有的慢), 如果没有数了就sem等待.
    
  Modify History:   
  1. Date:        Author:         Modification:
*************************************************/
#ifndef AV_RINGBUFFER
#define AV_RINGBUFFER
#include <stdint.h>
#include "ringbuffer.h"
#include "hal_interface_video.h"

enum{
  NEXT_IFRAME = 0, /* next key frame */
  LATEST_IFRAME    /* latest key frame */
};

typedef struct{
    ring_item *item; /* 根据item->seq来向前搜索, 这样不同的任务带自己的cookie可以取自己期望的下一帧 */
    uint8_t    dir;   //dir = NEXT_IFRAME, 向前搜索大于seq和pts的帧(最旧的一个I帧); dir = LATEST_IFRAME, 相反如直播需要实时(最新的一个I帧)
}av_cookie;

#define avframe_create(_frame_type, _size) ({\
	_frame_type *frame = (_frame_type *)malloc(sizeof(_frame_type) + _size);\
	if (frame){frame->size = _size;}\
	frame;\
}) 

typedef ringbuffer av_ringbuffer;

//init an av_ringbuffer
//return av_ringbuffer if success
/*
 * 通常audio 和 video 各申请一个av_ringbuffer
 */
#define av_ringbuffer_init(_size) ringbuffer_init(_size)

//deinit an av_ringbuffer
#define av_ringbuffer_deinit(_av) ringbuffer_deinit(_av)

//put a pointer of a frame, special for a frame from lower callback and frame strcut and data in seperate memory
//eg: frame.size =  av.size;
//	  av_ringbuffer_put(&frame, av.data)
/*
 * put vframe_s 或 aframe_s
 */
#define av_ringbuffer_put(_av, _pframe, _data) ({\
    ring_item *item = ringbuffer_put2(_av, (int8_t*)_pframe, sizeof(*_pframe), _data, _pframe->size);\
    item;})

/*
 * 根据cookie来移动自己的next_seq, audio and video frame 共用
**/
#define  av_ringbuffer_get_frame(rb, cookie, to, max_len,timeout) ({\
	uint32_t ret    = NG;\
    ring_item *item = NULL;\
	hmutex_lock(&rb->mutex);\
	if (!list_empty(&rb->list[RING_LIST_USING])){\
        if (cookie->item && /* 正常情况直接获取下一个帧, 否则进入后面的异常情况去搜索 */\
            (cookie->item->seq > rb->last_drop_seq || rb->last_drop_seq == UINT32_MAX) && cookie->item->seq < ringbuffer_get_lastseq_locked(rb)) {/* 直接获取下一个*/\
                 item = list_entry(cookie->item->link.next, ring_item, link);\
        }else if (cookie->dir == LATEST_IFRAME){/*直播查找最新的关键帧*/\
            struct list_head *pos, *n;\
            list_for_each_prev_safe(pos, n, &rb->list[RING_LIST_USING])\
            {\
                ring_item* tmp_item = list_entry(pos, ring_item, link);\
                av_ringbuffer_get_frame_locked(rb, tmp_item, to, sizeof(*to));\
                if((to->type & FRAME_TYPE_IFRAME) && (!cookie->item || tmp_item->seq >= cookie->item->seq)){\
                    item = tmp_item;\
                    break;\
                }\
			}\
		}else{/* dir = NEXT_IFRAME 查找比cookie->item新的关键帧, 如oss, 又不想丢太多 */\
		    struct list_head *pos, *n;\
            list_for_each_safe(pos, n, &rb->list[RING_LIST_USING])\
            {\
                ring_item* tmp_item = list_entry(pos, ring_item, link);\
                av_ringbuffer_get_frame_locked(rb, tmp_item, to, sizeof(*to));\
                if(((to->type & FRAME_TYPE_IFRAME)||(to->type & FRAME_TYPE_AUDIO)) && (!cookie->item || tmp_item->seq >= cookie->item->seq)/*&& (to->ts >= cookie->startPTS)*/) {/*头一个, 或者 下一个*/\
                    item = tmp_item;\
                    break;\
                }\
            }\
		}\
	}\
	hmutex_unlock(&rb->mutex);\
    if ( item ) {\
        cookie->item = item;\
        av_ringbuffer_get_frame_locked(rb, item, to, sizeof(*to) + item->size);\
        /*hlogd("av_ringbuffer_get_video_frame %u %X %u\n", cookie->item->seq, rb->last_drop_seq, ringbuffer_get_lastseq_locked(rb));*/\
        ret = OK;\
    } else {\
        ret = NG;\
        if (timeout) {\
            if ( hsem_wait_for(&rb->sem, timeout) ){/* 没有数据,调用端再重新call */\
                /* 正常, 收到了sem, 有新的av帧送入 */\
            } else {\
                /* 超时没收到 sem */\
            }\
        }\
    }\
    ret;})


#define av_ringbuffer_dump ringbuffer_dump
void av_ringbuffer_test();

#endif
