brcmjpeg.cpp 31 KB

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