/* * Copyright 2015 Rockchip Electronics Co. LTD * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define MODULE_TAG "jpege_api_v2" #include #include "mpp_err.h" #include "mpp_env.h" #include "mpp_mem.h" #include "mpp_common.h" #include "mpp_2str.h" #include "jpege_debug.h" #include "jpege_api_v2.h" #include "jpege_syntax.h" #include "mpp_enc_cfg_impl.h" #include "mpp_bitwrite.h" typedef struct { MppEncCfgSet *cfg; JpegeSyntax syntax; } JpegeCtx; #define QUANTIZE_TABLE_SIZE 64 /* * from RFC435 spec. */ static const RK_U8 jpege_luma_quantizer[QUANTIZE_TABLE_SIZE] = { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 }; static const RK_U8 jpege_chroma_quantizer[QUANTIZE_TABLE_SIZE] = { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }; RK_U32 jpege_debug = 0; static MPP_RET jpege_init_v2(void *ctx, EncImplCfg *cfg) { JpegeCtx *p = (JpegeCtx *)ctx; mpp_env_get_u32("jpege_debug", &jpege_debug, 0); jpege_dbg_func("enter ctx %p\n", ctx); p->cfg = cfg->cfg; mpp_assert(cfg->coding = MPP_VIDEO_CodingMJPEG); { /* init default rc config */ MppEncRcCfg *rc = &p->cfg->rc; MppEncJpegCfg *jpeg_cfg = &p->cfg->codec.jpeg; rc->fps_in_flex = 0; rc->fps_in_num = 30; rc->fps_in_denorm = 1; rc->fps_out_flex = 0; rc->fps_out_num = 30; rc->fps_out_denorm = 1; rc->rc_mode = MPP_ENC_RC_MODE_VBR; /* init default quant */ jpeg_cfg->quant = 10; } jpege_dbg_func("leave ctx %p\n", ctx); return MPP_OK; } static MPP_RET jpege_deinit_v2(void *ctx) { JpegeCtx *p = (JpegeCtx *)ctx; MppEncJpegCfg *jpeg_cfg = &p->cfg->codec.jpeg; jpege_dbg_func("enter ctx %p\n", ctx); MPP_FREE(jpeg_cfg->qtable_y); MPP_FREE(jpeg_cfg->qtable_u); MPP_FREE(jpeg_cfg->qtable_v); jpege_dbg_func("leave ctx %p\n", ctx); return MPP_OK; } static MPP_RET jpege_proc_prep_cfg(MppEncPrepCfg *dst, MppEncPrepCfg *src) { MPP_RET ret = MPP_OK; RK_U32 change = src->change; MppFrameFormat fmt = dst->format & MPP_FRAME_FMT_MASK; mpp_assert(change); if (change) { MppEncPrepCfg bak = *dst; if (change & MPP_ENC_PREP_CFG_CHANGE_FORMAT) dst->format = src->format; if (change & MPP_ENC_PREP_CFG_CHANGE_ROTATION) dst->rotation = src->rotation; /* jpeg encoder do not have mirring / denoise feature */ if ((change & MPP_ENC_PREP_CFG_CHANGE_INPUT) || (change & MPP_ENC_PREP_CFG_CHANGE_ROTATION)) { if (dst->rotation == MPP_ENC_ROT_90 || dst->rotation == MPP_ENC_ROT_270) { dst->width = src->height; dst->height = src->width; } else { dst->width = src->width; dst->height = src->height; } dst->hor_stride = src->hor_stride; dst->ver_stride = src->ver_stride; } if (dst->width < 16 && dst->width > 8192) { mpp_err_f("invalid width %d is not in range [16..8192]\n", dst->width); ret = MPP_NOK; } if (dst->height < 16 && dst->height > 8192) { mpp_err_f("invalid height %d is not in range [16..8192]\n", dst->height); ret = MPP_NOK; } if ((fmt != MPP_FMT_YUV420SP && fmt != MPP_FMT_YUV420P && fmt != MPP_FMT_YUV422SP_VU && fmt != MPP_FMT_YUV422_YUYV && fmt != MPP_FMT_YUV422_UYVY && fmt < MPP_FRAME_FMT_RGB) || fmt == MPP_FMT_RGB888 || fmt == MPP_FMT_BGR888) { mpp_err_f("invalid format %d is not supportted\n", dst->format); ret = MPP_NOK; } dst->change |= change; // parameter checking if (dst->rotation == MPP_ENC_ROT_90 || dst->rotation == MPP_ENC_ROT_270) { if (dst->height > dst->hor_stride || dst->width > dst->ver_stride) { mpp_err("invalid size w:h [%d:%d] stride [%d:%d]\n", dst->width, dst->height, dst->hor_stride, dst->ver_stride); ret = MPP_ERR_VALUE; } } else { if (dst->width > dst->hor_stride || dst->height > dst->ver_stride) { mpp_err("invalid size w:h [%d:%d] stride [%d:%d]\n", dst->width, dst->height, dst->hor_stride, dst->ver_stride); ret = MPP_ERR_VALUE; } } if (ret) { mpp_err_f("failed to accept new prep config\n"); *dst = bak; } else { jpege_dbg_ctrl("MPP_ENC_SET_PREP_CFG w:h [%d:%d] stride [%d:%d]\n", dst->width, dst->height, dst->hor_stride, dst->ver_stride); } } return ret; } /* gen quantizer table by q_factor according to RFC2435 spec. */ static MPP_RET jpege_gen_qt_by_qfactor(MppEncJpegCfg *cfg, RK_S32 *factor) { MPP_RET ret = MPP_OK; RK_U32 q, qfactor = *factor; RK_U32 i; RK_U8 *qtable_y = NULL; RK_U8 *qtable_c = NULL; if (!cfg->qtable_y) cfg->qtable_y = mpp_malloc(RK_U8, QUANTIZE_TABLE_SIZE); if (!cfg->qtable_u) cfg->qtable_u = mpp_malloc(RK_U8, QUANTIZE_TABLE_SIZE); if (NULL == cfg->qtable_y || NULL == cfg->qtable_u) { mpp_err_f("qtable is null, malloc err"); return MPP_ERR_MALLOC; } qtable_y = cfg->qtable_y; qtable_c = cfg->qtable_u; if (qfactor < 50) q = 5000 / qfactor; else q = 200 - (qfactor << 1); for (i = 0; i < QUANTIZE_TABLE_SIZE; i++) { RK_S16 lq = (jpege_luma_quantizer[i] * q + 50) / 100; RK_S16 cq = (jpege_chroma_quantizer[i] * q + 50) / 100; /* Limit the quantizers to 1 <= q <= 255 */ qtable_y[i] = MPP_CLIP3(1, 255, lq); qtable_c[i] = MPP_CLIP3(1, 255, cq); } return ret; } static MPP_RET jpege_proc_jpeg_cfg(MppEncJpegCfg *dst, MppEncJpegCfg *src, MppEncRcCfg *rc) { MPP_RET ret = MPP_OK; RK_U32 change = src->change; if (change) { MppEncJpegCfg bak = *dst; if (change & MPP_ENC_JPEG_CFG_CHANGE_QP) { dst->quant = src->quant; if (dst->quant < 0 || dst->quant > 10) { mpp_err_f("invalid quality level %d is not in range [0..10] set to default 8\n"); dst->quant = 8; } if (rc->rc_mode != MPP_ENC_RC_MODE_FIXQP) { mpp_log("setup quant %d change mode %s fixqp", dst->quant, strof_rc_mode(rc->rc_mode)); rc->rc_mode = MPP_ENC_RC_MODE_FIXQP; } } else if (change & MPP_ENC_JPEG_CFG_CHANGE_QFACTOR) { if (src->q_factor < 1 || src->q_factor > 99) { mpp_err_f("q_factor out of range, default set 80\n"); src->q_factor = 80; } if (dst->q_factor != src->q_factor) ret = jpege_gen_qt_by_qfactor(dst, &src->q_factor); dst->q_factor = src->q_factor; if (src->qf_min < 1 || src->qf_min > 99) { mpp_err_f("qf_min out of range, default set 1\n"); src->qf_min = 1; } dst->qf_min = src->qf_min; if (src->qf_max < 1 || src->qf_max > 99) { mpp_err_f("qf_max out of range, default set 99\n"); src->qf_max = 99; } dst->qf_max = src->qf_max; jpege_dbg_input("q_factor %d, qf_min %d, qf_max %d\n", dst->q_factor, dst->qf_min, dst->qf_max); } else if (change & MPP_ENC_JPEG_CFG_CHANGE_QTABLE) { if (src->qtable_y && src->qtable_u && src->qtable_v) { if (NULL == dst->qtable_y) dst->qtable_y = mpp_malloc(RK_U8, QUANTIZE_TABLE_SIZE); if (NULL == dst->qtable_u) dst->qtable_u = mpp_malloc(RK_U8, QUANTIZE_TABLE_SIZE); if (NULL == dst->qtable_y || NULL == dst->qtable_u) { mpp_err_f("qtable is null, malloc err\n"); return MPP_ERR_MALLOC; } /* check table value */ if (src->qtable_u != src->qtable_v) { RK_U32 i; for (i = 0; i < QUANTIZE_TABLE_SIZE; i++) { if (src->qtable_u[i] != src->qtable_v[i]) { RK_U32 j; jpege_dbg_input("qtable_u and qtable_v are different, use qtable_u\n"); for (j = 0; j < QUANTIZE_TABLE_SIZE; j++) jpege_dbg_input("qtable_u[%d] %d qtable_v[%d] %d\n", j, src->qtable_u[j], j, src->qtable_v[j]); break; } } } /* default use one chroma qtable, select qtable_u */ memcpy(dst->qtable_y, src->qtable_y, QUANTIZE_TABLE_SIZE); memcpy(dst->qtable_u, src->qtable_u, QUANTIZE_TABLE_SIZE); if (rc->rc_mode != MPP_ENC_RC_MODE_FIXQP) { mpp_log("setup qtable will change mode %s fixqp", strof_rc_mode(rc->rc_mode)); rc->rc_mode = MPP_ENC_RC_MODE_FIXQP; } } else { mpp_err_f("invalid qtable y %p u %p v %p\n", src->qtable_y, src->qtable_u, src->qtable_v); ret = MPP_ERR_NULL_PTR; } } if (ret) { mpp_err_f("failed to accept new rc config\n"); *dst = bak; } else { jpege_dbg_ctrl("MPP_ENC_SET_CODEC_CFG change 0x%x jpeg quant %d q_factor %d\n", change, dst->quant, dst->q_factor); dst->change = src->change; } dst->change = src->change; } return ret; } static MPP_RET jpege_proc_split_cfg(MppEncSliceSplit *dst, MppEncSliceSplit *src) { MPP_RET ret = MPP_OK; RK_U32 change = src->change; if (change & MPP_ENC_SPLIT_CFG_CHANGE_MODE) { dst->split_mode = src->split_mode; dst->split_arg = src->split_arg; } if (change & MPP_ENC_SPLIT_CFG_CHANGE_ARG) dst->split_arg = src->split_arg; dst->change |= change; return ret; } static MPP_RET jpege_proc_cfg(void *ctx, MpiCmd cmd, void *param) { JpegeCtx *p = (JpegeCtx *)ctx; MppEncCfgSet *cfg = p->cfg; MPP_RET ret = MPP_OK; jpege_dbg_func("enter ctx %p cmd %x param %p\n", ctx, cmd, param); switch (cmd) { case MPP_ENC_SET_CFG : { MppEncCfgImpl *impl = (MppEncCfgImpl *)param; MppEncCfgSet *src = &impl->cfg; if (src->prep.change) { ret |= jpege_proc_prep_cfg(&cfg->prep, &src->prep); src->prep.change = 0; } if (src->codec.jpeg.change) { ret |= jpege_proc_jpeg_cfg(&cfg->codec.jpeg, &src->codec.jpeg, &cfg->rc); src->codec.jpeg.change = 0; } if (src->split.change) { ret |= jpege_proc_split_cfg(&cfg->split, &src->split); src->split.change = 0; } } break; case MPP_ENC_SET_PREP_CFG : { ret = jpege_proc_prep_cfg(&cfg->prep, param); } break; case MPP_ENC_SET_CODEC_CFG : { MppEncCodecCfg *codec = (MppEncCodecCfg *)param; ret = jpege_proc_jpeg_cfg(&cfg->codec.jpeg, &codec->jpeg, &cfg->rc); } break; case MPP_ENC_SET_IDR_FRAME : case MPP_ENC_SET_OSD_PLT_CFG : case MPP_ENC_SET_OSD_DATA_CFG : case MPP_ENC_GET_SEI_DATA : case MPP_ENC_SET_SEI_CFG : { } break; default: mpp_err_f("No correspond cmd(%08x) found, and can not config!", cmd); ret = MPP_NOK; break; } jpege_dbg_func("leave ret %d\n", ret); return ret; } static MPP_RET jpege_start(void *ctx, HalEncTask *task) { JpegeCtx *p = (JpegeCtx *)ctx; JpegeSyntax syntax = p->syntax; MppPacket pkt = task->packet; RK_U8 *ptr = mpp_packet_get_pos(pkt); size_t buf_size = mpp_packet_get_size(pkt); RK_S32 size = 0; MppWriteCtx bit_ctx; MppWriteCtx *bits = &bit_ctx; mpp_writer_init(bits, ptr, buf_size); /* add SOI and APP0 data */ /* SOI */ mpp_writer_put_raw_bits(bits, 0xFFD8, 16); /* APP0 */ mpp_writer_put_raw_bits(bits, 0xFFE0, 16); /* length */ mpp_writer_put_raw_bits(bits, 0x0010, 16); /* "JFIF" ID */ /* Ident1 */ mpp_writer_put_raw_bits(bits, 0x4A46, 16); /* Ident2 */ mpp_writer_put_raw_bits(bits, 0x4946, 16); /* Ident3 */ mpp_writer_put_raw_bits(bits, 0x00, 8); /* Version */ mpp_writer_put_raw_bits(bits, 0x0102, 16); if (syntax.density_x && syntax.density_y) { /* Units */ mpp_writer_put_raw_bits(bits, syntax.units_type, 8); /* Xdensity */ mpp_writer_put_raw_bits(bits, syntax.density_x, 16); /* Ydensity */ mpp_writer_put_raw_bits(bits, syntax.density_y, 16); } else { /* Units */ mpp_writer_put_raw_bits(bits, 0, 8); /* Xdensity */ mpp_writer_put_raw_bits(bits, 0, 8); mpp_writer_put_raw_bits(bits, 1, 8); /* Ydensity */ mpp_writer_put_raw_bits(bits, 1, 16); } /* XThumbnail */ mpp_writer_put_raw_bits(bits, 0x00, 8); /* YThumbnail */ mpp_writer_put_raw_bits(bits, 0x00, 8); /* Do NOT write thumbnail */ size = mpp_writer_bytes(bits); mpp_packet_set_length(pkt, size); task->length += size; return MPP_OK; } static MPP_RET jpege_proc_hal(void *ctx, HalEncTask *task) { JpegeCtx *p = (JpegeCtx *)ctx; MppFrame frame = task->frame; JpegeSyntax *syntax = &p->syntax; MppEncCfgSet *cfg = p->cfg; MppEncPrepCfg *prep = &cfg->prep; MppEncCodecCfg *codec = &cfg->codec; MppEncSliceSplit *split = &cfg->split; jpege_dbg_func("enter ctx %p\n", ctx); syntax->width = prep->width; syntax->height = prep->height; syntax->hor_stride = prep->hor_stride; syntax->ver_stride = prep->ver_stride; syntax->mcu_w = MPP_ALIGN(prep->width, 16) / 16; syntax->mcu_h = MPP_ALIGN(prep->height, 16) / 16; syntax->format = prep->format; syntax->color = prep->color; syntax->rotation = prep->rotation; syntax->offset_x = mpp_frame_get_offset_x(frame); syntax->offset_y = mpp_frame_get_offset_y(frame); syntax->quality = codec->jpeg.quant; syntax->q_factor = codec->jpeg.q_factor; syntax->qf_min = codec->jpeg.qf_min; syntax->qf_max = codec->jpeg.qf_max; syntax->qtable_y = codec->jpeg.qtable_y; syntax->qtable_c = codec->jpeg.qtable_u; syntax->part_rows = 0; syntax->restart_ri = 0; syntax->low_delay = 0; if (split->split_mode) { RK_U32 mb_h = MPP_ALIGN(prep->height, 16) / 16; RK_U32 part_rows = 0; if (split->split_mode == MPP_ENC_SPLIT_BY_CTU) { RK_U32 part_mbs = split->split_arg; RK_U32 mb_w = MPP_ALIGN(prep->width, 16) / 16; RK_U32 mb_all = mb_w * mb_h; if (part_mbs > 0 && part_mbs <= mb_all) { part_rows = (part_mbs + mb_w - 1) / mb_w; if (part_rows >= mb_h) part_rows = 0; } else { mpp_err_f("warning: invalid split arg %d > max %d\n", part_mbs, mb_all); } } else { mpp_err_f("warning: only mcu split is supported\n"); } if (part_rows) { syntax->part_rows = part_rows; syntax->restart_ri = syntax->mcu_w * part_rows; syntax->low_delay = cfg->base.low_delay && part_rows; } } task->valid = 1; task->syntax.data = syntax; task->syntax.number = 1; jpege_dbg_func("leave ctx %p\n", ctx); return MPP_OK; } static MPP_RET jpege_add_Prefix(MppPacket pkt, RK_S32 *len, RK_U8 uuid[16], const void *data, RK_S32 size) { RK_U8 *ptr = mpp_packet_get_pos(pkt); size_t length = mpp_packet_get_length(pkt); size_t buf_size = mpp_packet_get_size(pkt); MppWriteCtx bit_ctx; MppWriteCtx *bits = &bit_ctx; const RK_U8 *user_data = data; RK_S32 i = 0, app_size = 0; mpp_writer_init(bits, ptr + length, buf_size - length); if ((size > 8) && user_data[0] == 0xFF && user_data[1] == 0xE1 && user_data[4] == 0x45 && user_data[5] == 0x78 && user_data[6] == 0x69 && user_data[7] == 0x66) { jpege_dbg_func("write EXIF data, total length %d\n", size); } else if ((size > 8) && user_data[0] == 0xFF && user_data[1] == 0xE2 && user_data[4] == 0x4D && user_data[5] == 0x50 && user_data[6] == 0x46 && user_data[7] == 0x00) { jpege_dbg_func("write MPF data, total length %d\n", size); } else { /* add user data to APP7 */ mpp_writer_put_raw_bits(bits, 0xFFE7, 16); /* length */ mpp_writer_put_raw_bits(bits, size + 2, 16); } /* data */ for (i = 0; i < size; i++) mpp_writer_put_raw_bits(bits, user_data[i], 8); app_size = mpp_writer_bytes(bits); *len = app_size; length += app_size; mpp_packet_set_length(pkt, length); (void)uuid; return MPP_OK; } const EncImplApi api_jpege = { .name = "jpege_control", .coding = MPP_VIDEO_CodingMJPEG, .ctx_size = sizeof(JpegeCtx), .flag = 0, .init = jpege_init_v2, .deinit = jpege_deinit_v2, .proc_cfg = jpege_proc_cfg, .gen_hdr = NULL, .start = jpege_start, .proc_dpb = NULL, .proc_hal = jpege_proc_hal, .add_prefix = jpege_add_Prefix, .sw_enc = NULL, };