Home | History | Annotate | Download | only in parser
      1 #include "viddec_pm.h"
      2 #include "viddec_fw_debug.h"
      3 #include "viddec_fw_common_defs.h"
      4 #include "viddec_pm_tags.h"
      5 /*
      6   Overview of tag association:
      7 
      8   Contribution flags:
      9   The current list has all the buffers which contribute to this particular workload. So we walkthrough the
     10   list and throw buf done for all the buffers which were consumed. This can be deduced from total bytes we
     11   in list which represents the bytes that were used for this acces unit.
     12   For buffers which were partially used and this can only be the last buffer we throw continued tag. The
     13   Parser manager tells us when to throw a continued tag. This will only happen when parser Manager detects
     14   that we reached end of current frame.
     15 
     16   Association Tags:
     17   These are the tags that FW generates which indicates how to associate metadata with Frames.
     18   The policy to determine which tag belongs to which frame is based on sc prefix position. If ES buffer starts with
     19   or has a sc prefix its associated to next decodable frame(based on first slice or header depending on codec).
     20   We use three state variables to determine where the frame starts and ends.
     21     frame_start_found: Indicates we saw the beggining of frame in current list of ES buffers(which represent current acces unit).
     22                        This is decremented on workload done since it normally means we detected frame end.
     23     found_fm_st_in_current_au:Indicates we saw the first slice in current access unit. Its mainly used to decide whether the first buffer
     24                               belongs to current frame or next frame. Its reset after its use.
     25     Frame Done: Indicates we detected end of frame pointed by current workload.
     26 
     27    Basic algo:
     28    If we find frame start and if first buffer doesn't start with SC prefix Every consumed buffer belongs to Next frame. If first buffer
     29    starts with SC prefix on that buffer belongs to Current frame.
     30    If we haven't found frame start every buffer belongs to current frame.
     31 
     32    TODO: Check for return codes from emitter
     33 */
     34 
     35 
     36 /*
     37   This function generates contribution tags current workload by walking through list of consumed buffers.
     38   If frame is done(ignore_partial is false) we generate continue tags for the last item in list(if its not completely consumed).
     39   This is used for all codecs except H264.
     40  */
     41 uint32_t viddec_pm_generic_generate_contribution_tags(void *parent, uint32_t ignore_partial)
     42 {
     43     uint32_t ret = PM_SUCCESS;
     44     viddec_pm_cxt_t *cxt = (viddec_pm_cxt_t *)parent;
     45     viddec_pm_utils_list_t *list = &(cxt->list);
     46 
     47     if(list->num_items != 0)
     48     {
     49         if(!cxt->late_frame_detect)
     50         {
     51             uint32_t num_items = 0;
     52             while((num_items < list->num_items) && (list->data[num_items].edpos <= (uint32_t)list->total_bytes))
     53             {/* Walkthrough Consumed buffers and dump the tags */
     54                 viddec_emit_contr_tag(&(cxt->emitter), &(list->sc_ibuf[num_items]), false, false);
     55                 num_items++;
     56             }
     57             /* Dump incomplete tags if required */
     58             if(!ignore_partial)
     59             {/* check to see if last item is not consumed and dump continued flag */
     60                 if((num_items < list->num_items)
     61                    && (list->data[num_items].edpos >= (uint32_t)list->total_bytes))
     62                 {
     63                     viddec_emit_contr_tag(&(cxt->emitter), &(list->sc_ibuf[num_items]), true, false);
     64                 }
     65             }
     66         }
     67         else
     68         {
     69             /* Only happens for dangling fields in MP2 Field pictures, in which case we find out the current frame was done in
     70                last access unit, which is similar to H264 */
     71             ret = viddec_pm_lateframe_generate_contribution_tags(parent, ignore_partial);
     72             cxt->late_frame_detect = false;
     73         }
     74     }
     75     return ret;
     76 }
     77 
     78 /*
     79   For H264 when a frame is done it really means current frame was done in last access unit. The current access unit represnted
     80   by list belongs to next frame. ignore_partial is false for frame done.
     81   When frame is not done we dump all consumed buffers into next workload else they go to current workload.
     82   If frame is done we throw a continued flag for first buffer in current workload if it was used in last access unit.
     83  */
     84 uint32_t viddec_pm_lateframe_generate_contribution_tags(void *parent, uint32_t ignore_partial)
     85 {
     86     uint32_t ret = PM_SUCCESS;
     87     viddec_pm_cxt_t *cxt = (viddec_pm_cxt_t *)parent;
     88     viddec_pm_utils_list_t *list = &(cxt->list);
     89 
     90     if(list->num_items != 0)
     91     {
     92         uint32_t num_items = 0;
     93         /* If start offset is not 0 then it was partially used in last access unit. !ignore_partial means frame done*/
     94         if((list->start_offset!= 0) && !ignore_partial)
     95         {/* Emit continue in current if necessary. */
     96             viddec_emit_contr_tag(&(cxt->emitter), &(list->sc_ibuf[num_items]), true, false);
     97         }
     98 
     99         while((num_items < list->num_items) && (list->data[num_items].edpos <= (uint32_t)list->total_bytes))
    100         {  /* Walkthrough Consumed buffers and dump the tags to current or Next*/
    101             viddec_emit_contr_tag(&(cxt->emitter), &(list->sc_ibuf[num_items]), false, !ignore_partial);
    102             num_items++;
    103         }
    104     }
    105     return ret;
    106 }
    107 
    108 /*
    109   This function dumps tags from temporary array into a workload(we indicate either current or next from using_next).
    110 */
    111 uint32_t viddec_pm_generate_missed_association_tags(viddec_pm_cxt_t *cxt, uint32_t using_next)
    112 {
    113     uint32_t i=0, ret = PM_SUCCESS;
    114 
    115     while((i < MAX_IBUFS_PER_SC) && (cxt->pending_tags.pending_tags[i] != INVALID_ENTRY))
    116     {
    117         viddec_emit_assoc_tag(&(cxt->emitter), cxt->pending_tags.pending_tags[i], using_next);
    118         cxt->pending_tags.pending_tags[i] = INVALID_ENTRY;
    119         i++;
    120     }
    121     return ret;
    122 }
    123 
    124 /* This function adds current list of es buffer to pending list. ignore_first when set tells us to ignore the first
    125    buffer in list.
    126 */
    127 void viddec_pm_add_tags_to_pendinglist(viddec_pm_cxt_t *cxt, uint32_t ignore_first)
    128 {
    129     viddec_pm_utils_list_t *list = &(cxt->list);
    130     vidded_pm_pending_tags_t *pend = &(cxt->pending_tags);
    131     uint32_t index=0, t_index=0;
    132 
    133     if(!ignore_first && (list->start_offset == 0))
    134     {/* If start offset is 0 we are saying that first buffer in list starts with start code */
    135         pend->first_buf_aligned = true;
    136     }
    137     else
    138     {/* We are ignoring first item in list since we already threw a tag for this buffer */
    139         index++;
    140         pend->first_buf_aligned  = false;
    141     }
    142 
    143     while( (index < list->num_items) && (list->data[index].edpos <= (uint32_t)list->total_bytes))
    144     {/* walk through consumed buffers and buffer id's in pending list */
    145         pend->pending_tags[t_index] = list->sc_ibuf[index].id;
    146         index++;t_index++;
    147     }
    148     if( (index < list->num_items) && (list->data[index].stpos < (uint32_t)list->total_bytes))
    149     {/* If last item is partially consumed still add it to pending tags since tag association is based on start of ES buffer */
    150         pend->pending_tags[t_index] = list->sc_ibuf[index].id;
    151     }
    152 }
    153 
    154 /* Helper function to emit a association tag from pending list and resetting the value to invalid entry */
    155 static inline void viddec_pm_emit_pending_tag_item(viddec_emitter *emit, vidded_pm_pending_tags_t *pend, uint32_t index, uint32_t using_next)
    156 {
    157     viddec_emit_assoc_tag(emit, pend->pending_tags[index], using_next);
    158     pend->pending_tags[index] = INVALID_ENTRY;
    159 }
    160 
    161 /*
    162   Tag association for mpeg2:
    163   start frame is detected in pict header extension, but pict header represents start of frame.
    164   To handle this we always store current AU list in temporary pending list. At the start of function
    165   we look to see if a frame start was found, if we did we start dumping items from pending list based
    166   on byte position of sc in first buffer of pending list. At the end we copy current list items to
    167   pending list.
    168   Limitation With Dangling fields: If we have AF1 AF2 BF1 CF1 CF2 as the sequence of fields
    169   Tag assocaiation will be fine for A & B, However the first buffer tag on C will fall into B
    170   We donot want to fix this issue right now as it means doubling size of pending list which
    171   increases memory usage. Normally dangling fields are thrown away so worst case we will miss
    172   one original PTS, So its OK not to fix it right now.
    173  */
    174 uint32_t viddec_mpeg2_add_association_tags(void *parent)
    175 {
    176     uint32_t ret = PM_SUCCESS;
    177     viddec_pm_cxt_t *cxt = (viddec_pm_cxt_t *)parent;
    178     vidded_pm_pending_tags_t *pend = &(cxt->pending_tags);
    179     uint32_t first_slice = false, index = 0;
    180     /* check to see if we found a frame start in current access unit */
    181     first_slice = cxt->frame_start_found && cxt->found_fm_st_in_current_au;
    182     cxt->found_fm_st_in_current_au = false;
    183     /* If we found frame start and first item in pending tags is start with start code
    184        then it needs to go to current frame. */
    185     if(first_slice && pend->first_buf_aligned && (pend->pending_tags[index] != INVALID_ENTRY))
    186     {
    187         viddec_pm_emit_pending_tag_item(&(cxt->emitter), pend, index, false);
    188         index++;
    189     }
    190     /* rest of list goes to current if frame start is not found else next frame */
    191     while((index < MAX_IBUFS_PER_SC) && (pend->pending_tags[index] != INVALID_ENTRY))
    192     {
    193         viddec_pm_emit_pending_tag_item(&(cxt->emitter), pend, index, cxt->frame_start_found);
    194         index++;
    195     }
    196     /* Copy items to temporary List */
    197     viddec_pm_add_tags_to_pendinglist(cxt, false);
    198     return ret;
    199 }
    200 
    201 /*
    202   Tag association for h264:
    203   In this case when we get frame done it means current frame was done in last access unit. The data in current list belongs
    204   to next frame. To handle this we always dump the buffered tags from last list and throw them in current/next frame based on pend state.
    205   If the first item in current list is on sc boundary, it has to go into next so we always throw that tag in next.
    206   For rest of items we store them in pending tags array and store inforamtion on where these stored tags should go into for
    207   next run. Thi is detemined by start frame. we do this because at this state our next should be current and "next next" should
    208   be next.
    209  */
    210 uint32_t viddec_h264_add_association_tags(void *parent)
    211 {
    212     uint32_t ret = PM_SUCCESS;
    213     viddec_pm_cxt_t *cxt = (viddec_pm_cxt_t *)parent;
    214     viddec_pm_utils_list_t *list = &(cxt->list);
    215     vidded_pm_pending_tags_t *pend = &(cxt->pending_tags);
    216     uint32_t first_slice = false, index = 0;
    217 
    218     /* Throw tags for items from pending list based on stored state  from last run */
    219     viddec_pm_generate_missed_association_tags(cxt, pend->using_next);
    220     first_slice = cxt->frame_start_found && cxt->found_fm_st_in_current_au;
    221     cxt->found_fm_st_in_current_au = false;
    222     /* If we saw frame start and first buffer is aligned to start code throw it into next */
    223     if(first_slice && (list->start_offset == 0))
    224     {
    225         viddec_emit_assoc_tag(&(cxt->emitter), list->sc_ibuf[index].id, cxt->frame_start_found && cxt->pending_tags.frame_done);
    226         index++;
    227     }
    228     /* add tags to pending list */
    229     viddec_pm_add_tags_to_pendinglist(cxt, (index != 0));
    230     /* We want to figure out where these buffers should go into. There are three possible cases
    231        current: If no frame start found these should go into next.
    232        next: If one frame start is found and frame is not done then it should go to next.
    233              if a frame is done then pm will push current out and next time we come here previous next is current.
    234        next next: If two frame starts are found then we want it to be next next workload, which is what next will be
    235                   when we get called next time.
    236     */
    237     pend->using_next = (!cxt->pending_tags.frame_done && (cxt->frame_start_found == 1)) || (cxt->frame_start_found > 1);
    238     return ret;
    239 }
    240 
    241 /*
    242   Tag association for vc1:
    243   Frame header represents start of new frame. If we saw a frame start in current access unit and the buffer starts
    244   with start code it needs to go to current frame.  Rest of items go to next if frame start found else current frame.
    245  */
    246 uint32_t viddec_generic_add_association_tags(void *parent)
    247 {
    248     uint32_t ret = PM_SUCCESS;
    249     viddec_pm_cxt_t *cxt = (viddec_pm_cxt_t *)parent;
    250     viddec_pm_utils_list_t *list = &(cxt->list);
    251     uint32_t not_first_slice = false, index = 0;
    252 
    253     /* We check to see if this access unit is not the first one with frame start. This evaluates to true in that case */
    254     not_first_slice = cxt->frame_start_found && !cxt->found_fm_st_in_current_au;
    255     cxt->found_fm_st_in_current_au = false;
    256     if(list->start_offset == 0)
    257     {/* If start offset is 0, we have start code at beggining of buffer. If frame start was detected in this
    258         access unit we put the tag in current else it goes to next */
    259         viddec_emit_assoc_tag(&(cxt->emitter), list->sc_ibuf[index].id, not_first_slice);
    260     }
    261     /* Skip first item always, for start_offset=0 its already been handled above*/
    262     index++;
    263     while( (index < list->num_items) && (list->data[index].edpos <= (uint32_t)list->total_bytes))
    264     {/* Walkthrough Consumed buffers and dump the tags to current or next*/
    265         viddec_emit_assoc_tag(&(cxt->emitter), list->sc_ibuf[index].id, cxt->frame_start_found);
    266         index++;
    267     }
    268     if( (index < list->num_items) && (list->data[index].stpos < (uint32_t)list->total_bytes))
    269     {/* Dump last item if it was partially consumed */
    270         viddec_emit_assoc_tag(&(cxt->emitter), list->sc_ibuf[index].id, cxt->frame_start_found);
    271     }
    272     return ret;
    273 }
    274 
    275 /*
    276   This function throws tags for buffers which were not used yet during flush.
    277  */
    278 void viddec_pm_generate_tags_for_unused_buffers_to_flush(viddec_pm_cxt_t *cxt)
    279 {
    280     viddec_pm_utils_list_t *list;
    281     uint32_t index=0;
    282 
    283     list = &(cxt->list);
    284     /* Generate association tags from temporary pending array */
    285     viddec_pm_generate_missed_association_tags(cxt, false);
    286     if(list->num_items > 0)
    287     {
    288         /* Throw contribution flag for first item as done */
    289         viddec_emit_contr_tag(&(cxt->emitter), &(list->sc_ibuf[index]), false, false);
    290         if(cxt->list.start_offset == 0)
    291         {/* Throw association for first item if it was not done already */
    292             viddec_emit_assoc_tag(&(cxt->emitter), list->sc_ibuf[index].id, false);
    293         }
    294         index++;
    295         while(index < list->num_items)
    296         {/* Walk through list and throw contribution and association flags */
    297             viddec_emit_contr_tag(&(cxt->emitter), &(list->sc_ibuf[index]), false, false);
    298             viddec_emit_assoc_tag(&(cxt->emitter), list->sc_ibuf[index].id, false);
    299             index++;
    300         }
    301     }
    302     /* Not required to re init list structure as flush takes care of it */
    303 }
    304 
    305