mirror of
https://gitee.com/Vancouver2017/luban-lite-t3e-pro.git
synced 2025-12-14 18:38:55 +00:00
361 lines
8.9 KiB
C
361 lines
8.9 KiB
C
/*
|
|
* Copyright (C) 2020-2024 ArtInChip Technology Co. Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Author: <qi.xu@artinchip.com>
|
|
* Desc: jpeg encoder
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "ve.h"
|
|
#include "ve_buffer.h"
|
|
#include "put_bits.h"
|
|
#include "jpeg_tables.h"
|
|
#include "jpeg_enc_ctx.h"
|
|
#include "mpp_mem.h"
|
|
#include "mpp_log.h"
|
|
|
|
static s32 __mjpeg_encode_init(struct mpp_encoder *ctx, struct encode_config *config)
|
|
{
|
|
if (!ctx || !config)
|
|
return -1;
|
|
|
|
struct jpeg_ctx *impl = (struct jpeg_ctx *)ctx;
|
|
|
|
impl->ve_fd = ve_open_device();
|
|
if (impl->ve_fd < 0) {
|
|
loge("ve open failed");
|
|
return -1;
|
|
}
|
|
impl->config.quality = config->quality;
|
|
impl->regs_base = ve_get_reg_base();
|
|
impl->alloc = ve_buffer_allocator_create(VE_BUFFER_TYPE_DMA);
|
|
|
|
mjpeg_build_huffman_codes(impl->huff_size_dc_luminance,
|
|
impl->huff_code_dc_luminance,
|
|
avpriv_mjpeg_bits_dc_luminance,
|
|
avpriv_mjpeg_val_dc);
|
|
mjpeg_build_huffman_codes(impl->huff_size_dc_chrominance,
|
|
impl->huff_code_dc_chrominance,
|
|
avpriv_mjpeg_bits_dc_chrominance,
|
|
avpriv_mjpeg_val_dc);
|
|
mjpeg_build_huffman_codes(impl->huff_size_ac_luminance,
|
|
impl->huff_code_ac_luminance,
|
|
avpriv_mjpeg_bits_ac_luminance,
|
|
avpriv_mjpeg_val_ac_luminance);
|
|
mjpeg_build_huffman_codes(impl->huff_size_ac_chrominance,
|
|
impl->huff_code_ac_chrominance,
|
|
avpriv_mjpeg_bits_ac_chrominance,
|
|
avpriv_mjpeg_val_ac_chrominance);
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 __mjpeg_encode_destroy(struct mpp_encoder *ctx)
|
|
{
|
|
if (!ctx)
|
|
return -1;
|
|
|
|
struct jpeg_ctx *impl = (struct jpeg_ctx *)ctx;
|
|
|
|
ve_close_device();
|
|
ve_buffer_allocator_destroy(impl->alloc);
|
|
mpp_free(impl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* table_class: 0 = DC coef, 1 = AC coefs */
|
|
static int put_huffman_table(struct put_bit_ctx *p, int table_class, int table_id,
|
|
const uint8_t *bits_table, const uint8_t *value_table)
|
|
{
|
|
int n, i;
|
|
|
|
put_bits(p, 4, table_class);
|
|
put_bits(p, 4, table_id);
|
|
|
|
n = 0;
|
|
for (i = 1; i <= 16; i++) {
|
|
n += bits_table[i];
|
|
put_bits(p, 8, bits_table[i]);
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
put_bits(p, 8, value_table[i]);
|
|
|
|
return n + 17;
|
|
}
|
|
|
|
static inline void put_marker(struct put_bit_ctx *p, enum JpegMarker code)
|
|
{
|
|
put_bits(p, 8, 0xff);
|
|
put_bits(p, 8, code);
|
|
}
|
|
|
|
static void jpeg_table_header(struct jpeg_ctx* s, struct put_bit_ctx *p)
|
|
{
|
|
int i, j, size;
|
|
uint8_t *ptr;
|
|
|
|
/* quant matrixes */
|
|
put_marker(p, DQT);
|
|
put_bits(p, 16, 2 + 2 * (1 + 64));
|
|
put_bits(p, 4, 0); /* 8 bit precision */
|
|
put_bits(p, 4, 0); /* table 0 */
|
|
for (i = 0; i < 64; i++) {
|
|
j = zigzag_direct[i];
|
|
put_bits(p, 8, s->luma_quant_table[j]);
|
|
}
|
|
|
|
put_bits(p, 4, 0); /* 8 bit precision */
|
|
put_bits(p, 4, 1); /* table 1 */
|
|
for (i = 0; i < 64; i++) {
|
|
j = zigzag_direct[i];
|
|
put_bits(p, 8, s->chroma_quant_table[j]);
|
|
}
|
|
|
|
/* huffman table */
|
|
put_marker(p, DHT);
|
|
flush_put_bits(p);
|
|
ptr = put_bits_ptr(p);
|
|
put_bits(p, 16, 0); /* patched later */
|
|
size = 2;
|
|
|
|
size += put_huffman_table(p, 0, 0, avpriv_mjpeg_bits_dc_luminance,
|
|
avpriv_mjpeg_val_dc);
|
|
size += put_huffman_table(p, 0, 1, avpriv_mjpeg_bits_dc_chrominance,
|
|
avpriv_mjpeg_val_dc);
|
|
|
|
size += put_huffman_table(p, 1, 0, avpriv_mjpeg_bits_ac_luminance,
|
|
avpriv_mjpeg_val_ac_luminance);
|
|
size += put_huffman_table(p, 1, 1, avpriv_mjpeg_bits_ac_chrominance,
|
|
avpriv_mjpeg_val_ac_chrominance);
|
|
|
|
ptr[0] = (size >> 8) & 0xff;
|
|
ptr[1] = (size) & 0xff;
|
|
}
|
|
|
|
static void jpeg_encode_pic_header(struct jpeg_ctx* s)
|
|
{
|
|
put_marker(&s->pb, SOI);
|
|
jpeg_table_header(s, &s->pb);
|
|
|
|
put_marker(&s->pb, SOF0);
|
|
int len = 2 + 1 + 2 + 2 + 1 + 3 * s->comp_num;
|
|
put_bits(&s->pb, 16, len);
|
|
put_bits(&s->pb, 8, 8); /* precision 8 bits/component */
|
|
|
|
if (s->cur_frame->buf.crop_en) {
|
|
put_bits(&s->pb, 16, s->cur_frame->buf.crop.height);
|
|
put_bits(&s->pb, 16, s->cur_frame->buf.crop.width);
|
|
} else {
|
|
put_bits(&s->pb, 16, s->height);
|
|
put_bits(&s->pb, 16, s->width);
|
|
}
|
|
put_bits(&s->pb, 8, s->comp_num); /* 3 or 4 components */
|
|
|
|
/* Y component */
|
|
put_bits(&s->pb, 8, 1); /* component number */
|
|
put_bits(&s->pb, 4, s->h_count[0]); /* H factor */
|
|
put_bits(&s->pb, 4, s->v_count[0]); /* V factor */
|
|
put_bits(&s->pb, 8, 0); /* select quant table */
|
|
|
|
if (s->comp_num > 1) {
|
|
/* Cb */
|
|
put_bits(&s->pb, 8, 2); /* component number */
|
|
put_bits(&s->pb, 4, s->h_count[1]); /* H factor */
|
|
put_bits(&s->pb, 4, s->v_count[1]); /* V factor */
|
|
put_bits(&s->pb, 8, 1); /* select quant table */
|
|
|
|
/* Cr */
|
|
put_bits(&s->pb, 8, 3); /* component number */
|
|
put_bits(&s->pb, 4, s->h_count[2]); /* H factor */
|
|
put_bits(&s->pb, 4, s->v_count[2]); /* V factor */
|
|
put_bits(&s->pb, 8, 1); /* select quant table */
|
|
}
|
|
|
|
logi("offset: %d", put_bits_count(&s->pb) / 8);
|
|
// the start address of VE must be 8 bytes aligned,
|
|
// we cannot pad 0x00 here, because some customer app
|
|
// cannot compatible this case.
|
|
// So we use COM marker.
|
|
int pad = (put_bits_count(&s->pb) / 8) % 8;
|
|
if (pad) {
|
|
pad = 8 - pad;
|
|
if (pad < 4) {
|
|
pad += 8;
|
|
}
|
|
|
|
put_marker(&s->pb, COM);
|
|
put_bits(&s->pb, 16, pad-2); // length
|
|
|
|
pad -= 4;
|
|
while (pad--) {
|
|
put_bits(&s->pb, 8, 0);
|
|
}
|
|
}
|
|
flush_put_bits(&s->pb);
|
|
|
|
s->header_offset = put_bits_count(&s->pb) / 8;
|
|
}
|
|
|
|
static void jpeg_init_hvcount(struct jpeg_ctx* s)
|
|
{
|
|
s->h_count[1] = s->h_count[2] = 1;
|
|
s->v_count[1] = s->v_count[2] = 1;
|
|
s->comp_num = 3;
|
|
s->uv_interleave = 0;
|
|
if (s->cur_frame->buf.format == MPP_FMT_YUV400) {
|
|
s->h_count[0] = s->v_count[0] = 1;
|
|
s->comp_num = 1;
|
|
} else if (s->cur_frame->buf.format == MPP_FMT_YUV444P) {
|
|
s->h_count[0] = s->v_count[0] = 1;
|
|
} else if (s->cur_frame->buf.format == MPP_FMT_YUV420P ||
|
|
s->cur_frame->buf.format == MPP_FMT_NV12) {
|
|
s->h_count[0] = s->v_count[0] = 2;
|
|
s->uv_interleave = s->cur_frame->buf.format != MPP_FMT_YUV420P;
|
|
} else if (s->cur_frame->buf.format == MPP_FMT_YUV422P) {
|
|
s->h_count[0] = 2;
|
|
s->v_count[0] = 1;
|
|
} else {
|
|
loge("not supprt this format: %d", s->cur_frame->buf.format);
|
|
}
|
|
}
|
|
|
|
static void set_quality(struct jpeg_ctx* s)
|
|
{
|
|
int i;
|
|
if (s->quality <= 0)
|
|
s->quality = 1;
|
|
if (s->quality > 100)
|
|
s->quality = 100;
|
|
|
|
// 1. quality = 1, produce "worst" quality, 5000*std_quant_table
|
|
// 2. quality = 50, produce "good" quality, std_quant_table
|
|
// 3. quality =100, produce "best" quality, the value of table are all 1
|
|
if (s->quality < 50) {
|
|
s->quality = 5000 / s->quality;
|
|
} else {
|
|
s->quality = 200 - s->quality * 2;
|
|
}
|
|
|
|
for (i=0; i<64; i++) {
|
|
s->luma_quant_table[i] = (std_luminance_quant_tbl[i] * s->quality + 50) / 100;
|
|
if (s->luma_quant_table[i] <= 0)
|
|
s->luma_quant_table[i] = 1;
|
|
if (s->luma_quant_table[i] > 255)
|
|
s->luma_quant_table[i] = 255;
|
|
|
|
s->chroma_quant_table[i] = (std_chrominance_quant_tbl[i] * s->quality + 50) / 100;
|
|
if (s->chroma_quant_table[i] <= 0)
|
|
s->chroma_quant_table[i] = 1;
|
|
if (s->chroma_quant_table[i] > 255)
|
|
s->chroma_quant_table[i] = 255;
|
|
}
|
|
}
|
|
|
|
int __mpp_encode_jpeg(struct mpp_encoder *ctx, struct mpp_frame* frame, struct mpp_packet* packet)
|
|
{
|
|
if (!ctx)
|
|
return -1;
|
|
|
|
struct jpeg_ctx *s = (struct jpeg_ctx *)ctx;
|
|
|
|
int ret = 0;
|
|
int i;
|
|
int comp = 3;
|
|
|
|
if (frame->buf.format == MPP_FMT_NV12) {
|
|
comp = 2;
|
|
} else if (frame->buf.format == MPP_FMT_YUV420P
|
|
|| frame->buf.format == MPP_FMT_YUV422P) {
|
|
comp = 3;
|
|
} else if (frame->buf.format == MPP_FMT_YUV400) {
|
|
comp = 1;
|
|
} else {
|
|
loge("unsupport format, %d", frame->buf.format);
|
|
return -1;
|
|
}
|
|
|
|
s->width = frame->buf.size.width;
|
|
s->height = frame->buf.size.height;
|
|
s->quality = s->config.quality;
|
|
s->y_stride = frame->buf.stride[0];
|
|
|
|
set_quality(s);
|
|
|
|
s->stream_num = packet->size / 256;
|
|
s->bitstream_vir_addr = (unsigned char *)(unsigned long)packet->phy_addr;
|
|
s->bitstream_phy_addr = packet->phy_addr;
|
|
|
|
s->cur_frame = frame;
|
|
|
|
init_put_bits(&s->pb, s->bitstream_vir_addr, packet->size);
|
|
for (i=0; i<comp; i++) {
|
|
s->phy_addr[i] = frame->buf.phy_addr[i];
|
|
}
|
|
|
|
jpeg_init_hvcount(s);
|
|
jpeg_encode_pic_header(s);
|
|
// we should flush (s->header_offset + 64) bytes, because
|
|
// flush_put_bits will write some data of SOS
|
|
ve_buffer_sync_range(NULL, s->bitstream_vir_addr, s->header_offset + 64, CACHE_CLEAN);
|
|
|
|
if (jpeg_hw_encode(s) < 0) {
|
|
loge("encode failed");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (s->encode_data_len > packet->size) {
|
|
loge("buf len(%d) is too small, we need (%d) bytes",
|
|
packet->size, s->encode_data_len + s->header_offset);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
packet->len = s->encode_data_len;
|
|
ve_buffer_sync_range(NULL, s->bitstream_vir_addr, s->encode_data_len, CACHE_INVALID);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int __mjpeg_encode_control(struct mpp_encoder *ctx, int cmd, void *param)
|
|
{
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
int __mjpeg_encode_reset(struct mpp_encoder *ctx)
|
|
{
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
struct enc_ops mjpeg_encoder = {
|
|
.name = "mjpeg",
|
|
.init = __mjpeg_encode_init,
|
|
.destory = __mjpeg_encode_destroy,
|
|
.encode = __mpp_encode_jpeg,
|
|
.control = __mjpeg_encode_control,
|
|
.reset = __mjpeg_encode_reset,
|
|
};
|
|
|
|
struct mpp_encoder* create_jpeg_encoder()
|
|
{
|
|
struct jpeg_ctx *s = (struct jpeg_ctx*)mpp_alloc(sizeof(struct jpeg_ctx));
|
|
if(s == NULL) {
|
|
loge("malloc jpeg_ctx failed");
|
|
return NULL;
|
|
}
|
|
memset(s, 0, sizeof(struct jpeg_ctx));
|
|
|
|
s->encoder.ops = &mjpeg_encoder;
|
|
|
|
return &s->encoder;
|
|
}
|