GstImxVpuFramebufferArray * gst_imx_vpu_framebuffer_array_new(ImxVpuColorFormat color_format, unsigned int frame_width, unsigned int frame_height, unsigned int framebuffer_alignment, gboolean uses_interlacing, gboolean chroma_interleave, unsigned int num_framebuffers, GstImxPhysMemAllocator *phys_mem_allocator)
{
	guint i;
	GstImxVpuFramebufferArray *framebuffer_array = g_object_new(gst_imx_vpu_framebuffer_array_get_type(), NULL);

	framebuffer_array->original_frame_width = frame_width;
	framebuffer_array->original_frame_height = frame_height;

	imx_vpu_calc_framebuffer_sizes(
		color_format,
		frame_width, frame_height,
		framebuffer_alignment,
		uses_interlacing,
		chroma_interleave,
		&(framebuffer_array->framebuffer_sizes)
	);

	framebuffer_array->framebuffers = (ImxVpuFramebuffer *)g_slice_alloc(sizeof(ImxVpuFramebuffer) * num_framebuffers);
	framebuffer_array->num_framebuffers = num_framebuffers;

	framebuffer_array->allocator = (GstAllocator *)gst_object_ref(GST_OBJECT(phys_mem_allocator));

	GST_DEBUG_OBJECT(framebuffer_array, "allocating and registering %u framebuffers", num_framebuffers);

	for (i = 0; i < num_framebuffers; ++i)
	{
		GstImxPhysMemory *memory;
		ImxVpuFramebuffer *framebuffer = &(framebuffer_array->framebuffers[i]);

		// TODO: pass on framebuffer_alignment to the internal allocator somehow
		memory = (GstImxPhysMemory *)gst_allocator_alloc(
			framebuffer_array->allocator,
			framebuffer_array->framebuffer_sizes.total_size,
			NULL
		);

		if (memory == NULL)
			return FALSE;

		/* When filling in the params, use "memory" as the user-defined context parameter
		 * This is useful to be able to later determine which memory block this framebuffer
		 * is associated with. See gst_imx_vpu_framebuffer_array_get_gst_phys_memory(). */
		imx_vpu_fill_framebuffer_params(
			framebuffer,
			&(framebuffer_array->framebuffer_sizes),
			GST_IMX_VPU_MEM_IMXVPUAPI_DMA_BUFFER(memory),
			memory
		);

		GST_DEBUG_OBJECT(
			framebuffer_array,
			"memory block %p   physical address %" GST_IMX_PHYS_ADDR_FORMAT "  ref count %d",
			(gpointer)memory,
			memory->phys_addr,
			GST_MINI_OBJECT_REFCOUNT_VALUE(memory)
		);
	}

	return framebuffer_array;
}
Exemple #2
0
static ImxVpuEncReturnCodes imx_vpu_jpeg_enc_open_internal(ImxVpuJPEGEncoder *jpeg_encoder)
{
	unsigned int i;
	ImxVpuEncOpenParams open_params;
	ImxVpuEncReturnCodes ret = IMX_VPU_ENC_RETURN_CODE_OK;

	assert(jpeg_encoder != NULL);
	assert(jpeg_encoder->frame_width > 0);
	assert(jpeg_encoder->frame_height > 0);
	assert(jpeg_encoder->encoder == NULL);

	imx_vpu_enc_set_default_open_params(IMX_VPU_CODEC_FORMAT_MJPEG, &open_params);
	open_params.frame_width = jpeg_encoder->frame_width;
	open_params.frame_height = jpeg_encoder->frame_height;
	open_params.codec_params.mjpeg_params.quality_factor = jpeg_encoder->quality_factor;

	if ((ret = imx_vpu_enc_open(&(jpeg_encoder->encoder), &open_params, jpeg_encoder->bitstream_buffer)) != IMX_VPU_ENC_RETURN_CODE_OK)
		goto error;

	if ((ret = imx_vpu_enc_get_initial_info(jpeg_encoder->encoder, &(jpeg_encoder->initial_info))) != IMX_VPU_ENC_RETURN_CODE_OK)
		goto error;

	jpeg_encoder->num_framebuffers = jpeg_encoder->initial_info.min_num_required_framebuffers;
	jpeg_encoder->framebuffers = IMX_VPU_ALLOC(sizeof(ImxVpuFramebuffer) * jpeg_encoder->num_framebuffers);
	jpeg_encoder->fb_dmabuffers = IMX_VPU_ALLOC(sizeof(ImxVpuDMABuffer *) * jpeg_encoder->num_framebuffers);

	memset(jpeg_encoder->framebuffers, 0, sizeof(ImxVpuFramebuffer) * jpeg_encoder->num_framebuffers);
	memset(jpeg_encoder->fb_dmabuffers, 0, sizeof(ImxVpuDMABuffer *) * jpeg_encoder->num_framebuffers);

	imx_vpu_calc_framebuffer_sizes(jpeg_encoder->color_format, jpeg_encoder->frame_width, jpeg_encoder->frame_height, jpeg_encoder->initial_info.framebuffer_alignment, 0, 0, &(jpeg_encoder->calculated_sizes));

	for (i = 0; i < jpeg_encoder->num_framebuffers; ++i)
	{
		jpeg_encoder->fb_dmabuffers[i] = imx_vpu_dma_buffer_allocate(jpeg_encoder->dma_buffer_allocator, jpeg_encoder->calculated_sizes.total_size, jpeg_encoder->initial_info.framebuffer_alignment, 0);
		if (jpeg_encoder->fb_dmabuffers[i] == NULL)
		{
			IMX_VPU_ERROR("could not allocate DMA buffer for framebuffer #%u", i);
			ret = IMX_VPU_ENC_RETURN_CODE_ERROR;
			goto error;
		}

		imx_vpu_fill_framebuffer_params(&(jpeg_encoder->framebuffers[i]), &(jpeg_encoder->calculated_sizes), jpeg_encoder->fb_dmabuffers[i], 0);
	}

	if ((ret = imx_vpu_enc_register_framebuffers(jpeg_encoder->encoder, jpeg_encoder->framebuffers, jpeg_encoder->num_framebuffers)) != IMX_VPU_ENC_RETURN_CODE_OK)
	{
		IMX_VPU_ERROR("could not register framebuffers: %s", imx_vpu_enc_error_string(ret));
		goto error;
	}

	return ret;

error:
	imx_vpu_jpeg_enc_close_internal(jpeg_encoder);

	return ret;
}
Context* init(FILE *input_file, FILE *output_file)
{
	Context *ctx;
	ImxVpuEncOpenParams open_params;
	unsigned int i;

	ctx = calloc(1, sizeof(Context));
	ctx->fin = input_file;
	ctx->fout = output_file;


	/* Set the open params. Use the default values (note that memset must still
	 * be called to ensure all values are set to 0 initially; the
	 * imx_vpu_enc_set_default_open_params() function does not do this!).
	 * Then, set a bitrate of 0 kbps, which tells the VPU to use constant quality
	 * mode instead (controlled by the quant_param field in ImxVpuEncParams).
	 * Frame width & height are also necessary, as are the frame rate numerator
	 * and denominator. */
	memset(&open_params, 0, sizeof(open_params));
	imx_vpu_enc_set_default_open_params(IMX_VPU_CODEC_FORMAT_H264, &open_params);
	open_params.bitrate = 0;
	open_params.frame_width = FRAME_WIDTH;
	open_params.frame_height = FRAME_HEIGHT;
	open_params.frame_rate_numerator = FPS_N;
	open_params.frame_rate_denominator = FPS_D;


	/* Load the VPU firmware */
	imx_vpu_enc_load();

	/* Retrieve information about the required bitstream buffer and allocate one based on this */
	imx_vpu_enc_get_bitstream_buffer_info(&(ctx->bitstream_buffer_size), &(ctx->bitstream_buffer_alignment));
	ctx->bitstream_buffer = imx_vpu_dma_buffer_allocate(
		imx_vpu_enc_get_default_allocator(),
		ctx->bitstream_buffer_size,
		ctx->bitstream_buffer_alignment,
		0
	);

	/* Open an encoder instance, using the previously allocated bitstream buffer */
	imx_vpu_enc_open(&(ctx->vpuenc), &open_params, ctx->bitstream_buffer);


	/* Retrieve the initial information to allocate framebuffers for the
	 * encoding process (unlike with decoding, these framebuffers are used
	 * only internally by the encoder as temporary storage; encoded data
	 * doesn't go in there, nor do raw input frames) */
	imx_vpu_enc_get_initial_info(ctx->vpuenc, &(ctx->initial_info));

	ctx->num_framebuffers = ctx->initial_info.min_num_required_framebuffers;
	fprintf(stderr, "num framebuffers: %u\n", ctx->num_framebuffers);

	/* Using the initial information, calculate appropriate framebuffer sizes */
	imx_vpu_calc_framebuffer_sizes(COLOR_FORMAT, FRAME_WIDTH, FRAME_HEIGHT, ctx->initial_info.framebuffer_alignment, 0, 0, &(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
	);


	/* Allocate memory blocks for the framebuffer and DMA buffer structures,
	 * and allocate the DMA buffers themselves */

	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);
	}

	/* allocate DMA buffers for the raw input frames. Since the encoder can only read
	 * raw input pixels from a DMA memory region, it is necessary to allocate one,
	 * and later copy the pixels into it. In production, it is generally a better
	 * idea to make sure that the raw input frames are already placed in DMA memory
	 * (either allocated by imx_vpu_dma_buffer_allocate() or by some other means of
	 * getting DMA / physically contiguous memory with known physical addresses). */
	ctx->input_fb_dmabuffer = 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->input_framebuffer), &(ctx->calculated_sizes), ctx->input_fb_dmabuffer, 0);

	/* Actual registration is done here. From this moment on, the VPU knows which buffers to use for
	 * storing temporary frames into. This call must not be done again until encoding is shut down. */
	imx_vpu_enc_register_framebuffers(ctx->vpuenc, ctx->framebuffers, ctx->num_framebuffers);

	return ctx;
}
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;
}
Context* init(FILE *input_file, FILE *output_file)
{
	Context *ctx;
	ImxVpuEncOpenParams open_params;
	unsigned int i;

	ctx = calloc(1, sizeof(Context));
	ctx->fin = input_file;
	ctx->fout = output_file;

	imx_vpu_enc_set_default_open_params(IMX_VPU_CODEC_FORMAT_H264, &open_params);
	open_params.frame_width = FRAME_WIDTH;
	open_params.frame_height = FRAME_HEIGHT;
	open_params.framerate = FPS;

	imx_vpu_enc_load();
	imx_vpu_enc_get_bitstream_buffer_info(&(ctx->bitstream_buffer_size), &(ctx->bitstream_buffer_alignment));
	ctx->bitstream_buffer = imx_vpu_dma_buffer_allocate(imx_vpu_enc_get_default_allocator(), ctx->bitstream_buffer_size, ctx->bitstream_buffer_alignment, 0);
	imx_vpu_enc_open(&(ctx->vpuenc), &open_params, ctx->bitstream_buffer);
	imx_vpu_enc_get_initial_info(ctx->vpuenc, &(ctx->initial_info));

	ctx->num_framebuffers = ctx->initial_info.min_num_required_framebuffers;
	fprintf(stderr, "num framebuffers: %u\n", ctx->num_framebuffers);

	imx_vpu_calc_framebuffer_sizes(COLOR_FORMAT, FRAME_WIDTH, FRAME_HEIGHT, ctx->initial_info.framebuffer_alignment, 0, &(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);
	}

	/* allocate DMA buffers for the input and output buffers. Use total_size as size for both;
	 * the output buffer will most likely contain data later that is much smaller than the input,
	 * but just to be on the safe side, make sure that even an uncompressed frame could fit */
	ctx->input_fb_dmabuffer = 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->input_framebuffer), &(ctx->calculated_sizes), ctx->input_fb_dmabuffer, 0);

	ctx->output_dmabuffer = imx_vpu_dma_buffer_allocate(imx_vpu_dec_get_default_allocator(), ctx->calculated_sizes.total_size, ctx->initial_info.framebuffer_alignment, 0);

	/* Actual registration is done here. From this moment on, the VPU knows which buffers to use for
	 * storing temporary pictures into. This call must not be done again until encoding is shut down. */
	imx_vpu_enc_register_framebuffers(ctx->vpuenc, ctx->framebuffers, ctx->num_framebuffers);

	return ctx;
}
Exemple #6
0
static int initial_info_callback(ImxVpuDecoder *decoder, ImxVpuDecInitialInfo *new_initial_info, unsigned int output_code, void *user_data)
{
	unsigned int i;
	ImxVpuDecReturnCodes ret;
	ImxVpuJPEGDecoder *jpeg_decoder = (ImxVpuJPEGDecoder *)user_data;

	IMXVPUAPI_UNUSED_PARAM(decoder);
	IMXVPUAPI_UNUSED_PARAM(output_code);

	imx_vpu_jpeg_dec_deallocate_framebuffers(jpeg_decoder);

	jpeg_decoder->initial_info = *new_initial_info;
	IMX_VPU_DEBUG(
		"initial info:  size: %ux%u pixel  rate: %u/%u  min num required framebuffers: %u  interlacing: %d  framebuffer alignment: %u  color format: %s",
		new_initial_info->frame_width,
		new_initial_info->frame_height,
		new_initial_info->frame_rate_numerator,
		new_initial_info->frame_rate_denominator,
		new_initial_info->min_num_required_framebuffers,
		new_initial_info->interlacing,
		new_initial_info->framebuffer_alignment,
		imx_vpu_color_format_string(new_initial_info->color_format)
	);

	jpeg_decoder->num_framebuffers = new_initial_info->min_num_required_framebuffers + jpeg_decoder->num_extra_framebuffers;

	imx_vpu_calc_framebuffer_sizes(new_initial_info->color_format, new_initial_info->frame_width, new_initial_info->frame_height, new_initial_info->framebuffer_alignment, new_initial_info->interlacing, 0, &(jpeg_decoder->calculated_sizes));
	IMX_VPU_DEBUG(
		"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",
		jpeg_decoder->calculated_sizes.aligned_frame_width, jpeg_decoder->calculated_sizes.aligned_frame_height,
		jpeg_decoder->calculated_sizes.y_stride, jpeg_decoder->calculated_sizes.cbcr_stride,
		jpeg_decoder->calculated_sizes.y_size, jpeg_decoder->calculated_sizes.cbcr_size, jpeg_decoder->calculated_sizes.mvcol_size,
		jpeg_decoder->calculated_sizes.total_size
	);

	jpeg_decoder->framebuffers = IMX_VPU_ALLOC(sizeof(ImxVpuFramebuffer) * jpeg_decoder->num_framebuffers);
	jpeg_decoder->fb_dmabuffers = IMX_VPU_ALLOC(sizeof(ImxVpuDMABuffer *) * jpeg_decoder->num_framebuffers);

	memset(jpeg_decoder->framebuffers, 0, sizeof(ImxVpuFramebuffer) * jpeg_decoder->num_framebuffers);
	memset(jpeg_decoder->fb_dmabuffers, 0, sizeof(ImxVpuDMABuffer *) * jpeg_decoder->num_framebuffers);

	for (i = 0; i < jpeg_decoder->num_framebuffers; ++i)
	{
		jpeg_decoder->fb_dmabuffers[i] = imx_vpu_dma_buffer_allocate(jpeg_decoder->dma_buffer_allocator, jpeg_decoder->calculated_sizes.total_size, jpeg_decoder->initial_info.framebuffer_alignment, 0);
		if (jpeg_decoder->fb_dmabuffers[i] == NULL)
		{
			IMX_VPU_ERROR("could not allocate DMA buffer for framebuffer #%u", i);
			goto error;
		}

		imx_vpu_fill_framebuffer_params(&(jpeg_decoder->framebuffers[i]), &(jpeg_decoder->calculated_sizes), jpeg_decoder->fb_dmabuffers[i], 0);
	}

	if ((ret = imx_vpu_dec_register_framebuffers(jpeg_decoder->decoder, jpeg_decoder->framebuffers, jpeg_decoder->num_framebuffers)) != IMX_VPU_DEC_RETURN_CODE_OK)
	{
		IMX_VPU_ERROR("could not register framebuffers: %s", imx_vpu_dec_error_string(ret));
		goto error;
	}

	return 1;

error:
	imx_vpu_jpeg_deallocate_dma_buffers(jpeg_decoder->fb_dmabuffers, jpeg_decoder->num_framebuffers);
	return 0;
}