1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/** \file
29 * Jpeg encoder and decoder library using the hardware jpeg codec
30 */
31
32#include "mmal.h"
33#include "util/mmal_component_wrapper.h"
34#include "util/mmal_util_params.h"
35#include "mmal_logging.h"
36#include "brcmjpeg.h"
37
38/*******************************************************************************
39* Defines
40*******************************************************************************/
41#define MMAL_COMPONENT_IMAGE_DECODE "vc.aggregator.pipeline:ril.image_decode:video_convert"
42#define MMAL_COMPONENT_IMAGE_ENCODE "vc.ril.image_encode"
43
44#define ENABLE_SLICE_MODE 0
45
46#define CHECK_MMAL_STATUS(status, jerr, msg, ...) \
47 if (status != MMAL_SUCCESS) {LOG_ERROR(msg, ## __VA_ARGS__); \
48 err = BRCMJPEG_ERROR_##jerr; goto error;}
49
50/*******************************************************************************
51* Type definitions
52*******************************************************************************/
53struct BRCMJPEG_T
54{
55 BRCMJPEG_TYPE_T type;
56 unsigned int ref_count;
57 unsigned int init;
58
59 MMAL_WRAPPER_T *mmal;
60 unsigned int slice_height;
61
62 VCOS_MUTEX_T lock;
63 VCOS_MUTEX_T process_lock;
64 VCOS_SEMAPHORE_T sema;
65};
66
67/*******************************************************************************
68* Local prototypes
69*******************************************************************************/
70static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *);
71static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *);
72static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
73static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
74static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
75static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *);
76static void brcmjpeg_destroy(BRCMJPEG_T *);
77
78static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T);
79static unsigned int brcmjpeg_copy_pixels(uint8_t *out, unsigned int out_size,
80 const uint8_t *in, unsigned int in_size, BRCMJPEG_PIXEL_FORMAT_T fmt,
81 unsigned int out_width, unsigned int out_height,
82 unsigned int in_width, unsigned int in_height,
83 unsigned int line_offset, unsigned int convert_from);
84
85static BRCMJPEG_T *brcmjpeg_encoder = NULL;
86static BRCMJPEG_T *brcmjpeg_decoder = NULL;
87
88/*******************************************************************************
89* Platform specific code
90*******************************************************************************/
91static VCOS_ONCE_T once = VCOS_ONCE_INIT;
92static VCOS_MUTEX_T brcmjpeg_lock;
93
94static void brcmjpeg_init_once(void)
95{
96 vcos_mutex_create(&brcmjpeg_lock, VCOS_FUNCTION);
97}
98
99#define LOCK() vcos_mutex_lock(&brcmjpeg_lock)
100#define UNLOCK() vcos_mutex_unlock(&brcmjpeg_lock)
101#define LOCK_COMP(ctx) vcos_mutex_lock(&(ctx)->lock)
102#define UNLOCK_COMP(ctx) vcos_mutex_unlock(&(ctx)->lock)
103#define LOCK_PROCESS(ctx) vcos_mutex_lock(&(ctx)->process_lock)
104#define UNLOCK_PROCESS(ctx) vcos_mutex_unlock(&(ctx)->process_lock)
105#define WAIT(ctx) vcos_semaphore_wait(&(ctx)->sema)
106#define SIGNAL(ctx) vcos_semaphore_post(&(ctx)->sema)
107
108/*******************************************************************************
109* Implementation
110*******************************************************************************/
111
112BRCMJPEG_STATUS_T brcmjpeg_create(BRCMJPEG_TYPE_T type, BRCMJPEG_T **ctx)
113{
114 BRCMJPEG_STATUS_T status = BRCMJPEG_SUCCESS;
115 BRCMJPEG_T **comp;
116
117 if (type == BRCMJPEG_TYPE_ENCODER)
118 comp = &brcmjpeg_encoder;
119 else
120 comp = &brcmjpeg_decoder;
121
122 vcos_once(&once, brcmjpeg_init_once);
123 LOCK();
124 if (!*comp)
125 {
126 int init1, init2, init3;
127 *comp = calloc(sizeof(BRCMJPEG_T), 1);
128 if (!*comp)
129 {
130 UNLOCK();
131 return BRCMJPEG_ERROR_NOMEM;
132 }
133 (*comp)->type = type;
134 init1 = vcos_mutex_create(&(*comp)->lock, "brcmjpeg lock") != VCOS_SUCCESS;
135 init2 = vcos_mutex_create(&(*comp)->process_lock, "brcmjpeg process lock") != VCOS_SUCCESS;
136 init3 = vcos_semaphore_create(&(*comp)->sema, "brcmjpeg sema", 0) != VCOS_SUCCESS;
137 if (init1 | init2 | init3)
138 {
139 if (init1) vcos_mutex_delete(&(*comp)->lock);
140 if (init2) vcos_mutex_delete(&(*comp)->process_lock);
141 if (init3) vcos_semaphore_delete(&(*comp)->sema);
142 free(comp);
143 UNLOCK();
144 return BRCMJPEG_ERROR_NOMEM;
145 }
146 }
147 (*comp)->ref_count++;
148 UNLOCK();
149
150 LOCK_COMP(*comp);
151 if (!(*comp)->init)
152 {
153 if (type == BRCMJPEG_TYPE_ENCODER)
154 status = brcmjpeg_init_encoder(*comp);
155 else
156 status = brcmjpeg_init_decoder(*comp);
157
158 (*comp)->init = status == BRCMJPEG_SUCCESS;
159 }
160 UNLOCK_COMP(*comp);
161
162 if (status != BRCMJPEG_SUCCESS)
163 brcmjpeg_release(*comp);
164
165 *ctx = *comp;
166 return status;
167}
168
169void brcmjpeg_acquire(BRCMJPEG_T *ctx)
170{
171 LOCK_COMP(ctx);
172 ctx->ref_count++;
173 UNLOCK_COMP(ctx);
174}
175
176void brcmjpeg_release(BRCMJPEG_T *ctx)
177{
178 LOCK_COMP(ctx);
179 if (--ctx->ref_count)
180 {
181 UNLOCK_COMP(ctx);
182 return;
183 }
184
185 LOCK();
186 if (ctx->type == BRCMJPEG_TYPE_ENCODER)
187 brcmjpeg_encoder = NULL;
188 else
189 brcmjpeg_decoder = NULL;
190 UNLOCK();
191 UNLOCK_COMP(ctx);
192
193 brcmjpeg_destroy(ctx);
194 return;
195}
196
197BRCMJPEG_STATUS_T brcmjpeg_process(BRCMJPEG_T *ctx, BRCMJPEG_REQUEST_T *req)
198{
199 BRCMJPEG_STATUS_T status;
200
201 /* Sanity check */
202 if ((req->input && req->input_handle) ||
203 (req->output && req->output_handle))
204 {
205 LOG_ERROR("buffer pointer and handle both set (%p/%u %p/%u)",
206 req->input, req->input_handle, req->output, req->output_handle);
207 return BRCMJPEG_ERROR_REQUEST;
208 }
209
210 LOCK_PROCESS(ctx);
211 if (ctx->type == BRCMJPEG_TYPE_ENCODER)
212 status = brcmjpeg_encode(ctx, req);
213 else
214 status = brcmjpeg_decode(ctx, req);
215 UNLOCK_PROCESS(ctx);
216
217 return status;
218}
219
220static void brcmjpeg_destroy(BRCMJPEG_T *ctx)
221{
222 if (ctx->mmal)
223 mmal_wrapper_destroy(ctx->mmal);
224 vcos_mutex_delete(&ctx->lock);
225 vcos_mutex_delete(&ctx->process_lock);
226 vcos_semaphore_delete(&ctx->sema);
227 free(ctx);
228}
229
230static void brcmjpeg_mmal_cb(MMAL_WRAPPER_T *wrapper)
231{
232 BRCMJPEG_T *ctx = wrapper->user_data;
233 SIGNAL(ctx);
234}
235
236static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *ctx)
237{
238 MMAL_STATUS_T status;
239 BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
240
241 /* Create encoder component */
242 status = mmal_wrapper_create(&ctx->mmal, MMAL_COMPONENT_IMAGE_ENCODE);
243 CHECK_MMAL_STATUS(status, INIT, "failed to create encoder");
244 ctx->mmal->user_data = ctx;
245 ctx->mmal->callback = brcmjpeg_mmal_cb;
246
247 /* Configure things that won't change from encode to encode */
248 mmal_port_parameter_set_boolean(ctx->mmal->control,
249 MMAL_PARAMETER_EXIF_DISABLE, MMAL_TRUE);
250
251 ctx->mmal->output[0]->format->encoding = MMAL_ENCODING_JPEG;
252 status = mmal_port_format_commit(ctx->mmal->output[0]);
253 CHECK_MMAL_STATUS(status, INIT, "failed to commit output port format");
254
255 ctx->mmal->output[0]->buffer_size = ctx->mmal->output[0]->buffer_size_min;
256 ctx->mmal->output[0]->buffer_num = 3;
257 status = mmal_wrapper_port_enable(ctx->mmal->output[0], 0);
258 CHECK_MMAL_STATUS(status, INIT, "failed to enable output port");
259
260 LOG_DEBUG("encoder initialised (output chunk size %i)",
261 ctx->mmal->output[0]->buffer_size);
262 return BRCMJPEG_SUCCESS;
263
264 error:
265 return err;
266}
267
268static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *ctx)
269{
270 MMAL_STATUS_T status;
271 BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
272
273 /* Create decoder component */
274 status = mmal_wrapper_create(&ctx->mmal, MMAL_COMPONENT_IMAGE_DECODE);
275 CHECK_MMAL_STATUS(status, INIT, "failed to create decoder");
276 ctx->mmal->user_data = ctx;
277 ctx->mmal->callback = brcmjpeg_mmal_cb;
278
279 /* Configure things that won't change from decode to decode */
280 ctx->mmal->input[0]->format->encoding = MMAL_ENCODING_JPEG;
281 status = mmal_port_format_commit(ctx->mmal->input[0]);
282 CHECK_MMAL_STATUS(status, INIT, "failed to commit input port format");
283
284 ctx->mmal->input[0]->buffer_size = ctx->mmal->input[0]->buffer_size_min;
285 ctx->mmal->input[0]->buffer_num = 3;
286 status = mmal_wrapper_port_enable(ctx->mmal->input[0], 0);
287 CHECK_MMAL_STATUS(status, INIT, "failed to enable input port");
288
289 LOG_DEBUG("decoder initialised (input chunk size %i)",
290 ctx->mmal->input[0]->buffer_size);
291 return BRCMJPEG_SUCCESS;
292
293 error:
294 return BRCMJPEG_ERROR_INIT;
295}
296
297/* Configuration which needs to be done on a per encode basis */
298static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *ctx,
299 BRCMJPEG_REQUEST_T *req)
300{
301 MMAL_STATUS_T status = MMAL_SUCCESS;
302 MMAL_FOURCC_T encoding = brcmjpeg_pixfmt_to_encoding(req->pixel_format);
303 MMAL_PORT_T *port_in;
304 BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
305 MMAL_BOOL_T slice_mode = MMAL_FALSE;
306
307 if (encoding == MMAL_ENCODING_UNKNOWN)
308 status = MMAL_EINVAL;
309 CHECK_MMAL_STATUS(status, INPUT_FORMAT, "format not supported (%i)",
310 req->pixel_format);
311
312 if (!req->buffer_width)
313 req->buffer_width = req->width;
314 if (!req->buffer_height)
315 req->buffer_height = req->height;
316 if (req->buffer_width < req->width || req->buffer_height < req->height)
317 status = MMAL_EINVAL;
318 CHECK_MMAL_STATUS(status, INPUT_FORMAT, "invalid buffer width/height "
319 "(%i<=%i %i<=%i)", req->buffer_width, req->width, req->buffer_height,
320 req->height);
321
322 ctx->slice_height = 0;
323 ctx->mmal->status = MMAL_SUCCESS;
324 port_in = ctx->mmal->input[0];
325
326 /* The input port needs to be re-configured to take into account
327 * the properties of the new frame to encode */
328 if (port_in->is_enabled)
329 {
330 status = mmal_wrapper_port_disable(port_in);
331 CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable input port");
332 }
333
334 port_in->format->encoding = encoding;
335 port_in->format->es->video.width =
336 port_in->format->es->video.crop.width = req->width;
337 port_in->format->es->video.height =
338 port_in->format->es->video.crop.height = req->height;
339 port_in->buffer_num = 1;
340
341 if (!req->input_handle &&
342 (port_in->format->encoding == MMAL_ENCODING_I420 ||
343 port_in->format->encoding == MMAL_ENCODING_I422))
344 {
345 if (port_in->format->encoding == MMAL_ENCODING_I420)
346 port_in->format->encoding = MMAL_ENCODING_I420_SLICE;
347 else if (port_in->format->encoding == MMAL_ENCODING_I422)
348 port_in->format->encoding = MMAL_ENCODING_I422_SLICE;
349 slice_mode = MMAL_TRUE;
350 port_in->buffer_num = 3;
351 }
352
353 status = mmal_port_format_commit(port_in);
354 CHECK_MMAL_STATUS(status, INPUT_FORMAT, "failed to commit input port format");
355
356 ctx->slice_height = slice_mode ? 16 : port_in->format->es->video.height;
357 port_in->buffer_size = port_in->buffer_size_min;
358
359 if (req->input_handle)
360 status = mmal_wrapper_port_enable(port_in, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY);
361 else
362 status = mmal_wrapper_port_enable(port_in, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE);
363 CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable input port");
364
365 mmal_port_parameter_set_uint32(ctx->mmal->output[0],
366 MMAL_PARAMETER_JPEG_Q_FACTOR, req->quality);
367
368 if (!ctx->mmal->output[0]->is_enabled)
369 {
370 status = mmal_wrapper_port_enable(ctx->mmal->output[0], 0);
371 CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port");
372 }
373
374 LOG_DEBUG("encoder configured (%4.4s:%ux%u|%ux%u slice: %u)",
375 (char *)&port_in->format->encoding,
376 port_in->format->es->video.crop.width, port_in->format->es->video.crop.height,
377 port_in->format->es->video.width, port_in->format->es->video.height,
378 ctx->slice_height);
379 return BRCMJPEG_SUCCESS;
380
381 error:
382 return err;
383}
384
385/* Configuration which needs to be done on a per decode basis */
386static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *ctx,
387 BRCMJPEG_REQUEST_T *req)
388{
389 MMAL_STATUS_T status = MMAL_SUCCESS;
390 MMAL_FOURCC_T encoding = brcmjpeg_pixfmt_to_encoding(req->pixel_format);
391 MMAL_PORT_T *port_out;
392 BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS;
393
394 if (encoding != MMAL_ENCODING_I420 &&
395 encoding != MMAL_ENCODING_I422 &&
396 encoding != MMAL_ENCODING_RGBA)
397 status = MMAL_EINVAL;
398 CHECK_MMAL_STATUS(status, OUTPUT_FORMAT, "format not supported");
399
400 ctx->slice_height = 0;
401 ctx->mmal->status = MMAL_SUCCESS;
402 port_out = ctx->mmal->output[0];
403
404 /* The input port needs to be re-configured to take into account
405 * the properties of the new frame to decode */
406 if (port_out->is_enabled)
407 {
408 status = mmal_wrapper_port_disable(port_out);
409 CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable output port");
410 }
411
412 /* We assume that we do not know the format of the new jpeg to be decoded
413 * and configure the input port for autodetecting the new format */
414 port_out->format->encoding = encoding;
415 port_out->format->es->video.width =
416 port_out->format->es->video.crop.width = 0;
417 port_out->format->es->video.height =
418 port_out->format->es->video.crop.height = 0;
419 status = mmal_port_format_commit(port_out);
420 CHECK_MMAL_STATUS(status, OUTPUT_FORMAT, "failed to commit output port format");
421
422 port_out->buffer_num = 1;
423 if (req->output_handle)
424 status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY);
425 else
426 status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE);
427 CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port");
428
429 LOG_DEBUG("decoder configured (%4.4s:%ux%u|%ux%u)", (char *)&port_out->format->encoding,
430 port_out->format->es->video.crop.width, port_out->format->es->video.crop.height,
431 port_out->format->es->video.width, port_out->format->es->video.height);
432 return BRCMJPEG_SUCCESS;
433
434 error:
435 return err;
436}
437
438static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *ctx,
439 BRCMJPEG_REQUEST_T *je)
440{
441 BRCMJPEG_STATUS_T err;
442 MMAL_STATUS_T status = MMAL_SUCCESS;
443 MMAL_BUFFER_HEADER_T *in, *out;
444 MMAL_BOOL_T eos = MMAL_FALSE;
445 const uint8_t *outBuf = je->output;
446 unsigned int loop = 0, slices = 0, outBufSize = je->output_alloc_size;
447 MMAL_PORT_T *port_in = ctx->mmal->input[0];
448 MMAL_PORT_T *port_out = ctx->mmal->output[0];
449
450 je->output_size = 0;
451 err = brcmjpeg_configure_encoder(ctx, je);
452 if (err != BRCMJPEG_SUCCESS)
453 return err;
454
455 /* Then we read the encoded data back from the encoder */
456
457 while (!eos && status == MMAL_SUCCESS)
458 {
459 /* send buffers to be filled */
460 while (mmal_wrapper_buffer_get_empty(port_out, &out, 0) == MMAL_SUCCESS)
461 {
462 out->data = (uint8_t *)outBuf;
463 out->alloc_size = MMAL_MIN(port_out->buffer_size, outBufSize);
464 outBufSize -= out->alloc_size;
465 outBuf += out->alloc_size;
466 status = mmal_port_send_buffer(port_out, out);
467 CHECK_MMAL_STATUS(status, EXECUTE, "failed to send buffer");
468 }
469
470 /* Send slices to be encoded */
471 if (slices * ctx->slice_height < port_in->format->es->video.height &&
472 mmal_wrapper_buffer_get_empty(port_in, &in, 0) == MMAL_SUCCESS)
473 {
474 if (je->input_handle)
475 {
476 in->data = (uint8_t *)je->input_handle;
477 in->length = in->alloc_size = je->input_size;
478 }
479 else
480 {
481 in->length = brcmjpeg_copy_pixels(in->data, in->alloc_size,
482 je->input, je->input_size, je->pixel_format,
483 port_in->format->es->video.width,
484 ctx->slice_height, je->buffer_width, je->buffer_height,
485 slices * ctx->slice_height, 1);
486 if (!in->length)
487 status = MMAL_EINVAL;
488 CHECK_MMAL_STATUS(status, INPUT_BUFFER, "input buffer too small");
489 }
490
491 slices++;
492 if (slices * ctx->slice_height >= port_in->format->es->video.height)
493 in->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
494 status = mmal_port_send_buffer(port_in, in);
495 CHECK_MMAL_STATUS(status, EXECUTE, "failed to send buffer");
496 }
497
498 status = mmal_wrapper_buffer_get_full(port_out, &out, 0);
499 if (status == MMAL_EAGAIN)
500 {
501 status = MMAL_SUCCESS;
502 WAIT(ctx);
503 continue;
504 }
505 CHECK_MMAL_STATUS(status, EXECUTE, "failed to get full buffer");
506
507 LOG_DEBUG("received %i bytes", out->length);
508 je->output_size += out->length;
509 eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
510
511 /* Detect when the encoder is running out of space for its output */
512 if (++loop >= port_out->buffer_num && !eos && !out->length)
513 {
514 LOG_ERROR("no more output space for encoder");
515 status = MMAL_EINVAL;
516 }
517
518 mmal_buffer_header_release(out);
519 }
520
521 /* Check if buffer was too small */
522 CHECK_MMAL_STATUS(status, OUTPUT_BUFFER, "output buffer too small");
523
524 LOG_DEBUG("encoded W:%ixH:%i:%i (%i bytes) in %i slices",
525 je->width, je->height, je->pixel_format, je->output_size, slices);
526 mmal_port_flush(port_out);
527 return BRCMJPEG_SUCCESS;
528
529 error:
530 mmal_wrapper_port_disable(port_in);
531 mmal_wrapper_port_disable(port_out);
532 return err;
533}
534
535static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *ctx,
536 BRCMJPEG_REQUEST_T *jd)
537{
538 BRCMJPEG_STATUS_T err;
539 MMAL_STATUS_T status;
540 MMAL_BUFFER_HEADER_T *in, *out;
541 MMAL_BOOL_T eos = MMAL_FALSE;
542 const uint8_t *inBuf = jd->input;
543 unsigned int slices = 0, inBufSize = jd->input_size;
544 MMAL_PORT_T *port_in = ctx->mmal->input[0];
545 MMAL_PORT_T *port_out = ctx->mmal->output[0];
546 LOG_DEBUG("decode %i bytes", jd->input_size);
547
548 jd->output_size = 0;
549 err = brcmjpeg_configure_decoder(ctx, jd);
550 if (err != BRCMJPEG_SUCCESS)
551 return err;
552
553 while (!eos)
554 {
555 /* Send as many chunks of data to decode as we can */
556 while (inBufSize)
557 {
558 status = mmal_wrapper_buffer_get_empty(port_in, &in, 0);
559 if (status == MMAL_EAGAIN)
560 break;
561 CHECK_MMAL_STATUS(status, EXECUTE, "failed to get empty buffer (%i)", status);
562
563 in->data = (uint8_t *)inBuf;
564 in->length = MMAL_MIN(port_in->buffer_size, inBufSize);
565 in->alloc_size = in->length;
566 inBufSize -= in->length;
567 inBuf += in->length;
568 in->flags = inBufSize ? 0 : MMAL_BUFFER_HEADER_FLAG_EOS;
569 LOG_DEBUG("send decode in (%i bytes)", in->length);
570 status = mmal_port_send_buffer(port_in, in);
571 CHECK_MMAL_STATUS(status, EXECUTE, "failed to send input buffer");
572 }
573
574 /* Check for decoded data */
575 status = mmal_wrapper_buffer_get_full(port_out, &out, 0);
576 if (status == MMAL_EAGAIN)
577 {
578 WAIT(ctx);
579 continue;
580 }
581 CHECK_MMAL_STATUS(status, EXECUTE, "error decoding");
582
583 /* Check if a new format has been auto-detected by the decoder */
584 if (out->cmd == MMAL_EVENT_FORMAT_CHANGED)
585 {
586 MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(out);
587
588 if (event)
589 mmal_format_copy(port_out->format, event->format);
590 mmal_buffer_header_release(out);
591
592 if (!event)
593 status = MMAL_EINVAL;
594 CHECK_MMAL_STATUS(status, EXECUTE, "invalid format change event");
595
596 LOG_DEBUG("new format (%4.4s:%ux%u|%ux%u)", (char *)&event->format->encoding,
597 event->format->es->video.crop.width, event->format->es->video.crop.height,
598 event->format->es->video.width, event->format->es->video.height);
599
600 /* re-setup the output port for the new format */
601 status = mmal_wrapper_port_disable(port_out);
602 CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable output port");
603
604 ctx->slice_height = event->format->es->video.height;
605 if (ENABLE_SLICE_MODE && !jd->output_handle)
606 {
607 /* setup slice mode */
608 if (port_out->format->encoding == MMAL_ENCODING_I420 ||
609 port_out->format->encoding == MMAL_ENCODING_I422)
610 {
611 if (port_out->format->encoding == MMAL_ENCODING_I420)
612 port_out->format->encoding = MMAL_ENCODING_I420_SLICE;
613 if (port_out->format->encoding == MMAL_ENCODING_I422)
614 port_out->format->encoding = MMAL_ENCODING_I422_SLICE;
615 ctx->slice_height = 16;
616 port_out->buffer_num = 3;
617 }
618 }
619
620 LOG_DEBUG("using slice size %u", ctx->slice_height);
621 status = mmal_port_format_commit(port_out);
622 CHECK_MMAL_STATUS(status, EXECUTE, "invalid format change event");
623 port_out->buffer_size = port_out->buffer_size_min;
624 if (jd->output_handle)
625 status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY);
626 else
627 status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE);
628 CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port");
629
630 /* send all our output buffers to the decoder */
631 while (mmal_wrapper_buffer_get_empty(port_out, &out, 0) == MMAL_SUCCESS)
632 {
633 if (jd->output_handle)
634 {
635 out->data = (uint8_t*)jd->output_handle;
636 out->alloc_size = jd->output_alloc_size;
637 }
638 status = mmal_port_send_buffer(port_out, out);
639 CHECK_MMAL_STATUS(status, EXECUTE, "failed to send output buffer");
640 }
641
642 continue;
643 }
644
645 /* We have part of our output frame */
646 jd->width = port_out->format->es->video.crop.width;
647 if (!jd->width)
648 jd->width = port_out->format->es->video.width;
649 if (jd->output_handle)
650 jd->buffer_width = port_out->format->es->video.width;
651 if (!jd->buffer_width)
652 jd->buffer_width = jd->width;
653 jd->height = port_out->format->es->video.crop.height;
654 if (!jd->height)
655 jd->height = port_out->format->es->video.height;
656 if (jd->output_handle)
657 jd->buffer_height = port_out->format->es->video.height;
658 if (!jd->buffer_height)
659 jd->buffer_height = jd->height;
660
661 if (jd->output_handle)
662 {
663 jd->output_size += out->length;
664 }
665 else
666 {
667 jd->output_size = brcmjpeg_copy_pixels(jd->output, jd->output_alloc_size,
668 out->data, out->length, jd->pixel_format,
669 jd->buffer_width, jd->buffer_height,
670 port_out->format->es->video.width,
671 ctx->slice_height, slices * ctx->slice_height, 0);
672 slices++;
673 }
674
675 eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
676 out->length = 0;
677 if (eos)
678 {
679 mmal_buffer_header_release(out);
680 }
681 else
682 {
683 status = mmal_port_send_buffer(port_out, out);
684 CHECK_MMAL_STATUS(status, EXECUTE, "failed to send output buffer");
685 }
686
687 if (!jd->output_size)
688 status = MMAL_EINVAL;
689 CHECK_MMAL_STATUS(status, OUTPUT_BUFFER, "invalid output buffer");
690 }
691
692 LOG_DEBUG("decoded W:%ixH%i:(W%ixH%i):%i in %i slices",
693 jd->width, jd->height, jd->buffer_width, jd->buffer_height,
694 jd->pixel_format, slices);
695 mmal_port_flush(port_in);
696 return BRCMJPEG_SUCCESS;
697
698 error:
699 mmal_port_flush(port_in);
700 return err;
701}
702
703/*****************************************************************************/
704static struct {
705 BRCMJPEG_PIXEL_FORMAT_T pixel_format;
706 MMAL_FOURCC_T encoding;
707} mmal_raw_conversion[] = {
708 {PIXEL_FORMAT_I420, MMAL_ENCODING_I420},
709 {PIXEL_FORMAT_YV12, MMAL_ENCODING_I420},
710 {PIXEL_FORMAT_I422, MMAL_ENCODING_I422},
711 {PIXEL_FORMAT_YV16, MMAL_ENCODING_I422},
712 {PIXEL_FORMAT_YUYV, MMAL_ENCODING_I422},
713 {PIXEL_FORMAT_RGBA, MMAL_ENCODING_RGBA},
714 {PIXEL_FORMAT_UNKNOWN, MMAL_ENCODING_UNKNOWN} };
715
716static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T pixel_format)
717{
718 unsigned int i;
719 for (i = 0; mmal_raw_conversion[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
720 if (mmal_raw_conversion[i].pixel_format == pixel_format)
721 break;
722 return mmal_raw_conversion[i].encoding;
723}
724
725// Copy a raw frame from 1 buffer to another, taking care of
726// stride / height differences between the input and output buffers.
727static unsigned int brcmjpeg_copy_pixels(uint8_t *out, unsigned int out_size,
728 const uint8_t *in, unsigned int in_size, BRCMJPEG_PIXEL_FORMAT_T fmt,
729 unsigned int out_width, unsigned int out_height,
730 unsigned int in_width, unsigned int in_height,
731 unsigned int line_offset, unsigned int convert_from)
732{
733 struct {
734 uint8_t *data;
735 unsigned int pitch;
736 unsigned int height;
737 } planes[2][3];
738 unsigned int num_planes = 0;
739 unsigned int i, size = 0;
740 unsigned int in_height_full = in_height;
741 unsigned int out_height_full = out_height;
742 unsigned int k = convert_from ? 1 : 0;
743
744 // Sanity check line_offset
745 if (line_offset >= (convert_from ? in_height : out_height))
746 return 0;
747
748 if (convert_from)
749 in_height -= line_offset;
750 else
751 out_height -= line_offset;
752
753 if (fmt == PIXEL_FORMAT_I420 ||
754 fmt == PIXEL_FORMAT_YV12)
755 {
756 planes[0][0].data = out;
757 planes[0][0].pitch = out_width;
758 planes[0][0].height = out_height;
759
760 planes[1][0].data = (uint8_t *)in;
761 planes[1][0].pitch = in_width;
762 planes[1][0].height = in_height;
763
764 planes[0][1].pitch = planes[0][2].pitch = out_width / 2;
765 planes[0][1].height = planes[0][2].height = out_height / 2;
766 planes[0][1].data = planes[0][0].data + out_width * out_height_full;
767 planes[0][2].data = planes[0][1].data + out_width * out_height_full / 4;
768
769 planes[1][1].pitch = planes[1][2].pitch = in_width / 2;
770 planes[1][1].height = planes[1][2].height = in_height / 2;
771 planes[1][1].data = planes[1][0].data + in_width * in_height_full;
772 planes[1][2].data = planes[1][1].data + in_width * in_height_full / 4;
773
774 if (fmt == PIXEL_FORMAT_YV12)
775 {
776 // We need to swap U and V
777 uint8_t *tmp = planes[1][2].data;
778 planes[1][2].data = planes[1][1].data;
779 planes[1][1].data = tmp;
780 }
781
782 // Add the line offset
783 planes[k][0].data += planes[k][0].pitch * line_offset;
784 planes[k][1].data += planes[k][1].pitch * line_offset/2;
785 planes[k][2].data += planes[k][2].pitch * line_offset/2;
786
787 num_planes = 3;
788 size = out_width * out_height_full * 3 / 2;
789
790 if (in_size < in_width * in_height * 3 / 2)
791 return 0;
792
793 } else if (fmt == PIXEL_FORMAT_I422 ||
794 fmt == PIXEL_FORMAT_YV16 ||
795 fmt == PIXEL_FORMAT_YUYV)
796 {
797 planes[0][0].data = out;
798 planes[0][0].pitch = out_width;
799 planes[0][0].height = out_height;
800
801 planes[1][0].data = (uint8_t *)in;
802 planes[1][0].pitch = in_width;
803 planes[1][0].height = in_height;
804
805 planes[0][1].pitch = planes[0][2].pitch = out_width / 2;
806 planes[0][1].height = planes[0][2].height = out_height;
807 planes[0][1].data = planes[0][0].data + out_width * out_height_full;
808 planes[0][2].data = planes[0][1].data + out_width * out_height_full / 2;
809
810 planes[1][1].pitch = planes[1][2].pitch = in_width / 2;
811 planes[1][1].height = planes[1][2].height = in_height;
812 planes[1][1].data = planes[1][0].data + in_width * in_height_full;
813 planes[1][2].data = planes[1][1].data + in_width * in_height_full / 2;
814
815 // Add the line offset
816 planes[k][0].data += planes[k][0].pitch * line_offset;
817 planes[k][1].data += planes[k][1].pitch * line_offset;
818 planes[k][2].data += planes[k][2].pitch * line_offset;
819 if (fmt == PIXEL_FORMAT_YUYV)
820 planes[k][0].data += planes[k][0].pitch * line_offset;
821
822 if (fmt == PIXEL_FORMAT_YV16)
823 {
824 // We need to swap U and V
825 uint8_t *tmp = planes[1][2].data;
826 planes[1][2].data = planes[1][1].data;
827 planes[1][1].data = tmp;
828 }
829
830 num_planes = 3;
831 size = out_width * out_height_full * 2;
832
833 if (in_size < in_width * in_height * 2)
834 return 0;
835 } else if (fmt == PIXEL_FORMAT_RGBA)
836 {
837 planes[0][0].data = out;
838 planes[0][0].pitch = out_width * 4;
839 planes[0][0].height = out_height;
840
841 planes[1][0].data = (uint8_t *)in;
842 planes[1][0].pitch = in_width * 4;
843 planes[1][0].height = in_height;
844
845 // Add the line offset
846 planes[k][0].data += planes[k][0].pitch * line_offset;
847
848 num_planes = 1;
849 size = out_width * out_height_full * 4;
850
851 if (in_size < in_width * in_height * 4)
852 return 0;
853 }
854
855 if (out_size < size)
856 return 0;
857
858 // Special case for YUYV where don't just copy but convert to/from I422
859 if (fmt == PIXEL_FORMAT_YUYV)
860 {
861 unsigned int width = in_width > out_width ? out_width : in_width;
862 unsigned int height = in_height > out_height ? out_height : in_height;
863 uint8_t *y = planes[convert_from ? 0 : 1][0].data;
864 uint8_t *u = planes[convert_from ? 0 : 1][1].data;
865 uint8_t *v = planes[convert_from ? 0 : 1][2].data;
866 uint8_t *yuyv = planes[convert_from ? 1 : 0][0].data;
867 unsigned int y_diff = (convert_from ? out_width : in_width) - width;
868 unsigned int yuyv_diff = ((convert_from ? in_width : out_width) - width) * 2;
869
870 while (height--)
871 {
872 if (convert_from)
873 for (i = width / 2; i; i--)
874 {
875 *y++ = *yuyv++;
876 *u++ = *yuyv++;
877 *y++ = *yuyv++;
878 *v++ = *yuyv++;
879 }
880 else
881 for (i = width / 2; i; i--)
882 {
883 *yuyv++ = *y++;
884 *yuyv++ = *u++;
885 *yuyv++ = *y++;
886 *yuyv++ = *v++;
887 }
888
889 yuyv += yuyv_diff;
890 y += y_diff;
891 u += y_diff >> 1;
892 v += y_diff >> 1;
893 }
894
895 return size;
896 }
897
898 for (i = 0; i < num_planes; i++)
899 {
900 unsigned int width = MMAL_MIN(planes[0][i].pitch, planes[1][i].pitch);
901 unsigned int height = MMAL_MIN(planes[0][i].height, planes[1][i].height);
902 uint8_t *data_out = planes[0][i].data;
903 uint8_t *data_in = planes[1][i].data;
904
905 while (height--)
906 {
907 memcpy(data_out, data_in, width);
908 data_out += planes[0][i].pitch;
909 data_in += planes[1][i].pitch;
910 }
911 }
912
913 return size;
914}
915