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 #include "viddec_parser_ops.h"
      6 #include "viddec_vc1_parse.h"
      7 #include "viddec_mp4_parse.h"
      8 #include "viddec_mpeg2_parse.h"
      9 #include "viddec_h264_parse.h"
     10 /*
     11   Overview of Parser manager:
     12   Parser manager is the glue between Kernel(main.c) and actual codecs. We abstract common functionality as much as we can
     13   in this module. The parser Manager context allocates memory for Parsers. At any point in time there is only one active stream.
     14   During open stream we setup all necessary initialisation for the codec we are handling. The parser manager context is
     15   stored on DDR when the current stream gets swapped out by the kernel. When the next stream comes in it has it's own
     16   version of parser manager.
     17   Parser manager is reponsible for providing information on when its a good time to swap a stream.
     18   High level algorithm of parser Manager once a stream is opened and active(RET's are returns to Kernel):
     19 
     20   1. create a list data structure to hold any incoming ES descriptors.
     21   2. Check to see if any of the ES buffers Desc in current list has data to be processed. If not request kernel(RET) for a buffer.
     22   3. If data is present parse until a scprefix+sc is found. If not goto step2.
     23   4. If startcode detected update list state to make ES data look like Linear buffer.
     24   5. Setup required state to provide getbits interface for codecs to access bit stream maximum 32bits at a time.
     25   6. Setup Current & Next workloads provided by Kernel.
     26   7. Call the codec to parse the data we collected between start codes.
     27   8. Query to see if we parsed frame worth of data.
     28   9. Do necessary TAG association and remove used buffers from List.
     29   10. Send information to kernel on whether workload is done or Not.(RET). When kernel reschedules start from step2.
     30 
     31   Kernel can swap current stream at RET points described above.
     32 
     33   Other additional things supported:
     34   - Generic start code detect function which is same for most of codecs.
     35   - Memory Management.
     36   - Flush of stream.
     37   - Emulation prevention.
     38   - Interface to emit necessary tags for codec specific types.
     39 */
     40 
     41 
     42 /* check to see if codec needs emulation prevention */
     43 #define EMUL_REQD(codec) ((codec == MFD_STREAM_FORMAT_VC1) || (codec_type == MFD_STREAM_FORMAT_H264) ? 1: 0)
     44 
     45 #ifdef RTL_SIMULATION
     46 extern void output_omar_wires( unsigned int value );
     47 #else
     48 #define output_omar_wires(x)
     49 #endif
     50 
     51 /* Place to store Function pointers for all supported interfaces for each codec */
     52 viddec_parser_ops_t parser_ops[MFD_STREAM_FORMAT_MAX];
     53 
     54 
     55 
     56 /* we need to define as external function so that for host mode we can use the same code without
     57    modifications by overloading dma function with a copy function
     58 */
     59 extern uint32_t cp_using_dma(uint32_t ddr_addr, uint32_t local_addr, uint32_t size, char to_ddr, char swap);
     60 
     61 void viddec_pm_init_ops()
     62 {
     63     viddec_vc1_get_ops(&parser_ops[MFD_STREAM_FORMAT_VC1]);
     64     parser_ops[MFD_STREAM_FORMAT_VC1].parse_sc = viddec_parse_sc;
     65     parser_ops[MFD_STREAM_FORMAT_VC1].gen_contrib_tags = viddec_pm_generic_generate_contribution_tags;
     66     parser_ops[MFD_STREAM_FORMAT_VC1].gen_assoc_tags = viddec_generic_add_association_tags;
     67 
     68     viddec_mpeg2_get_ops(&parser_ops[MFD_STREAM_FORMAT_MPEG]);
     69     parser_ops[MFD_STREAM_FORMAT_MPEG].parse_sc = viddec_parse_sc;
     70     parser_ops[MFD_STREAM_FORMAT_MPEG].gen_contrib_tags = viddec_pm_generic_generate_contribution_tags;
     71     parser_ops[MFD_STREAM_FORMAT_MPEG].gen_assoc_tags = viddec_mpeg2_add_association_tags;
     72 
     73     viddec_h264_get_ops(&parser_ops[MFD_STREAM_FORMAT_H264]);
     74     parser_ops[MFD_STREAM_FORMAT_H264].parse_sc = viddec_parse_sc;
     75     parser_ops[MFD_STREAM_FORMAT_H264].gen_contrib_tags = viddec_pm_lateframe_generate_contribution_tags;
     76     parser_ops[MFD_STREAM_FORMAT_H264].gen_assoc_tags = viddec_h264_add_association_tags;
     77 
     78     viddec_mp4_get_ops(&parser_ops[MFD_STREAM_FORMAT_MPEG42]);
     79     parser_ops[MFD_STREAM_FORMAT_MPEG42].gen_contrib_tags = viddec_pm_generic_generate_contribution_tags;
     80     parser_ops[MFD_STREAM_FORMAT_MPEG42].gen_assoc_tags = viddec_generic_add_association_tags;
     81 }
     82 
     83 /*
     84   Returns size of persistent DDR memory required for the codec. If the required memory is less than max allocated
     85   scratch memory in FW we always give the max scratch size.
     86 */
     87 uint32_t viddec_pm_get_parser_sizes(uint32_t codec_type, viddec_parser_memory_sizes_t *size)
     88 {
     89     parser_ops[codec_type].get_cxt_size(size);
     90     if(size->context_size > MAX_CODEC_CXT_SIZE)
     91     {
     92         DEB("ERROR: size(%d) of context for codec=%d is greater than max=%d\n",size->context_size,codec_type,MAX_CODEC_CXT_SIZE);
     93     }
     94     size->context_size = sizeof(viddec_pm_cxt_t);
     95     return 1;
     96 }
     97 
     98 /*
     99   Initialize the scratch memory allocated to the stream based on clean. if clean is true initialize to
    100   start state, if not then preserve stream information.
    101 */
    102 void viddec_pm_init_context(viddec_pm_cxt_t *cxt, uint32_t codec_type, uint32_t *persist_mem, uint32_t clean)
    103 {
    104     int i;
    105 
    106     for(i=0; i<MAX_IBUFS_PER_SC; i++)
    107     {
    108         cxt->pending_tags.pending_tags[i] = INVALID_ENTRY;
    109     }
    110     cxt->frame_start_found = false;
    111     cxt->found_fm_st_in_current_au = false;
    112     cxt->late_frame_detect = (MFD_STREAM_FORMAT_H264 == codec_type) ? true:false;
    113     cxt->pending_tags.first_buf_aligned = cxt->pending_tags.using_next = cxt->pending_tags.frame_done =false;
    114     cxt->next_workload_error_eos = VIDDEC_FW_WORKLOAD_ERR_FLUSHED_FRAME | VIDDEC_FW_WORKLOAD_ERR_NOTDECODABLE;
    115     viddec_pm_utils_list_init(&(cxt->list));
    116     cxt->cur_buf.list_index = -1;
    117     cxt->parse_cubby.phase=0;
    118     parser_ops[codec_type].init((void *)&(cxt->codec_data[0]), persist_mem, !clean);
    119     if(clean)
    120     {
    121         cxt->pending_inband_tags = 0;
    122     }
    123     else
    124     {
    125         /* TODO: Enable this once codecs support this function */
    126         //parser_ops[codec_type].flush_preserve((void *)&(cxt->codec_data[0]), persist_mem);
    127     }
    128 
    129 }
    130 
    131 void viddec_pm_update_time(viddec_pm_cxt_t *cxt, uint32_t time)
    132 {
    133     viddec_emit_time(&(cxt->emitter), time);
    134 }
    135 
    136 /* add an esbuffer to list */
    137 static inline uint32_t viddec_pm_add_es_buf_to_list(viddec_pm_cxt_t *cxt, viddec_input_buffer_t *es_buf)
    138 {
    139     uint32_t val , ret = PM_OVERFLOW;
    140 
    141     val = viddec_pm_utils_list_addbuf(&(cxt->list), es_buf);
    142     if(val == 1) ret = PM_SUCCESS;
    143     return ret;
    144 }
    145 
    146 static inline uint32_t viddec_pm_check_inband_messages(viddec_pm_sc_cur_buf_t *cur_buf, uint32_t *type)
    147 {
    148     uint32_t ret=false;
    149     if(cur_buf->cur_es->flags != 0)
    150     {
    151         /* update offset to point to next position for loading data */
    152         cur_buf->cur_offset +=(cur_buf->cur_size);
    153         cur_buf->cur_size = 0;
    154         switch(cur_buf->cur_es->flags)
    155         {
    156             case VIDDEC_STREAM_EOS:
    157             {
    158                 *type = PM_EOS;
    159             }
    160             break;
    161             case VIDDEC_STREAM_DISCONTINUITY:
    162             {
    163                 *type = PM_DISCONTINUITY;
    164             }
    165             default:
    166                 break;
    167         }
    168         ret =true;
    169     }
    170     return ret;
    171 }
    172 
    173 /* creates an ibuf from the current position in list. Fills sc_parse_cubby_cxt */
    174 uint32_t viddec_pm_create_ibuf(viddec_pm_cxt_t *cxt)
    175 {
    176     uint32_t ret = PM_NO_DATA;
    177 #ifndef VBP
    178     viddec_sc_parse_cubby_cxt_t *cubby = &(cxt->parse_cubby);
    179 #endif
    180     viddec_pm_sc_cur_buf_t *cur_buf = &(cxt->cur_buf);
    181     viddec_pm_utils_list_t *list = &(cxt->list);
    182 
    183     /* Step1: check if list is Empty, If yes return No data */
    184     if(list->num_items > 0)
    185     {
    186         /* Step 2: Check to see If current index into list is empty & we have data in list,
    187            if so increment index and initialise it*/
    188         if(cur_buf->list_index == -1)
    189         {
    190             if(viddec_pm_utils_list_getbyte_position(list,
    191                                                      list->first_scprfx_length+1,
    192                                                      (uint32_t *)&(cur_buf->list_index),
    193                                                      &(cur_buf->cur_offset)) != 1)
    194             {/* This return's offset and index from where we have to start for sc detect */
    195                 cur_buf->cur_size = 0;
    196                 cur_buf->cur_es = &(list->sc_ibuf[cur_buf->list_index]);
    197             }
    198             else
    199             {
    200                 return PM_NO_DATA;
    201             }
    202         }
    203 
    204         /* Step3: If we are done with current buffer then try to go to next item in list */
    205         if((cur_buf->cur_offset + cur_buf->cur_size) >= cur_buf->cur_es->len)
    206         {
    207             /* Need to handle In band messages before going to next buffer */
    208             //if(viddec_pm_check_inband_messages(cur_buf))
    209             if(viddec_pm_check_inband_messages(cur_buf, &ret))
    210             {
    211                 return ret;
    212             }
    213             /* If no items in list after the current buffer return no data */
    214             if((uint32_t)(cur_buf->list_index + 1) >=  list->num_items)
    215             {
    216                 return PM_NO_DATA;
    217             }
    218             cur_buf->list_index++;
    219             cur_buf->cur_es = &(list->sc_ibuf[cur_buf->list_index]);
    220             cur_buf->cur_offset = cur_buf->cur_size = 0;
    221         }
    222         /* Step4: Fill the cubby with data to send to parser sc code function */
    223         {
    224             int32_t data_left;
    225             /* data left is the leftout size in current ES buffer */
    226             data_left = cur_buf->cur_es->len -  (cur_buf->cur_offset + cur_buf->cur_size);
    227 
    228             /* update offset to point to next position for loading data */
    229             cur_buf->cur_offset +=(cur_buf->cur_size);
    230 
    231 #ifndef VBP
    232             /* Load maximum of array size */
    233             if(data_left >= SC_DETECT_BUF_SIZE)
    234             {
    235                 data_left = SC_DETECT_BUF_SIZE;
    236             }
    237             /* can be zero if we have zero sized buffers in our list.EX:NEW segment */
    238             if(data_left > 0)
    239             {/* do a copy using Linear Dma */
    240                 uint32_t size , ddr_addr = 0, ddr_mask=0;
    241                 /* get ddr adress of current offset in ES buffer */
    242 #ifdef HOST_ONLY
    243                 ddr_addr = cur_buf->cur_offset + (uint32_t)cur_buf->cur_es->buf;
    244 #else
    245                 ddr_addr = cur_buf->cur_offset + cur_buf->cur_es->phys;
    246 #endif
    247                 ddr_mask = (ddr_addr & 3);
    248                 ddr_addr = ddr_addr & ~3;
    249                 /* return from this function can be more bytes based on input buf alignment.
    250                    The adress for local memory we are sending is on DWORD boundary so it should be safe.
    251                 */
    252 
    253                 size = cp_using_dma(ddr_addr, (uint32_t)&(cxt->scbuf[0]), data_left+ddr_mask, 0,1);//false, true);
    254                 cubby->size = data_left;
    255 
    256                 /* point to actual memory location which has the data(skip aligment bytes) */
    257                 cubby->buf = &(cxt->scbuf[ddr_mask]);
    258                 cur_buf->cur_size = data_left;
    259                 ret = PM_SUCCESS;
    260             }
    261             else
    262             {
    263                 /* If we completely consumed this buffer or this is a zero sized buffer we want to check inband messages */
    264                 //if(viddec_pm_check_inband_messages(cur_buf))
    265                 if(viddec_pm_check_inband_messages(cur_buf, &ret))
    266                 {
    267                     return ret;
    268                 }
    269             }
    270 #else
    271       ret = PM_SUCCESS;
    272 #endif
    273         }
    274     }
    275 
    276     return ret;
    277 }
    278 
    279 /*
    280   Read data from esbuffer list and parse for start codes or EOS. If we consumed all the data we return no data left.
    281 */
    282 static inline uint32_t viddec_pm_parse_for_sccode(viddec_pm_cxt_t *cxt, viddec_parser_ops_t *func)
    283 {
    284     uint32_t ret = PM_NO_DATA;
    285     uint32_t sc_boundary_found = 0;
    286 
    287     while(!sc_boundary_found)
    288     {
    289         /* Create an buffer from list to parse */
    290         ret = viddec_pm_create_ibuf(cxt);
    291         switch(ret)
    292         {
    293             case PM_NO_DATA:
    294             {/* No data in esbuffer list for parsing sc */
    295                 sc_boundary_found = 1;
    296             }
    297             break;
    298             case PM_EOS:
    299             case PM_DISCONTINUITY:
    300             {
    301                 sc_boundary_found = 1;
    302                 cxt->list.end_offset = cxt->cur_buf.cur_offset+1;
    303                 cxt->parse_cubby.phase = 0;
    304                 /* we didn't find a start code so second start code length would be 0 */
    305                 cxt->sc_prefix_info.second_scprfx_length = 0;
    306                 //cxt->sc_prefix_info.next_sc = VIDDEC_PARSE_EOS;
    307                 if(ret == PM_EOS)
    308                 {
    309                     cxt->sc_prefix_info.next_sc = VIDDEC_PARSE_EOS;
    310                 }
    311                 if(ret == PM_DISCONTINUITY)
    312                 {
    313                     cxt->sc_prefix_info.next_sc = VIDDEC_PARSE_DISCONTINUITY;
    314                 }
    315             }
    316             break;
    317             case PM_SUCCESS:
    318             default:
    319             {
    320                 /* parse the created buffer for sc */
    321                 ret = func->parse_sc((void *)&(cxt->parse_cubby), (void *)&(cxt->codec_data[0]), &(cxt->sc_prefix_info));
    322                 if(ret == 1)
    323                 {
    324                     cxt->list.end_offset = cxt->parse_cubby.sc_end_pos + cxt->cur_buf.cur_offset;
    325                     cxt->parse_cubby.phase = 0;
    326                     cxt->list.total_bytes+=cxt->parse_cubby.sc_end_pos;
    327                     ret = PM_SC_FOUND;
    328                     sc_boundary_found = 1;
    329                     break;
    330                 }
    331                 else
    332                 {
    333                     cxt->list.total_bytes+=cxt->cur_buf.cur_size;
    334                 }
    335             }
    336             break;
    337         }
    338     }
    339 
    340     return ret;
    341 }
    342 
    343 /*
    344   Once we are ready to flush the current workload, we update current workload on DDR with our internal information
    345   that was not written before like num of items in workload, errors in stream etc...
    346 */
    347 void viddec_pm_finalize_workload(viddec_pm_cxt_t *cxt, uint32_t codec_type, uint32_t codec_errors)
    348 {
    349     viddec_emit_set_codec(&(cxt->emitter), codec_type);
    350     viddec_emit_set_codec_errors(&(cxt->emitter), codec_errors);
    351     viddec_emit_flush_current_wkld(&(cxt->emitter));
    352     output_omar_wires( 0x5 );
    353     output_omar_wires( 0x1 );
    354 }
    355 
    356 /*
    357   After parsing between start codes we cleanup our list so that it has only buffers that are not consumed yet.
    358 */
    359 uint32_t viddec_pm_finalize_list(viddec_pm_cxt_t *cxt)
    360 {
    361     uint32_t ret=1;
    362 
    363     viddec_pm_utils_list_remove_used_entries(&(cxt->list), cxt->sc_prefix_info.second_scprfx_length);
    364     cxt->cur_buf.list_index = -1;
    365     cxt->list.first_scprfx_length = cxt->sc_prefix_info.second_scprfx_length;
    366     return ret;
    367 }
    368 
    369 /* Case to handle if we encounter list overflow without seeing second start code */
    370 void viddec_pm_handle_buffer_overflow(viddec_pm_cxt_t *cxt, uint32_t codec_type, viddec_input_buffer_t *es_buf)
    371 {
    372     uint32_t indx=0;
    373     while(indx< (uint32_t)cxt->list.num_items)
    374     {/* Dump tags for all entries in list to prevent buffer leak */
    375         viddec_emit_contr_tag(&(cxt->emitter), &(cxt->list.sc_ibuf[indx]), false, true);
    376         viddec_emit_assoc_tag(&(cxt->emitter), cxt->list.sc_ibuf[indx].id, true);
    377         indx++;
    378     }
    379     /* Dump tags for the new buffer that was received */
    380     viddec_emit_contr_tag(&(cxt->emitter), es_buf, 0, true);
    381     viddec_emit_assoc_tag(&(cxt->emitter), es_buf->id, true);
    382     /* Set errors on both current and next as both can be invalid */
    383     viddec_emit_set_workload_error(&(cxt->emitter),
    384                                    (VIDDEC_FW_WORKLOAD_ERR_BUFFERS_OVERFLOW | VIDDEC_FW_WORKLOAD_ERR_NOTDECODABLE),
    385                                    true);
    386     viddec_emit_set_workload_error(&(cxt->emitter),
    387                                    (VIDDEC_FW_WORKLOAD_ERR_BUFFERS_OVERFLOW | VIDDEC_FW_WORKLOAD_ERR_NOTDECODABLE),
    388                                    false);
    389     /* cleanup the pending tags */
    390     viddec_pm_generate_missed_association_tags(cxt, true);
    391     viddec_pm_finalize_workload(cxt, codec_type, 0);
    392     WRITE_SVEN(SVEN_MODULE_EVENT_GV_FW_FATAL_BUFFER_OVERLFOW, (int)es_buf->phys, (int)es_buf->len, 0, 0, 0, 0);
    393 }
    394 
    395 static inline void viddec_pm_handle_post_inband_messages(viddec_pm_cxt_t *cxt, uint32_t m_type)
    396 {
    397     if((m_type & ~(0xFF))== PM_INBAND_MESSAGES)
    398     {
    399         /* If EOS decide set error on next workload too */
    400         viddec_emit_set_workload_error(&(cxt->emitter), cxt->next_workload_error_eos, true);
    401         if(m_type == PM_EOS)
    402         {
    403             viddec_emit_set_inband_tag(&(cxt->emitter), VIDDEC_WORKLOAD_IBUF_EOS, true);
    404         }
    405         if(m_type == PM_DISCONTINUITY)
    406         {
    407             cxt->pending_inband_tags = PM_DISCONTINUITY;
    408         }
    409     }
    410 }
    411 
    412 static inline uint32_t viddec_pm_handle_new_es_buffer(viddec_pm_cxt_t *cxt, uint32_t codec_type, viddec_input_buffer_t *es_buf)
    413 {
    414     uint32_t state = PM_SUCCESS;
    415     if(es_buf != NULL)
    416     {
    417         state = viddec_pm_add_es_buf_to_list(cxt, es_buf);
    418         if(state == PM_OVERFLOW)
    419         {
    420             viddec_pm_handle_buffer_overflow(cxt, codec_type, es_buf);
    421         }
    422     }
    423     return state;
    424 }
    425 
    426 static inline void viddec_pm_handle_pre_inband_messages(viddec_pm_cxt_t *cxt)
    427 {
    428     if(cxt->pending_inband_tags == PM_DISCONTINUITY)
    429     {
    430         viddec_emit_set_inband_tag(&(cxt->emitter), VIDDEC_WORKLOAD_IBUF_DISCONTINUITY, false);
    431         cxt->pending_inband_tags = 0;
    432     }
    433 }
    434 
    435 /*
    436   Main function of parser manager.
    437   It searches until start codes are found int he list if not through return type indicates kernel to provide more buffers.
    438   If a start code is found it calls the codec to parse the syntax data it accumulated so far.
    439   If codec says a frame is not done then continues to find the next start code.
    440   If codec says frame is done it does tag association and indicates kernel a frame is done.
    441 */
    442 uint32_t viddec_pm_parse_es_buffer(viddec_pm_cxt_t *cxt, uint32_t codec_type, viddec_input_buffer_t *es_buf)
    443 {
    444     uint32_t state = PM_SUCCESS;
    445 
    446     /* Step1: Append Es buffer to list */
    447     viddec_pm_handle_pre_inband_messages(cxt);
    448     state = viddec_pm_handle_new_es_buffer(cxt, codec_type, es_buf);
    449     if(state == PM_SUCCESS)
    450     {
    451         uint32_t scdetect_ret;
    452         output_omar_wires( 0x3 );
    453         /* Step2: Phase1 of parsing, parse until a sc is found */
    454         scdetect_ret = viddec_pm_parse_for_sccode(cxt,&parser_ops[codec_type]);
    455         switch(scdetect_ret)
    456         {
    457             case PM_NO_DATA:
    458             {
    459                 /* Step3: If we consumed all the data indicate we need more buffers */
    460                 state = PM_NO_DATA;
    461                 break;
    462             }
    463             case PM_EOS:
    464             case PM_DISCONTINUITY:
    465             case PM_SC_FOUND:
    466             {
    467                 uint32_t codec_errors=0;
    468                 /* Create necessary state information to make the ES buffers look like linear data */
    469                 viddec_pm_utils_list_updatebytepos(&(cxt->list), cxt->sc_prefix_info.second_scprfx_length);
    470                 if(cxt->sc_prefix_info.first_sc_detect != 1)
    471                 {
    472                     /* Step4: If we saw two start codes init state and call codec to parse */
    473                     uint32_t codec_ret;
    474                     /* Initialise the state to provide get bits for codecs */
    475                     viddec_pm_utils_bstream_init(&(cxt->getbits), &(cxt->list), EMUL_REQD(codec_type));
    476                     output_omar_wires( 0x1 );
    477                     /* call the codec to do synatax parsing */
    478                     parser_ops[codec_type].parse_syntax((void *)cxt, (void *)&(cxt->codec_data[0]));
    479                     /* Check and see if frame start was detected. If we did update frame start in current au */
    480                     if(parser_ops[codec_type].is_frame_start((void *)&(cxt->codec_data[0])) == true)
    481                     {
    482                         cxt->frame_start_found += 1;
    483                         cxt->found_fm_st_in_current_au = true;
    484                     }
    485                     /* Query to see if we reached end of current frame */
    486                     codec_ret = parser_ops[codec_type].is_wkld_done((void *)cxt,
    487                                                                     (void *)&(cxt->codec_data[0]),
    488                                                                     (uint32_t)(cxt->sc_prefix_info.next_sc),
    489                                                                     &codec_errors);
    490 
    491                     state = (codec_ret == VIDDEC_PARSE_FRMDONE) ? PM_WKLD_DONE : PM_SUCCESS;
    492                     /* generate contribution and association tags */
    493                     cxt->pending_tags.frame_done = (codec_ret == VIDDEC_PARSE_FRMDONE);
    494                     parser_ops[codec_type].gen_assoc_tags(cxt);
    495                     parser_ops[codec_type].gen_contrib_tags(cxt, (state != PM_WKLD_DONE));
    496                 }
    497                 else
    498                 {
    499                     /* Step4: If this is the first start code in this stream, clean up and return */
    500                     if(cxt->list.total_bytes != 0)
    501                     {
    502                         viddec_pm_generic_generate_contribution_tags(cxt, true);
    503                         viddec_generic_add_association_tags(cxt);
    504                     }
    505                     else
    506                     {
    507                         if(cxt->list.num_items >= 1)
    508                         {
    509                             uint32_t indx=0;
    510                             while((indx< (uint32_t)cxt->list.num_items) && (cxt->list.sc_ibuf[indx].len == 0))
    511                             {/* Dump all zero sized buffers until we see a buffer with valid data */
    512                                 viddec_emit_contr_tag(&(cxt->emitter), &(cxt->list.sc_ibuf[indx]), false, false);
    513                                 viddec_emit_assoc_tag(&(cxt->emitter), cxt->list.sc_ibuf[indx].id, false);
    514                                 indx++;
    515                             }
    516                         }
    517                     }
    518                     if((scdetect_ret & ~(0xFF))!= PM_INBAND_MESSAGES)
    519                     {
    520                         state = PM_SUCCESS;//state = PM_FIRST_SC_FOUND;
    521                         cxt->sc_prefix_info.first_sc_detect = 0;
    522                     }
    523                     else
    524                     {
    525                         state = PM_WKLD_DONE;
    526                     }
    527                 }
    528 
    529                 viddec_pm_handle_post_inband_messages(cxt, scdetect_ret);
    530 
    531                 /* Step 5: If current frame is done, finalise the workload state with necessary information */
    532                 if(state == PM_WKLD_DONE)
    533                 {
    534                     DEB("\nFRAME ... DONE\n");
    535                     /* we decrement frame start. This can be 0 in cases like sending junk data with EOS */
    536                     cxt->frame_start_found -= (cxt->frame_start_found)? 1: 0;
    537                     if((scdetect_ret & ~(0xFF))== PM_INBAND_MESSAGES)
    538                     {/* If EOS dump pending tags and set state */
    539                         viddec_pm_generate_missed_association_tags(cxt, false);
    540                         state = scdetect_ret;
    541                     }
    542                     /* Write back stored state of workloads to memory to prepare for psuhing to output queue */
    543                     viddec_pm_finalize_workload(cxt, codec_type, codec_errors);
    544                 }
    545                 /* Step 6: Reset the list to prepare for next iteration */
    546                 viddec_pm_finalize_list(cxt);
    547                 break;
    548             }
    549             default:
    550                 break;
    551         }
    552     }//if(state == PM_SUCCESS)
    553     return state;
    554 } // viddec_pm_parse_es_buffer
    555