mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-05 14:30:00 +01:00
avcodec/rkmppenc: add h264/hevc rkmpp encoder
Bump rockchip_mpp to 1.3.8. Signed-off-by: Zhao Zhili <zhilizhao@tencent.com>
This commit is contained in:
@@ -13,6 +13,7 @@ version <next>:
|
|||||||
- D3D12 AV1 encoder
|
- D3D12 AV1 encoder
|
||||||
- ProRes Vulkan hwaccel
|
- ProRes Vulkan hwaccel
|
||||||
- DPX Vulkan hwaccel
|
- DPX Vulkan hwaccel
|
||||||
|
- Rockchip H.264/HEVC hardware encoder
|
||||||
|
|
||||||
|
|
||||||
version 8.0:
|
version 8.0:
|
||||||
|
|||||||
4
configure
vendored
4
configure
vendored
@@ -3484,6 +3484,7 @@ h264_qsv_decoder_select="h264_mp4toannexb_bsf qsvdec"
|
|||||||
h264_qsv_encoder_select="atsc_a53 qsvenc"
|
h264_qsv_encoder_select="atsc_a53 qsvenc"
|
||||||
h264_rkmpp_decoder_deps="rkmpp"
|
h264_rkmpp_decoder_deps="rkmpp"
|
||||||
h264_rkmpp_decoder_select="h264_mp4toannexb_bsf"
|
h264_rkmpp_decoder_select="h264_mp4toannexb_bsf"
|
||||||
|
h264_rkmpp_encoder_deps="rkmpp"
|
||||||
h264_vaapi_encoder_select="atsc_a53 cbs_h264 vaapi_encode"
|
h264_vaapi_encoder_select="atsc_a53 cbs_h264 vaapi_encode"
|
||||||
h264_vulkan_encoder_select="atsc_a53 cbs_h264 vulkan_encode"
|
h264_vulkan_encoder_select="atsc_a53 cbs_h264 vulkan_encode"
|
||||||
h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m"
|
h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m"
|
||||||
@@ -3508,6 +3509,7 @@ hevc_qsv_decoder_select="hevc_mp4toannexb_bsf qsvdec"
|
|||||||
hevc_qsv_encoder_select="hevcparse qsvenc"
|
hevc_qsv_encoder_select="hevcparse qsvenc"
|
||||||
hevc_rkmpp_decoder_deps="rkmpp"
|
hevc_rkmpp_decoder_deps="rkmpp"
|
||||||
hevc_rkmpp_decoder_select="hevc_mp4toannexb_bsf"
|
hevc_rkmpp_decoder_select="hevc_mp4toannexb_bsf"
|
||||||
|
hevc_rkmpp_encoder_deps="rkmpp"
|
||||||
hevc_vaapi_encoder_deps="VAEncPictureParameterBufferHEVC"
|
hevc_vaapi_encoder_deps="VAEncPictureParameterBufferHEVC"
|
||||||
hevc_vaapi_encoder_select="atsc_a53 cbs_h265 vaapi_encode"
|
hevc_vaapi_encoder_select="atsc_a53 cbs_h265 vaapi_encode"
|
||||||
hevc_vulkan_encoder_select="atsc_a53 cbs_h265 vulkan_encode"
|
hevc_vulkan_encoder_select="atsc_a53 cbs_h265 vulkan_encode"
|
||||||
@@ -7366,7 +7368,7 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0" ope
|
|||||||
check_lib openssl openssl/ssl.h DTLS_get_data_mtu -lssl -lcrypto -lws2_32 -lgdi32 ||
|
check_lib openssl openssl/ssl.h DTLS_get_data_mtu -lssl -lcrypto -lws2_32 -lgdi32 ||
|
||||||
die "ERROR: openssl (>= 1.1.1) not found"; }
|
die "ERROR: openssl (>= 1.1.1) not found"; }
|
||||||
enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init
|
enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init
|
||||||
enabled rkmpp && { require_pkg_config rkmpp "rockchip_mpp >= 1.3.7" rockchip/rk_mpi.h mpp_create &&
|
enabled rkmpp && { require_pkg_config rkmpp "rockchip_mpp >= 1.3.8" "rockchip/rk_mpi.h rockchip/mpp_buffer.h" "mpp_create mpp_buffer_sync_begin_f" &&
|
||||||
{ enabled libdrm ||
|
{ enabled libdrm ||
|
||||||
die "ERROR: rkmpp requires --enable-libdrm"; }
|
die "ERROR: rkmpp requires --enable-libdrm"; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,6 +446,7 @@ OBJS-$(CONFIG_H264_OMX_ENCODER) += omx.o
|
|||||||
OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec.o
|
OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec.o
|
||||||
OBJS-$(CONFIG_H264_QSV_ENCODER) += qsvenc_h264.o
|
OBJS-$(CONFIG_H264_QSV_ENCODER) += qsvenc_h264.o
|
||||||
OBJS-$(CONFIG_H264_RKMPP_DECODER) += rkmppdec.o
|
OBJS-$(CONFIG_H264_RKMPP_DECODER) += rkmppdec.o
|
||||||
|
OBJS-$(CONFIG_H264_RKMPP_ENCODER) += rkmppenc.o
|
||||||
OBJS-$(CONFIG_H264_VAAPI_ENCODER) += vaapi_encode_h264.o h264_levels.o \
|
OBJS-$(CONFIG_H264_VAAPI_ENCODER) += vaapi_encode_h264.o h264_levels.o \
|
||||||
h2645data.o hw_base_encode_h264.o
|
h2645data.o hw_base_encode_h264.o
|
||||||
OBJS-$(CONFIG_H264_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h264.o \
|
OBJS-$(CONFIG_H264_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h264.o \
|
||||||
@@ -475,6 +476,7 @@ OBJS-$(CONFIG_HEVC_OH_ENCODER) += ohcodec.o ohenc.o
|
|||||||
OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o
|
OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o
|
||||||
OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc/ps_enc.o
|
OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc/ps_enc.o
|
||||||
OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o
|
OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o
|
||||||
|
OBJS-$(CONFIG_HEVC_RKMPP_ENCODER) += rkmppenc.o
|
||||||
OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o h265_profile_level.o \
|
OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o h265_profile_level.o \
|
||||||
h2645data.o hw_base_encode_h265.o
|
h2645data.o hw_base_encode_h265.o
|
||||||
OBJS-$(CONFIG_HEVC_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h265.o \
|
OBJS-$(CONFIG_HEVC_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h265.o \
|
||||||
|
|||||||
@@ -156,11 +156,13 @@ extern const FFCodec ff_h264_mediacodec_encoder;
|
|||||||
extern const FFCodec ff_h264_mmal_decoder;
|
extern const FFCodec ff_h264_mmal_decoder;
|
||||||
extern const FFCodec ff_h264_qsv_decoder;
|
extern const FFCodec ff_h264_qsv_decoder;
|
||||||
extern const FFCodec ff_h264_rkmpp_decoder;
|
extern const FFCodec ff_h264_rkmpp_decoder;
|
||||||
|
extern const FFCodec ff_h264_rkmpp_encoder;
|
||||||
extern const FFCodec ff_hap_encoder;
|
extern const FFCodec ff_hap_encoder;
|
||||||
extern const FFCodec ff_hap_decoder;
|
extern const FFCodec ff_hap_decoder;
|
||||||
extern const FFCodec ff_hevc_decoder;
|
extern const FFCodec ff_hevc_decoder;
|
||||||
extern const FFCodec ff_hevc_qsv_decoder;
|
extern const FFCodec ff_hevc_qsv_decoder;
|
||||||
extern const FFCodec ff_hevc_rkmpp_decoder;
|
extern const FFCodec ff_hevc_rkmpp_decoder;
|
||||||
|
extern const FFCodec ff_hevc_rkmpp_encoder;
|
||||||
extern const FFCodec ff_hevc_v4l2m2m_decoder;
|
extern const FFCodec ff_hevc_v4l2m2m_decoder;
|
||||||
extern const FFCodec ff_hnm4_video_decoder;
|
extern const FFCodec ff_hnm4_video_decoder;
|
||||||
extern const FFCodec ff_hq_hqa_decoder;
|
extern const FFCodec ff_hq_hqa_decoder;
|
||||||
|
|||||||
584
libavcodec/rkmppenc.c
Normal file
584
libavcodec/rkmppenc.c
Normal file
@@ -0,0 +1,584 @@
|
|||||||
|
/*
|
||||||
|
* RockChip MPP Video Encoder
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Zhao Zhili <quinkblack@foxmail.com>
|
||||||
|
*
|
||||||
|
* FFmpeg is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config_components.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <rockchip/mpp_frame.h>
|
||||||
|
#include <rockchip/mpp_packet.h>
|
||||||
|
#include <rockchip/rk_mpi.h>
|
||||||
|
|
||||||
|
#include "libavutil/avassert.h"
|
||||||
|
#include "libavutil/hwcontext.h"
|
||||||
|
#include "libavutil/hwcontext_drm.h"
|
||||||
|
#include "libavutil/imgutils.h"
|
||||||
|
#include "libavutil/log.h"
|
||||||
|
#include "libavutil/mem.h"
|
||||||
|
#include "libavutil/opt.h"
|
||||||
|
|
||||||
|
#include "avcodec.h"
|
||||||
|
#include "codec_internal.h"
|
||||||
|
#include "encode.h"
|
||||||
|
#include "hwconfig.h"
|
||||||
|
|
||||||
|
#define RKMPP_TIME_BASE AV_TIME_BASE_Q
|
||||||
|
#define RKMPP_ALIGN_SIZE 16
|
||||||
|
|
||||||
|
typedef struct RKMPPEncoderContext {
|
||||||
|
const AVClass *av_class;
|
||||||
|
|
||||||
|
MppCtx enc;
|
||||||
|
MppApi *mpi;
|
||||||
|
MppEncCfg cfg;
|
||||||
|
AVFrame *frame;
|
||||||
|
|
||||||
|
MppFrameFormat pix_fmt;
|
||||||
|
int mpp_stride;
|
||||||
|
int mpp_height;
|
||||||
|
// When pix_fmt isn't hardware pixel format
|
||||||
|
MppBufferGroup buf_group;
|
||||||
|
MppBuffer frame_buf;
|
||||||
|
|
||||||
|
MppEncRcMode rc_mode;
|
||||||
|
bool eof_sent;
|
||||||
|
} RKMPPEncoderContext;
|
||||||
|
|
||||||
|
static const enum AVPixelFormat rkmpp_pix_fmts[] = {
|
||||||
|
AV_PIX_FMT_DRM_PRIME,
|
||||||
|
AV_PIX_FMT_NV12,
|
||||||
|
AV_PIX_FMT_YUV420P,
|
||||||
|
AV_PIX_FMT_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static av_cold int rkmpp_close_encoder(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
|
||||||
|
if (ctx->enc) {
|
||||||
|
ctx->mpi->reset(ctx->enc);
|
||||||
|
mpp_destroy(ctx->enc);
|
||||||
|
ctx->enc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->cfg) {
|
||||||
|
mpp_enc_cfg_deinit(ctx->cfg);
|
||||||
|
ctx->cfg = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->frame_buf) {
|
||||||
|
mpp_buffer_put(ctx->frame_buf);
|
||||||
|
ctx->frame_buf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->buf_group) {
|
||||||
|
mpp_buffer_group_put(ctx->buf_group);
|
||||||
|
ctx->buf_group = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_frame_free(&ctx->frame);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_create_frame_buf(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
|
||||||
|
ctx->frame = av_frame_alloc();
|
||||||
|
if (!ctx->frame)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
if (avctx->pix_fmt == AV_PIX_FMT_DRM_PRIME)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int ret = mpp_buffer_group_get_internal(&ctx->buf_group,
|
||||||
|
MPP_BUFFER_TYPE_DRM | MPP_BUFFER_FLAGS_CACHABLE);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to create buffer group, %d\n",
|
||||||
|
ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = av_image_get_buffer_size(avctx->pix_fmt, ctx->mpp_stride,
|
||||||
|
ctx->mpp_height, 1);
|
||||||
|
if (n < 0)
|
||||||
|
return ret;
|
||||||
|
ret = mpp_buffer_get(ctx->buf_group, &ctx->frame_buf, n);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to get frame buffer, %d\n",
|
||||||
|
ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_export_extradata(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
MppEncHeaderMode mode = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ?
|
||||||
|
MPP_ENC_HEADER_MODE_DEFAULT : MPP_ENC_HEADER_MODE_EACH_IDR;
|
||||||
|
|
||||||
|
int ret = ctx->mpi->control(ctx->enc, MPP_ENC_SET_HEADER_MODE, &mode);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to set header mode: %d\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t size = 4096;
|
||||||
|
avctx->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if (!avctx->extradata)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
MppPacket packet = NULL;
|
||||||
|
mpp_packet_init(&packet, avctx->extradata, size);
|
||||||
|
mpp_packet_set_length(packet, 0);
|
||||||
|
ret = ctx->mpi->control(ctx->enc, MPP_ENC_GET_HDR_SYNC, packet);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to get header: %d\n", ret);
|
||||||
|
ret = AVERROR_EXTERNAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
avctx->extradata_size = mpp_packet_get_length(packet);
|
||||||
|
if (avctx->extradata_size == 0 || avctx->extradata_size > size) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Invalid extradata size %d\n",
|
||||||
|
avctx->extradata_size);
|
||||||
|
ret = AVERROR_EXTERNAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
mpp_packet_deinit(&packet);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static av_cold int rkmpp_init_encoder(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
MppCodingType codectype;
|
||||||
|
switch (avctx->codec_id) {
|
||||||
|
case AV_CODEC_ID_H264:
|
||||||
|
codectype = MPP_VIDEO_CodingAVC;
|
||||||
|
break;
|
||||||
|
case AV_CODEC_ID_HEVC:
|
||||||
|
codectype = MPP_VIDEO_CodingHEVC;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
av_unreachable("Invalid codec_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mpp_check_support_format(MPP_CTX_ENC, codectype);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "The device doesn't support %s\n",
|
||||||
|
avcodec_get_name(avctx->codec_id));
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mpp_create(&ctx->enc, &ctx->mpi);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to create MPP context (%d).\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mpp_init(ctx->enc, MPP_CTX_ENC, codectype);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to initialize MPP context (%d).\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mpp_enc_cfg_init(&ctx->cfg);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to initialize config (%d).\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MppEncCfg cfg = ctx->cfg;
|
||||||
|
ret = ctx->mpi->control(ctx->enc, MPP_ENC_GET_CFG, cfg);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to get encoder config: %d\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:width", avctx->width);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:height", avctx->height);
|
||||||
|
ctx->mpp_stride = FFALIGN(avctx->width, RKMPP_ALIGN_SIZE);
|
||||||
|
ctx->mpp_height = FFALIGN(avctx->height, RKMPP_ALIGN_SIZE);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", ctx->mpp_stride);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", ctx->mpp_height);
|
||||||
|
|
||||||
|
if (avctx->pix_fmt == AV_PIX_FMT_DRM_PRIME || avctx->pix_fmt == AV_PIX_FMT_NV12)
|
||||||
|
ctx->pix_fmt = MPP_FMT_YUV420SP;
|
||||||
|
else if (avctx->pix_fmt == AV_PIX_FMT_YUV420P)
|
||||||
|
ctx->pix_fmt = MPP_FMT_YUV420P;
|
||||||
|
else // Can only happen during development
|
||||||
|
return AVERROR_BUG;
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:format", ctx->pix_fmt);
|
||||||
|
|
||||||
|
if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED)
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:colorspace", avctx->colorspace);
|
||||||
|
if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED)
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:colorprim", avctx->color_primaries);
|
||||||
|
if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED)
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:colortrc", avctx->color_trc);
|
||||||
|
static_assert((int)AVCOL_RANGE_MPEG == (int)MPP_FRAME_RANGE_MPEG &&
|
||||||
|
(int)AVCOL_RANGE_JPEG == (int)MPP_FRAME_RANGE_JPEG &&
|
||||||
|
(int)AVCOL_RANGE_UNSPECIFIED == (int) MPP_FRAME_RANGE_UNSPECIFIED,
|
||||||
|
"MppFrameColorRange not equal to AVColorRange");
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "prep:colorrange", avctx->color_range);
|
||||||
|
|
||||||
|
/* These two options sound like variable frame rate from the doc, but they
|
||||||
|
* are not. When they are false, bitrate control is based on frame numbers
|
||||||
|
* and framerate. But when they are true, bitrate control is based on wall
|
||||||
|
* clock time, not based on frame timestamps, which makes these options
|
||||||
|
* almost useless, except in certain rare realtime case.
|
||||||
|
*/
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", 0);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", 0);
|
||||||
|
if (avctx->framerate.den > 0 && avctx->framerate.num > 0) {
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", avctx->framerate.num);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denom", avctx->framerate.den);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", avctx->framerate.num);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denom", avctx->framerate.den);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avctx->gop_size >= 0)
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:gop", avctx->gop_size);
|
||||||
|
|
||||||
|
mpp_enc_cfg_set_u32(cfg, "rc:mode", ctx->rc_mode);
|
||||||
|
if (avctx->bit_rate > 0) {
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:bps_target", avctx->bit_rate);
|
||||||
|
if (avctx->rc_buffer_size >= avctx->bit_rate) {
|
||||||
|
int seconds = round((double)avctx->rc_buffer_size / avctx->bit_rate);
|
||||||
|
// 60 is the upper bound from the doc
|
||||||
|
seconds = FFMIN(seconds, 60);
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:stats_time", seconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (avctx->rc_max_rate > 0)
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:bps_max", avctx->rc_max_rate);
|
||||||
|
if (avctx->rc_min_rate > 0)
|
||||||
|
mpp_enc_cfg_set_s32(cfg, "rc:bps_min", avctx->rc_min_rate);
|
||||||
|
|
||||||
|
mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
|
||||||
|
|
||||||
|
ret = ctx->mpi->control(ctx->enc, MPP_ENC_SET_CFG, cfg);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to set config: %d\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rkmpp_create_frame_buf(avctx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = rkmpp_export_extradata(avctx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_output_pkt(AVCodecContext *avctx, AVPacket *pkt, MppPacket packet)
|
||||||
|
{
|
||||||
|
if (mpp_packet_get_eos(packet)) {
|
||||||
|
av_log(avctx, AV_LOG_INFO, "Receive eos packet\n");
|
||||||
|
return AVERROR_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = mpp_packet_get_length(packet);
|
||||||
|
void *data = mpp_packet_get_pos(packet);
|
||||||
|
|
||||||
|
if (!size || !data) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Encoder return empty packet\n");
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = ff_get_encode_buffer(avctx, pkt, size, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
memcpy(pkt->data, data, size);
|
||||||
|
|
||||||
|
int64_t pts = mpp_packet_get_pts(packet);
|
||||||
|
int64_t dts = mpp_packet_get_dts(packet);
|
||||||
|
|
||||||
|
pkt->pts = av_rescale_q(pts, RKMPP_TIME_BASE, avctx->time_base);
|
||||||
|
/* dts is always zero currently, since rkmpp copy dts from MppFrame to
|
||||||
|
* MppPacket, and we don't set dts for MppFrame (it make no sense for
|
||||||
|
* encoder). rkmpp encoder doesn't support reordering, so we can just
|
||||||
|
* set dts as pts.
|
||||||
|
*
|
||||||
|
* TODO: remove this workaround once rkmpp fixed the issue.
|
||||||
|
*/
|
||||||
|
if (dts)
|
||||||
|
pkt->dts = av_rescale_q(dts, RKMPP_TIME_BASE, avctx->time_base);
|
||||||
|
else
|
||||||
|
pkt->dts = pkt->pts;
|
||||||
|
|
||||||
|
MppMeta meta = mpp_packet_get_meta(packet);
|
||||||
|
if (!meta) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to get meta from mpp packet\n");
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int key_frame = 0;
|
||||||
|
ret = mpp_meta_get_s32(meta, KEY_OUTPUT_INTRA, &key_frame);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to get key frame info\n");
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_frame)
|
||||||
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_set_hw_frame(AVCodecContext *avctx, MppFrame frame)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
AVBufferRef *hw_ref = ctx->frame->hw_frames_ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!hw_ref)
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
|
AVHWFramesContext *hwframes = (AVHWFramesContext *)hw_ref->data;
|
||||||
|
if (hwframes->sw_format != AV_PIX_FMT_NV12)
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
|
|
||||||
|
const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)ctx->frame->data[0];
|
||||||
|
const AVDRMLayerDescriptor *layer = &desc->layers[0];
|
||||||
|
|
||||||
|
int stride = layer->planes[0].pitch;
|
||||||
|
int vertical = layer->planes[1].offset / stride;
|
||||||
|
if (stride != ctx->mpp_stride || vertical != ctx->mpp_height) {
|
||||||
|
// Update stride info
|
||||||
|
ctx->mpp_stride = stride;
|
||||||
|
ctx->mpp_height = vertical;
|
||||||
|
mpp_enc_cfg_set_s32(ctx->cfg, "prep:hor_stride", ctx->mpp_stride);
|
||||||
|
mpp_enc_cfg_set_s32(ctx->cfg, "prep:ver_stride", ctx->mpp_height);
|
||||||
|
ret = ctx->mpi->control(ctx->enc, MPP_ENC_SET_CFG, ctx->cfg);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Failed to set config: %d\n", ret);
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mpp_frame_set_hor_stride(frame, stride);
|
||||||
|
mpp_frame_set_ver_stride(frame, vertical);
|
||||||
|
|
||||||
|
MppBuffer buffer = {0};
|
||||||
|
MppBufferInfo info = {
|
||||||
|
.type = MPP_BUFFER_TYPE_DRM,
|
||||||
|
.size = desc->objects[0].size,
|
||||||
|
.fd = desc->objects[0].fd,
|
||||||
|
};
|
||||||
|
ret = mpp_buffer_import(&buffer, &info);
|
||||||
|
if (ret != MPP_OK)
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
|
||||||
|
mpp_frame_set_buffer(frame, buffer);
|
||||||
|
mpp_buffer_put(buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_set_sw_frame(AVCodecContext *avctx, MppFrame frame)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
AVFrame *f = ctx->frame;
|
||||||
|
|
||||||
|
mpp_buffer_sync_begin(ctx->frame_buf);
|
||||||
|
void *buf = mpp_buffer_get_ptr(ctx->frame_buf);
|
||||||
|
|
||||||
|
uint8_t *dst[4] = {NULL};
|
||||||
|
int dst_linesizes[4] = {0};
|
||||||
|
int ret = av_image_fill_linesizes(dst_linesizes, f->format, ctx->mpp_stride);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
ret = av_image_fill_pointers(dst, f->format, ctx->mpp_height, buf,
|
||||||
|
dst_linesizes);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
av_image_copy2(dst, dst_linesizes, f->data, f->linesize,
|
||||||
|
f->format, f->width, f->height);
|
||||||
|
mpp_frame_set_hor_stride(frame, ctx->mpp_stride);
|
||||||
|
mpp_frame_set_ver_stride(frame, ctx->mpp_height);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mpp_buffer_sync_end(ctx->frame_buf);
|
||||||
|
if (!ret)
|
||||||
|
mpp_frame_set_buffer(frame, ctx->frame_buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_send_frame(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
MppFrame frame = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = mpp_frame_init(&frame);
|
||||||
|
if (ret != MPP_OK) {
|
||||||
|
ret = AVERROR_EXTERNAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->frame->buf[0]) {
|
||||||
|
if (ctx->frame->format == AV_PIX_FMT_DRM_PRIME)
|
||||||
|
ret = rkmpp_set_hw_frame(avctx, frame);
|
||||||
|
else
|
||||||
|
ret = rkmpp_set_sw_frame(avctx, frame);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
mpp_frame_set_fmt(frame, ctx->pix_fmt);
|
||||||
|
mpp_frame_set_width(frame, ctx->frame->width);
|
||||||
|
mpp_frame_set_height(frame, ctx->frame->height);
|
||||||
|
mpp_frame_set_pts(frame, av_rescale_q(ctx->frame->pts,
|
||||||
|
avctx->time_base, RKMPP_TIME_BASE));
|
||||||
|
} else {
|
||||||
|
mpp_frame_set_buffer(frame, NULL);
|
||||||
|
mpp_frame_set_eos(frame, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ctx->mpi->encode_put_frame(ctx->enc, frame);
|
||||||
|
if (ret != MPP_OK)
|
||||||
|
ret = AVERROR_EXTERNAL;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (frame)
|
||||||
|
mpp_frame_deinit(&frame);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rkmpp_receive(AVCodecContext *avctx, AVPacket *pkt)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
MppPacket packet = NULL;
|
||||||
|
int ret = ctx->mpi->encode_get_packet(ctx->enc, &packet);
|
||||||
|
|
||||||
|
if (ret == MPP_OK && packet) {
|
||||||
|
ret = rkmpp_output_pkt(avctx, pkt, packet);
|
||||||
|
mpp_packet_deinit(&packet);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->eof_sent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ctx->frame->buf[0]) {
|
||||||
|
ret = ff_encode_get_frame(avctx, ctx->frame);
|
||||||
|
if (ret < 0 && ret != AVERROR_EOF)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rkmpp_send_frame(avctx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!ctx->frame->buf[0])
|
||||||
|
ctx->eof_sent = true;
|
||||||
|
else
|
||||||
|
av_frame_unref(ctx->frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static av_cold void rkmpp_flush(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
RKMPPEncoderContext *ctx = avctx->priv_data;
|
||||||
|
ctx->mpi->reset(ctx->enc);
|
||||||
|
ctx->eof_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const AVCodecHWConfigInternal *const rkmpp_hw_configs[] = {
|
||||||
|
HW_CONFIG_ENCODER_FRAMES(DRM_PRIME, DRM),
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OFFSET(x) offsetof(RKMPPEncoderContext, x)
|
||||||
|
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
|
||||||
|
static const AVOption rkmpp_options[] = {
|
||||||
|
{"rc", "rate-control mode",
|
||||||
|
OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = MPP_ENC_RC_MODE_VBR }, MPP_ENC_RC_MODE_VBR, INT_MAX, VE, .unit = "rc"},
|
||||||
|
{"vbr", "Variable bitrate mode",
|
||||||
|
0, AV_OPT_TYPE_CONST, {.i64 = MPP_ENC_RC_MODE_VBR}, 0, 0, VE, .unit = "rc"},
|
||||||
|
{"cbr", "Constant bitrate mode",
|
||||||
|
0, AV_OPT_TYPE_CONST, {.i64 = MPP_ENC_RC_MODE_CBR}, 0, 0, VE, .unit = "rc"},
|
||||||
|
{"avbr", "Adaptive bit rate mode",
|
||||||
|
0, AV_OPT_TYPE_CONST, {.i64 = MPP_ENC_RC_MODE_AVBR}, 0, 0, VE, .unit = "rc"},
|
||||||
|
{NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const AVClass rkmpp_enc_class = {
|
||||||
|
.class_name = "rkmpp_enc",
|
||||||
|
.item_name = av_default_item_name,
|
||||||
|
.version = LIBAVUTIL_VERSION_INT,
|
||||||
|
.option = rkmpp_options,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RKMPP_ENC(NAME, ID) \
|
||||||
|
const FFCodec ff_##NAME##_rkmpp_encoder = { \
|
||||||
|
.p.name = #NAME "_rkmpp", \
|
||||||
|
CODEC_LONG_NAME(#NAME " (rkmpp)"), \
|
||||||
|
.p.type = AVMEDIA_TYPE_VIDEO, \
|
||||||
|
.p.id = ID, \
|
||||||
|
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | \
|
||||||
|
AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_ENCODER_FLUSH, \
|
||||||
|
.priv_data_size = sizeof(RKMPPEncoderContext), \
|
||||||
|
CODEC_PIXFMTS_ARRAY(rkmpp_pix_fmts), \
|
||||||
|
.color_ranges = AVCOL_RANGE_MPEG | AVCOL_RANGE_JPEG, \
|
||||||
|
.init = rkmpp_init_encoder, \
|
||||||
|
FF_CODEC_RECEIVE_PACKET_CB(rkmpp_receive), \
|
||||||
|
.close = rkmpp_close_encoder, \
|
||||||
|
.flush = rkmpp_flush, \
|
||||||
|
.p.priv_class = &rkmpp_enc_class, \
|
||||||
|
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \
|
||||||
|
.p.wrapper_name = "rkmpp", \
|
||||||
|
.hw_configs = rkmpp_hw_configs, \
|
||||||
|
};
|
||||||
|
|
||||||
|
#if CONFIG_H264_RKMPP_ENCODER
|
||||||
|
RKMPP_ENC(h264, AV_CODEC_ID_H264)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_HEVC_RKMPP_ENCODER
|
||||||
|
RKMPP_ENC(hevc, AV_CODEC_ID_HEVC)
|
||||||
|
#endif
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
#include "version_major.h"
|
#include "version_major.h"
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_MINOR 20
|
#define LIBAVCODEC_VERSION_MINOR 21
|
||||||
#define LIBAVCODEC_VERSION_MICRO 100
|
#define LIBAVCODEC_VERSION_MICRO 100
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||||
|
|||||||
Reference in New Issue
Block a user