blob: fb9c222caca53728faf0c96741ba62a53ce2a70c [file] [log] [blame]
/* 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 <pthread.h>
// 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;
}