/* * Copyright (C) 2020-2024 ArtInChip Technology Co. Ltd * * SPDX-License-Identifier: Apache-2.0 * * Author: * Desc: jpeg encoder */ #include #include #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; iphy_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; }