static pj_status_t pj_vpx_codec_decode(pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { vpx_private *vpx = (vpx_private*) codec->codec_data; vpx_image_t *img; vpx_codec_iter_t iter; int i, res; PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output, PJ_EINVAL); vpx->dec_frame_len = 0; /* TODO : packet parsing is absolutely incomplete here !!!! * We should manage extensions, partitions etc * */ for (i = 0; i < pkt_count; ++i) { pj_uint8_t *data; pj_uint8_t extended_bit, s_bit, partition_id; unsigned extension_len = 0; unsigned payload_size = packets[i].size; if(payload_size == 0) { continue; } data = packets[i].buf; extended_bit = (*data) & 0x80; s_bit = (*data) & 0x20; partition_id = (*data) & 0x1F; PJ_UNUSED_ARG(s_bit); PJ_UNUSED_ARG(partition_id); /* First octet is for */ /* |X|R|N|S|PartID | */ if(extended_bit) { pj_uint8_t i_bit, l_bit, t_bit, k_bit; (data)++; extension_len++; i_bit = (*data) & 0x80; l_bit = (*data) & 0x40; t_bit = (*data) & 0x20; k_bit = (*data) & 0x10; if(payload_size <= 1) { PJ_LOG(4, (THIS_FILE, "Error decoding VP8 extended attributes")); continue; } /* We have extension in octet 2 */ /* |I|L|T|K| RSV | */ if (i_bit) { data++; if(payload_size <= 2){ PJ_LOG(4, (THIS_FILE, "Error decoding VP8 extended picture ID attribute")); continue; } // I present check M flag for long picture ID if ((*data) & 0x80) { data++; } } if (l_bit) { data++; } if (t_bit || k_bit) { data++; } } data++; payload_size = packets[i].size - (data - (pj_uint8_t*)packets[i].buf); //PJ_LOG(4, (THIS_FILE, "Unpack RTP %d size %d, start %d", i, packets[i].size, s[0] & 0x10)); if((vpx->dec_frame_len + payload_size) < vpx->dec_buf_size) { pj_memcpy(vpx->dec_buf + vpx->dec_frame_len, data, payload_size); vpx->dec_frame_len += payload_size; } else { PJ_LOG(1, (THIS_FILE, "Buffer is too small")); } } if(vpx->dec_frame_len == 0){ PJ_LOG(1, (THIS_FILE, "No content for these packets")); return PJ_SUCCESS; } res = vpx_codec_decode(&vpx->decoder, vpx->dec_buf, vpx->dec_frame_len, NULL, VPX_DL_REALTIME); switch (res) { case VPX_CODEC_UNSUP_BITSTREAM: case VPX_CODEC_UNSUP_FEATURE: case VPX_CODEC_CORRUPT_FRAME: /* Fatal errors to the stream, request a keyframe to see if we can recover */ PJ_LOG(4, (THIS_FILE, "Fatal error decoding stream: (%d) %s", res, vpx_codec_err_to_string(res))); pjmedia_event event; pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, NULL, codec); pjmedia_event_publish(NULL, codec, &event, 0); return PJMEDIA_CODEC_EBADBITSTREAM; case VPX_CODEC_OK: break; default: PJ_LOG(4, (THIS_FILE, "Failed to decode packets: (%d) %s", res, vpx_codec_err_to_string(res))); return PJMEDIA_ERROR; } iter = NULL; for (;;) { pj_status_t status; img = vpx_codec_get_frame(&vpx->decoder, &iter); if (img == NULL) break; status = pj_vpx_codec_decode_whole(codec, img, &packets[0].timestamp, out_size, output); if (status != PJ_SUCCESS) { PJ_LOG(4, (THIS_FILE, "Failed to decode frame")); /* XXX stop processing and request keyframe? */ } } return PJ_SUCCESS; }
static pj_status_t pj_vpx_codec_decode(pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { vpx_private *vpx = (vpx_private*) codec->codec_data; pj_status_t status; pj_uint8_t *p; pj_uint8_t *s; vpx_image_t *img; vpx_codec_iter_t iter = NULL; int i, res; PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output, PJ_EINVAL); i = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; s = packets[0].buf; p = vpx->dec_buf; vpx->dec_frame_len = 0; /* TODO : packet parsing is absolutely incomplete here !!!! * We should manage extensions, partitions etc * */ for (i = 0; i < pkt_count; ++i) { unsigned extension_len = 0; unsigned payload_size = packets[i].size; if(payload_size <= 0){ continue; } s = packets[i].buf; /* First octet is for */ /* |X|R|N|S|PartID | */ if( s[0] & 0x80 ){ if(payload_size <= 1){ continue; } /* We have extension in octet 2 */ /* |I|L|T|K| RSV | */ if( s[1] & 0x80 ){ extension_len++; if(payload_size <= 2){ continue; } // I present check M flag for long picture ID if( s[2] & 0x80 ) extension_len++; } if( s[1] & 0x40 ) extension_len++; if( (s[1] & 0x20) | (s[1] & 0x10) ) extension_len++; } s += (extension_len + 1); payload_size -= (extension_len + 1); //PJ_LOG(4, (THIS_FILE, "Unpack RTP %d size %d, start %d", i, packets[i].size, s[0] & 0x10)); if((vpx->dec_frame_len + payload_size) < vpx->dec_buf_size) { pj_memcpy((p + vpx->dec_frame_len), s, payload_size); vpx->dec_frame_len += payload_size; } else { PJ_LOG(1, (THIS_FILE, "Buffer is too small")); } } if(vpx->dec_frame_len == 0){ PJ_LOG(1, (THIS_FILE, "No content for these packets")); return PJ_SUCCESS; } res = vpx_codec_decode(&vpx->decoder, vpx->dec_buf, vpx->dec_frame_len, 0, VPX_DL_REALTIME); if (res == VPX_CODEC_UNSUP_BITSTREAM){ PJ_LOG(2, (THIS_FILE, "More likely we are missing a keyframe, request it")); /* Broadcast missing keyframe event */ pjmedia_event event; pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, &packets[0].timestamp, codec); pjmedia_event_publish(NULL, codec, &event, 0); } else if (res != VPX_CODEC_OK) { PJ_LOG(1, (THIS_FILE, "Failed to decode packet : %s of size %d", vpx_codec_err_to_string(res), vpx->dec_frame_len)); /* TODO : better error code. Map with res? */ return PJ_SUCCESS; } /* No need to loop here for first implementation */ img = vpx_codec_get_frame(&vpx->decoder, &iter); if (img != NULL) { /* We have a frame available */ pj_vpx_codec_decode_whole(codec, img, &packets[0].timestamp, out_size, output); } return PJ_SUCCESS; }