1309 lines
37 KiB
C
1309 lines
37 KiB
C
/*
|
|
* Copyright (C) 2020-2023 Artinchip Technology Co. Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* author: <qi.xu@artinchip.com>
|
|
* Desc: jpeg hardware decode demo
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <linux/types.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/time.h>
|
|
#include <video/artinchip_fb.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/dma-heap.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/fb.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include "ve.h"
|
|
#include "ve_buffer.h"
|
|
#include "read_bits.h"
|
|
#include "mpp_log.h"
|
|
|
|
#define MAX_COMPONENTS 4
|
|
#define MAX_INDEX 4
|
|
|
|
#define JPEG420 0
|
|
#define JPEG411 1 // not support
|
|
#define JPEG422 2
|
|
#define JPEG444 3
|
|
#define JPEG422T 4
|
|
#define JPEG400 5
|
|
#define JPEGERR 6
|
|
|
|
#define ALIGN_8B(x) (((x) + (7)) & ~(7))
|
|
#define ALIGN_16B(x) (((x) + (15)) & ~(15))
|
|
#define ALIGN_1024B(x) (((x) + (1023)) & ~(1023))
|
|
|
|
enum jpeg_marker {
|
|
/* start of frame */
|
|
SOF0 = 0xc0, /* baseline */
|
|
SOF1 = 0xc1, /* extended sequential, huffman */
|
|
SOF2 = 0xc2, /* progressive, huffman */
|
|
SOF3 = 0xc3, /* lossless, huffman */
|
|
DHT = 0xc4, /* define huffman tables */
|
|
SOI = 0xd8, /* start of image */
|
|
EOI = 0xd9, /* end of image */
|
|
SOS = 0xda, /* start of scan */
|
|
DQT = 0xdb, /* define quantization tables */
|
|
DRI = 0xdd, /* define restart interval */
|
|
};
|
|
|
|
struct jpeg_huffman_table {
|
|
unsigned short start_code[16]; // start_code[i], the minimum code of huffman code length i
|
|
unsigned short max_code[16]; // max_code[i], the max code of huffman code length i
|
|
unsigned char offset[16]; // the offset in val table of huffman code length i
|
|
unsigned char bits_table[16];
|
|
unsigned char symbol[256]; // huffman symbol
|
|
unsigned short code[256]; // HUFFCODE table
|
|
unsigned char len[256];
|
|
unsigned int total_code; // total number of huffman code
|
|
};
|
|
|
|
struct jpeg_ctx {
|
|
struct read_bit_context gb;
|
|
unsigned long regs_base;
|
|
|
|
unsigned char* buf_start;
|
|
int buf_size;
|
|
int ve_fd;
|
|
int dma_fd;
|
|
int input_buf_fd;
|
|
unsigned int input_phy_addr;
|
|
struct mpp_frame output_frame;
|
|
unsigned int frame_phy_addr[3];
|
|
|
|
enum mpp_pixel_format pix_fmt; // if we support out_pix_fmt, pix_fmt = out_pix_fmt
|
|
enum mpp_pixel_format out_pix_fmt; // output pixel format from config
|
|
int yuv2rgb;
|
|
int uv_interleave;
|
|
|
|
int start_code;
|
|
const uint8_t *raw_scan_buffer;
|
|
size_t raw_scan_buffer_size;
|
|
|
|
struct jpeg_huffman_table huffman_table[2][MAX_INDEX]; // [DC/AC][index]
|
|
|
|
uint16_t q_matrixes[MAX_INDEX][64]; // 8x8 quant matrix, [index][q_matrix_pos]
|
|
|
|
int first_picture;
|
|
|
|
int nb_mcu_width; // mcu aligned width
|
|
int nb_mcu_height; // mcu aligned height
|
|
int width, height;
|
|
int nb_components;
|
|
int component_id[MAX_COMPONENTS];
|
|
int h_count[MAX_COMPONENTS]; // horizontal count for each component
|
|
int v_count[MAX_COMPONENTS]; // vertical count for each component
|
|
int comp_index[MAX_COMPONENTS];
|
|
int dc_index[MAX_COMPONENTS];
|
|
int ac_index[MAX_COMPONENTS];
|
|
int quant_index[MAX_COMPONENTS]; // quant table index for each component
|
|
int got_picture; // we found a SOF and picture is valid,
|
|
|
|
int restart_interval;
|
|
int restart_count;
|
|
|
|
int have_dht; // use the default huffman table, if there is no DHT marker
|
|
|
|
int rm_h_stride[MAX_COMPONENTS]; // hor stride after post-process
|
|
int rm_v_stride[MAX_COMPONENTS]; // ver stride after post-process
|
|
int rm_h_real_size[MAX_COMPONENTS]; // hor real size after post-process
|
|
int rm_v_real_size[MAX_COMPONENTS]; // ver real size after post-process
|
|
int h_offset[MAX_COMPONENTS]; // hor crop offset after post-process
|
|
int v_offset[MAX_COMPONENTS]; // ver crop offset after post-process
|
|
};
|
|
|
|
/********************* config regs ********************************************/
|
|
// regs
|
|
#define JPG_REG_OFFSET_ADDR 0x2000
|
|
#define VE_CLK_REG 0x00
|
|
#define VE_RST_REG 0x04
|
|
#define VE_INIT_REG 0x08
|
|
#define VE_IRQ_REG 0x0C
|
|
#define VE_JPG_EN_REG 0x14
|
|
#define JPG_START_REG (JPG_REG_OFFSET_ADDR + 0x00)
|
|
#define JPG_STATUS_REG (JPG_REG_OFFSET_ADDR + 0x04)
|
|
#define JPG_START_POS_REG (JPG_REG_OFFSET_ADDR + 0x0c)
|
|
#define JPG_CTRL_REG (JPG_REG_OFFSET_ADDR + 0x10)
|
|
#define JPG_SIZE_REG (JPG_REG_OFFSET_ADDR + 0x14)
|
|
#define JPG_MCU_INFO_REG (JPG_REG_OFFSET_ADDR + 0x18)
|
|
#define JPG_ROTMIR_REG (JPG_REG_OFFSET_ADDR + 0x1C)
|
|
#define JPG_SCALE_REG (JPG_REG_OFFSET_ADDR + 0x20)
|
|
#define JPG_CLIP_REG (JPG_REG_OFFSET_ADDR + 0x28)
|
|
#define JPG_HANDLE_NUM_REG (JPG_REG_OFFSET_ADDR + 0x2C)
|
|
#define JPG_UV_REG (JPG_REG_OFFSET_ADDR + 0x30)
|
|
#define JPG_FRAME_IDX_REG (JPG_REG_OFFSET_ADDR + 0x40)
|
|
#define JPG_HUFF_INFO_REG (JPG_REG_OFFSET_ADDR + 0x80)
|
|
#define JPG_HUFF_ADDR_REG (JPG_REG_OFFSET_ADDR + 0x84)
|
|
#define JPG_HUFF_VAL_REG (JPG_REG_OFFSET_ADDR + 0x88)
|
|
#define JPG_QMAT_INFO_REG (JPG_REG_OFFSET_ADDR + 0x90)
|
|
#define JPG_QMAT_ADDR_REG (JPG_REG_OFFSET_ADDR + 0x94)
|
|
#define JPG_QMAT_VAL_REG (JPG_REG_OFFSET_ADDR + 0x98)
|
|
#define JPG_RST_INTERVAL_REG (JPG_REG_OFFSET_ADDR + 0xb0)
|
|
#define JPG_INTRRUPT_EN_REG (JPG_REG_OFFSET_ADDR + 0xc0)
|
|
#define JPG_CYCLES_REG (JPG_REG_OFFSET_ADDR + 0xc8)
|
|
#define JPG_SUB_CTRL_REG (JPG_REG_OFFSET_ADDR + 0x100)
|
|
#define JPG_WD_PTR (JPG_REG_OFFSET_ADDR + 0x114)
|
|
#define JPG_MEM_SA_REG (JPG_REG_OFFSET_ADDR + 0x140)
|
|
#define JPG_MEM_EA_REG (JPG_REG_OFFSET_ADDR + 0x144)
|
|
#define JPG_MEM_IA_REG (JPG_REG_OFFSET_ADDR + 0x148)
|
|
#define JPG_MEM_HA_REG (JPG_REG_OFFSET_ADDR + 0x14C)
|
|
#define JPG_RBIT_OFFSET_REG (JPG_REG_OFFSET_ADDR + 0x160)
|
|
#define JPG_WBIT_OFFSET_REG (JPG_REG_OFFSET_ADDR + 0x164)
|
|
#define JPG_REQ_REG (JPG_REG_OFFSET_ADDR + 0x200)
|
|
#define JPG_STREAM_END_ADDR_REG (JPG_REG_OFFSET_ADDR + 0x208)
|
|
#define JPG_STREAM_READ_PTR_REG (JPG_REG_OFFSET_ADDR + 0x210)
|
|
#define JPG_STREAM_START_ADDR_REG (JPG_REG_OFFSET_ADDR + 0x214)
|
|
#define JPG_STREAM_INT_ADDR_REG (JPG_REG_OFFSET_ADDR + 0x218)
|
|
#define JPG_DATA_CNT_REG (JPG_REG_OFFSET_ADDR + 0x21c)
|
|
#define JPG_COMMAND_REG (JPG_REG_OFFSET_ADDR + 0x220)
|
|
#define JPG_BUSY_REG (JPG_REG_OFFSET_ADDR + 0x224)
|
|
#define JPG_BITREQ_EN_REG (JPG_REG_OFFSET_ADDR + 0x228)
|
|
#define JPG_CUR_POS_REG (JPG_REG_OFFSET_ADDR + 0x22c)
|
|
#define JPG_BAS_ADDR_REG (JPG_REG_OFFSET_ADDR + 0x230)
|
|
#define JPG_STREAM_NUM_REG (JPG_REG_OFFSET_ADDR + 0x234)
|
|
|
|
#define __io_br() do {} while (0)
|
|
#define __io_aw() do {} while (0)
|
|
#ifdef ARCH_RISCV
|
|
#define __io_ar(v) __asm__ __volatile__ ("fence i,r" : : : "memory")
|
|
#define __io_bw() __asm__ __volatile__ ("fence w,o" : : : "memory")
|
|
#else
|
|
#define __io_ar(v) do {} while (0)
|
|
#define __io_bw() do {} while (0)
|
|
#endif
|
|
#define read_reg_u32(offset) ({ uint32_t __v; __io_br(); __v = (*((volatile uint32_t*)(offset))); __io_ar(__v); __v; })
|
|
//#define write_reg_u32(offset,v) ({ __io_bw(); (*((volatile uint32_t *)(offset)) = (v)); __io_aw(); })
|
|
static void write_reg_u32(unsigned long offset, uint32_t v)
|
|
{
|
|
//printf("write regs: %08x, %08x\n", offset, v);
|
|
({ __io_bw(); (*((volatile uint32_t *)(offset)) = (v)); __io_aw(); });
|
|
}
|
|
|
|
static void ve_config_ve_top_reg(struct jpeg_ctx *s)
|
|
{
|
|
int i;
|
|
|
|
logd("config mjpeg reg");
|
|
write_reg_u32(s->regs_base + VE_CLK_REG, 1);
|
|
|
|
write_reg_u32(s->regs_base + VE_RST_REG, 0);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
uint32_t val = read_reg_u32(s->regs_base + VE_RST_REG);
|
|
if ((val >> 16) == 0)
|
|
break;
|
|
}
|
|
|
|
write_reg_u32(s->regs_base + VE_INIT_REG, 1);
|
|
write_reg_u32(s->regs_base + VE_IRQ_REG, 1);
|
|
write_reg_u32(s->regs_base + VE_JPG_EN_REG, 1);
|
|
}
|
|
|
|
static void ve_config_bitstream_register(struct jpeg_ctx *s, int offset, int is_last)
|
|
{
|
|
int busy = 1;
|
|
logi("config bitstream");
|
|
// Note: if it is the last stream, we need add 1 here, or it will halt
|
|
int stream_num = (s->buf_size + 255) / 256 +1 ;
|
|
|
|
uint32_t packet_base_addr = s->input_phy_addr;
|
|
uint32_t base_addr = (packet_base_addr + offset) & (~7);
|
|
// stream end address, 256 byte align
|
|
uint32_t end_addr = packet_base_addr + (stream_num * 256);
|
|
write_reg_u32(s->regs_base + JPG_STREAM_END_ADDR_REG, end_addr);
|
|
|
|
// stream read ptr
|
|
write_reg_u32(s->regs_base + JPG_STREAM_READ_PTR_REG, 0);
|
|
|
|
// bas address
|
|
write_reg_u32(s->regs_base + JPG_BAS_ADDR_REG, base_addr);
|
|
// start address of bitstream
|
|
write_reg_u32(s->regs_base + JPG_STREAM_START_ADDR_REG, base_addr);
|
|
|
|
write_reg_u32(s->regs_base + JPG_STREAM_INT_ADDR_REG, 0);
|
|
|
|
// read data cnt 64x32bit
|
|
write_reg_u32(s->regs_base + JPG_DATA_CNT_REG, 64);
|
|
|
|
// bit request enable
|
|
write_reg_u32(s->regs_base + JPG_BITREQ_EN_REG, 1);
|
|
|
|
write_reg_u32(s->regs_base + JPG_CUR_POS_REG, 0);
|
|
write_reg_u32(s->regs_base + JPG_STREAM_NUM_REG, stream_num);
|
|
|
|
write_reg_u32(s->regs_base + JPG_MEM_SA_REG, 0);
|
|
write_reg_u32(s->regs_base + JPG_MEM_EA_REG, 0x7f);
|
|
write_reg_u32(s->regs_base + JPG_MEM_IA_REG, 0);
|
|
write_reg_u32(s->regs_base + JPG_MEM_HA_REG, 0);
|
|
|
|
write_reg_u32(s->regs_base + JPG_STATUS_REG, 0xf);
|
|
write_reg_u32(s->regs_base + JPG_REQ_REG, 1);
|
|
do {
|
|
busy = read_reg_u32(s->regs_base + JPG_BUSY_REG);
|
|
} while(busy == 1);
|
|
|
|
// init sub module before start
|
|
write_reg_u32(s->regs_base + JPG_SUB_CTRL_REG, 4);
|
|
write_reg_u32(s->regs_base + JPG_RBIT_OFFSET_REG, (offset & 7)*8);
|
|
write_reg_u32(s->regs_base + JPG_SUB_CTRL_REG, 2);
|
|
}
|
|
|
|
/*
|
|
quant matrix should be stored in zigzag order, it is also the order parse from DQT
|
|
*/
|
|
static void ve_config_quant_matrix(struct jpeg_ctx *s)
|
|
{
|
|
int comp, i;
|
|
uint32_t val;
|
|
|
|
logd("config quant matrix");
|
|
|
|
for (comp = 0; comp < 3; comp++) {
|
|
write_reg_u32(s->regs_base + JPG_QMAT_INFO_REG, (comp << 6) | 3);
|
|
write_reg_u32(s->regs_base + JPG_QMAT_ADDR_REG, comp << 6);
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
val = s->q_matrixes[s->quant_index[comp]][i];
|
|
write_reg_u32(s->regs_base + JPG_QMAT_VAL_REG, val);
|
|
}
|
|
}
|
|
|
|
write_reg_u32(s->regs_base + JPG_QMAT_INFO_REG, 0);
|
|
}
|
|
|
|
/*
|
|
4 huffman tables, including DC_Luma\DC_Chroma\AC_LUMA\AC_Chroma
|
|
every table including 4 parts:
|
|
1)start_code: the minimum huffman code in all codes with the same code length,
|
|
for example, the minimum code of code length 3 is 3'b101, it is 16'Hffff if not exist
|
|
2)max_code: the max huffman code in all codes with the same code length, it is 16'Hffff if not exist
|
|
3)huff_offset: the symbol offset in huff_val, the symbol is the start code in code length i
|
|
4)huff_val: the symbols of all the huffman code
|
|
|
|
DC_Luma huff_val address 0-11
|
|
DC_Chroma huff_val address 12-23
|
|
AC_Luma huff_val address 24-185
|
|
AC_Chroma huff_val address 186-347
|
|
*/
|
|
static void ve_config_huffman_table(struct jpeg_ctx *s)
|
|
{
|
|
int i;
|
|
uint32_t val;
|
|
|
|
logd("config huffman table");
|
|
|
|
//* 1. config start_code
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, 3);
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, 0);
|
|
|
|
// DC_LUMA -> DC_CHROMA -> AC_LUMA ->AC_CHROMA ->
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[0][s->dc_index[0]].start_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[0][s->dc_index[1]].start_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[1][s->ac_index[0]].start_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[1][s->ac_index[1]].start_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
|
|
//* 2. config max code
|
|
logd("max_code");
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, (1<< 10) | 3);
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, (1<< 10));
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[0][s->dc_index[0]].max_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[0][s->dc_index[1]].max_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[1][s->ac_index[0]].max_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[1][s->ac_index[1]].max_code[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
//* 3. config huffman offset
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, (2<<10) | 3);
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, (2<<10));
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[0][s->dc_index[0]].offset[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[0][s->dc_index[1]].offset[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[1][s->ac_index[0]].offset[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
val = s->huffman_table[1][s->ac_index[1]].offset[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
//* 4.1 config huffman val (dc luma)
|
|
logd("huff_val dc_luma");
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, (3<<10) | 3);
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, (3<<10));
|
|
|
|
if (s->huffman_table[0][s->dc_index[0]].total_code > 12) {
|
|
loge("dc luma code num : %d", s->huffman_table[0][s->dc_index[0]].total_code);
|
|
//abort();
|
|
}
|
|
for (i = 0; i < s->huffman_table[0][s->dc_index[0]].total_code; i++) {
|
|
val = s->huffman_table[0][s->dc_index[0]].symbol[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
//* 4.2 config huffman val (dc chroma)
|
|
logd("huff_val dc_chroma");
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, (3<<10) | 3);
|
|
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, (3<<10) | 12);
|
|
if (s->huffman_table[0][s->dc_index[1]].total_code > 12) {
|
|
loge("dc chroma code num : %d", s->huffman_table[0][s->dc_index[1]].total_code);
|
|
//abort();
|
|
}
|
|
for (i = 0; i < s->huffman_table[0][s->dc_index[1]].total_code; i++) {
|
|
val = s->huffman_table[0][s->dc_index[1]].symbol[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
logv("write JPG_HUFF_VAL_REG %02X %02X", JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
//* 4.3 config huffman val (ac luma)
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, (3<<10) | 3);
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, (3<<10) | 24);
|
|
|
|
if (s->huffman_table[1][s->ac_index[0]].total_code > 162) {
|
|
loge("ac luma code num : %d", s->huffman_table[1][s->ac_index[0]].total_code);
|
|
//abort();
|
|
}
|
|
for (i = 0; i < s->huffman_table[1][s->ac_index[0]].total_code; i++) {
|
|
val = s->huffman_table[1][s->ac_index[0]].symbol[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
//* 4.4 config huffman val (ac chroma)
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, (3<<10) | 3);
|
|
write_reg_u32(s->regs_base + JPG_HUFF_ADDR_REG, (3<<10) | 186);
|
|
|
|
if (s->huffman_table[1][s->ac_index[1]].total_code > 162) {
|
|
loge("dc chroma code num : %d", s->huffman_table[1][s->ac_index[1]].total_code);
|
|
//abort();
|
|
}
|
|
for (i = 0; i < s->huffman_table[1][s->ac_index[1]].total_code; i++) {
|
|
val = s->huffman_table[1][s->ac_index[1]].symbol[i];
|
|
write_reg_u32(s->regs_base + JPG_HUFF_VAL_REG, val);
|
|
}
|
|
|
|
// clear
|
|
write_reg_u32(s->regs_base + JPG_HUFF_INFO_REG, 0);
|
|
}
|
|
|
|
static void config_jpeg_picture_info_register(struct jpeg_ctx *s)
|
|
{
|
|
logi("config picture info");
|
|
uint32_t val;
|
|
int color_mode = 0;
|
|
if (s->pix_fmt == MPP_FMT_YUV420P || s->pix_fmt == MPP_FMT_NV12
|
|
|| s->pix_fmt == MPP_FMT_NV21) {
|
|
color_mode = 0;
|
|
} else if(s->pix_fmt == MPP_FMT_YUV444P) {
|
|
color_mode = 3;
|
|
} else if (s->pix_fmt == MPP_FMT_YUV400) {
|
|
color_mode = 4;
|
|
} else if (s->pix_fmt == MPP_FMT_YUV422P || s->pix_fmt == MPP_FMT_NV16
|
|
|| s->pix_fmt == MPP_FMT_NV61) {
|
|
color_mode = 1;
|
|
} else {
|
|
loge("not supprt this format(%d)", s->pix_fmt);
|
|
//abort();
|
|
}
|
|
|
|
val = s->output_frame.buf.stride[0] | (s->uv_interleave << 16) | (color_mode << 17);
|
|
|
|
#define FRAME_FORMAT_REG 0x1400
|
|
#define FRAME_SIZE_REG 0x1404
|
|
#define FRAME_YADDR_REG 0x1408
|
|
#define FRAME_CBADDR_REG 0x140C
|
|
#define FRAME_CRADDR_REG 0x1410
|
|
|
|
logi("size: %d %d", s->rm_h_real_size[0], s->rm_h_real_size[1]);
|
|
write_reg_u32(s->regs_base + FRAME_FORMAT_REG, val);
|
|
write_reg_u32(s->regs_base + FRAME_SIZE_REG, (s->width << 16) | s->height);
|
|
write_reg_u32(s->regs_base + FRAME_YADDR_REG, s->frame_phy_addr[0]);
|
|
write_reg_u32(s->regs_base + FRAME_CBADDR_REG, s->frame_phy_addr[1]);
|
|
write_reg_u32(s->regs_base + FRAME_CRADDR_REG, s->frame_phy_addr[2]);
|
|
}
|
|
|
|
static void config_header_info(struct jpeg_ctx *s)
|
|
{
|
|
uint32_t val = 0;
|
|
|
|
write_reg_u32(s->regs_base + JPG_START_POS_REG, 0);
|
|
|
|
val = (s->have_dht << 6) | (s->dc_index[0] <<10) | (s->dc_index[1] <<9) | (s->dc_index[2] << 8) |
|
|
(s->ac_index[0] <<14) | (s->ac_index[1] <<13) | (s->ac_index[2] <<12);
|
|
write_reg_u32(s->regs_base + JPG_CTRL_REG, val);
|
|
|
|
logd("picture size");
|
|
int mcu_w = s->h_count[0] * 8;
|
|
int mcu_h = s->v_count[0] * 8;
|
|
int jpeg_hsize = (s->width + mcu_w - 1) / mcu_w * mcu_w;
|
|
int jpeg_vsize = (s->height + mcu_h - 1) / mcu_h * mcu_h;
|
|
write_reg_u32(s->regs_base + JPG_SIZE_REG, (jpeg_hsize << 16) | jpeg_vsize);
|
|
|
|
int tatal_blks = s->h_count[0] * s->v_count[0] +
|
|
s->h_count[1] * s->v_count[1] + s->h_count[2] * s->v_count[2];
|
|
val = s->v_count[1] | (s->h_count[1] << 2) | (s->v_count[1] << 4) | (s->h_count[1] << 6) |
|
|
(s->v_count[0] << 8) | (s->h_count[0] << 10) | (s->nb_components << 12) |
|
|
(tatal_blks << 16);
|
|
write_reg_u32(s->regs_base + JPG_MCU_INFO_REG, val);
|
|
|
|
int num = 12 / tatal_blks;
|
|
write_reg_u32(s->regs_base + JPG_HANDLE_NUM_REG, num > 4? 4: num);
|
|
|
|
write_reg_u32(s->regs_base + JPG_UV_REG, s->uv_interleave);
|
|
write_reg_u32(s->regs_base + JPG_FRAME_IDX_REG, 0);
|
|
write_reg_u32(s->regs_base + JPG_RST_INTERVAL_REG, s->restart_interval);
|
|
write_reg_u32(s->regs_base + JPG_INTRRUPT_EN_REG, 0);
|
|
}
|
|
|
|
int ve_decode_jpeg(struct jpeg_ctx *s, int byte_offset)
|
|
{
|
|
unsigned int status;
|
|
|
|
ve_get_client();
|
|
|
|
//* 1. config ve top
|
|
ve_config_ve_top_reg(s);
|
|
|
|
//* 2. config header info
|
|
config_header_info(s);
|
|
config_jpeg_picture_info_register(s);
|
|
|
|
//* 3. config quant matrix
|
|
ve_config_quant_matrix(s);
|
|
|
|
//* 4. config huffman table
|
|
if(s->have_dht) {
|
|
ve_config_huffman_table(s);
|
|
}
|
|
|
|
//* 5. config bitstream
|
|
ve_config_bitstream_register(s, byte_offset, 1);
|
|
|
|
//* 6. decode start
|
|
write_reg_u32(s->regs_base + JPG_START_REG, 1);
|
|
|
|
if(ve_wait(&status) < 0) {
|
|
loge("ve wait irq timeout");
|
|
logi("read JPG_STATUS_REG %x", read_reg_u32(s->regs_base + JPG_STATUS_REG));
|
|
|
|
ve_reset();
|
|
ve_put_client();
|
|
return -1;
|
|
}
|
|
|
|
if(status > 1) {
|
|
loge("status error");
|
|
ve_reset();
|
|
ve_put_client();
|
|
return -1;
|
|
}
|
|
|
|
logi("ve status %x", status);
|
|
|
|
logi("read JPG_CYCLES_REG %x", read_reg_u32(s->regs_base + JPG_CYCLES_REG));
|
|
|
|
ve_put_client();
|
|
return 0;
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
static void print_help(char* prog)
|
|
{
|
|
printf("%s\n", prog);
|
|
printf("Compile time: %s\n", __TIME__);
|
|
printf("Usage:\n\n"
|
|
"Options:\n"
|
|
" -i input file name\n"
|
|
" -h help\n\n"
|
|
"End:\n");
|
|
}
|
|
|
|
static int get_file_size(FILE* fp)
|
|
{
|
|
int len = 0;
|
|
fseek(fp, 0, SEEK_END);
|
|
len = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
return len;
|
|
}
|
|
|
|
/************************** render frame *************************************/
|
|
static int set_fb_layer_alpha(int fb0_fd, int val)
|
|
{
|
|
int ret = 0;
|
|
struct aicfb_alpha_config alpha = {0};
|
|
|
|
if (fb0_fd < 0)
|
|
return -1;
|
|
|
|
alpha.layer_id = 1;
|
|
alpha.enable = 1;
|
|
alpha.mode = 1;
|
|
alpha.value = val;
|
|
ret = ioctl(fb0_fd, AICFB_UPDATE_ALPHA_CONFIG, &alpha);
|
|
if (ret < 0)
|
|
loge("fb ioctl() AICFB_UPDATE_ALPHA_CONFIG failed!");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void video_layer_set(int fb0_fd, struct mpp_buf *picture_buf)
|
|
{
|
|
struct aicfb_layer_data layer = {0};
|
|
int dmabuf_num = 0;
|
|
struct dma_buf_info dmabuf_fd[3];
|
|
int data_size[3];
|
|
int i;
|
|
|
|
if (fb0_fd < 0)
|
|
return;
|
|
|
|
layer.layer_id = AICFB_LAYER_TYPE_VIDEO;
|
|
layer.enable = 1;
|
|
layer.scale_size.width = 0;
|
|
layer.scale_size.height= 0;
|
|
|
|
layer.pos.x = 0;
|
|
layer.pos.y = 0;
|
|
memcpy(&layer.buf, picture_buf, sizeof(struct mpp_buf));
|
|
|
|
logi("width: %d, height: %d, format: %d", picture_buf->size.width,
|
|
picture_buf->size.height, picture_buf->format);
|
|
logi("x: %d, y: %d, width: %d, height: %d", picture_buf->crop.x, picture_buf->crop.y,
|
|
picture_buf->crop.width, picture_buf->crop.height);
|
|
|
|
data_size[0] = picture_buf->size.height * picture_buf->stride[0];
|
|
if (picture_buf->format == MPP_FMT_ARGB_8888) {
|
|
dmabuf_num = 1;
|
|
} else if (picture_buf->format == MPP_FMT_RGBA_8888) {
|
|
dmabuf_num = 1;
|
|
} else if (picture_buf->format == MPP_FMT_RGB_888) {
|
|
dmabuf_num = 1;
|
|
} else if (picture_buf->format == MPP_FMT_YUV420P) {
|
|
dmabuf_num = 3;
|
|
data_size[1] = data_size[2] = data_size[0]/4;
|
|
} else if (picture_buf->format == MPP_FMT_YUV444P) {
|
|
dmabuf_num = 3;
|
|
data_size[1] = data_size[2] = data_size[0];
|
|
} else if (picture_buf->format == MPP_FMT_YUV422P) {
|
|
dmabuf_num = 3;
|
|
data_size[1] = data_size[2] = data_size[0]/2;
|
|
} else if (picture_buf->format == MPP_FMT_YUV400) {
|
|
dmabuf_num = 1;
|
|
} else {
|
|
loge("no support picture foramt %d, default argb8888", picture_buf->format);
|
|
}
|
|
|
|
#if 0
|
|
unsigned char* hw_data[3];
|
|
FILE* fp_save = fopen("save.bin", "wb");
|
|
for (i=0; i<dmabuf_num; i++) {
|
|
hw_data[i] = mmap(NULL, data_size[i], PROT_READ, MAP_SHARED, picture_buf->fd[i], 0);
|
|
if (hw_data[i] == MAP_FAILED) {
|
|
loge("dmabuf alloc mmap failed!");
|
|
return ;
|
|
}
|
|
if(fp_save)
|
|
fwrite(hw_data[i], 1, data_size[i], fp_save);
|
|
}
|
|
|
|
for (i=0; i<dmabuf_num; i++) {
|
|
munmap(hw_data[i], data_size[i]);
|
|
}
|
|
fclose(fp_save);
|
|
#endif
|
|
//* add dmabuf to de driver
|
|
for(i=0; i<dmabuf_num; i++) {
|
|
dmabuf_fd[i].fd = picture_buf->fd[i];
|
|
if (ioctl(fb0_fd, AICFB_ADD_DMABUF, &dmabuf_fd[i]) < 0)
|
|
loge("fb ioctl() AICFB_UPDATE_LAYER_CONFIG failed!");
|
|
}
|
|
|
|
//* update layer config (it is async interface)
|
|
if (ioctl(fb0_fd, AICFB_UPDATE_LAYER_CONFIG, &layer) < 0)
|
|
loge("fb ioctl() AICFB_UPDATE_LAYER_CONFIG failed!");
|
|
|
|
//* wait vsync (wait layer config)
|
|
ioctl(fb0_fd, AICFB_WAIT_FOR_VSYNC, NULL);
|
|
|
|
//* display this picture 2 seconds
|
|
usleep(2000000);
|
|
|
|
//* disable layer
|
|
layer.enable = 0;
|
|
if(ioctl(fb0_fd, AICFB_UPDATE_LAYER_CONFIG, &layer) < 0)
|
|
loge("fb ioctl() AICFB_UPDATE_LAYER_CONFIG failed!");
|
|
|
|
//* remove dmabuf to de driver
|
|
for(i=0; i<dmabuf_num; i++) {
|
|
if (ioctl(fb0_fd, AICFB_RM_DMABUF, &dmabuf_fd[i]) < 0)
|
|
loge("fb ioctl() AICFB_UPDATE_LAYER_CONFIG failed!");
|
|
}
|
|
}
|
|
|
|
static int render_frame(struct mpp_frame* frame)
|
|
{
|
|
int fb0_fd = 0;
|
|
struct fb_fix_screeninfo finfo;
|
|
|
|
//* 1. open fb0
|
|
fb0_fd = open("/dev/fb0", O_RDWR);
|
|
if (fb0_fd < 0) {
|
|
logw("open fb0 failed!");
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(fb0_fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
|
|
loge("read fixed information failed!");
|
|
close(fb0_fd);
|
|
return -1;
|
|
}
|
|
|
|
//* 2 disp frame;
|
|
set_fb_layer_alpha(fb0_fd, 10);
|
|
video_layer_set(fb0_fd, &frame->buf);
|
|
|
|
if (fb0_fd >= 0)
|
|
close(fb0_fd);
|
|
return 0;
|
|
}
|
|
|
|
/************************ parse jpeg header **************************************************/
|
|
/* return the 8 bit start code value and update the search
|
|
state. Return -1 if no start code found */
|
|
static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end)
|
|
{
|
|
const uint8_t *buf_ptr;
|
|
unsigned int v, v2;
|
|
int val;
|
|
int skipped = 0;
|
|
|
|
buf_ptr = *pbuf_ptr;
|
|
while (buf_end - buf_ptr > 1) {
|
|
v = *buf_ptr++;
|
|
v2 = *buf_ptr;
|
|
if ((v == 0xff) && (v2 >= SOF0) && (v2 <= 0xfe) && buf_ptr < buf_end) {
|
|
val = *buf_ptr++;
|
|
goto found;
|
|
}
|
|
skipped++;
|
|
}
|
|
buf_ptr = buf_end;
|
|
val = -1;
|
|
found:
|
|
*pbuf_ptr = buf_ptr;
|
|
|
|
return val;
|
|
}
|
|
|
|
static int mjpeg_find_marker(struct jpeg_ctx *s,
|
|
const uint8_t **buf_ptr, const uint8_t *buf_end,
|
|
const uint8_t **unescaped_buf_ptr,
|
|
int *unescaped_buf_size)
|
|
{
|
|
int start_code;
|
|
start_code = find_marker(buf_ptr, buf_end);
|
|
|
|
*unescaped_buf_ptr = *buf_ptr;
|
|
*unescaped_buf_size = buf_end - *buf_ptr;
|
|
|
|
return start_code;
|
|
}
|
|
|
|
static void skip_variable_marker(struct jpeg_ctx *s)
|
|
{
|
|
int left = read_bits(&s->gb, 16);
|
|
left -= 2;
|
|
while(left) {
|
|
skip_bits(&s->gb, 8);
|
|
left --;
|
|
}
|
|
}
|
|
|
|
static void fill_huffman_startcode(struct jpeg_ctx *s, int class, int index, const uint8_t *bits_table)
|
|
{
|
|
int i, j, k, nb, code;
|
|
|
|
code = 0;
|
|
k = 0;
|
|
for (i=1; i<=16; i++) {
|
|
nb = bits_table[i];
|
|
s->huffman_table[class][index].start_code[i - 1] = code;
|
|
s->huffman_table[class][index].max_code[i - 1] = code + nb-1;
|
|
|
|
for (j=0; j<nb; j++) {
|
|
s->huffman_table[class][index].code[k] = code;
|
|
s->huffman_table[class][index].len[k] = i;
|
|
k++;
|
|
code++;
|
|
}
|
|
code <<= 1;
|
|
}
|
|
|
|
s->huffman_table[class][index].total_code = k;
|
|
for (i=16; i>=1; i--) {
|
|
if (bits_table[i] == 0) {
|
|
s->huffman_table[class][index].start_code[i - 1] = 0xffff;
|
|
s->huffman_table[class][index].max_code[i - 1] = 0xffff;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* decode huffman tables and build VLC decoders */
|
|
int mjpeg_decode_dht(struct jpeg_ctx *s)
|
|
{
|
|
int len, index, i, class, n, v, code_max;
|
|
uint8_t bits_table[17];
|
|
|
|
logd("====== DHT (huffman table) ======");
|
|
|
|
len = read_bits(&s->gb, 16) - 2;
|
|
|
|
if (8*len > read_bits_left(&s->gb)) {
|
|
loge("dht: len %d is too large", len);
|
|
return -1;
|
|
}
|
|
|
|
while (len > 0) {
|
|
if (len < 17)
|
|
return -1;
|
|
class = read_bits(&s->gb, 4);
|
|
if (class >= 2)
|
|
return -1;
|
|
index = read_bits(&s->gb, 4);
|
|
if (index >= 4)
|
|
return -1;
|
|
|
|
logi("class: %d, index: %d", class, index);
|
|
// initial huffman table
|
|
for (i = 0; i < 16; i++) {
|
|
s->huffman_table[class][index].offset[i] = 0;
|
|
s->huffman_table[class][index].start_code[i] = 0;
|
|
}
|
|
for (i = 0; i < 256; i++)
|
|
s->huffman_table[class][index].symbol[i] = 0;
|
|
|
|
n = 0;
|
|
// number of huffman code with code length i
|
|
bits_table[0] = 0;
|
|
|
|
//* 1. parse BITS(BITS is the number of huffman code which is the same
|
|
// code length,
|
|
// the max code length is 16), generate HUFFSIZE according BITS
|
|
// table
|
|
for (i = 1; i <= 16; i++) {
|
|
s->huffman_table[class][index].offset[i - 1] = n;
|
|
bits_table[i] = read_bits(&s->gb, 8);
|
|
s->huffman_table[class][index].bits_table[i - 1] = bits_table[i];
|
|
n += bits_table[i];
|
|
}
|
|
len -= 17;
|
|
if (len < n || n > 256)
|
|
return -1;
|
|
|
|
code_max = 0;
|
|
//* 2.parse HUFFVAL table, the huffman code to symbol
|
|
for (i = 0; i < n; i++) {
|
|
v = read_bits(&s->gb, 8);
|
|
if (v > code_max)
|
|
code_max = v;
|
|
s->huffman_table[class][index].symbol[i] = v;
|
|
}
|
|
len -= n;
|
|
|
|
//* 3. generate HUFFCODE table, it is used for config ve
|
|
fill_huffman_startcode(s, class, index, bits_table);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* quantize tables */
|
|
int mjpeg_decode_dqt(struct jpeg_ctx *s)
|
|
{
|
|
int len, index, i;
|
|
|
|
len = read_bits(&s->gb, 16) - 2;
|
|
|
|
if (8 * len > read_bits_left(&s->gb)) {
|
|
loge("dqt: len %d is too large", len);
|
|
return -1;
|
|
}
|
|
|
|
while (len >= 65) {
|
|
int pr = read_bits(&s->gb, 4);
|
|
if (pr > 1) {
|
|
loge("dqt: invalid precision");
|
|
return -1;
|
|
}
|
|
if (pr != 0) {
|
|
loge("dqt: only support 8 bit precision");
|
|
return -1;
|
|
}
|
|
index = read_bits(&s->gb, 4);
|
|
if (index >= 4)
|
|
return -1;
|
|
logd("index=%d", index);
|
|
/* read quant table */
|
|
for (i = 0; i < 64; i++) {
|
|
s->q_matrixes[index][i] = read_bits(&s->gb, pr ? 16 : 8);
|
|
}
|
|
|
|
len -= 1 + 64 * (1 + pr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mjpeg_decode_sof(struct jpeg_ctx *s)
|
|
{
|
|
int len, nb_components, i, bits;
|
|
int phy_h_stride[4]; // hor stride ( before post-process)
|
|
int phy_v_stride[4]; // ver stride ( before post-process)
|
|
int h_real_size[4]; // hor real size ( before post-process)
|
|
int v_real_size[4]; // hor real size ( before post-process)
|
|
|
|
logd("===== ff_mjpeg_decode_sof ====== ");
|
|
|
|
len = read_bits(&s->gb, 16);
|
|
bits = read_bits(&s->gb, 8);
|
|
|
|
if (bits > 16 || bits < 1) {
|
|
loge("bits %d is invalid", bits);
|
|
return -1;
|
|
}
|
|
|
|
/* only 8 bits/component accepted */
|
|
if (bits != 8) {
|
|
loge("only support 8 bits, bits %d is invalid", bits);
|
|
return -1;
|
|
}
|
|
|
|
s->height = read_bits(&s->gb, 16);
|
|
s->width = read_bits(&s->gb, 16);
|
|
|
|
if (s->width < 1 || s->height < 1) {
|
|
loge("size too small: width %d, height %d", s->width, s->height);
|
|
return -1;
|
|
}
|
|
|
|
nb_components = read_bits(&s->gb, 8);
|
|
if (nb_components <= 0 || nb_components > MAX_COMPONENTS)
|
|
return -1;
|
|
|
|
if (len != 8 + 3 * nb_components) {
|
|
loge("decode_sof0: error, len(%d) mismatch %d components", len, nb_components);
|
|
return -1;
|
|
}
|
|
|
|
s->nb_components = nb_components;
|
|
for (i = 0; i < nb_components; i++) {
|
|
/* component id */
|
|
s->component_id[i] = read_bits(&s->gb, 8) - 1;
|
|
s->h_count[i] = read_bits(&s->gb, 4);
|
|
s->v_count[i] = read_bits(&s->gb, 4);
|
|
s->quant_index[i] = read_bits(&s->gb, 8);
|
|
if (s->quant_index[i] >= 4) {
|
|
loge("quant_index is invalid");
|
|
return -1;
|
|
}
|
|
if (!s->h_count[i] || !s->v_count[i]) {
|
|
loge("Invalid sampling factor in component %d %d:%d",
|
|
i, s->h_count[i], s->v_count[i]);
|
|
return -1;
|
|
}
|
|
|
|
logd("component %d %d:%d id: %d quant:%d",i, s->h_count[i], s->v_count[i],
|
|
s->component_id[i], s->quant_index[i]);
|
|
}
|
|
|
|
logi("s->width %d, s->height %d", s->width, s->height);
|
|
|
|
if (s->h_count[0] == 2 && s->v_count[0] == 2 && s->h_count[1] == 1 &&
|
|
s->v_count[1] == 1 && s->h_count[2] == 1 && s->v_count[2] == 1) {
|
|
s->pix_fmt = MPP_FMT_YUV420P;
|
|
logi("pixel format: yuv420");
|
|
} else if (s->h_count[0] == 2 && s->v_count[0] == 1 && s->h_count[1] == 1 &&
|
|
s->v_count[1] == 1 && s->h_count[2] == 1 && s->v_count[2] == 1) {
|
|
s->pix_fmt = MPP_FMT_YUV422P;
|
|
logi("pixel format: yuv422");
|
|
} else if (s->h_count[0] == 1 && s->v_count[0] == 1 && s->h_count[1] == 1 &&
|
|
s->v_count[1] == 1 && s->h_count[2] == 1 && s->v_count[2] == 1) {
|
|
s->pix_fmt = MPP_FMT_YUV444P;
|
|
logi("pixel format: yuv444");
|
|
} else if (s->h_count[0] == 1 && s->v_count[0] == 2 && s->h_count[1] == 1 &&
|
|
s->v_count[1] == 2 && s->h_count[2] == 1 && s->v_count[2] == 2) {
|
|
s->pix_fmt = MPP_FMT_YUV444P;
|
|
logi("pixel format: ffmpeg yuv444");
|
|
} else if (s->h_count[1] == 0 && s->v_count[1] == 0 && s->h_count[2] == 0 &&
|
|
s->v_count[2] == 0) {
|
|
s->pix_fmt = MPP_FMT_YUV400;
|
|
logi("pixel format: yuv400");
|
|
} else {
|
|
loge("Not support format! h_count: %d %d %d, v_count: %d %d %d",
|
|
s->h_count[0], s->h_count[1], s->h_count[2],
|
|
s->v_count[0], s->v_count[1], s->v_count[2]);
|
|
return -1;
|
|
}
|
|
|
|
//* get the output size of scale down
|
|
s->nb_mcu_width = (s->width + 8 * s->h_count[0] - 1) / (8 * s->h_count[0]);
|
|
s->nb_mcu_height = (s->height + 8 * s->v_count[0] - 1) / (8 * s->v_count[0]);
|
|
int h_stride_y = (s->nb_mcu_width * s->h_count[0] * 8);
|
|
int v_stride_y = (s->nb_mcu_height * s->v_count[0] * 8);
|
|
phy_h_stride[0] = phy_h_stride[1] = phy_h_stride[2] = (h_stride_y + 15) / 16 * 16;
|
|
phy_v_stride[0] = phy_v_stride[1] = phy_v_stride[2] = (v_stride_y + 15) / 16 * 16;
|
|
h_real_size[0] = s->width;
|
|
v_real_size[0] = s->height;
|
|
|
|
if (s->pix_fmt == MPP_FMT_YUV420P) {
|
|
phy_h_stride[1] = phy_h_stride[2] = phy_h_stride[0] / 2;
|
|
phy_v_stride[1] = phy_v_stride[2] = phy_v_stride[0] / 2;
|
|
h_real_size[1] = h_real_size[2] = h_real_size[0] / 2;
|
|
v_real_size[1] = v_real_size[2] = v_real_size[0] / 2;
|
|
} else if (s->pix_fmt == MPP_FMT_YUV444P || s->pix_fmt == MPP_FMT_YUV400) {
|
|
phy_h_stride[0] = (h_stride_y + 7) / 8 * 8;
|
|
phy_v_stride[0] = (v_stride_y + 7) / 8 * 8;
|
|
phy_h_stride[1] = phy_h_stride[2] = phy_h_stride[0];
|
|
phy_v_stride[1] = phy_v_stride[2] = phy_v_stride[0];
|
|
h_real_size[1] = h_real_size[2] = h_real_size[0];
|
|
v_real_size[1] = v_real_size[2] = v_real_size[0];
|
|
} else if (s->pix_fmt == MPP_FMT_YUV422P) {
|
|
phy_h_stride[1] = phy_h_stride[2] = phy_h_stride[0] / 2;
|
|
phy_v_stride[1] = phy_v_stride[2] = phy_v_stride[0];
|
|
h_real_size[1] = h_real_size[2] = h_real_size[0] / 2;
|
|
v_real_size[1] = v_real_size[2] = v_real_size[0];
|
|
}
|
|
|
|
//* get the output size of rotate
|
|
for (int k = 0; k < 3; k++) {
|
|
s->rm_h_real_size[k] = h_real_size[k];
|
|
s->rm_v_real_size[k] = v_real_size[k];
|
|
s->rm_h_stride[k] = phy_h_stride[k];
|
|
s->rm_v_stride[k] = phy_v_stride[k];
|
|
}
|
|
|
|
s->got_picture = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mjpeg_decode_sos(struct jpeg_ctx *s,
|
|
const uint8_t *mb_bitmask,
|
|
int mb_bitmask_size)
|
|
{
|
|
int len, nb_components, i;
|
|
int index, id;
|
|
|
|
logd("===== ff_mjpeg_decode_sos ====== ");
|
|
|
|
if (!s->got_picture) {
|
|
logw("Can not process SOS before SOF, skipping");
|
|
return -1;
|
|
}
|
|
|
|
//* 1. parse SOS info
|
|
len = read_bits(&s->gb, 16);
|
|
nb_components = read_bits(&s->gb, 8);
|
|
if (nb_components == 0 || nb_components > MAX_COMPONENTS) {
|
|
return -1;
|
|
}
|
|
if (len != 6 + 2 * nb_components) {
|
|
loge("decode_sos: invalid len (%d)", len);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < nb_components; i++) {
|
|
id = read_bits(&s->gb, 8) - 1;
|
|
/* find component index */
|
|
for (index = 0; index < s->nb_components; index++)
|
|
if (id == s->component_id[index])
|
|
break;
|
|
if (index == s->nb_components) {
|
|
loge("decode_sos: index(%d) out of components", index);
|
|
return -1;
|
|
}
|
|
|
|
s->dc_index[i] = read_bits(&s->gb, 4);
|
|
s->ac_index[i] = read_bits(&s->gb, 4);
|
|
|
|
if (s->dc_index[i] < 0 || s->ac_index[i] < 0 ||
|
|
s->dc_index[i] >= 4 || s->ac_index[i] >= 4) {
|
|
loge("index out of range");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
skip_bits(&s->gb, 8); /* JPEG Ss / lossless JPEG predictor /JPEG-LS NEAR */
|
|
skip_bits(&s->gb, 8); /* JPEG Se / JPEG-LS ILV */
|
|
skip_bits(&s->gb, 4); /* Ah */
|
|
skip_bits(&s->gb, 4); /* Al */
|
|
|
|
int sos_size = read_bits_count(&s->gb) / 8;
|
|
logd("sos_size %d", sos_size);
|
|
|
|
s->output_frame.buf.size.height = s->rm_v_stride[0];
|
|
s->output_frame.buf.size.width = s->rm_h_stride[0];
|
|
s->output_frame.buf.stride[0] = s->rm_h_stride[0];
|
|
s->output_frame.buf.stride[1] = s->rm_h_stride[1];
|
|
s->output_frame.buf.stride[2] = s->rm_h_stride[2];
|
|
s->output_frame.buf.format = s->pix_fmt;
|
|
s->output_frame.buf.crop_en = 1;
|
|
s->output_frame.buf.crop.x = 0;
|
|
s->output_frame.buf.crop.y = 0;
|
|
s->output_frame.buf.crop.width = s->width;
|
|
s->output_frame.buf.crop.height = s->height;
|
|
mpp_buf_alloc(s->dma_fd, &s->output_frame.buf);
|
|
|
|
logi("width: %d, height: %d", s->output_frame.buf.size.width, s->output_frame.buf.size.height);
|
|
|
|
ve_add_dma_buf(s->output_frame.buf.fd[0], &s->frame_phy_addr[0]);
|
|
ve_add_dma_buf(s->output_frame.buf.fd[1], &s->frame_phy_addr[1]);
|
|
ve_add_dma_buf(s->output_frame.buf.fd[2], &s->frame_phy_addr[2]);
|
|
|
|
int offset = (s->raw_scan_buffer - s->buf_start) + sos_size;
|
|
logw("offste: %d", offset);
|
|
if (ve_decode_jpeg(s, offset))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mjpeg_decode_dri(struct jpeg_ctx *s)
|
|
{
|
|
if (read_bits(&s->gb, 16) != 4)
|
|
return -1;
|
|
s->restart_interval = read_bits(&s->gb, 16);
|
|
s->restart_count = 0;
|
|
logd("restart interval: %d", s->restart_interval);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int decode_jpeg(struct jpeg_ctx *s, FILE* fp, int buf_size)
|
|
{
|
|
const uint8_t *buf_end, *buf_ptr;
|
|
const uint8_t *unescaped_buf_ptr;
|
|
int unescaped_buf_size;
|
|
int start_code;
|
|
int ret = 0;
|
|
|
|
int align_size = ALIGN_1024B(buf_size + 1024);
|
|
s->ve_fd = ve_open_device();
|
|
s->regs_base = ve_get_reg_base();
|
|
|
|
// bitstream buffer
|
|
s->dma_fd = dmabuf_device_open();
|
|
s->input_buf_fd = dmabuf_alloc(s->dma_fd, align_size);
|
|
unsigned char* vir_addr = dmabuf_mmap(s->input_buf_fd, align_size);
|
|
fread(vir_addr, buf_size, 1, fp);
|
|
dmabuf_sync(s->input_buf_fd, CACHE_CLEAN);
|
|
|
|
s->buf_start = vir_addr;
|
|
s->buf_size = buf_size;
|
|
|
|
ve_add_dma_buf(s->input_buf_fd, &s->input_phy_addr);
|
|
|
|
if (vir_addr[0] != 0xff && vir_addr[1] != 0xd8) {
|
|
loge("the file is not jpeg");
|
|
return -1;
|
|
}
|
|
|
|
buf_ptr = vir_addr;
|
|
buf_end = buf_ptr + buf_size;
|
|
|
|
while (buf_ptr < buf_end) {
|
|
/* find start next marker */
|
|
start_code = mjpeg_find_marker(s, &buf_ptr, buf_end,
|
|
&unescaped_buf_ptr,
|
|
&unescaped_buf_size);
|
|
/* EOF */
|
|
if (start_code < 0) {
|
|
break;
|
|
} else if (unescaped_buf_size > INT_MAX / 8) {
|
|
loge("MJPEG packet 0x%x too big (%d/%d), corrupt data?",
|
|
start_code, unescaped_buf_size, buf_size);
|
|
return -1;
|
|
}
|
|
|
|
ret = init_read_bits(&s->gb, unescaped_buf_ptr, unescaped_buf_size*8);
|
|
|
|
if (ret < 0) {
|
|
loge("invalid buffer");
|
|
goto fail;
|
|
}
|
|
|
|
s->start_code = start_code;
|
|
logi("startcode: %02x", start_code);
|
|
|
|
ret = -1;
|
|
|
|
switch (start_code) {
|
|
case DQT:
|
|
ret = mjpeg_decode_dqt(s);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
case SOI:
|
|
s->restart_interval = 0;
|
|
s->restart_count = 0;
|
|
/* nothing to do on SOI */
|
|
break;
|
|
case DHT:
|
|
s->have_dht = 1;
|
|
if ((ret = mjpeg_decode_dht(s)) < 0) {
|
|
loge("huffman table decode error");
|
|
goto fail;
|
|
}
|
|
break;
|
|
case SOF0:
|
|
case SOF1:
|
|
case SOF2:
|
|
if ((ret = mjpeg_decode_sof(s)) < 0)
|
|
goto fail;
|
|
break;
|
|
|
|
case EOI:
|
|
if (!s->got_picture) {
|
|
logw("Found EOI before any SOF, ignoring");
|
|
break;
|
|
}
|
|
s->got_picture = 0;
|
|
|
|
goto the_end;
|
|
case SOS:
|
|
s->raw_scan_buffer = buf_ptr;
|
|
s->raw_scan_buffer_size = buf_end - buf_ptr;
|
|
|
|
if ((ret = mjpeg_decode_sos(s, NULL, 0)) < 0)
|
|
goto fail;
|
|
|
|
render_frame(&s->output_frame);
|
|
goto the_end;
|
|
case DRI:
|
|
if ((ret = mjpeg_decode_dri(s)) < 0)
|
|
return ret;
|
|
break;
|
|
default:
|
|
logi("skip this marker: %02x", start_code);
|
|
skip_variable_marker(s);
|
|
break;
|
|
}
|
|
|
|
/* eof process start code */
|
|
buf_ptr += (read_bits_count(&s->gb) + 7) / 8;
|
|
logd("marker parser used %d bytes (%d bits)", (read_bits_count(&s->gb) + 7) / 8, read_bits_count(&s->gb));
|
|
}
|
|
|
|
loge("No JPEG data found in image");
|
|
return -1;
|
|
fail:
|
|
s->got_picture = 0;
|
|
|
|
the_end:
|
|
|
|
dmabuf_munmap(vir_addr, align_size);
|
|
if(s->input_buf_fd > -1) {
|
|
ve_rm_dma_buf(s->input_buf_fd, s->input_phy_addr);
|
|
dmabuf_free(s->input_buf_fd);
|
|
}
|
|
|
|
if(s->output_frame.buf.fd[0] > -1)
|
|
ve_rm_dma_buf(s->output_frame.buf.fd[0], s->frame_phy_addr[0]);
|
|
if(s->output_frame.buf.fd[1] > -1)
|
|
ve_rm_dma_buf(s->output_frame.buf.fd[1], s->frame_phy_addr[1]);
|
|
if(s->output_frame.buf.fd[2] > -1)
|
|
ve_rm_dma_buf(s->output_frame.buf.fd[2], s->frame_phy_addr[2]);
|
|
|
|
mpp_buf_free(&s->output_frame.buf);
|
|
|
|
ve_close_device();
|
|
dmabuf_device_close(s->dma_fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
int file_len;
|
|
FILE* fp = NULL;
|
|
struct jpeg_ctx ctx;
|
|
memset(&ctx, 0, sizeof(struct jpeg_ctx));
|
|
|
|
while (1) {
|
|
opt = getopt(argc, argv, "i:h");
|
|
if (opt == -1) {
|
|
break;
|
|
}
|
|
switch (opt) {
|
|
case 'i':
|
|
logd("file path: %s", optarg);
|
|
fp = fopen(optarg, "rb");
|
|
|
|
break;
|
|
case 'h':
|
|
default:
|
|
print_help(argv[0]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (fp == NULL) {
|
|
print_help(argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
|
|
file_len = get_file_size(fp);
|
|
logi("file_len: %d", file_len);
|
|
|
|
decode_jpeg(&ctx, fp, file_len);
|
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
}
|