|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914 |
- /*
- Copyright (c) 2012, Broadcom Europe Ltd
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the copyright holder nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- /** \file
- * Jpeg encoder and decoder library using the hardware jpeg codec
- */
-
- #include "mmal.h"
- #include "util/mmal_component_wrapper.h"
- #include "util/mmal_util_params.h"
- #include "mmal_logging.h"
- #include "brcmjpeg.h"
-
- /*******************************************************************************
- * Defines
- *******************************************************************************/
- #define MMAL_COMPONENT_IMAGE_DECODE "vc.aggregator.pipeline:ril.image_decode:video_convert"
- #define MMAL_COMPONENT_IMAGE_ENCODE "vc.ril.image_encode"
-
- #define ENABLE_SLICE_MODE 0
-
- #define CHECK_MMAL_STATUS(status, jerr, msg, ...) \
- if (status != MMAL_SUCCESS) {LOG_ERROR(msg, ## __VA_ARGS__); \
- err = BRCMJPEG_ERROR_##jerr; goto error;}
-
- /*******************************************************************************
- * Type definitions
- *******************************************************************************/
- struct BRCMJPEG_T
- {
- BRCMJPEG_TYPE_T type;
- unsigned int ref_count;
- unsigned int init;
-
- MMAL_WRAPPER_T *mmal;
- unsigned int slice_height;
-
- VCOS_MUTEX_T lock;
- VCOS_MUTEX_T process_lock;
- VCOS_SEMAPHORE_T sema;
- };
-
- /*******************************************************************************
- * Local prototypes
- *******************************************************************************/
- static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *);
- static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *);
- static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
- static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
- static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
- static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
- static void brcmjpeg_destroy(BRCMJPEG_T *);
-
- static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T);
- static unsigned int brcmjpeg_copy_pixels(uint8_t *out, unsigned int out_size,
- const uint8_t *in, unsigned int in_size, BRCMJPEG_PIXEL_FORMAT_T fmt,
- unsigned int out_width, unsigned int out_height,
- unsigned int in_width, unsigned int in_height,
- unsigned int line_offset, unsigned int convert_from);
-
- static BRCMJPEG_T *brcmjpeg_encoder = NULL;
- static BRCMJPEG_T *brcmjpeg_decoder = NULL;
-
- /*******************************************************************************
- * Platform specific code
- *******************************************************************************/
- static VCOS_ONCE_T once = VCOS_ONCE_INIT;
- static VCOS_MUTEX_T brcmjpeg_lock;
-
- static void brcmjpeg_init_once(void)
- {
- vcos_mutex_create(&brcmjpeg_lock, VCOS_FUNCTION);
- }
-
- #define LOCK() vcos_mutex_lock(&brcmjpeg_lock)
- #define UNLOCK() vcos_mutex_unlock(&brcmjpeg_lock)
- #define LOCK_COMP(ctx) vcos_mutex_lock(&(ctx)->lock)
- #define UNLOCK_COMP(ctx) vcos_mutex_unlock(&(ctx)->lock)
- #define LOCK_PROCESS(ctx) vcos_mutex_lock(&(ctx)->process_lock)
- #define UNLOCK_PROCESS(ctx) vcos_mutex_unlock(&(ctx)->process_lock)
- #define WAIT(ctx) vcos_semaphore_wait(&(ctx)->sema)
- #define SIGNAL(ctx) vcos_semaphore_post(&(ctx)->sema)
-
- /*******************************************************************************
- * Implementation
- *******************************************************************************/
-
- BRCMJPEG_STATUS_T brcmjpeg_create(BRCMJPEG_TYPE_T type, BRCMJPEG_T **ctx)
- {
- BRCMJPEG_STATUS_T status = BRCMJPEG_SUCCESS;
- BRCMJPEG_T **comp;
-
- if (type == BRCMJPEG_TYPE_ENCODER)
- comp = &brcmjpeg_encoder;
- else
- comp = &brcmjpeg_decoder;
-
- vcos_once(&once, brcmjpeg_init_once);
- LOCK();
- if (!*comp)
- {
- int init1, init2, init3;
- *comp = calloc(sizeof(BRCMJPEG_T), 1);
- if (!*comp)
- {
- UNLOCK();
- return BRCMJPEG_ERROR_NOMEM;
- }
- (*comp)->type = type;
- init1 = vcos_mutex_create(&(*comp)->lock, "brcmjpeg lock") != VCOS_SUCCESS;
- init2 = vcos_mutex_create(&(*comp)->process_lock, "brcmjpeg process lock") != VCOS_SUCCESS;
- init3 = vcos_semaphore_create(&(*comp)->sema, "brcmjpeg sema", 0) != VCOS_SUCCESS;
- if (init1 | init2 | init3)
- {
- if (init1) vcos_mutex_delete(&(*comp)->lock);
- if (init2) vcos_mutex_delete(&(*comp)->process_lock);
- if (init3) vcos_semaphore_delete(&(*comp)->sema);
- free(comp);
- UNLOCK();
- return BRCMJPEG_ERROR_NOMEM;
- }
- }
- (*comp)->ref_count++;
- UNLOCK();
-
- LOCK_COMP(*comp);
- if (!(*comp)->init)
- {
- if (type == BRCMJPEG_TYPE_ENCODER)
- status = brcmjpeg_init_encoder(*comp);
- else
- status = brcmjpeg_init_decoder(*comp);
-
- (*comp)->init = status == BRCMJPEG_SUCCESS;
- }
- UNLOCK_COMP(*comp);
-
- if (status != BRCMJPEG_SUCCESS)
- brcmjpeg_release(*comp);
-
- *ctx = *comp;
- return status;
- }
-
- void brcmjpeg_acquire(BRCMJPEG_T *ctx)
- {
- LOCK_COMP(ctx);
- ctx->ref_count++;
- UNLOCK_COMP(ctx);
- }
-
- void brcmjpeg_release(BRCMJPEG_T *ctx)
- {
- LOCK_COMP(ctx);
- if (--ctx->ref_count)
- {
- UNLOCK_COMP(ctx);
- return;
- }
-
- LOCK();
- if (ctx->type == BRCMJPEG_TYPE_ENCODER)
- brcmjpeg_encoder = NULL;
- else
- brcmjpeg_decoder = NULL;
- UNLOCK();
- UNLOCK_COMP(ctx);
-
- brcmjpeg_destroy(ctx);
- return;
- }
-
- BRCMJPEG_STATUS_T brcmjpeg_process(BRCMJPEG_T *ctx, BRCMJPEG_REQUEST_T *req)
- {
- BRCMJPEG_STATUS_T status;
-
- /* Sanity check */
- if ((req->input && req->input_handle) ||
- (req->output && req->output_handle))
- {
- LOG_ERROR("buffer pointer and handle both set (%p/%u %p/%u)",
- req->input, req->input_handle, req->output, req->output_handle);
- return BRCMJPEG_ERROR_REQUEST;
- }
-
- LOCK_PROCESS(ctx);
- if (ctx->type == BRCMJPEG_TYPE_ENCODER)
- status = brcmjpeg_encode(ctx, req);
- else
- status = brcmjpeg_decode(ctx, req);
- UNLOCK_PROCESS(ctx);
-
- return status;
- }
-
- static void brcmjpeg_destroy(BRCMJPEG_T *ctx)
- {
- if (ctx->mmal)
- mmal_wrapper_destroy(ctx->mmal);
- vcos_mutex_delete(&ctx->lock);
- vcos_mutex_delete(&ctx->process_lock);
- vcos_semaphore_delete(&ctx->sema);
- free(ctx);
- }
-
- static void brcmjpeg_mmal_cb(MMAL_WRAPPER_T *wrapper)
- {
- BRCMJPEG_T *ctx = wrapper->user_data;
- SIGNAL(ctx);
- }
-
- static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *ctx)
- {
- MMAL_STATUS_T status;
- BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
-
- /* Create encoder component */
- status = mmal_wrapper_create(&ctx->mmal, MMAL_COMPONENT_IMAGE_ENCODE);
- CHECK_MMAL_STATUS(status, INIT, "failed to create encoder");
- ctx->mmal->user_data = ctx;
- ctx->mmal->callback = brcmjpeg_mmal_cb;
-
- /* Configure things that won't change from encode to encode */
- mmal_port_parameter_set_boolean(ctx->mmal->control,
- MMAL_PARAMETER_EXIF_DISABLE, MMAL_TRUE);
-
- ctx->mmal->output[0]->format->encoding = MMAL_ENCODING_JPEG;
- status = mmal_port_format_commit(ctx->mmal->output[0]);
- CHECK_MMAL_STATUS(status, INIT, "failed to commit output port format");
-
- ctx->mmal->output[0]->buffer_size = ctx->mmal->output[0]->buffer_size_min;
- ctx->mmal->output[0]->buffer_num = 3;
- status = mmal_wrapper_port_enable(ctx->mmal->output[0], 0);
- CHECK_MMAL_STATUS(status, INIT, "failed to enable output port");
-
- LOG_DEBUG("encoder initialised (output chunk size %i)",
- ctx->mmal->output[0]->buffer_size);
- return BRCMJPEG_SUCCESS;
-
- error:
- return err;
- }
-
- static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *ctx)
- {
- MMAL_STATUS_T status;
- BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
-
- /* Create decoder component */
- status = mmal_wrapper_create(&ctx->mmal, MMAL_COMPONENT_IMAGE_DECODE);
- CHECK_MMAL_STATUS(status, INIT, "failed to create decoder");
- ctx->mmal->user_data = ctx;
- ctx->mmal->callback = brcmjpeg_mmal_cb;
-
- /* Configure things that won't change from decode to decode */
- ctx->mmal->input[0]->format->encoding = MMAL_ENCODING_JPEG;
- status = mmal_port_format_commit(ctx->mmal->input[0]);
- CHECK_MMAL_STATUS(status, INIT, "failed to commit input port format");
-
- ctx->mmal->input[0]->buffer_size = ctx->mmal->input[0]->buffer_size_min;
- ctx->mmal->input[0]->buffer_num = 3;
- status = mmal_wrapper_port_enable(ctx->mmal->input[0], 0);
- CHECK_MMAL_STATUS(status, INIT, "failed to enable input port");
-
- LOG_DEBUG("decoder initialised (input chunk size %i)",
- ctx->mmal->input[0]->buffer_size);
- return BRCMJPEG_SUCCESS;
-
- error:
- return BRCMJPEG_ERROR_INIT;
- }
-
- /* Configuration which needs to be done on a per encode basis */
- static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *ctx,
- BRCMJPEG_REQUEST_T *req)
- {
- MMAL_STATUS_T status = MMAL_SUCCESS;
- MMAL_FOURCC_T encoding = brcmjpeg_pixfmt_to_encoding(req->pixel_format);
- MMAL_PORT_T *port_in;
- BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
- MMAL_BOOL_T slice_mode = MMAL_FALSE;
-
- if (encoding == MMAL_ENCODING_UNKNOWN)
- status = MMAL_EINVAL;
- CHECK_MMAL_STATUS(status, INPUT_FORMAT, "format not supported (%i)",
- req->pixel_format);
-
- if (!req->buffer_width)
- req->buffer_width = req->width;
- if (!req->buffer_height)
- req->buffer_height = req->height;
- if (req->buffer_width < req->width || req->buffer_height < req->height)
- status = MMAL_EINVAL;
- CHECK_MMAL_STATUS(status, INPUT_FORMAT, "invalid buffer width/height "
- "(%i<=%i %i<=%i)", req->buffer_width, req->width, req->buffer_height,
- req->height);
-
- ctx->slice_height = 0;
- ctx->mmal->status = MMAL_SUCCESS;
- port_in = ctx->mmal->input[0];
-
- /* The input port needs to be re-configured to take into account
- * the properties of the new frame to encode */
- if (port_in->is_enabled)
- {
- status = mmal_wrapper_port_disable(port_in);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable input port");
- }
-
- port_in->format->encoding = encoding;
- port_in->format->es->video.width =
- port_in->format->es->video.crop.width = req->width;
- port_in->format->es->video.height =
- port_in->format->es->video.crop.height = req->height;
- port_in->buffer_num = 1;
-
- if (!req->input_handle &&
- (port_in->format->encoding == MMAL_ENCODING_I420 ||
- port_in->format->encoding == MMAL_ENCODING_I422))
- {
- if (port_in->format->encoding == MMAL_ENCODING_I420)
- port_in->format->encoding = MMAL_ENCODING_I420_SLICE;
- else if (port_in->format->encoding == MMAL_ENCODING_I422)
- port_in->format->encoding = MMAL_ENCODING_I422_SLICE;
- slice_mode = MMAL_TRUE;
- port_in->buffer_num = 3;
- }
-
- status = mmal_port_format_commit(port_in);
- CHECK_MMAL_STATUS(status, INPUT_FORMAT, "failed to commit input port format");
-
- ctx->slice_height = slice_mode ? 16 : port_in->format->es->video.height;
- port_in->buffer_size = port_in->buffer_size_min;
-
- if (req->input_handle)
- status = mmal_wrapper_port_enable(port_in, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY);
- else
- status = mmal_wrapper_port_enable(port_in, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable input port");
-
- mmal_port_parameter_set_uint32(ctx->mmal->output[0],
- MMAL_PARAMETER_JPEG_Q_FACTOR, req->quality);
-
- if (!ctx->mmal->output[0]->is_enabled)
- {
- status = mmal_wrapper_port_enable(ctx->mmal->output[0], 0);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port");
- }
-
- LOG_DEBUG("encoder configured (%4.4s:%ux%u|%ux%u slice: %u)",
- (char *)&port_in->format->encoding,
- port_in->format->es->video.crop.width, port_in->format->es->video.crop.height,
- port_in->format->es->video.width, port_in->format->es->video.height,
- ctx->slice_height);
- return BRCMJPEG_SUCCESS;
-
- error:
- return err;
- }
-
- /* Configuration which needs to be done on a per decode basis */
- static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *ctx,
- BRCMJPEG_REQUEST_T *req)
- {
- MMAL_STATUS_T status = MMAL_SUCCESS;
- MMAL_FOURCC_T encoding = brcmjpeg_pixfmt_to_encoding(req->pixel_format);
- MMAL_PORT_T *port_out;
- BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
-
- if (encoding != MMAL_ENCODING_I420 &&
- encoding != MMAL_ENCODING_I422 &&
- encoding != MMAL_ENCODING_RGBA)
- status = MMAL_EINVAL;
- CHECK_MMAL_STATUS(status, OUTPUT_FORMAT, "format not supported");
-
- ctx->slice_height = 0;
- ctx->mmal->status = MMAL_SUCCESS;
- port_out = ctx->mmal->output[0];
-
- /* The input port needs to be re-configured to take into account
- * the properties of the new frame to decode */
- if (port_out->is_enabled)
- {
- status = mmal_wrapper_port_disable(port_out);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable output port");
- }
-
- /* We assume that we do not know the format of the new jpeg to be decoded
- * and configure the input port for autodetecting the new format */
- port_out->format->encoding = encoding;
- port_out->format->es->video.width =
- port_out->format->es->video.crop.width = 0;
- port_out->format->es->video.height =
- port_out->format->es->video.crop.height = 0;
- status = mmal_port_format_commit(port_out);
- CHECK_MMAL_STATUS(status, OUTPUT_FORMAT, "failed to commit output port format");
-
- port_out->buffer_num = 1;
- if (req->output_handle)
- status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY);
- else
- status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port");
-
- LOG_DEBUG("decoder configured (%4.4s:%ux%u|%ux%u)", (char *)&port_out->format->encoding,
- port_out->format->es->video.crop.width, port_out->format->es->video.crop.height,
- port_out->format->es->video.width, port_out->format->es->video.height);
- return BRCMJPEG_SUCCESS;
-
- error:
- return err;
- }
-
- static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *ctx,
- BRCMJPEG_REQUEST_T *je)
- {
- BRCMJPEG_STATUS_T err;
- MMAL_STATUS_T status = MMAL_SUCCESS;
- MMAL_BUFFER_HEADER_T *in, *out;
- MMAL_BOOL_T eos = MMAL_FALSE;
- const uint8_t *outBuf = je->output;
- unsigned int loop = 0, slices = 0, outBufSize = je->output_alloc_size;
- MMAL_PORT_T *port_in = ctx->mmal->input[0];
- MMAL_PORT_T *port_out = ctx->mmal->output[0];
-
- je->output_size = 0;
- err = brcmjpeg_configure_encoder(ctx, je);
- if (err != BRCMJPEG_SUCCESS)
- return err;
-
- /* Then we read the encoded data back from the encoder */
-
- while (!eos && status == MMAL_SUCCESS)
- {
- /* send buffers to be filled */
- while (mmal_wrapper_buffer_get_empty(port_out, &out, 0) == MMAL_SUCCESS)
- {
- out->data = (uint8_t *)outBuf;
- out->alloc_size = MMAL_MIN(port_out->buffer_size, outBufSize);
- outBufSize -= out->alloc_size;
- outBuf += out->alloc_size;
- status = mmal_port_send_buffer(port_out, out);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to send buffer");
- }
-
- /* Send slices to be encoded */
- if (slices * ctx->slice_height < port_in->format->es->video.height &&
- mmal_wrapper_buffer_get_empty(port_in, &in, 0) == MMAL_SUCCESS)
- {
- if (je->input_handle)
- {
- in->data = (uint8_t *)je->input_handle;
- in->length = in->alloc_size = je->input_size;
- }
- else
- {
- in->length = brcmjpeg_copy_pixels(in->data, in->alloc_size,
- je->input, je->input_size, je->pixel_format,
- port_in->format->es->video.width,
- ctx->slice_height, je->buffer_width, je->buffer_height,
- slices * ctx->slice_height, 1);
- if (!in->length)
- status = MMAL_EINVAL;
- CHECK_MMAL_STATUS(status, INPUT_BUFFER, "input buffer too small");
- }
-
- slices++;
- if (slices * ctx->slice_height >= port_in->format->es->video.height)
- in->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
- status = mmal_port_send_buffer(port_in, in);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to send buffer");
- }
-
- status = mmal_wrapper_buffer_get_full(port_out, &out, 0);
- if (status == MMAL_EAGAIN)
- {
- status = MMAL_SUCCESS;
- WAIT(ctx);
- continue;
- }
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to get full buffer");
-
- LOG_DEBUG("received %i bytes", out->length);
- je->output_size += out->length;
- eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
-
- /* Detect when the encoder is running out of space for its output */
- if (++loop >= port_out->buffer_num && !eos && !out->length)
- {
- LOG_ERROR("no more output space for encoder");
- status = MMAL_EINVAL;
- }
-
- mmal_buffer_header_release(out);
- }
-
- /* Check if buffer was too small */
- CHECK_MMAL_STATUS(status, OUTPUT_BUFFER, "output buffer too small");
-
- LOG_DEBUG("encoded W:%ixH:%i:%i (%i bytes) in %i slices",
- je->width, je->height, je->pixel_format, je->output_size, slices);
- mmal_port_flush(port_out);
- return BRCMJPEG_SUCCESS;
-
- error:
- mmal_wrapper_port_disable(port_in);
- mmal_wrapper_port_disable(port_out);
- return err;
- }
-
- static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *ctx,
- BRCMJPEG_REQUEST_T *jd)
- {
- BRCMJPEG_STATUS_T err;
- MMAL_STATUS_T status;
- MMAL_BUFFER_HEADER_T *in, *out;
- MMAL_BOOL_T eos = MMAL_FALSE;
- const uint8_t *inBuf = jd->input;
- unsigned int slices = 0, inBufSize = jd->input_size;
- MMAL_PORT_T *port_in = ctx->mmal->input[0];
- MMAL_PORT_T *port_out = ctx->mmal->output[0];
- LOG_DEBUG("decode %i bytes", jd->input_size);
-
- jd->output_size = 0;
- err = brcmjpeg_configure_decoder(ctx, jd);
- if (err != BRCMJPEG_SUCCESS)
- return err;
-
- while (!eos)
- {
- /* Send as many chunks of data to decode as we can */
- while (inBufSize)
- {
- status = mmal_wrapper_buffer_get_empty(port_in, &in, 0);
- if (status == MMAL_EAGAIN)
- break;
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to get empty buffer (%i)", status);
-
- in->data = (uint8_t *)inBuf;
- in->length = MMAL_MIN(port_in->buffer_size, inBufSize);
- in->alloc_size = in->length;
- inBufSize -= in->length;
- inBuf += in->length;
- in->flags = inBufSize ? 0 : MMAL_BUFFER_HEADER_FLAG_EOS;
- LOG_DEBUG("send decode in (%i bytes)", in->length);
- status = mmal_port_send_buffer(port_in, in);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to send input buffer");
- }
-
- /* Check for decoded data */
- status = mmal_wrapper_buffer_get_full(port_out, &out, 0);
- if (status == MMAL_EAGAIN)
- {
- WAIT(ctx);
- continue;
- }
- CHECK_MMAL_STATUS(status, EXECUTE, "error decoding");
-
- /* Check if a new format has been auto-detected by the decoder */
- if (out->cmd == MMAL_EVENT_FORMAT_CHANGED)
- {
- MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(out);
-
- if (event)
- mmal_format_copy(port_out->format, event->format);
- mmal_buffer_header_release(out);
-
- if (!event)
- status = MMAL_EINVAL;
- CHECK_MMAL_STATUS(status, EXECUTE, "invalid format change event");
-
- LOG_DEBUG("new format (%4.4s:%ux%u|%ux%u)", (char *)&event->format->encoding,
- event->format->es->video.crop.width, event->format->es->video.crop.height,
- event->format->es->video.width, event->format->es->video.height);
-
- /* re-setup the output port for the new format */
- status = mmal_wrapper_port_disable(port_out);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable output port");
-
- ctx->slice_height = event->format->es->video.height;
- if (ENABLE_SLICE_MODE && !jd->output_handle)
- {
- /* setup slice mode */
- if (port_out->format->encoding == MMAL_ENCODING_I420 ||
- port_out->format->encoding == MMAL_ENCODING_I422)
- {
- if (port_out->format->encoding == MMAL_ENCODING_I420)
- port_out->format->encoding = MMAL_ENCODING_I420_SLICE;
- if (port_out->format->encoding == MMAL_ENCODING_I422)
- port_out->format->encoding = MMAL_ENCODING_I422_SLICE;
- ctx->slice_height = 16;
- port_out->buffer_num = 3;
- }
- }
-
- LOG_DEBUG("using slice size %u", ctx->slice_height);
- status = mmal_port_format_commit(port_out);
- CHECK_MMAL_STATUS(status, EXECUTE, "invalid format change event");
- port_out->buffer_size = port_out->buffer_size_min;
- if (jd->output_handle)
- status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY);
- else
- status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port");
-
- /* send all our output buffers to the decoder */
- while (mmal_wrapper_buffer_get_empty(port_out, &out, 0) == MMAL_SUCCESS)
- {
- if (jd->output_handle)
- {
- out->data = (uint8_t*)jd->output_handle;
- out->alloc_size = jd->output_alloc_size;
- }
- status = mmal_port_send_buffer(port_out, out);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to send output buffer");
- }
-
- continue;
- }
-
- /* We have part of our output frame */
- jd->width = port_out->format->es->video.crop.width;
- if (!jd->width)
- jd->width = port_out->format->es->video.width;
- if (jd->output_handle)
- jd->buffer_width = port_out->format->es->video.width;
- if (!jd->buffer_width)
- jd->buffer_width = jd->width;
- jd->height = port_out->format->es->video.crop.height;
- if (!jd->height)
- jd->height = port_out->format->es->video.height;
- if (jd->output_handle)
- jd->buffer_height = port_out->format->es->video.height;
- if (!jd->buffer_height)
- jd->buffer_height = jd->height;
-
- if (jd->output_handle)
- {
- jd->output_size += out->length;
- }
- else
- {
- jd->output_size = brcmjpeg_copy_pixels(jd->output, jd->output_alloc_size,
- out->data, out->length, jd->pixel_format,
- jd->buffer_width, jd->buffer_height,
- port_out->format->es->video.width,
- ctx->slice_height, slices * ctx->slice_height, 0);
- slices++;
- }
-
- eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
- out->length = 0;
- if (eos)
- {
- mmal_buffer_header_release(out);
- }
- else
- {
- status = mmal_port_send_buffer(port_out, out);
- CHECK_MMAL_STATUS(status, EXECUTE, "failed to send output buffer");
- }
-
- if (!jd->output_size)
- status = MMAL_EINVAL;
- CHECK_MMAL_STATUS(status, OUTPUT_BUFFER, "invalid output buffer");
- }
-
- LOG_DEBUG("decoded W:%ixH%i:(W%ixH%i):%i in %i slices",
- jd->width, jd->height, jd->buffer_width, jd->buffer_height,
- jd->pixel_format, slices);
- mmal_port_flush(port_in);
- return BRCMJPEG_SUCCESS;
-
- error:
- mmal_port_flush(port_in);
- return err;
- }
-
- /*****************************************************************************/
- static struct {
- BRCMJPEG_PIXEL_FORMAT_T pixel_format;
- MMAL_FOURCC_T encoding;
- } mmal_raw_conversion[] = {
- {PIXEL_FORMAT_I420, MMAL_ENCODING_I420},
- {PIXEL_FORMAT_YV12, MMAL_ENCODING_I420},
- {PIXEL_FORMAT_I422, MMAL_ENCODING_I422},
- {PIXEL_FORMAT_YV16, MMAL_ENCODING_I422},
- {PIXEL_FORMAT_YUYV, MMAL_ENCODING_I422},
- {PIXEL_FORMAT_RGBA, MMAL_ENCODING_RGBA},
- {PIXEL_FORMAT_UNKNOWN, MMAL_ENCODING_UNKNOWN} };
-
- static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T pixel_format)
- {
- unsigned int i;
- for (i = 0; mmal_raw_conversion[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
- if (mmal_raw_conversion[i].pixel_format == pixel_format)
- break;
- return mmal_raw_conversion[i].encoding;
- }
-
- // Copy a raw frame from 1 buffer to another, taking care of
- // stride / height differences between the input and output buffers.
- static unsigned int brcmjpeg_copy_pixels(uint8_t *out, unsigned int out_size,
- const uint8_t *in, unsigned int in_size, BRCMJPEG_PIXEL_FORMAT_T fmt,
- unsigned int out_width, unsigned int out_height,
- unsigned int in_width, unsigned int in_height,
- unsigned int line_offset, unsigned int convert_from)
- {
- struct {
- uint8_t *data;
- unsigned int pitch;
- unsigned int height;
- } planes[2][3];
- unsigned int num_planes = 0;
- unsigned int i, size = 0;
- unsigned int in_height_full = in_height;
- unsigned int out_height_full = out_height;
- unsigned int k = convert_from ? 1 : 0;
-
- // Sanity check line_offset
- if (line_offset >= (convert_from ? in_height : out_height))
- return 0;
-
- if (convert_from)
- in_height -= line_offset;
- else
- out_height -= line_offset;
-
- if (fmt == PIXEL_FORMAT_I420 ||
- fmt == PIXEL_FORMAT_YV12)
- {
- planes[0][0].data = out;
- planes[0][0].pitch = out_width;
- planes[0][0].height = out_height;
-
- planes[1][0].data = (uint8_t *)in;
- planes[1][0].pitch = in_width;
- planes[1][0].height = in_height;
-
- planes[0][1].pitch = planes[0][2].pitch = out_width / 2;
- planes[0][1].height = planes[0][2].height = out_height / 2;
- planes[0][1].data = planes[0][0].data + out_width * out_height_full;
- planes[0][2].data = planes[0][1].data + out_width * out_height_full / 4;
-
- planes[1][1].pitch = planes[1][2].pitch = in_width / 2;
- planes[1][1].height = planes[1][2].height = in_height / 2;
- planes[1][1].data = planes[1][0].data + in_width * in_height_full;
- planes[1][2].data = planes[1][1].data + in_width * in_height_full / 4;
-
- if (fmt == PIXEL_FORMAT_YV12)
- {
- // We need to swap U and V
- uint8_t *tmp = planes[1][2].data;
- planes[1][2].data = planes[1][1].data;
- planes[1][1].data = tmp;
- }
-
- // Add the line offset
- planes[k][0].data += planes[k][0].pitch * line_offset;
- planes[k][1].data += planes[k][1].pitch * line_offset/2;
- planes[k][2].data += planes[k][2].pitch * line_offset/2;
-
- num_planes = 3;
- size = out_width * out_height_full * 3 / 2;
-
- if (in_size < in_width * in_height * 3 / 2)
- return 0;
-
- } else if (fmt == PIXEL_FORMAT_I422 ||
- fmt == PIXEL_FORMAT_YV16 ||
- fmt == PIXEL_FORMAT_YUYV)
- {
- planes[0][0].data = out;
- planes[0][0].pitch = out_width;
- planes[0][0].height = out_height;
-
- planes[1][0].data = (uint8_t *)in;
- planes[1][0].pitch = in_width;
- planes[1][0].height = in_height;
-
- planes[0][1].pitch = planes[0][2].pitch = out_width / 2;
- planes[0][1].height = planes[0][2].height = out_height;
- planes[0][1].data = planes[0][0].data + out_width * out_height_full;
- planes[0][2].data = planes[0][1].data + out_width * out_height_full / 2;
-
- planes[1][1].pitch = planes[1][2].pitch = in_width / 2;
- planes[1][1].height = planes[1][2].height = in_height;
- planes[1][1].data = planes[1][0].data + in_width * in_height_full;
- planes[1][2].data = planes[1][1].data + in_width * in_height_full / 2;
-
- // Add the line offset
- planes[k][0].data += planes[k][0].pitch * line_offset;
- planes[k][1].data += planes[k][1].pitch * line_offset;
- planes[k][2].data += planes[k][2].pitch * line_offset;
- if (fmt == PIXEL_FORMAT_YUYV)
- planes[k][0].data += planes[k][0].pitch * line_offset;
-
- if (fmt == PIXEL_FORMAT_YV16)
- {
- // We need to swap U and V
- uint8_t *tmp = planes[1][2].data;
- planes[1][2].data = planes[1][1].data;
- planes[1][1].data = tmp;
- }
-
- num_planes = 3;
- size = out_width * out_height_full * 2;
-
- if (in_size < in_width * in_height * 2)
- return 0;
- } else if (fmt == PIXEL_FORMAT_RGBA)
- {
- planes[0][0].data = out;
- planes[0][0].pitch = out_width * 4;
- planes[0][0].height = out_height;
-
- planes[1][0].data = (uint8_t *)in;
- planes[1][0].pitch = in_width * 4;
- planes[1][0].height = in_height;
-
- // Add the line offset
- planes[k][0].data += planes[k][0].pitch * line_offset;
-
- num_planes = 1;
- size = out_width * out_height_full * 4;
-
- if (in_size < in_width * in_height * 4)
- return 0;
- }
-
- if (out_size < size)
- return 0;
-
- // Special case for YUYV where don't just copy but convert to/from I422
- if (fmt == PIXEL_FORMAT_YUYV)
- {
- unsigned int width = in_width > out_width ? out_width : in_width;
- unsigned int height = in_height > out_height ? out_height : in_height;
- uint8_t *y = planes[convert_from ? 0 : 1][0].data;
- uint8_t *u = planes[convert_from ? 0 : 1][1].data;
- uint8_t *v = planes[convert_from ? 0 : 1][2].data;
- uint8_t *yuyv = planes[convert_from ? 1 : 0][0].data;
- unsigned int y_diff = (convert_from ? out_width : in_width) - width;
- unsigned int yuyv_diff = ((convert_from ? in_width : out_width) - width) * 2;
-
- while (height--)
- {
- if (convert_from)
- for (i = width / 2; i; i--)
- {
- *y++ = *yuyv++;
- *u++ = *yuyv++;
- *y++ = *yuyv++;
- *v++ = *yuyv++;
- }
- else
- for (i = width / 2; i; i--)
- {
- *yuyv++ = *y++;
- *yuyv++ = *u++;
- *yuyv++ = *y++;
- *yuyv++ = *v++;
- }
-
- yuyv += yuyv_diff;
- y += y_diff;
- u += y_diff >> 1;
- v += y_diff >> 1;
- }
-
- return size;
- }
-
- for (i = 0; i < num_planes; i++)
- {
- unsigned int width = MMAL_MIN(planes[0][i].pitch, planes[1][i].pitch);
- unsigned int height = MMAL_MIN(planes[0][i].height, planes[1][i].height);
- uint8_t *data_out = planes[0][i].data;
- uint8_t *data_in = planes[1][i].data;
-
- while (height--)
- {
- memcpy(data_out, data_in, width);
- data_out += planes[0][i].pitch;
- data_in += planes[1][i].pitch;
- }
- }
-
- return size;
- }
|