1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, 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 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON 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 |
25 | SOFTWARE, 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 | *******************************************************************************/ |
53 | struct 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 | *******************************************************************************/ |
70 | static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *); |
71 | static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *); |
72 | static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); |
73 | static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); |
74 | static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); |
75 | static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); |
76 | static void brcmjpeg_destroy(BRCMJPEG_T *); |
77 | |
78 | static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T); |
79 | static 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 | |
85 | static BRCMJPEG_T *brcmjpeg_encoder = NULL; |
86 | static BRCMJPEG_T *brcmjpeg_decoder = NULL; |
87 | |
88 | /******************************************************************************* |
89 | * Platform specific code |
90 | *******************************************************************************/ |
91 | static VCOS_ONCE_T once = VCOS_ONCE_INIT; |
92 | static VCOS_MUTEX_T brcmjpeg_lock; |
93 | |
94 | static 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 | |
112 | BRCMJPEG_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 | |
169 | void brcmjpeg_acquire(BRCMJPEG_T *ctx) |
170 | { |
171 | LOCK_COMP(ctx); |
172 | ctx->ref_count++; |
173 | UNLOCK_COMP(ctx); |
174 | } |
175 | |
176 | void 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 | |
197 | BRCMJPEG_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 | |
220 | static 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 | |
230 | static void brcmjpeg_mmal_cb(MMAL_WRAPPER_T *wrapper) |
231 | { |
232 | BRCMJPEG_T *ctx = wrapper->user_data; |
233 | SIGNAL(ctx); |
234 | } |
235 | |
236 | static 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 | |
268 | static 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 */ |
298 | static 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 */ |
386 | static 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 | |
438 | static 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 | |
535 | static 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 | /*****************************************************************************/ |
704 | static 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 | |
716 | static 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. |
727 | static 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 | |