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