/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define ATRACE_TAG ATRACE_TAG_CAMERA // System dependencies #include // JPEG dependencies #include "mm_jpeg_dbg.h" #include "mm_jpeg_mpo.h" #define M_APP0 0xe0 #define M_APP1 0xe1 #define M_APP2 0xe2 #define M_EOI 0xd9 #define M_SOI 0xd8 /** READ_LONG: * @b: Buffer start addr * @o: Buffer offset to start reading * * Read long value from the specified buff addr at given offset **/ #define READ_LONG(b, o) \ (uint32_t)(((uint32_t)b[o] << 24) + \ ((uint32_t)b[o+1] << 16) + \ ((uint32_t)b[o+2] << 8) + \ ((uint32_t)b[o+3])) /** READ_LONG_LITTLE: * @b: Buffer start addr * @o: Buffer offset to start reading * * Read long value from the specified buff addr at given offset * in Little Endian **/ #define READ_LONG_LITTLE(b, o) \ (uint32_t)(((uint32_t)b[o + 3] << 24) + \ ((uint32_t) b[o + 2] << 16) + \ ((uint32_t) b[o + 1] << 8) + \ (uint32_t) b[o]); /** READ_LONG: * @b: Buffer start addr * @o: Buffer offset to start reading * * Read short value from the specified buff addr at given * offset **/ #define READ_SHORT(b, o) \ (uint16_t) (((uint16_t)b[o] << 8) + \ (uint16_t) b[o + 1]); /*Mutex to serializa MPO composition*/ static pthread_mutex_t g_mpo_lock = PTHREAD_MUTEX_INITIALIZER; /** mm_jpeg_mpo_write_long_little_endian * * Arguments: * @buffer_addr: image start addr * @buff_offset: offset in the buffer * @buffer_size: Size of the buffer * @value: Value to write * @overflow : Overflow flag * * Return: * None * * Description: * Write value at the given offset * **/ void mm_jpeg_mpo_write_long_little_endian(uint8_t *buff_addr, uint32_t buff_offset, uint32_t buffer_size, int value, uint8_t *overflow) { if (buff_offset + 3 >= buffer_size) { *overflow = TRUE; } if (!(*overflow)) { buff_addr[buff_offset + 3] = (uint8_t)((value >> 24) & 0xFF); buff_addr[buff_offset + 2] = (uint8_t)((value >> 16) & 0xFF); buff_addr[buff_offset + 1] = (uint8_t)((value >> 8) & 0xFF); buff_addr[buff_offset] = (uint8_t)(value & 0xFF); } } /** mm_jpeg_mpo_write_long * * Arguments: * @buffer_addr: image start addr * @buff_offset: offset in the buffer * @buffer_size: Size of the buffer * @value: Value to write * @overflow : Overflow flag * * Return: * None * * Description: * Write value at the given offset * **/ void mm_jpeg_mpo_write_long(uint8_t *buff_addr, uint32_t buff_offset, uint32_t buffer_size, int value, uint8_t *overflow) { if ((buff_offset + 3) >= buffer_size) { *overflow = TRUE; } if (!(*overflow)) { buff_addr[buff_offset] = (uint8_t)((value >> 24) & 0xFF); buff_addr[buff_offset+1] = (uint8_t)((value >> 16) & 0xFF); buff_addr[buff_offset+2] = (uint8_t)((value >> 8) & 0xFF); buff_addr[buff_offset+3] = (uint8_t)(value & 0xFF); } } /** mm_jpeg_mpo_get_app_marker * * Arguments: * @buffer_addr: Jpeg image start addr * @buffer_size: Size of the Buffer * @app_marker: app_marker to find * * Return: * Start offset of the specified app marker * * Description: * Gets the start offset of the given app marker * **/ uint8_t *mm_jpeg_mpo_get_app_marker(uint8_t *buffer_addr, int buffer_size, int app_marker) { int32_t byte; uint8_t *p_current_addr = NULL, *p_start_offset = NULL; uint16_t app_marker_size = 0; p_current_addr = buffer_addr; do { do { byte = *(p_current_addr); p_current_addr++; } while ((byte != 0xFF) && (p_current_addr < (buffer_addr + (buffer_size - 1)))); //If 0xFF is not found at all, break if (byte != 0xFF) { LOGD("0xFF not found"); break; } //Read the next byte after 0xFF byte = *(p_current_addr); LOGD("Byte %x", byte); if (byte == app_marker) { LOGD("Byte %x", byte); p_start_offset = ++p_current_addr; break; } else if (byte != M_SOI) { app_marker_size = READ_SHORT(p_current_addr, 1); LOGD("size %d", app_marker_size); p_current_addr += app_marker_size; } } while ((byte != M_EOI) && (p_current_addr < (buffer_addr + (buffer_size - 1)))); return p_start_offset; } /** mm_jpeg_mpo_get_mp_header * * Arguments: * @app2_marker: app2_marker start offset * * Return: * Start offset of the MP header * * Description: * Get the start offset of the MP header (before the MP * Endian field). All offsets in the MP header need to be * specified wrt this start offset. * **/ uint8_t *mm_jpeg_mpo_get_mp_header(uint8_t *app2_start_offset) { uint8_t *mp_headr_start_offset = NULL; if (app2_start_offset != NULL) { mp_headr_start_offset = app2_start_offset + MP_APP2_FIELD_LENGTH_BYTES + MP_FORMAT_IDENTIFIER_BYTES; } return mp_headr_start_offset; } /** mm_jpeg_mpo_update_header * * Arguments: * @mpo_info: MPO Info * * Return: * 0 - Success * -1 - otherwise * * Description: * Update the MP Index IFD of the first image with info * about about all other images. * **/ int mm_jpeg_mpo_update_header(mm_jpeg_mpo_info_t *mpo_info) { uint8_t *app2_start_off_addr = NULL, *mp_headr_start_off_addr = NULL; uint32_t mp_index_ifd_offset = 0, current_offset = 0, mp_entry_val_offset = 0; uint8_t *aux_start_addr = NULL; uint8_t overflow_flag = 0; int i = 0, rc = -1; uint32_t endianess = MPO_LITTLE_ENDIAN, offset_to_nxt_ifd = 8; uint16_t ifd_tag_count = 0; //Get the addr of the App Marker app2_start_off_addr = mm_jpeg_mpo_get_app_marker( mpo_info->output_buff.buf_vaddr, mpo_info->primary_image.buf_filled_len, M_APP2); if (!app2_start_off_addr) { LOGE("Cannot find App2 marker. MPO composition failed" ); return rc; } LOGD("app2_start_off_addr %p = %x", app2_start_off_addr, *app2_start_off_addr); //Get the addr of the MP Headr start offset. //All offsets in the MP header are wrt to this addr mp_headr_start_off_addr = mm_jpeg_mpo_get_mp_header(app2_start_off_addr); if (!mp_headr_start_off_addr) { LOGE("mp headr start offset is NULL. MPO composition failed" ); return rc; } LOGD("mp_headr_start_off_addr %x", *mp_headr_start_off_addr); current_offset = mp_headr_start_off_addr - mpo_info->output_buff.buf_vaddr; endianess = READ_LONG(mpo_info->output_buff.buf_vaddr, current_offset); LOGD("Endianess %d", endianess); //Add offset to first ifd current_offset += MP_ENDIAN_BYTES; //Read the value to get MP Index IFD. if (endianess == MPO_LITTLE_ENDIAN) { offset_to_nxt_ifd = READ_LONG_LITTLE(mpo_info->output_buff.buf_vaddr, current_offset); } else { offset_to_nxt_ifd = READ_LONG(mpo_info->output_buff.buf_vaddr, current_offset); } LOGD("offset_to_nxt_ifd %d", offset_to_nxt_ifd); current_offset = ((mp_headr_start_off_addr + offset_to_nxt_ifd) - mpo_info->output_buff.buf_vaddr); mp_index_ifd_offset = current_offset; LOGD("mp_index_ifd_offset %d", mp_index_ifd_offset); //Traverse to MP Entry value ifd_tag_count = READ_SHORT(mpo_info->output_buff.buf_vaddr, current_offset); LOGD("Tag count in MP entry %d", ifd_tag_count); current_offset += MP_INDEX_COUNT_BYTES; /* Get MP Entry Value offset - Count * 12 (Each tag is 12 bytes)*/ current_offset += (ifd_tag_count * 12); /*Add Offset to next IFD*/ current_offset += MP_INDEX_OFFSET_OF_NEXT_IFD_BYTES; mp_entry_val_offset = current_offset; LOGD("MP Entry value offset %d", mp_entry_val_offset); //Update image size for primary image current_offset += MP_INDEX_ENTRY_INDIVIDUAL_IMAGE_ATTRIBUTE_BYTES; if (endianess == MPO_LITTLE_ENDIAN) { mm_jpeg_mpo_write_long_little_endian(mpo_info->output_buff.buf_vaddr, current_offset, mpo_info->output_buff_size, mpo_info->primary_image.buf_filled_len, &overflow_flag); } else { mm_jpeg_mpo_write_long(mpo_info->output_buff.buf_vaddr, current_offset, mpo_info->output_buff_size, mpo_info->primary_image.buf_filled_len, &overflow_flag); } aux_start_addr = mpo_info->output_buff.buf_vaddr + mpo_info->primary_image.buf_filled_len; for (i = 0; i < mpo_info->num_of_images - 1; i++) { //Go to MP Entry val for each image mp_entry_val_offset += MP_INDEX_ENTRY_VALUE_BYTES; current_offset = mp_entry_val_offset; //Update image size current_offset += MP_INDEX_ENTRY_INDIVIDUAL_IMAGE_ATTRIBUTE_BYTES; if (endianess == MPO_LITTLE_ENDIAN) { mm_jpeg_mpo_write_long_little_endian(mpo_info->output_buff.buf_vaddr, current_offset, mpo_info->output_buff_size, mpo_info->aux_images[i].buf_filled_len, &overflow_flag); } else { mm_jpeg_mpo_write_long(mpo_info->output_buff.buf_vaddr, current_offset, mpo_info->output_buff_size, mpo_info->aux_images[i].buf_filled_len, &overflow_flag); } LOGD("aux[start_addr %x", *aux_start_addr); //Update the offset current_offset += MP_INDEX_ENTRY_INDIVIDUAL_IMAGE_SIZE_BYTES; if (endianess == MPO_LITTLE_ENDIAN) { mm_jpeg_mpo_write_long_little_endian(mpo_info->output_buff.buf_vaddr, current_offset, mpo_info->output_buff_size, aux_start_addr - mp_headr_start_off_addr, &overflow_flag); } else { mm_jpeg_mpo_write_long(mpo_info->output_buff.buf_vaddr, current_offset, mpo_info->output_buff_size, aux_start_addr - mp_headr_start_off_addr, &overflow_flag); } aux_start_addr += mpo_info->aux_images[i].buf_filled_len; } if (!overflow_flag) { rc = 0; } return rc; } /** mm_jpeg_mpo_compose * * Arguments: * @mpo_info: MPO Info * * Return: * 0 - Success * -1 - otherwise * * Description: * Compose MPO image from multiple JPEG images * **/ int mm_jpeg_mpo_compose(mm_jpeg_mpo_info_t *mpo_info) { uint8_t *aux_write_offset = NULL; int i = 0, rc = -1; pthread_mutex_lock(&g_mpo_lock); //Primary image needs to be copied to the o/p buffer if its not already if (mpo_info->output_buff.buf_filled_len == 0) { if (mpo_info->primary_image.buf_filled_len < mpo_info->output_buff_size) { memcpy(mpo_info->output_buff.buf_vaddr, mpo_info->primary_image.buf_vaddr, mpo_info->primary_image.buf_filled_len); mpo_info->output_buff.buf_filled_len += mpo_info->primary_image.buf_filled_len; } else { LOGE("O/P buffer not large enough. MPO composition failed"); pthread_mutex_unlock(&g_mpo_lock); return rc; } } //Append each Aux image to the buffer for (i = 0; i < mpo_info->num_of_images - 1; i++) { if ((mpo_info->output_buff.buf_filled_len + mpo_info->aux_images[i].buf_filled_len) <= mpo_info->output_buff_size) { aux_write_offset = mpo_info->output_buff.buf_vaddr + mpo_info->output_buff.buf_filled_len; memcpy(aux_write_offset, mpo_info->aux_images[i].buf_vaddr, mpo_info->aux_images[i].buf_filled_len); mpo_info->output_buff.buf_filled_len += mpo_info->aux_images[i].buf_filled_len; } else { LOGE("O/P buffer not large enough. MPO composition failed"); pthread_mutex_unlock(&g_mpo_lock); return rc; } } rc = mm_jpeg_mpo_update_header(mpo_info); pthread_mutex_unlock(&g_mpo_lock); return rc; }