mirror of
https://github.com/nyanmisaka/mpp.git
synced 2025-10-09 19:10:15 +08:00

git-svn-id: https://10.10.10.66:8443/svn/MediaProcessPlatform/trunk/mpp@1068 6e48237b-75ef-9749-8fc9-41990f28c85a
459 lines
14 KiB
C++
459 lines
14 KiB
C++
/*
|
|
* 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_enc"
|
|
|
|
#include "string.h"
|
|
|
|
#include "mpp_log.h"
|
|
#include "mpp_mem.h"
|
|
|
|
#include "mpp.h"
|
|
#include "mpp_enc.h"
|
|
#include "mpp_frame_impl.h"
|
|
#include "mpp_packet.h"
|
|
#include "mpp_packet_impl.h"
|
|
#include "hal_h264e_api.h"
|
|
|
|
static MPP_RET release_task_in_port(MppPort port)
|
|
{
|
|
MPP_RET ret = MPP_OK;
|
|
MppPacket packet = NULL;
|
|
MppFrame frame = NULL;
|
|
MppTask mpp_task;
|
|
|
|
do {
|
|
ret = mpp_port_dequeue(port, &mpp_task);
|
|
if (ret)
|
|
break;
|
|
|
|
if (mpp_task) {
|
|
packet = NULL;
|
|
frame = NULL;
|
|
ret = mpp_task_meta_get_frame(mpp_task, MPP_META_KEY_INPUT_FRM, &frame);
|
|
if (frame) {
|
|
mpp_frame_deinit(&frame);
|
|
frame = NULL;
|
|
}
|
|
ret = mpp_task_meta_get_packet(mpp_task, MPP_META_KEY_OUTPUT_PKT, &packet);
|
|
if (packet) {
|
|
mpp_packet_deinit(&packet);
|
|
packet = NULL;
|
|
}
|
|
|
|
mpp_port_enqueue(port, mpp_task);
|
|
mpp_task = NULL;
|
|
} else
|
|
break;
|
|
} while (1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void *mpp_enc_control_thread(void *data)
|
|
{
|
|
Mpp *mpp = (Mpp*)data;
|
|
MppEnc *enc = mpp->mEnc;
|
|
MppThread *thd_enc = mpp->mThreadCodec;
|
|
EncTask task; // TODO
|
|
HalTaskInfo task_info;
|
|
MppPort input = mpp_task_queue_get_port(mpp->mInputTaskQueue, MPP_PORT_OUTPUT);
|
|
MppPort output = mpp_task_queue_get_port(mpp->mOutputTaskQueue, MPP_PORT_INPUT);
|
|
MppTask mpp_task = NULL;
|
|
MPP_RET ret = MPP_OK;
|
|
MppFrame frame = NULL;
|
|
MppPacket packet = NULL;
|
|
|
|
memset(&task_info, 0, sizeof(HalTaskInfo));
|
|
|
|
while (MPP_THREAD_RUNNING == thd_enc->get_status()) {
|
|
thd_enc->lock();
|
|
ret = mpp_port_dequeue(input, &mpp_task);
|
|
if (ret || NULL == mpp_task) {
|
|
thd_enc->wait();
|
|
}
|
|
thd_enc->unlock();
|
|
|
|
if (mpp_task != NULL) {
|
|
mpp_task_meta_get_frame (mpp_task, MPP_META_KEY_INPUT_FRM, &frame);
|
|
mpp_task_meta_get_packet(mpp_task, MPP_META_KEY_OUTPUT_PKT, &packet);
|
|
|
|
mpp_assert(frame);
|
|
|
|
memset(&task, 0, sizeof(EncTask));
|
|
|
|
if (mpp_frame_get_buffer(frame)) {
|
|
/*
|
|
* if there is available buffer in the input frame do encoding
|
|
*/
|
|
if (NULL == packet) {
|
|
RK_U32 width = enc->encCfg.width;
|
|
RK_U32 height = enc->encCfg.height;
|
|
RK_U32 size = width * height;
|
|
MppBuffer buffer = NULL;
|
|
|
|
mpp_buffer_get(mpp->mPacketGroup, &buffer, size);
|
|
mpp_log("create buffer size %d fd %d\n", size, mpp_buffer_get_fd(buffer));
|
|
mpp_packet_init_with_buffer(&packet, buffer);
|
|
mpp_buffer_put(buffer);
|
|
}
|
|
mpp_assert(packet);
|
|
|
|
mpp_packet_set_pts(packet, mpp_frame_get_pts(frame));
|
|
|
|
task.ctrl_frm_buf_in = mpp_frame_get_buffer(frame);
|
|
task.ctrl_pkt_buf_out = mpp_packet_get_buffer(packet);
|
|
controller_encode(mpp->mEnc->controller, &task);
|
|
|
|
task_info.enc.syntax.data = (void *)(&(task.syntax_data));
|
|
mpp_hal_reg_gen((mpp->mEnc->hal), &task_info);
|
|
mpp_hal_hw_start((mpp->mEnc->hal), &task_info);
|
|
/*vpuWaitResult = */mpp_hal_hw_wait((mpp->mEnc->hal), &task_info); // TODO need to check the return value
|
|
|
|
RK_U32 outputStreamSize = 0;
|
|
controller_config(mpp->mEnc->controller, GET_OUTPUT_STREAM_SIZE, (void*)&outputStreamSize);
|
|
|
|
mpp_packet_set_length(packet, outputStreamSize);
|
|
} else {
|
|
/*
|
|
* else init a empty packet for output
|
|
*/
|
|
mpp_packet_new(&packet);
|
|
}
|
|
|
|
if (mpp_frame_get_eos(frame))
|
|
mpp_packet_set_eos(packet);
|
|
|
|
/*
|
|
* first clear output packet
|
|
* then enqueue task back to input port
|
|
* final user will release the mpp_frame they had input
|
|
*/
|
|
mpp_task_meta_set_frame(mpp_task, MPP_META_KEY_INPUT_FRM, frame);
|
|
mpp_port_enqueue(input, mpp_task);
|
|
mpp_task = NULL;
|
|
|
|
// send finished task to output port
|
|
mpp_port_dequeue(output, &mpp_task);
|
|
mpp_task_meta_set_packet(mpp_task, MPP_META_KEY_OUTPUT_PKT, packet);
|
|
|
|
{
|
|
RK_S32 is_intra = task.syntax_data.frame_coding_type;
|
|
RK_U32 flag = mpp_packet_get_flag(packet);
|
|
|
|
mpp_task_meta_set_s32(mpp_task, MPP_META_KEY_OUTPUT_INTRA, is_intra);
|
|
if (is_intra) {
|
|
mpp_packet_set_flag(packet, flag | MPP_PACKET_FLAG_INTRA);
|
|
}
|
|
}
|
|
|
|
// setup output task here
|
|
mpp_port_enqueue(output, mpp_task);
|
|
mpp_task = NULL;
|
|
packet = NULL;
|
|
frame = NULL;
|
|
}
|
|
}
|
|
|
|
// clear remain task in output port
|
|
release_task_in_port(mpp->mInputPort);
|
|
release_task_in_port(input);
|
|
release_task_in_port(mpp->mOutputPort);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *mpp_enc_hal_thread(void *data)
|
|
{
|
|
Mpp *mpp = (Mpp*)data;
|
|
MppThread *hal = mpp->mThreadHal;
|
|
mpp_list *frames = mpp->mFrames;
|
|
mpp_list *tasks = mpp->mTasks;
|
|
|
|
while (MPP_THREAD_RUNNING == hal->get_status()) {
|
|
/*
|
|
* hal thread wait for dxva interface intput firt
|
|
*/
|
|
hal->lock();
|
|
if (0 == tasks->list_size())
|
|
hal->wait();
|
|
hal->unlock();
|
|
|
|
// get_config
|
|
// register genertation
|
|
if (tasks->list_size()) {
|
|
HalDecTask *task;
|
|
tasks->lock();
|
|
tasks->del_at_head(&task, sizeof(task));
|
|
mpp->mTaskGetCount++;
|
|
tasks->unlock();
|
|
|
|
// hal->mpp_hal_reg_gen(current);
|
|
|
|
/*
|
|
* wait previous register set done
|
|
*/
|
|
// hal->mpp_hal_hw_wait(previous);
|
|
|
|
/*
|
|
* send current register set to hardware
|
|
*/
|
|
// hal->mpp_hal_hw_start(current);
|
|
|
|
/*
|
|
* mark previous buffer is complete
|
|
*/
|
|
// change dpb slot status
|
|
// signal()
|
|
// mark frame in output queue
|
|
// wait up output thread to get a output frame
|
|
|
|
// for test
|
|
MppBuffer buffer;
|
|
mpp_buffer_get(mpp->mFrameGroup, &buffer, SZ_1M);
|
|
|
|
MppFrame frame;
|
|
mpp_frame_init(&frame);
|
|
mpp_frame_set_buffer(frame, buffer);
|
|
frames->lock();
|
|
frames->add_at_tail(&frame, sizeof(frame));
|
|
mpp->mFramePutCount++;
|
|
frames->signal();
|
|
frames->unlock();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static MPP_RET mpp_extra_info_generate(h264e_control_extra_info_cfg *info, const H264EncConfig *encCfg)
|
|
{
|
|
info->chroma_qp_index_offset = encCfg->chroma_qp_index_offset;
|
|
info->enable_cabac = encCfg->enable_cabac;
|
|
info->pic_init_qp = encCfg->pic_init_qp;
|
|
info->pic_luma_height = encCfg->height;
|
|
info->pic_luma_width = encCfg->width;
|
|
info->transform8x8_mode = encCfg->transform8x8_mode;
|
|
|
|
info->input_image_format = encCfg->input_image_format;
|
|
info->profile_idc = encCfg->profile;
|
|
info->level_idc = encCfg->level;
|
|
info->keyframe_max_interval = encCfg->keyframe_max_interval;
|
|
info->second_chroma_qp_index_offset = encCfg->second_chroma_qp_index_offset;
|
|
info->pps_id = encCfg->pps_id;
|
|
return MPP_OK;
|
|
}
|
|
|
|
MPP_RET mpp_enc_init(MppEnc **enc, MppCodingType coding)
|
|
{
|
|
MPP_RET ret;
|
|
MppBufSlots frame_slots = NULL;
|
|
MppBufSlots packet_slots = NULL;
|
|
Controller controller = NULL;
|
|
MppHal hal = NULL;
|
|
MppEnc *p = NULL;
|
|
RK_S32 task_count = 2;
|
|
IOInterruptCB cb = {NULL, NULL};
|
|
|
|
if (NULL == enc) {
|
|
mpp_err_f("failed to malloc context\n");
|
|
return MPP_ERR_NULL_PTR;
|
|
}
|
|
|
|
*enc = NULL;
|
|
|
|
p = mpp_calloc(MppEnc, 1);
|
|
if (NULL == p) {
|
|
mpp_err_f("failed to malloc context\n");
|
|
return MPP_ERR_MALLOC;
|
|
}
|
|
|
|
do {
|
|
ret = mpp_buf_slot_init(&frame_slots);
|
|
if (ret) {
|
|
mpp_err_f("could not init frame buffer slot\n");
|
|
break;
|
|
}
|
|
|
|
ret = mpp_buf_slot_init(&packet_slots);
|
|
if (ret) {
|
|
mpp_err_f("could not init packet buffer slot\n");
|
|
break;
|
|
}
|
|
|
|
mpp_buf_slot_setup(packet_slots, task_count);
|
|
cb.callBack = mpp_enc_notify;
|
|
cb.opaque = p;
|
|
|
|
ControllerCfg controller_cfg = {
|
|
coding,
|
|
task_count,
|
|
cb,
|
|
};
|
|
|
|
ret = controller_init(&controller, &controller_cfg);
|
|
if (ret) {
|
|
mpp_err_f("could not init parser\n");
|
|
break;
|
|
}
|
|
cb.callBack = hal_enc_callback;
|
|
cb.opaque = controller;
|
|
// then init hal with task count from parser
|
|
MppHalCfg hal_cfg = {
|
|
MPP_CTX_ENC,
|
|
coding,
|
|
HAL_MODE_LIBVPU,
|
|
HAL_VEPU,
|
|
frame_slots,
|
|
packet_slots,
|
|
NULL,
|
|
1/*controller_cfg.task_count*/, // TODO
|
|
0,
|
|
cb,
|
|
};
|
|
|
|
ret = mpp_hal_init(&hal, &hal_cfg);
|
|
if (ret) {
|
|
mpp_err_f("could not init hal\n");
|
|
break;
|
|
}
|
|
|
|
p->coding = coding;
|
|
p->controller = controller;
|
|
p->hal = hal;
|
|
p->tasks = hal_cfg.tasks;
|
|
p->frame_slots = frame_slots;
|
|
p->packet_slots = packet_slots;
|
|
*enc = p;
|
|
return MPP_OK;
|
|
} while (0);
|
|
|
|
mpp_enc_deinit(p);
|
|
return MPP_NOK;
|
|
|
|
}
|
|
|
|
MPP_RET mpp_enc_deinit(MppEnc *enc)
|
|
{
|
|
if (NULL == enc) {
|
|
mpp_err_f("found NULL input\n");
|
|
return MPP_ERR_NULL_PTR;
|
|
}
|
|
|
|
if (enc->controller) {
|
|
controller_deinit(enc->controller);
|
|
enc->controller = NULL;
|
|
}
|
|
|
|
if (enc->hal) {
|
|
mpp_hal_deinit(enc->hal);
|
|
enc->hal = NULL;
|
|
}
|
|
|
|
if (enc->frame_slots) {
|
|
mpp_buf_slot_deinit(enc->frame_slots);
|
|
enc->frame_slots = NULL;
|
|
}
|
|
|
|
if (enc->packet_slots) {
|
|
mpp_buf_slot_deinit(enc->packet_slots);
|
|
enc->packet_slots = NULL;
|
|
}
|
|
|
|
mpp_free(enc);
|
|
return MPP_OK;
|
|
}
|
|
|
|
MPP_RET mpp_enc_reset(MppEnc *enc)
|
|
{
|
|
(void)enc;
|
|
return MPP_OK;
|
|
}
|
|
|
|
MPP_RET mpp_enc_notify(void *ctx, void *info)
|
|
{
|
|
// TODO
|
|
(void)ctx;
|
|
(void)info;
|
|
return MPP_OK;
|
|
}
|
|
|
|
MPP_RET mpp_enc_control(MppEnc *enc, MpiCmd cmd, void *param)
|
|
{
|
|
if (NULL == enc) {
|
|
mpp_err_f("found NULL input enc %p\n", enc);
|
|
return MPP_ERR_NULL_PTR;
|
|
}
|
|
|
|
// TODO
|
|
MppEncConfig *mppCfg = (MppEncConfig*)param;
|
|
H264EncConfig *encCfg = &(enc->encCfg);
|
|
|
|
switch (cmd) {
|
|
case MPP_ENC_SET_CFG:
|
|
encCfg->streamType = H264ENC_BYTE_STREAM;//H264ENC_NAL_UNIT_STREAM; // decide whether stream start with start code,e.g."00 00 00 01"
|
|
encCfg->frameRateDenom = 1;
|
|
if (0 != mppCfg->profile)
|
|
encCfg->profile = (h264e_profile)mppCfg->profile;
|
|
else
|
|
encCfg->profile = H264_PROFILE_BASELINE;
|
|
if (0 != mppCfg->level)
|
|
encCfg->level = (H264EncLevel)mppCfg->level;
|
|
else
|
|
encCfg->level = H264ENC_LEVEL_4_0;
|
|
if (0 != mppCfg->width && 0 != mppCfg->height) {
|
|
encCfg->width = mppCfg->width;
|
|
encCfg->height = mppCfg->height;
|
|
mpp_log("width %d height %d", encCfg->width, encCfg->height);
|
|
} else
|
|
mpp_err("Width or height is not set, width %d height %d", mppCfg->width, mppCfg->height);
|
|
if (0 != mppCfg->fps_in)
|
|
encCfg->frameRateNum = mppCfg->fps_in;
|
|
else
|
|
encCfg->frameRateNum = 30;
|
|
if (mppCfg->gop > 0)
|
|
encCfg->intraPicRate = mppCfg->gop;
|
|
else
|
|
encCfg->intraPicRate = 30;
|
|
if (0 != mppCfg->cabac_en)
|
|
encCfg->enable_cabac = mppCfg->cabac_en; // TODO
|
|
else
|
|
encCfg->enable_cabac = 0;
|
|
if (0 != mppCfg->trans8x8_en)
|
|
encCfg->transform8x8_mode = mppCfg->trans8x8_en; // TODO
|
|
else
|
|
encCfg->transform8x8_mode = 0;
|
|
encCfg->chroma_qp_index_offset = 2; // TODO
|
|
encCfg->pic_init_qp = 26; // TODO
|
|
encCfg->keyframe_max_interval = 150; // TODO
|
|
encCfg->second_chroma_qp_index_offset = 2; // TODO
|
|
encCfg->pps_id = 0; // TODO
|
|
encCfg->input_image_format = H264ENC_YUV420_SEMIPLANAR; // TODO
|
|
|
|
controller_config(enc->controller, SET_ENC_CFG, (void *)encCfg);
|
|
|
|
mpp_extra_info_generate(&(enc->extra_info_cfg), &(enc->encCfg));
|
|
mpp_hal_control(enc->hal, MPP_ENC_SET_EXTRA_INFO, (void*)(&(enc->extra_info_cfg)));
|
|
mpp_hal_control(enc->hal, MPP_ENC_GET_EXTRA_INFO, (void*)(&(enc->extra_info)));
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return MPP_OK;
|
|
}
|