1/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * 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
9 *       copyright notice, this list of conditions and the following
10 *       disclaimer in the documentation and/or other materials provided
11 *       with the distribution.
12 *     * Neither the name of The Linux Foundation nor the names of its
13 *       contributors may be used to endorse or promote products derived
14 *       from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30#define ATRACE_TAG ATRACE_TAG_CAMERA
31
32// System dependencies
33#include <pthread.h>
34
35// JPEG dependencies
36#include "mm_jpeg_dbg.h"
37#include "mm_jpeg_mpo.h"
38
39#define M_APP0    0xe0
40#define M_APP1    0xe1
41#define M_APP2    0xe2
42#define M_EOI     0xd9
43#define M_SOI     0xd8
44
45/** READ_LONG:
46 *  @b: Buffer start addr
47 *  @o: Buffer offset to start reading
48 *
49 *  Read long value from the specified buff addr at given offset
50 **/
51#define READ_LONG(b, o)  \
52  (uint32_t)(((uint32_t)b[o] << 24) + \
53  ((uint32_t)b[o+1] << 16) + \
54  ((uint32_t)b[o+2] << 8) + \
55  ((uint32_t)b[o+3]))
56
57/** READ_LONG_LITTLE:
58 *  @b: Buffer start addr
59 *  @o: Buffer offset to start reading
60 *
61 *  Read long value from the specified buff addr at given offset
62 *  in Little Endian
63 **/
64#define READ_LONG_LITTLE(b, o)  \
65  (uint32_t)(((uint32_t)b[o + 3] << 24) + \
66  ((uint32_t) b[o + 2] << 16) + \
67  ((uint32_t) b[o + 1] << 8) + \
68  (uint32_t) b[o]);
69
70/** READ_LONG:
71 *  @b: Buffer start addr
72 *  @o: Buffer offset to start reading
73 *
74 *  Read short value from the specified buff addr at given
75 *  offset
76 **/
77#define READ_SHORT(b, o)  \
78  (uint16_t) (((uint16_t)b[o] << 8) + \
79  (uint16_t) b[o + 1]);
80
81/*Mutex to serializa MPO composition*/
82static pthread_mutex_t g_mpo_lock = PTHREAD_MUTEX_INITIALIZER;
83
84/** mm_jpeg_mpo_write_long_little_endian
85 *
86 *  Arguments:
87 *    @buffer_addr: image start addr
88 *    @buff_offset: offset in the buffer
89 *    @buffer_size: Size of the buffer
90 *    @value: Value to write
91 *    @overflow : Overflow flag
92 *
93 *  Return:
94 *       None
95 *
96 *  Description:
97 *       Write value at the given offset
98 *
99 **/
100void mm_jpeg_mpo_write_long_little_endian(uint8_t *buff_addr, uint32_t buff_offset,
101  uint32_t buffer_size, int value, uint8_t *overflow)
102{
103  if (buff_offset + 3 >= buffer_size) {
104    *overflow = TRUE;
105  }
106
107  if (!(*overflow)) {
108    buff_addr[buff_offset + 3] = (uint8_t)((value >> 24) & 0xFF);
109    buff_addr[buff_offset + 2] = (uint8_t)((value >> 16) & 0xFF);
110    buff_addr[buff_offset + 1] = (uint8_t)((value >> 8) & 0xFF);
111    buff_addr[buff_offset] = (uint8_t)(value & 0xFF);
112  }
113}
114
115/** mm_jpeg_mpo_write_long
116 *
117 *  Arguments:
118 *    @buffer_addr: image start addr
119 *    @buff_offset: offset in the buffer
120 *    @buffer_size: Size of the buffer
121 *    @value: Value to write
122 *    @overflow : Overflow flag
123 *
124 *  Return:
125 *       None
126 *
127 *  Description:
128 *       Write value at the given offset
129 *
130 **/
131void mm_jpeg_mpo_write_long(uint8_t *buff_addr, uint32_t buff_offset,
132  uint32_t buffer_size, int value, uint8_t *overflow)
133{
134  if ((buff_offset + 3) >= buffer_size) {
135    *overflow = TRUE;
136  }
137
138  if (!(*overflow)) {
139    buff_addr[buff_offset] = (uint8_t)((value >> 24) & 0xFF);
140    buff_addr[buff_offset+1] = (uint8_t)((value >> 16) & 0xFF);
141    buff_addr[buff_offset+2] = (uint8_t)((value >> 8) & 0xFF);
142    buff_addr[buff_offset+3] = (uint8_t)(value & 0xFF);
143  }
144}
145
146/** mm_jpeg_mpo_get_app_marker
147 *
148 *  Arguments:
149 *    @buffer_addr: Jpeg image start addr
150 *    @buffer_size: Size of the Buffer
151 *    @app_marker: app_marker to find
152 *
153 *  Return:
154 *       Start offset of the specified app marker
155 *
156 *  Description:
157 *       Gets the start offset of the given app marker
158 *
159 **/
160uint8_t *mm_jpeg_mpo_get_app_marker(uint8_t *buffer_addr, int buffer_size,
161  int app_marker)
162{
163  int32_t byte;
164  uint8_t *p_current_addr = NULL, *p_start_offset = NULL;
165  uint16_t app_marker_size = 0;
166
167  p_current_addr = buffer_addr;
168  do {
169    do {
170      byte = *(p_current_addr);
171      p_current_addr++;
172    }
173    while ((byte != 0xFF) &&
174      (p_current_addr < (buffer_addr + (buffer_size - 1))));
175
176    //If 0xFF is not found at all, break
177    if (byte != 0xFF) {
178      LOGD("0xFF not found");
179      break;
180    }
181
182    //Read the next byte after 0xFF
183    byte = *(p_current_addr);
184    LOGD("Byte %x", byte);
185    if (byte == app_marker) {
186      LOGD("Byte %x", byte);
187      p_start_offset = ++p_current_addr;
188      break;
189    } else if (byte != M_SOI) {
190      app_marker_size = READ_SHORT(p_current_addr, 1);
191      LOGD("size %d", app_marker_size);
192      p_current_addr += app_marker_size;
193    }
194  }
195  while ((byte != M_EOI) &&
196    (p_current_addr < (buffer_addr + (buffer_size - 1))));
197
198  return p_start_offset;
199}
200
201/** mm_jpeg_mpo_get_mp_header
202 *
203 *  Arguments:
204 *    @app2_marker: app2_marker start offset
205 *
206 *  Return:
207 *       Start offset of the MP header
208 *
209 *  Description:
210 *       Get the start offset of the MP header (before the MP
211 *       Endian field). All offsets in the MP header need to be
212 *       specified wrt this start offset.
213 *
214 **/
215uint8_t *mm_jpeg_mpo_get_mp_header(uint8_t *app2_start_offset)
216{
217  uint8_t *mp_headr_start_offset = NULL;
218
219  if (app2_start_offset != NULL) {
220    mp_headr_start_offset = app2_start_offset + MP_APP2_FIELD_LENGTH_BYTES +
221      MP_FORMAT_IDENTIFIER_BYTES;
222  }
223
224  return mp_headr_start_offset;
225}
226
227/** mm_jpeg_mpo_update_header
228 *
229 *  Arguments:
230 *    @mpo_info: MPO Info
231 *
232 *  Return:
233 *       0 - Success
234 *       -1 - otherwise
235 *
236 *  Description:
237 *      Update the MP Index IFD of the first image with info
238 *      about about all other images.
239 *
240 **/
241int mm_jpeg_mpo_update_header(mm_jpeg_mpo_info_t *mpo_info)
242{
243  uint8_t *app2_start_off_addr = NULL, *mp_headr_start_off_addr = NULL;
244  uint32_t mp_index_ifd_offset = 0, current_offset = 0, mp_entry_val_offset = 0;
245  uint8_t *aux_start_addr = NULL;
246  uint8_t overflow_flag = 0;
247  int i = 0, rc = -1;
248  uint32_t endianess = MPO_LITTLE_ENDIAN, offset_to_nxt_ifd = 8;
249  uint16_t ifd_tag_count = 0;
250
251  //Get the addr of the App Marker
252  app2_start_off_addr = mm_jpeg_mpo_get_app_marker(
253    mpo_info->output_buff.buf_vaddr, mpo_info->primary_image.buf_filled_len, M_APP2);
254  if (!app2_start_off_addr) {
255    LOGE("Cannot find App2 marker. MPO composition failed" );
256    return rc;
257  }
258  LOGD("app2_start_off_addr %p = %x",
259    app2_start_off_addr, *app2_start_off_addr);
260
261  //Get the addr of the MP Headr start offset.
262  //All offsets in the MP header are wrt to this addr
263  mp_headr_start_off_addr = mm_jpeg_mpo_get_mp_header(app2_start_off_addr);
264  if (!mp_headr_start_off_addr) {
265    LOGE("mp headr start offset is NULL. MPO composition failed" );
266    return rc;
267  }
268  LOGD("mp_headr_start_off_addr %x",
269    *mp_headr_start_off_addr);
270
271  current_offset = mp_headr_start_off_addr - mpo_info->output_buff.buf_vaddr;
272
273  endianess = READ_LONG(mpo_info->output_buff.buf_vaddr, current_offset);
274  LOGD("Endianess %d", endianess);
275
276  //Add offset to first ifd
277  current_offset += MP_ENDIAN_BYTES;
278
279  //Read the value to get MP Index IFD.
280  if (endianess == MPO_LITTLE_ENDIAN) {
281    offset_to_nxt_ifd = READ_LONG_LITTLE(mpo_info->output_buff.buf_vaddr,
282      current_offset);
283  } else {
284    offset_to_nxt_ifd = READ_LONG(mpo_info->output_buff.buf_vaddr,
285      current_offset);
286  }
287  LOGD("offset_to_nxt_ifd %d", offset_to_nxt_ifd);
288
289  current_offset = ((mp_headr_start_off_addr + offset_to_nxt_ifd) -
290    mpo_info->output_buff.buf_vaddr);
291  mp_index_ifd_offset = current_offset;
292  LOGD("mp_index_ifd_offset %d",
293    mp_index_ifd_offset);
294
295  //Traverse to MP Entry value
296  ifd_tag_count = READ_SHORT(mpo_info->output_buff.buf_vaddr, current_offset);
297  LOGD("Tag count in MP entry %d", ifd_tag_count);
298  current_offset += MP_INDEX_COUNT_BYTES;
299
300  /* Get MP Entry Value offset - Count * 12 (Each tag is 12 bytes)*/
301  current_offset += (ifd_tag_count * 12);
302  /*Add Offset to next IFD*/
303  current_offset += MP_INDEX_OFFSET_OF_NEXT_IFD_BYTES;
304
305  mp_entry_val_offset = current_offset;
306  LOGD("MP Entry value offset %d",
307    mp_entry_val_offset);
308
309  //Update image size for primary image
310  current_offset += MP_INDEX_ENTRY_INDIVIDUAL_IMAGE_ATTRIBUTE_BYTES;
311  if (endianess == MPO_LITTLE_ENDIAN) {
312    mm_jpeg_mpo_write_long_little_endian(mpo_info->output_buff.buf_vaddr,
313      current_offset, mpo_info->output_buff_size,
314      mpo_info->primary_image.buf_filled_len, &overflow_flag);
315  } else {
316    mm_jpeg_mpo_write_long(mpo_info->output_buff.buf_vaddr,
317      current_offset, mpo_info->output_buff_size,
318      mpo_info->primary_image.buf_filled_len, &overflow_flag);
319  }
320
321  aux_start_addr = mpo_info->output_buff.buf_vaddr +
322    mpo_info->primary_image.buf_filled_len;
323
324  for (i = 0; i < mpo_info->num_of_images - 1; i++) {
325    //Go to MP Entry val for each image
326    mp_entry_val_offset += MP_INDEX_ENTRY_VALUE_BYTES;
327    current_offset = mp_entry_val_offset;
328
329    //Update image size
330    current_offset += MP_INDEX_ENTRY_INDIVIDUAL_IMAGE_ATTRIBUTE_BYTES;
331    if (endianess == MPO_LITTLE_ENDIAN) {
332      mm_jpeg_mpo_write_long_little_endian(mpo_info->output_buff.buf_vaddr,
333        current_offset, mpo_info->output_buff_size,
334        mpo_info->aux_images[i].buf_filled_len, &overflow_flag);
335    } else {
336      mm_jpeg_mpo_write_long(mpo_info->output_buff.buf_vaddr,
337        current_offset, mpo_info->output_buff_size,
338        mpo_info->aux_images[i].buf_filled_len, &overflow_flag);
339    }
340    LOGD("aux[start_addr %x", *aux_start_addr);
341    //Update the offset
342    current_offset += MP_INDEX_ENTRY_INDIVIDUAL_IMAGE_SIZE_BYTES;
343    if (endianess == MPO_LITTLE_ENDIAN) {
344      mm_jpeg_mpo_write_long_little_endian(mpo_info->output_buff.buf_vaddr,
345        current_offset, mpo_info->output_buff_size,
346        aux_start_addr - mp_headr_start_off_addr, &overflow_flag);
347    } else {
348      mm_jpeg_mpo_write_long(mpo_info->output_buff.buf_vaddr,
349        current_offset, mpo_info->output_buff_size,
350        aux_start_addr - mp_headr_start_off_addr, &overflow_flag);
351    }
352    aux_start_addr += mpo_info->aux_images[i].buf_filled_len;
353  }
354  if (!overflow_flag) {
355    rc = 0;
356  }
357  return rc;
358}
359
360/** mm_jpeg_mpo_compose
361 *
362 *  Arguments:
363 *    @mpo_info: MPO Info
364 *
365 *  Return:
366 *       0 - Success
367 *      -1 - otherwise
368 *
369 *  Description:
370 *      Compose MPO image from multiple JPEG images
371 *
372 **/
373int mm_jpeg_mpo_compose(mm_jpeg_mpo_info_t *mpo_info)
374{
375  uint8_t *aux_write_offset = NULL;
376  int i = 0, rc = -1;
377
378  pthread_mutex_lock(&g_mpo_lock);
379
380  //Primary image needs to be copied to the o/p buffer if its not already
381  if (mpo_info->output_buff.buf_filled_len == 0) {
382    if (mpo_info->primary_image.buf_filled_len < mpo_info->output_buff_size) {
383      memcpy(mpo_info->output_buff.buf_vaddr, mpo_info->primary_image.buf_vaddr,
384        mpo_info->primary_image.buf_filled_len);
385      mpo_info->output_buff.buf_filled_len +=
386        mpo_info->primary_image.buf_filled_len;
387    } else {
388      LOGE("O/P buffer not large enough. MPO composition failed");
389      pthread_mutex_unlock(&g_mpo_lock);
390      return rc;
391    }
392  }
393  //Append each Aux image to the buffer
394  for (i = 0; i < mpo_info->num_of_images - 1; i++) {
395    if ((mpo_info->output_buff.buf_filled_len +
396      mpo_info->aux_images[i].buf_filled_len) <= mpo_info->output_buff_size) {
397      aux_write_offset = mpo_info->output_buff.buf_vaddr +
398        mpo_info->output_buff.buf_filled_len;
399      memcpy(aux_write_offset, mpo_info->aux_images[i].buf_vaddr,
400        mpo_info->aux_images[i].buf_filled_len);
401      mpo_info->output_buff.buf_filled_len +=
402        mpo_info->aux_images[i].buf_filled_len;
403    } else {
404      LOGE("O/P buffer not large enough. MPO composition failed");
405      pthread_mutex_unlock(&g_mpo_lock);
406      return rc;
407    }
408  }
409
410  rc = mm_jpeg_mpo_update_header(mpo_info);
411  pthread_mutex_unlock(&g_mpo_lock);
412
413  return rc;
414}
415