From d4c78fc4caf206b4effa5350808803600754214c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 23 Dec 2012 09:56:03 +0100 Subject: [PATCH] mfc: Add mfc_decoder library from FXI --- sys/mfc/mfc_decoder/mfc_decoder.c | 658 ++++++++++++++++++++++++++++++ sys/mfc/mfc_decoder/mfc_decoder.h | 189 +++++++++ 2 files changed, 847 insertions(+) create mode 100644 sys/mfc/mfc_decoder/mfc_decoder.c create mode 100644 sys/mfc/mfc_decoder/mfc_decoder.h diff --git a/sys/mfc/mfc_decoder/mfc_decoder.c b/sys/mfc/mfc_decoder/mfc_decoder.c new file mode 100644 index 0000000000..b5b7648169 --- /dev/null +++ b/sys/mfc/mfc_decoder/mfc_decoder.c @@ -0,0 +1,658 @@ +/* + * Copyright 2012 FXI Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Author: Haavard Kvaalen + */ + +#include "mfc_decoder.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "MFC-DEC" +#include + + +#define MAX_DECODER_INPUT_BUFFER_SIZE (1024 * 3072) +#define NUM_INPUT_PLANES 1 +#define NUM_OUTPUT_PLANES 2 +#define MAX_DECODING_TIME 50 +#define MFC_PATH "/dev/video8" + +static int mfc_in_use; + +/* Protects the mfc_in_use variable */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +struct mfc_dec_context { + int fd; + int num_input_buffers; + int num_output_buffers; + struct mfc_buffer *input_buffer; + struct mfc_buffer *output_buffer; + /* + * Number of decoded frames the MFC needs to have access to to + * decode correctly. + */ + int required_output_buffers; + int has_free_input_buffers; + /* + * Number of frames that have been decoded. We cannot return them + * if this number is less than required_output_buffers. + */ + int output_frames_available; + int input_frames_queued; + /* We have reached end of stream */ + int eos_reached; + struct { + int w; + int h; + } output_size; + struct { + int left; + int top; + int w; + int h; + } crop_size; +}; + +struct mfc_buffer { + struct { + int length; + int bytesused; + void *data; + } plane[2]; + int index; + int state; +}; + + +enum { + BUFFER_FREE, + BUFFER_ENQUEUED, + BUFFER_DEQUEUED, +}; + +static unsigned int to_v4l2_codec(enum mfc_codec_type codec) +{ + switch (codec) { + case CODEC_TYPE_H264: + return V4L2_PIX_FMT_H264; + case CODEC_TYPE_VC1: + return V4L2_PIX_FMT_VC1_ANNEX_G; + case CODEC_TYPE_VC1_RCV: + return V4L2_PIX_FMT_VC1_ANNEX_L; + case CODEC_TYPE_MPEG4: + return V4L2_PIX_FMT_MPEG4; + case CODEC_TYPE_MPEG1: + return V4L2_PIX_FMT_MPEG1; + case CODEC_TYPE_MPEG2: + return V4L2_PIX_FMT_MPEG2; + case CODEC_TYPE_H263: + return V4L2_PIX_FMT_H263; + } + LOGE("Invalid codec type %d", codec); + return 0; +} + +int mfc_dec_set_codec(struct mfc_dec_context *ctx, enum mfc_codec_type codec) +{ + struct v4l2_format fmt = { + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .fmt = { + .pix_mp = { + .num_planes = 1, + .plane_fmt = { + [0] = { + .sizeimage = MAX_DECODER_INPUT_BUFFER_SIZE, + }, + }, + }, + }, + }; + fmt.fmt.pix_mp.pixelformat = to_v4l2_codec(codec); + + int ret = ioctl(ctx->fd, VIDIOC_S_FMT, &fmt); + if (ret) + LOGE("Unable to set input format"); + return ret; +} + +static int request_input_buffers(struct mfc_dec_context *ctx, int num) +{ + struct v4l2_requestbuffers reqbuf = { + .count = num, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .memory = V4L2_MEMORY_MMAP, + }; + int i; + + ctx->input_buffer = calloc(num, sizeof (struct mfc_buffer)); + if (!ctx->input_buffer) { + LOGE("Failed to allocate space for input buffer meta data"); + return -1; + } + + if (ioctl(ctx->fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + LOGE("Unable to request input buffers"); + return -1; + } + ctx->num_input_buffers = reqbuf.count; + LOGI("Requested %d input buffers, got %d", num, reqbuf.count); + for (i = 0; i < num; i++) { + void *ptr; + struct v4l2_plane planes[NUM_INPUT_PLANES] = {{.length = 0}}; + struct v4l2_buffer buffer = { + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .memory = V4L2_MEMORY_MMAP, + .index = i, + .length = NUM_INPUT_PLANES, + .m = { + .planes = planes, + }, + }; + if (ioctl(ctx->fd, VIDIOC_QUERYBUF, &buffer) < 0) { + LOGE("Query of input buffer failed"); + return -1; + } + ptr = mmap(NULL, buffer.m.planes[0].length, PROT_READ | PROT_WRITE, + MAP_SHARED, ctx->fd, buffer.m.planes[0].m.mem_offset); + if (ptr == MAP_FAILED) { + LOGE("Failed to map input buffer"); + return -1; + } + ctx->input_buffer[i].plane[0].length = planes[0].length; + ctx->input_buffer[i].plane[0].data = ptr; + ctx->input_buffer[i].index = i; + ctx->input_buffer[i].state = BUFFER_FREE; + } + ctx->has_free_input_buffers = 1; + return 0; +} + + +static int request_output_buffers(struct mfc_dec_context *ctx, int num) +{ + struct v4l2_requestbuffers reqbuf = { + .count = num, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .memory = V4L2_MEMORY_MMAP, + }; + int i; + + ctx->output_buffer = calloc(num, sizeof (struct mfc_buffer)); + if (!ctx->output_buffer) { + LOGE("Failed to allocate space for output buffer meta data"); + return -1; + } + + if (ioctl(ctx->fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + LOGE("Unable to request output buffers"); + return -1; + } + ctx->num_output_buffers = reqbuf.count; + LOGI("Requested %d output buffers, got %d", num, reqbuf.count); + for (i = 0; i < ctx->num_output_buffers; i++) { + int p; + struct v4l2_plane planes[NUM_OUTPUT_PLANES] = {{.length = 0}}; + struct v4l2_buffer buffer = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .memory = V4L2_MEMORY_MMAP, + .index = i, + .length = NUM_OUTPUT_PLANES, + .m = { + .planes = planes, + }, + }; + ctx->output_buffer[i].index = i; + if (ioctl(ctx->fd, VIDIOC_QUERYBUF, &buffer) < 0) { + LOGE("Query of output buffer failed"); + return -1; + } + for (p = 0; p < NUM_OUTPUT_PLANES; p++) { + void *ptr = mmap(NULL, buffer.m.planes[p].length, + PROT_READ | PROT_WRITE, MAP_SHARED, + ctx->fd, buffer.m.planes[p].m.mem_offset); + if (ptr == MAP_FAILED) { + LOGE("Failed to map output buffer"); + return -1; + } + ctx->output_buffer[i].plane[p].length = planes[p].length; + ctx->output_buffer[i].plane[p].data = ptr; + } + if (mfc_dec_enqueue_output(ctx, &ctx->output_buffer[i]) < 0) + return -1; + } + return 0; +} + +struct mfc_dec_context* mfc_dec_create(unsigned int codec, int num_input_buffers) +{ + struct mfc_dec_context *ctx; + + pthread_mutex_lock(&mutex); + if (mfc_in_use) { + LOGE("Rejected because MFC is already in use"); + pthread_mutex_unlock(&mutex); + return NULL; + } + mfc_in_use = 1; + pthread_mutex_unlock(&mutex); + + ctx = calloc(1, sizeof (struct mfc_dec_context)); + // The first frame never generate any output. + // TODO: do this better + ctx->output_frames_available = -1; + if (!ctx) { + LOGE("Unable to allocate memory for context"); + return NULL; + } + LOGI("Opening MFC device node at: %s", MFC_PATH); + ctx->fd = open(MFC_PATH, O_RDWR, 0); + if (ctx->fd == -1) { + LOGE("Unable to open MFC device node: %d", errno); + free(ctx); + return NULL; + } + if (mfc_dec_set_codec(ctx, codec) || + request_input_buffers(ctx, num_input_buffers)) { + mfc_dec_destroy(ctx); + return NULL; + } + return ctx; +} + +static int get_output_format(struct mfc_dec_context *ctx) +{ + struct v4l2_format fmt = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }; + + if (ioctl(ctx->fd, VIDIOC_G_FMT, &fmt) < 0) { + LOGE("Failed to get output format"); + return -1; + } + + ctx->output_size.w = fmt.fmt.pix_mp.width; + ctx->output_size.h = fmt.fmt.pix_mp.height; + + return 0; +} + +static int get_crop_data(struct mfc_dec_context *ctx) +{ + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }; + if (ioctl(ctx->fd, VIDIOC_G_CROP, &crop) < 0) { + LOGE("Unable to get crop data"); + return -1; + } + ctx->crop_size.left = crop.c.left; + ctx->crop_size.top = crop.c.top; + ctx->crop_size.w = crop.c.width; + ctx->crop_size.h = crop.c.height; + return 0; +} + +int get_minimum_output_buffers(struct mfc_dec_context *ctx) +{ + struct v4l2_control ctrl = { + .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + }; + if (ioctl(ctx->fd, VIDIOC_G_CTRL, &ctrl) < 0) { + LOGE("Failed to get number of output buffers required"); + return -1; + } + ctx->required_output_buffers = ctrl.value; + return 0; +} + +static int start_input_stream(struct mfc_dec_context *ctx) +{ + int type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (ioctl(ctx->fd, VIDIOC_STREAMON, &type) < 0) { + LOGE("Unable to start input stream"); + return -1; + } + return 0; +} + +static int start_output_stream(struct mfc_dec_context *ctx) +{ + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + if (ioctl(ctx->fd, VIDIOC_STREAMON, &type) < 0) { + LOGE("Unable to start output stream"); + return -1; + } + return 0; +} + +int mfc_dec_init(struct mfc_dec_context *ctx, int extra_buffers) +{ + if (start_input_stream(ctx) < 0) + return -1; + + if (get_output_format(ctx) || + get_crop_data(ctx) || + get_minimum_output_buffers(ctx)) + return -1; + + if (request_output_buffers(ctx, ctx->required_output_buffers + extra_buffers)) + return -1; + + if (start_output_stream(ctx) < 0) + return -1; + + return 0; +} + +void mfc_dec_get_output_size(struct mfc_dec_context *ctx, int *w, int *h) +{ + *w = ctx->output_size.w; + *h = ctx->output_size.h; +} + +void mfc_dec_get_crop_size(struct mfc_dec_context *ctx, + int *left, int *top, int *w, int *h) +{ + *left = ctx->crop_size.left; + *top = ctx->crop_size.top; + *w = ctx->crop_size.w; + *h = ctx->crop_size.h; +} + +void mfc_dec_destroy(struct mfc_dec_context *ctx) +{ + int i; + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + if (ioctl(ctx->fd, VIDIOC_STREAMOFF, &type) < 0) + LOGE("Streamoff failed on output"); + + type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (ioctl(ctx->fd, VIDIOC_STREAMOFF, &type) < 0) + LOGE("Streamoff failed on input"); + + for (i = 0; i < ctx->num_input_buffers; i++) { + if (ctx->input_buffer[i].plane[0].data) + munmap(ctx->input_buffer[i].plane[0].data, + ctx->input_buffer[i].plane[0].length); + } + for (i = 0; i < ctx->num_output_buffers; i++) { + int j; + for (j = 0; j < NUM_OUTPUT_PLANES; j++) + if (ctx->output_buffer[i].plane[j].data) + munmap(ctx->output_buffer[i].plane[j].data, + ctx->output_buffer[i].plane[j].length); + } + close(ctx->fd); + pthread_mutex_lock(&mutex); + mfc_in_use = 0; + pthread_mutex_unlock(&mutex); + LOGI("MFC device closed"); + free(ctx); +} + +int mfc_dec_enqueue_input(struct mfc_dec_context *ctx, struct mfc_buffer *buffer) +{ + struct v4l2_plane planes[NUM_INPUT_PLANES] = { + [0] = { + .bytesused = buffer->plane[0].bytesused, + }, + }; + struct v4l2_buffer qbuf = { + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .memory = V4L2_MEMORY_MMAP, + .index = buffer->index, + .length = NUM_INPUT_PLANES, + .m = { + .planes = planes, + }, + }; + + if (ioctl(ctx->fd, VIDIOC_QBUF, &qbuf) < 0) { + LOGE("Enqueuing of input buffer %d failed; prev state: %d", + buffer->index, buffer->state); + return -1; + } + + ctx->input_frames_queued++; + buffer->state = BUFFER_ENQUEUED; + if (buffer->plane[0].bytesused == 0) + ctx->eos_reached = 1; + return 0; +} + +int input_dqbuf(struct mfc_dec_context *ctx, struct mfc_buffer **buffer) +{ + struct v4l2_buffer qbuf = { + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .memory = V4L2_MEMORY_MMAP, + }; + struct pollfd fd = { + .fd = ctx->fd, + .events = POLLOUT | POLLERR, + }; + int pollret; + + pollret = poll(&fd, 1, MAX_DECODING_TIME); + if (pollret < 0) { + LOGE("%s: Poll returned error: %d", __func__, errno); + return -1; + } + if (pollret == 0) { + LOGI("%s: timed out", __func__); + return -2; + } + + if (ioctl(ctx->fd, VIDIOC_DQBUF, &qbuf) < 0) { + LOGE("Dequeuing failed"); + return -1; + } + ctx->input_buffer[qbuf.index].plane[0].bytesused = 0; + *buffer = &ctx->input_buffer[qbuf.index]; + ctx->output_frames_available++; + ctx->input_frames_queued--; + return 0; +} + +int mfc_dec_dequeue_input(struct mfc_dec_context *ctx, struct mfc_buffer **buffer) +{ + if (ctx->has_free_input_buffers) { + int i; + *buffer = NULL; + for (i = 0; i < ctx->num_input_buffers; i++) { + if (ctx->input_buffer[i].state == BUFFER_FREE) + *buffer = &ctx->input_buffer[i]; + } + if (!*buffer) { + int ret; + ctx->has_free_input_buffers = 0; + if ((ret = input_dqbuf(ctx, buffer)) < 0) + return ret; + } + } else { + int ret = input_dqbuf(ctx, buffer); + if (ret < 0) + return ret; + } + (*buffer)->state = BUFFER_DEQUEUED; + return 0; +} + +static int release_input_buffer(struct mfc_dec_context *ctx) +{ + struct mfc_buffer *buffer; + struct pollfd fd = { + .fd = ctx->fd, + .events = POLLOUT | POLLERR, + }; + int pollret; + + if (ctx->input_frames_queued == 0) { + LOGI("Nothing to release!"); + return -1; + } + + pollret = poll(&fd, 1, MAX_DECODING_TIME); + if (pollret < 0) { + LOGE("%s: Poll returned error: %d", __func__, errno); + return -1; + } + if (pollret == 0) { + LOGI("%s: timed out", __func__); + return -2; + } + + LOGV("releasing frame; frames queued: %d", ctx->input_frames_queued); + input_dqbuf(ctx, &buffer); + buffer->state = BUFFER_FREE; + ctx->has_free_input_buffers = 1; + return 0; +} + +int mfc_dec_enqueue_output(struct mfc_dec_context *ctx, struct mfc_buffer *buffer) +{ + struct v4l2_plane planes[NUM_OUTPUT_PLANES] = {{.length = 0}}; + struct v4l2_buffer qbuf = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .memory = V4L2_MEMORY_MMAP, + .index = buffer->index, + .length = NUM_OUTPUT_PLANES, + .m = { + .planes = planes, + }, + }; + if (ioctl(ctx->fd, VIDIOC_QBUF, &qbuf) < 0) { + LOGE("Enqueuing of output buffer %d failed; prev state: %d", + buffer->index, buffer->state); + return -1; + } + buffer->state = BUFFER_ENQUEUED; + return 0; +} + +int mfc_dec_dequeue_output(struct mfc_dec_context *ctx, struct mfc_buffer **buffer) +{ + int i; + struct v4l2_plane planes[NUM_OUTPUT_PLANES]; + struct v4l2_buffer qbuf = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .memory = V4L2_MEMORY_MMAP, + .m = { + .planes = planes, + }, + .length = NUM_OUTPUT_PLANES, + }; + + if (ioctl(ctx->fd, VIDIOC_DQBUF, &qbuf) < 0) { + LOGE("Dequeuing failed"); + return -1; + } + + for (i = 0; i < NUM_OUTPUT_PLANES; i++) + ctx->output_buffer[qbuf.index].plane[i].bytesused = qbuf.m.planes[i].bytesused; + + *buffer = &(ctx->output_buffer[qbuf.index]); + ctx->output_frames_available--; + return 0; +} + +int mfc_dec_output_available(struct mfc_dec_context *ctx) +{ + if (ctx->eos_reached) { + if (ctx->input_frames_queued > 0 && + ctx->output_frames_available <= ctx->required_output_buffers) { + release_input_buffer(ctx); + } + + return ctx->output_frames_available > 0; + } + return ctx->output_frames_available >= ctx->required_output_buffers; +} + +int mfc_dec_flush(struct mfc_dec_context *ctx) +{ + int type, i; + int force_dequeue_output = 0; + while (ctx->input_frames_queued > 0) { + int status; + struct mfc_buffer *buffer; + /* Make sure there is room for the decode to finish */ + if (mfc_dec_output_available(ctx) || force_dequeue_output) { + if (mfc_dec_dequeue_output(ctx, &buffer) < 0) + return -1; + if (mfc_dec_enqueue_output(ctx, buffer) < 0) + return -1; + force_dequeue_output = 0; + } + + status = release_input_buffer(ctx); + if (status == -2) + force_dequeue_output = 1; + if (status == -1) + break; + } + + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + if (ioctl(ctx->fd, VIDIOC_STREAMOFF, &type) < 0) { + LOGE("Unable to stop output stream"); + return -1; + } + + for (i = 0; i < ctx->num_output_buffers; i++) { + if (ctx->output_buffer[i].state == BUFFER_ENQUEUED) + if (mfc_dec_enqueue_output(ctx, &ctx->output_buffer[i]) < 0) + return -1; + } + + if (start_output_stream(ctx) < 0) + return -1; + + ctx->output_frames_available = 0; + ctx->eos_reached = 0; + + return 0; +} + +void* mfc_buffer_get_input_data(struct mfc_buffer *buffer) +{ + return buffer->plane[0].data; +} + +int mfc_buffer_get_input_max_size(struct mfc_buffer *buffer) +{ + return buffer->plane[0].length; +} + + +void mfc_buffer_set_input_size(struct mfc_buffer *buffer, int size) +{ + buffer->plane[0].bytesused = size; +} + +void mfc_buffer_get_output_data(struct mfc_buffer *buffer, + void **ybuf, void **uvbuf) +{ + *ybuf = buffer->plane[0].data; + *uvbuf = buffer->plane[1].data; +} diff --git a/sys/mfc/mfc_decoder/mfc_decoder.h b/sys/mfc/mfc_decoder/mfc_decoder.h new file mode 100644 index 0000000000..fc9db87a51 --- /dev/null +++ b/sys/mfc/mfc_decoder/mfc_decoder.h @@ -0,0 +1,189 @@ +/* + * Copyright 2012 FXI Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Author: Haavard Kvaalen + */ + +/* + * Interface for decoding of various video formats using the Samsung + * Multi-Format Video Codec (MFC). + */ + +#ifndef VIDEO_EXYNOS4_MFC_V4L2_INCLUDE_MFC_DECODER_H +#define VIDEO_EXYNOS4_MFC_V4L2_INCLUDE_MFC_DECODER_H + +struct mfc_buffer; +struct mfc_dec_context; + +enum mfc_codec_type { + CODEC_TYPE_H264, + CODEC_TYPE_VC1, /* VC1 advanced profile */ + CODEC_TYPE_VC1_RCV, /* VC1 simple/main profile */ + CODEC_TYPE_MPEG4, + CODEC_TYPE_MPEG1, + CODEC_TYPE_MPEG2, + CODEC_TYPE_H263, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Open the MFC decoding device node, and allocate input buffers. + * + * Returns a mfc_dec_context. Note that the context can only be used + * for decoding one stream, i.e. mfc_dec_init can only be called once. + * + * Args: + * codec: Codec type (this can later be changed with mfc_set_codec()). + * num_input_buffers: Numbers of input buffers. There is never any + * need to enqueue more than one buffer for correct decoding. + * + * Returns: A new mfc_dec_context if successful, NULL on error. This + * context is needed for most other calls below, and should be + * deallocated with mfc_dec_destroy(). + */ +struct mfc_dec_context* mfc_dec_create(enum mfc_codec_type codec, + int num_input_buffers); + +/* + * Destroy context created with mfc_dec_create(). + */ +void mfc_dec_destroy(struct mfc_dec_context*); + +/* + * Initialize video decode. Before this function is called, the + * initial input frame (which contains the header) must be enqueued. + * + * This function allocate output buffers. All output buffers will be + * enqueued initially. The actual number of output buffers depend on the + * + * Args: + * extra_buffers: Numbers of output buffers that can be kept + * dequeued at any time. + * + * Returns: Zero for success, negative value on failure. + */ +int mfc_dec_init(struct mfc_dec_context*, int extra_buffers); + + +/* + * This function may be called only before mfc_dec_init(). It is only + * necessary to call if the codec type is different from the one + * supplied to mfc_dec_create(). + * + * Args: + * codec: Codec type + * + * Returns: Zero for success, negative value on failure. + */ +int mfc_dec_set_codec(struct mfc_dec_context*, enum mfc_codec_type codec); + + +/* + * Get size of image output from the MFC. The data might be larger + * than the actual video because of MFC alignment requirements (see + * the crop size below). This function can only be called after + * mfc_dec_init() has been called. + * + * Args: + * w: Width (output). + * h: Height (output). + * + * Returns: Zero for success, negative value on failure. + */ +void mfc_dec_get_output_size(struct mfc_dec_context*, int *w, int *h); +void mfc_dec_get_crop_size(struct mfc_dec_context*, + int *left, int *top, int *w, int *h); + +/* + * Check if there are output frames that can be dequeued. + * + * Returns: Positive value if a frame is available, zero if not. + */ +int mfc_dec_output_available(struct mfc_dec_context*); + +/* + * This module use 'input' and 'output' for input to, and output from + * the MFC. The VFL2 names for these interfaces are OUTPUT (for the + * input) and CAPTURE (for the output). + * + * These functions return zero for success, negative value on failure. + * + * When the end of stream has been reached, an empty input frame + * should be enqueued after the last valid input frame. This signal + * to the MFC that EOS has been reached. After this no more input + * frames can be enqueued. + */ + +/* Enqueue frame containing compressed data */ +int mfc_dec_enqueue_input(struct mfc_dec_context*, struct mfc_buffer *buffer); +/* + * Dequeue a processed input frame. Will block until one is available. + * + * Returns 0 on success, -1 on failure, and -2 on timeout. A timeout + * typically happens if there are no free output buffers available. + */ +int mfc_dec_dequeue_input(struct mfc_dec_context*, struct mfc_buffer **buffer); +/* Enqueue empty output frame */ +int mfc_dec_enqueue_output(struct mfc_dec_context*, struct mfc_buffer *buffer); +/* + * Dequeue output frame with image data. This should only be called + * when mfc_dec_output_available() returns true. If this is called + * when mfc_dec_output_available() is not true, subsequent video + * frames may not decode correctly. + */ +int mfc_dec_dequeue_output(struct mfc_dec_context*, struct mfc_buffer **buffer); + + +/* + * Flush (discard) all enqueued output and input frames. This is + * typically used if we want to seek. + * + * Calling flush resets the "end of stream" state that is entered by + * enqueuing an empty input frame. Thus it is safe to seek at the end + * of the stream as long as mfc_dec_flush() is called first. + */ +int mfc_dec_flush(struct mfc_dec_context*); + +/* + * Get pointer to the input data in 'buffer'. + */ +void* mfc_buffer_get_input_data(struct mfc_buffer *buffer); + +/* + * Get maximum size of input buffer 'buffer' in bytes. + */ +int mfc_buffer_get_input_max_size(struct mfc_buffer *buffer); + +/* + * Set size of data that has been put into 'buffer' in bytes. + */ +void mfc_buffer_set_input_size(struct mfc_buffer *buffer, int size); + +/* + * Get pointers to data in output buffer 'buffer'. + */ +void mfc_buffer_get_output_data(struct mfc_buffer *buffer, + void **ybuf, void **uvbuf); + +#ifdef __cplusplus +} +#endif + + +#endif /* VIDEO_EXYNOS4_MFC_V4L2_INCLUDE_MFC_DECODER_H */