diff --git a/mpp/base/CMakeLists.txt b/mpp/base/CMakeLists.txt index 490011aa..84e27d8b 100644 --- a/mpp/base/CMakeLists.txt +++ b/mpp/base/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(mpp_base STATIC mpp_task_impl.cpp mpp_task.cpp mpp_meta.cpp + mpp_bitwrite.c mpp_bitread.c mpp_bitput.c ) diff --git a/mpp/base/inc/mpp_bitwrite.h b/mpp/base/inc/mpp_bitwrite.h new file mode 100644 index 00000000..26e43982 --- /dev/null +++ b/mpp/base/inc/mpp_bitwrite.h @@ -0,0 +1,67 @@ +/* + * Copyright 2015 - 2017 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. + */ + +#ifndef __MPP_BITWRITER_H__ +#define __MPP_BITWRITER_H__ + +#include "rk_type.h" +#include "mpp_err.h" + +/* + * Mpp bitstream writer for H.264/H.265 + */ +typedef struct MppWriteCtx_t { + RK_U8 *buffer; /* point to first byte of stream */ + RK_U8 *stream; /* Pointer to next byte of stream */ + RK_U32 size; /* Byte size of stream buffer */ + RK_U32 byte_cnt; /* Byte counter */ + RK_U32 byte_buffer; /* Byte buffer */ + RK_U32 buffered_bits; /* Amount of bits in byte buffer, [0-7] */ + RK_U32 zero_bytes; /* Amount of consecutive zero bytes */ + RK_S32 overflow; /* This will signal a buffer overflow */ + RK_U32 emul_cnt; /* Counter for emulation_3_byte, needed in SEI */ +} MppWriteCtx; + +MPP_RET mpp_writer_init(MppWriteCtx *ctx, void *p, RK_S32 size); +MPP_RET mpp_writer_reset(MppWriteCtx *ctx); + +/* check overflow status */ +MPP_RET mpp_writer_status(MppWriteCtx *ctx); + +/* write raw bit without emulation prevention 0x03 byte */ +void mpp_writer_put_raw_bits(MppWriteCtx *ctx, RK_S32 val, RK_S32 len); + +/* write bit with emulation prevention 0x03 byte */ +void mpp_writer_put_bits(MppWriteCtx *ctx, RK_S32 val, RK_S32 len); + +/* insert zero bits until byte-aligned */ +void mpp_writer_align_zero(MppWriteCtx *ctx); + +/* insert one bits until byte-aligned */ +void mpp_writer_align_one(MppWriteCtx *ctx); + +/* insert one bit then pad to byte-align with zero */ +void mpp_writer_trailing(MppWriteCtx * ctx); + +void mpp_writer_put_ue(MppWriteCtx *ctx, RK_U32 val); +void mpp_writer_put_se(MppWriteCtx *ctx, RK_S32 val); + +RK_S32 mpp_writer_bytes(MppWriteCtx *ctx); +RK_S32 mpp_writer_bits(MppWriteCtx *ctx); + +RK_S32 mpp_exp_golomb_signed(RK_S32 val); + +#endif /* __MPP_BITWRITER_H__ */ diff --git a/mpp/base/mpp_bitwrite.c b/mpp/base/mpp_bitwrite.c new file mode 100644 index 00000000..b65fcd82 --- /dev/null +++ b/mpp/base/mpp_bitwrite.c @@ -0,0 +1,235 @@ +/* + * Copyright 2015 - 2017 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. + */ + +#include "mpp_log.h" +#include "mpp_mem.h" + +#include "mpp_bitwrite.h" + +MPP_RET mpp_writer_status(MppWriteCtx *ctx) +{ + if (ctx->byte_cnt > ctx->size) { + ctx->overflow = 1; + return MPP_NOK; + } + + return MPP_OK; +} + +MPP_RET mpp_writer_reset(MppWriteCtx *ctx) +{ + ctx->stream = ctx->buffer; + ctx->byte_cnt = 0; + ctx->byte_buffer = 0; + ctx->buffered_bits = 0; + ctx->zero_bytes = 0; + ctx->overflow = 0; + ctx->emul_cnt = 0; + + return MPP_OK; +} + +MPP_RET mpp_writer_init(MppWriteCtx *ctx, void *p, RK_S32 size) +{ + MPP_RET ret; + + ctx->buffer = p; + ctx->stream = p; + ctx->size = size; + ctx->byte_cnt = 0; + ctx->overflow = 0; + ctx->byte_buffer = 0; + ctx->buffered_bits = 0; + ctx->zero_bytes = 0; + ctx->emul_cnt = 0; + + ret = mpp_writer_status(ctx); + if (ret) + mpp_err_f("failed to init with overflow config\n"); + + return ret; +} + +void mpp_writer_put_raw_bits(MppWriteCtx *ctx, RK_S32 val, RK_S32 len) +{ + RK_S32 bits; + RK_U32 byte_buffer = ctx->byte_buffer; + RK_U8*stream = ctx->stream; + + if (mpp_writer_status(ctx)) + return; + + mpp_assert(val < (1 << len)); + mpp_assert(len < 25); + + bits = len + ctx->buffered_bits; + val <<= (32 - bits); + byte_buffer = byte_buffer | val; + + while (bits > 7) { + *stream = (RK_U8)(byte_buffer >> 24); + + bits -= 8; + byte_buffer <<= 8; + stream++; + ctx->byte_cnt++; + } + + ctx->byte_buffer = byte_buffer; + ctx->buffered_bits = (RK_U8)bits; + ctx->stream = stream; + + return; +} + +void mpp_writer_put_bits(MppWriteCtx * ctx, RK_S32 val, RK_S32 len) +{ + RK_S32 bits; + RK_U8 *stream = ctx->stream; + RK_U32 byte_buffer = ctx->byte_buffer; + + if (val) { + mpp_assert(val < (1 << len)); + mpp_assert(len < 25); + } + bits = len + ctx->buffered_bits; + + byte_buffer = byte_buffer | ((RK_U32) val << (32 - bits)); + + while (bits > 7) { + RK_S32 zeroBytes = ctx->zero_bytes; + RK_S32 byteCnt = ctx->byte_cnt; + + if (mpp_writer_status(ctx)) + return; + + *stream = (RK_U8) (byte_buffer >> 24); + + byteCnt++; + if ((zeroBytes == 2) && (*stream < 4)) { + *stream++ = 3; + *stream = (RK_U8) (byte_buffer >> 24); + byteCnt++; + zeroBytes = 0; + ctx->emul_cnt++; + } + + if (*stream == 0) + zeroBytes++; + else + zeroBytes = 0; + + bits -= 8; + byte_buffer <<= 8; + stream++; + ctx->zero_bytes = zeroBytes; + ctx->byte_cnt = byteCnt; + ctx->stream = stream; + } + + ctx->buffered_bits = (RK_U8) bits; + ctx->byte_buffer = byte_buffer; +} + +void mpp_writer_align_zero(MppWriteCtx *ctx) +{ + if (ctx->buffered_bits) + mpp_writer_put_raw_bits(ctx, 0, 8 - ctx->buffered_bits); +} + +void mpp_writer_align_one(MppWriteCtx *ctx) +{ + if (ctx->buffered_bits) { + RK_S32 len = 8 - ctx->buffered_bits; + + mpp_writer_put_raw_bits(ctx, (1 << len) - 1, len); + } +} + +void mpp_writer_trailing(MppWriteCtx *ctx) +{ + mpp_writer_put_bits(ctx, 1, 1); + if (ctx->buffered_bits) + mpp_writer_put_bits(ctx, 0, 8 - ctx->buffered_bits); +} + +void mpp_writer_put_ue(MppWriteCtx *ctx, RK_U32 val) +{ + RK_U32 num_bits = 0; + + val++; + while (val >> ++num_bits); + + if (num_bits > 12) { + RK_U32 tmp; + + tmp = num_bits - 1; + + if (tmp > 24) { + tmp -= 24; + mpp_writer_put_bits(ctx, 0, 24); + } + + mpp_writer_put_bits(ctx, 0, tmp); + + if (num_bits > 24) { + num_bits -= 24; + mpp_writer_put_bits(ctx, val >> num_bits, 24); + val = val >> num_bits; + } + + mpp_writer_put_bits(ctx, val, num_bits); + } else { + mpp_writer_put_bits(ctx, val, 2 * num_bits - 1); + } +} + +void mpp_writer_put_se(MppWriteCtx *ctx, RK_S32 val) +{ + RK_U32 tmp; + + if (val > 0) + tmp = (RK_U32)(2 * val - 1); + else + tmp = (RK_U32)(-2 * val); + + mpp_writer_put_ue(ctx, tmp); +} + +RK_S32 mpp_writer_bytes(MppWriteCtx *ctx) +{ + return ctx->byte_cnt + (ctx->buffered_bits > 0); +} + +RK_S32 mpp_writer_bits(MppWriteCtx *ctx) +{ + return ctx->byte_cnt * 8 + ctx->buffered_bits; +} + +RK_S32 mpp_exp_golomb_signed(RK_S32 val) +{ + RK_S32 tmp = 0; + + if (val > 0) + val = 2 * val; + else + val = -2 * val + 1; + + while (val >> ++tmp) + ; + + return tmp * 2 - 1; +} diff --git a/mpp/base/test/CMakeLists.txt b/mpp/base/test/CMakeLists.txt index 9fa5ea1e..3fe77d9a 100644 --- a/mpp/base/test/CMakeLists.txt +++ b/mpp/base/test/CMakeLists.txt @@ -28,3 +28,5 @@ add_mpp_base_test(mpp_buffer) # mpp_packet unit test add_mpp_base_test(mpp_packet) +# mpp_bitwriter unit test +add_mpp_base_test(mpp_bit) diff --git a/mpp/base/test/mpp_bit_test.c b/mpp/base/test/mpp_bit_test.c new file mode 100644 index 00000000..433695ac --- /dev/null +++ b/mpp/base/test/mpp_bit_test.c @@ -0,0 +1,128 @@ +/* + * 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 "mpp_bit_test" + +#include + +#include "mpp_log.h" +#include "mpp_common.h" +#include "mpp_bitwrite.h" + +#define BIT_WRITER_BUFFER_SIZE 1024 + +/* + * type is for operation type + * 0 - plain bit put + * 1 - bit put with detection + * 2 - write ue + * 3 - write se + * 4 - align byte + */ +typedef enum BitOpsType_e { + BIT_PUT_NO03, + BIT_PUT, + BIT_PUT_UE, + BIT_PUT_SE, + BIT_ALIGN_BYTE, +} BitOpsType; + +typedef struct BitOps_t { + BitOpsType type; + RK_S32 val; + RK_S32 len; +} BitOps; + +static BitOps bit_ops[] = { + { BIT_PUT_NO03, 0, 8, }, + { BIT_PUT, 0, 3, }, + { BIT_PUT, 0, 15, }, + { BIT_PUT, 0, 23, }, + { BIT_PUT_UE, 17, 0, }, + { BIT_PUT_SE, 9, 0, }, + { BIT_ALIGN_BYTE, 0, 0, }, +}; + +void proc_bit_ops(MppWriteCtx *writer, BitOps *ops) +{ + switch (ops->type) { + case BIT_PUT_NO03 : { + mpp_writer_put_raw_bits(writer, ops->val, ops->len); + } break; + case BIT_PUT : { + mpp_writer_put_bits(writer, ops->val, ops->len); + } break; + case BIT_PUT_UE : { + mpp_writer_put_ue(writer, ops->val); + } break; + case BIT_PUT_SE : { + mpp_writer_put_ue(writer, ops->val); + } break; + case BIT_ALIGN_BYTE : { + mpp_writer_trailing(writer); + } break; + default : { + mpp_err("invalid ops type %d\n", ops->type); + } break; + } +} + +int main() +{ + MPP_RET ret = MPP_ERR_UNKNOW; + void *data = NULL; + size_t size = BIT_WRITER_BUFFER_SIZE; + MppWriteCtx writer; + RK_U32 len_byte; + RK_U32 i; + RK_U32 buf_len = 0; + char buf[BIT_WRITER_BUFFER_SIZE]; + + mpp_log("mpp_bit_test start\n"); + + data = malloc(size); + if (NULL == data) { + mpp_err("mpp_bit_test malloc failed\n"); + goto TEST_FAILED; + } + + mpp_writer_init(&writer, data, size); + + for (i = 0; i < MPP_ARRAY_ELEMS(bit_ops); i++) + proc_bit_ops(&writer, &bit_ops[i]); + + len_byte = writer.byte_cnt; + + for (i = 0; i < len_byte; i++) { + buf_len += snprintf(buf + buf_len, sizeof(buf) - buf_len, + "%02x ", writer.buffer[i]); + } + + mpp_log("stream %s\n", buf); + + ret = MPP_OK; +TEST_FAILED: + if (data) + free(data); + + if (ret) + mpp_log("mpp_bit_test failed\n"); + else + mpp_log("mpp_bit_test success\n"); + + return ret; +} +