ImxVpuDecReturnCodes imx_vpu_jpeg_dec_frame_finished(ImxVpuJPEGDecoder *jpeg_decoder, ImxVpuFramebuffer *framebuffer) { assert(framebuffer != NULL); assert(jpeg_decoder != NULL); assert(jpeg_decoder->decoder != NULL); return imx_vpu_dec_mark_framebuffer_as_displayed(jpeg_decoder->decoder, framebuffer); }
Retval run(Context *ctx) { unsigned int output_code; { long size; void *buf; fseek(ctx->fin, 0, SEEK_END); size = ftell(ctx->fin); fseek(ctx->fin, 0, SEEK_SET); buf = malloc(size); fread(buf, 1, size, ctx->fin); ImxVpuEncodedFrame encoded_frame; encoded_frame.data.virtual_address = buf; encoded_frame.data_size = size; /* Codec data is out-of-band data that is typically stored in a separate space * in containers for each elementary stream; JPEG data does not need it */ encoded_frame.codec_data = NULL; encoded_frame.codec_data_size = 0; fprintf(stderr, "encoded input frame: size: %u byte\n", encoded_frame.data_size); /* Perform the actual decoding */ imx_vpu_dec_decode(ctx->vpudec, &encoded_frame, &output_code); free(buf); } /* Initial info is now available; this usually happens right after the * first frame is decoded, and this is the situation where one must register * output framebuffers, which the decoder then uses like a buffer pool for * picking buffers to decode frame into */ if (output_code & IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE) { unsigned int i; imx_vpu_dec_get_initial_info(ctx->vpudec, &(ctx->initial_info)); fprintf( stderr, "initial info: size: %ux%u pixel rate: %u/%u min num required framebuffers: %u interlacing: %d framebuffer alignment: %u color format: ", ctx->initial_info.frame_width, ctx->initial_info.frame_height, ctx->initial_info.frame_rate_numerator, ctx->initial_info.frame_rate_denominator, ctx->initial_info.min_num_required_framebuffers, ctx->initial_info.interlacing, ctx->initial_info.framebuffer_alignment ); switch (ctx->initial_info.color_format) { case IMX_VPU_COLOR_FORMAT_YUV420: fprintf(stderr, "YUV 4:2:0"); break; case IMX_VPU_COLOR_FORMAT_YUV422_HORIZONTAL: fprintf(stderr, "YUV 4:2:2 horizontal"); break; case IMX_VPU_COLOR_FORMAT_YUV422_VERTICAL: fprintf(stderr, "YUV 4:2:2 vertical"); break; case IMX_VPU_COLOR_FORMAT_YUV444: fprintf(stderr, "YUV 4:4:4"); break; case IMX_VPU_COLOR_FORMAT_YUV400: fprintf(stderr, "YUV 4:0:0 (8-bit grayscale)"); break; } fprintf(stderr, "\n"); ctx->num_framebuffers = ctx->initial_info.min_num_required_framebuffers; imx_vpu_calc_framebuffer_sizes(ctx->initial_info.color_format, ctx->initial_info.frame_width, ctx->initial_info.frame_height, ctx->initial_info.framebuffer_alignment, ctx->initial_info.interlacing, &(ctx->calculated_sizes)); fprintf( stderr, "calculated sizes: frame width&height: %dx%d Y stride: %u CbCr stride: %u Y size: %u CbCr size: %u MvCol size: %u total size: %u\n", ctx->calculated_sizes.aligned_frame_width, ctx->calculated_sizes.aligned_frame_height, ctx->calculated_sizes.y_stride, ctx->calculated_sizes.cbcr_stride, ctx->calculated_sizes.y_size, ctx->calculated_sizes.cbcr_size, ctx->calculated_sizes.mvcol_size, ctx->calculated_sizes.total_size ); ctx->framebuffers = malloc(sizeof(ImxVpuFramebuffer) * ctx->num_framebuffers); ctx->fb_dmabuffers = malloc(sizeof(ImxVpuDMABuffer*) * ctx->num_framebuffers); for (i = 0; i < ctx->num_framebuffers; ++i) { /* Allocate a DMA buffer for each framebuffer. It is possible to specify alternate allocators; * all that is required is that the allocator provides physically contiguous memory * (necessary for DMA transfers) and respecs the alignment value. */ ctx->fb_dmabuffers[i] = imx_vpu_dma_buffer_allocate(imx_vpu_dec_get_default_allocator(), ctx->calculated_sizes.total_size, ctx->initial_info.framebuffer_alignment, 0); imx_vpu_fill_framebuffer_params(&(ctx->framebuffers[i]), &(ctx->calculated_sizes), ctx->fb_dmabuffers[i], 0); } /* Actual registration is done here. From this moment on, the VPU knows which buffers to use for * storing decoded pictures into. This call must not be done again until decoding is shut down or * IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE is set again. */ imx_vpu_dec_register_framebuffers(ctx->vpudec, ctx->framebuffers, ctx->num_framebuffers); } /* Enable drain mode. All available input data is * inserted. Now We want one output picture. */ imx_vpu_dec_enable_drain_mode(ctx->vpudec, 1); /* Get the decoded picture out of the VPU */ { ImxVpuEncodedFrame encoded_frame; /* In drain mode there is no input data */ encoded_frame.data.virtual_address = NULL; encoded_frame.data_size = 0; encoded_frame.codec_data = NULL; encoded_frame.codec_data_size = 0; encoded_frame.context = NULL; imx_vpu_dec_decode(ctx->vpudec, &encoded_frame, &output_code); /* A decoded picture is available for further processing. Retrieve it, do something * with it, and once the picture is no longer needed, mark it as displayed. This * marks it internally as available for further decoding by the VPU. */ if (output_code & IMX_VPU_DEC_OUTPUT_CODE_DECODED_PICTURE_AVAILABLE) { ImxVpuPicture decoded_picture; uint8_t *mapped_virtual_address; size_t num_out_byte = ctx->calculated_sizes.y_size + ctx->calculated_sizes.cbcr_size * 2; /* This call retrieves information about the decoded picture, including * a pointer to the corresponding framebuffer structure. This must not be called more * than once after IMX_VPU_DEC_OUTPUT_CODE_DECODED_PICTURE_AVAILABLE was set. */ imx_vpu_dec_get_decoded_picture(ctx->vpudec, &decoded_picture); fprintf(stderr, "decoded output picture: writing %u byte", num_out_byte); /* Map buffer to the local address space, dump the decoded frame to file, * and unmap again. The decoded frame uses the I420 color format for all * bitstream formats (h.264, MPEG2 etc.), with one exception; with motion JPEG data, * the format can be different. See imxvpuapi.h for details. */ mapped_virtual_address = imx_vpu_dma_buffer_map(decoded_picture.framebuffer->dma_buffer, IMX_VPU_MAPPING_FLAG_READ_ONLY); fwrite(mapped_virtual_address, 1, num_out_byte, ctx->fout); imx_vpu_dma_buffer_unmap(decoded_picture.framebuffer->dma_buffer); /* Mark the framebuffer as displayed, thus returning it to the list of *framebuffers available for decoding. */ imx_vpu_dec_mark_framebuffer_as_displayed(ctx->vpudec, decoded_picture.framebuffer); } } return RETVAL_OK; }
ImxVpuDecReturnCodes imx_vpu_dec_decode_frame(ImxVpuDecoder *decoder, ImxVpuEncodedFrame const *encoded_frame, unsigned int *output_code) { VpuDecRetCode ret; VpuBufferNode node; int buf_ret_code; node.pVirAddr = encoded_frame->virtual_address; node.pPhyAddr = 0; /* encoded data is always read from a regular memory block, not a DMA buffer */ node.nSize = encoded_frame->data_size; node.sCodecData.pData = encoded_frame->codec_data; node.sCodecData.nSize = encoded_frame->codec_data_size; decoder->pending_user_data = encoded_frame->user_data; ret = VPU_DecDecodeBuf(decoder->handle, &node, &buf_ret_code); IMX_VPU_LOG("VPU_DecDecodeBuf buf ret code: 0x%x", buf_ret_code); *output_code = dec_convert_outcode(buf_ret_code); if (ret != VPU_DEC_RET_SUCCESS) { IMX_VPU_ERROR("decoding frame failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); return dec_convert_retcode(ret); } if (decoder->recalculate_num_avail_framebuffers) { decoder->num_available_framebuffers = decoder->num_framebuffers - decoder->num_framebuffers_in_use; IMX_VPU_LOG("recalculated number of available framebuffers to %d", decoder->num_available_framebuffers); decoder->recalculate_num_avail_framebuffers = FALSE; } if (buf_ret_code & VPU_DEC_INIT_OK) { decoder->delay_pending_user_data = TRUE; decoder->last_pending_user_data = decoder->pending_user_data; } if (buf_ret_code & VPU_DEC_FLUSH) { IMX_VPU_INFO("VPU requested a decoder flush"); ret = VPU_DecFlushAll(decoder->handle); if (ret == VPU_DEC_RET_FAILURE_TIMEOUT) { IMX_VPU_WARNING("timeout detected, resetting decoder"); ret = VPU_DecReset(decoder->handle); if (ret != VPU_DEC_RET_SUCCESS) { ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); IMX_VPU_ERROR("resetting decoder failed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } else *output_code |= IMX_VPU_DEC_OUTPUT_CODE_INTERNAL_RESET; } else if (ret != VPU_DEC_RET_SUCCESS) { ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); IMX_VPU_ERROR("flushing decoder failed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } else IMX_VPU_INFO("flushed decoder"); } if (buf_ret_code & VPU_DEC_RESOLUTION_CHANGED) { IMX_VPU_INFO("resolution changed - resetting internal states"); *output_code |= IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE; decoder->delay_pending_user_data = TRUE; decoder->recalculate_num_avail_framebuffers = FALSE; decoder->num_user_data = 0; if (decoder->user_data_for_frames != NULL) IMX_VPU_FREE(decoder->user_data_for_frames, sizeof(void*) * decoder->num_framebuffers); if (decoder->wrapper_framebuffers != NULL) IMX_VPU_FREE(decoder->wrapper_framebuffers, sizeof(VpuFrameBuffer*) * decoder->num_framebuffers); decoder->user_data_for_frames = NULL; decoder->wrapper_framebuffers = NULL; } if (buf_ret_code & VPU_DEC_NO_ENOUGH_INBUF) { /* Not dropping frame here on purpose; the next input frame may * complete the input */ } { void *user_data = decoder->delay_pending_user_data ? decoder->last_pending_user_data : decoder->pending_user_data; /* The first time this location is reached, VPU_DEC_INIT_OK will be set in the output_code. * This implies that the framebuffers have not been allocated and registered yet, * so no user data can be stored yet. * With codec formats that produce consumption info, this is not a problem, because * VPU_DEC_ONE_FRM_CONSUMED will be returned only when framebuffers are present. * But with other formats, an explicit decoder->framebuffers != NULL check is necessary * (see below). The user_data pointer does not get lost; it is stored in last_pending_user_data. */ if ((buf_ret_code & VPU_DEC_ONE_FRM_CONSUMED) && !(buf_ret_code & VPU_DEC_OUTPUT_DROPPED)) { int fb_index; VpuDecFrameLengthInfo consumed_frame_info; ret = VPU_DecGetConsumedFrameInfo(decoder->handle, &consumed_frame_info); if (ret != VPU_DEC_RET_SUCCESS) { ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); IMX_VPU_ERROR("getting consumed frame info failed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } fb_index = dec_get_wrapper_framebuffer_index(decoder, consumed_frame_info.pFrame); if (consumed_frame_info.pFrame != NULL) { if ((fb_index >= 0) && (fb_index < (int)(decoder->num_framebuffers))) { IMX_VPU_LOG("framebuffer index %d for framebuffer %p user data %p", fb_index, (void *)(consumed_frame_info.pFrame), user_data); decoder->user_data_for_frames[fb_index] = user_data; } else IMX_VPU_ERROR("framebuffer index %d for framebuffer %p user data %p out of bounds", fb_index, (void *)(consumed_frame_info.pFrame), user_data); } else IMX_VPU_WARNING("consumed frame info contains a NULL frame"); } else if (!(decoder->consumption_info_available) && (decoder->framebuffers != NULL)) { if (decoder->num_user_data < (int)(decoder->num_framebuffers)) { decoder->user_data_for_frames[decoder->num_user_data] = user_data; decoder->num_user_data++; IMX_VPU_LOG("user data %p stored as newest", user_data); IMX_VPU_TRACE("incremented number of userdata pointers to %d", decoder->num_user_data); } else IMX_VPU_WARNING("too many user data pointers in memory - cannot store current one"); } decoder->last_pending_user_data = decoder->pending_user_data; decoder->pending_user_data = NULL; } if ((buf_ret_code & VPU_DEC_ONE_FRM_CONSUMED) && !(buf_ret_code & VPU_DEC_OUTPUT_DROPPED)) { decoder->num_available_framebuffers--; decoder->num_times_counter_decremented++; IMX_VPU_LOG("decremented number of available framebuffers to %d (with consumed frame info); number of times decremented is now %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented); } if (buf_ret_code & VPU_DEC_OUTPUT_NODIS) { if ((encoded_frame->virtual_address != NULL) && (decoder->codec_format == IMX_VPU_CODEC_FORMAT_VP8)) *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DECODE_ONLY; } /* VPU_DEC_NO_ENOUGH_BUF handled by caller - should be treated as an error */ if ((buf_ret_code & VPU_DEC_OUTPUT_DIS) && !(decoder->consumption_info_available)) { decoder->num_available_framebuffers--; decoder->num_times_counter_decremented++; IMX_VPU_LOG("decremented number of available framebuffers to %d (no consumed frame info); number of times decremented is now %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented); } else if (buf_ret_code & VPU_DEC_OUTPUT_MOSAIC_DIS) { IMX_VPU_TRACE("dropping mosaic frame"); /* mosaic frames do not seem to be useful for anything, so they are just dropped here */ ImxVpuDecReturnCodes imxret; ImxVpuDecodedFrame decoded_frame; if ((imxret = imx_vpu_dec_get_decoded_frame(decoder, &decoded_frame)) != IMX_VPU_DEC_RETURN_CODE_OK) { IMX_VPU_ERROR("error getting output mosaic frame: %s", imx_vpu_dec_error_string(imxret)); return imxret; } if ((imxret = imx_vpu_dec_mark_framebuffer_as_displayed(decoder, decoded_frame.framebuffer)) != IMX_VPU_DEC_RETURN_CODE_OK) { IMX_VPU_ERROR("error marking mosaic frame as displayed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } decoder->dropped_frame_user_data = decoded_frame.user_data; *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; } else if (buf_ret_code & VPU_DEC_OUTPUT_DROPPED) { // TODO make this work for formats with consumption info if (decoder->num_user_data > 0) { decoder->dropped_frame_user_data = decoder->user_data_for_frames[0]; decoder->user_data_for_frames[0] = NULL; memmove(decoder->user_data_for_frames, decoder->user_data_for_frames + 1, sizeof(void*) * (decoder->num_user_data - 1)); decoder->num_user_data--; } else decoder->dropped_frame_user_data = NULL; } /* In case the VPU didn't use the input and no consumed frame info is available, * drop the input frame to make sure timestamps are okay * (If consumed frame info is present it is still possible it might be used for input-output frame * associations; unlikely to occur thought) */ if ((encoded_frame->virtual_address != NULL) && !(buf_ret_code & (VPU_DEC_ONE_FRM_CONSUMED | VPU_DEC_INPUT_USED | VPU_DEC_RESOLUTION_CHANGED))) { decoder->dropped_frame_user_data = encoded_frame->user_data; *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; } return IMX_VPU_DEC_RETURN_CODE_OK; }