OrangePi3588Media/src/hw_codec.cpp

880 lines
30 KiB
C++

#include "hw/rk3588_defaults.h"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <deque>
#include <string>
#include <vector>
#include "utils/logger.h"
#if defined(RK3588_ENABLE_MPP)
extern "C" {
#include <rockchip/mpp_buffer.h>
#include <rockchip/mpp_frame.h>
#include <rockchip/mpp_packet.h>
#include <rockchip/rk_mpi.h>
}
#include <unistd.h>
#endif
#if defined(RK3588_ENABLE_FFMPEG)
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
}
#endif
namespace rk3588 {
namespace {
std::string ToLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
return static_cast<char>(std::tolower(c));
});
return s;
}
std::vector<uint8_t> ReadByteArray(const SimpleJson& config, const std::string& key) {
std::vector<uint8_t> out;
const SimpleJson* node = config.Find(key);
if (!node || !node->IsArray()) return out;
const auto& arr = node->AsArray();
out.reserve(arr.size());
for (const auto& v : arr) {
int iv = v.AsInt(-1);
if (iv < 0) continue;
if (iv > 255) iv = 255;
out.push_back(static_cast<uint8_t>(iv));
}
return out;
}
std::string ReadBackend(const SimpleJson& config) {
std::string backend = ToLower(config.ValueOr<std::string>("backend", ""));
if (!backend.empty()) return backend;
if (config.ValueOr<bool>("use_mpp", false)) return "mpp";
if (config.ValueOr<bool>("use_ffmpeg", false)) return "ffmpeg";
return "";
}
} // namespace
#if defined(RK3588_ENABLE_MPP)
namespace {
inline int Align16(int v) { return (v + 15) & ~15; }
static bool IsAnnexB(const uint8_t* data, size_t size) {
if (!data || size < 4) return false;
if (data[0] == 0 && data[1] == 0 && data[2] == 1) return true;
if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1) return true;
return false;
}
class MppDecoder : public IDecoder {
public:
~MppDecoder() override { Close(); }
Status Open(const SimpleJson& config) override {
Close();
std::string codec = ToLower(config.ValueOr<std::string>("codec", ""));
if (codec.empty()) return FailStatus("codec not set");
MppCodingType coding = MPP_VIDEO_CodingAVC;
if (codec == "h264") {
coding = MPP_VIDEO_CodingAVC;
} else if (codec == "h265" || codec == "hevc") {
coding = MPP_VIDEO_CodingHEVC;
} else {
return FailStatus("unsupported codec");
}
if (mpp_create(&ctx_, &mpi_) != MPP_OK) {
return FailStatus("mpp_create failed");
}
if (mpp_init(ctx_, MPP_CTX_DEC, coding) != MPP_OK) {
return FailStatus("mpp_init failed");
}
MppDecCfg cfg = nullptr;
mpp_dec_cfg_init(&cfg);
if (mpi_->control(ctx_, MPP_DEC_GET_CFG, cfg) != MPP_OK) {
mpp_dec_cfg_deinit(cfg);
return FailStatus("MPP_DEC_GET_CFG failed");
}
if (mpp_dec_cfg_set_u32(cfg, "base:split_parse", 1) != MPP_OK) {
mpp_dec_cfg_deinit(cfg);
return FailStatus("MPP_DEC_SET_CFG failed");
}
mpi_->control(ctx_, MPP_DEC_SET_CFG, cfg);
mpp_dec_cfg_deinit(cfg);
auto extradata = ReadByteArray(config, "extradata");
if (!extradata.empty()) {
if (!IsAnnexB(extradata.data(), extradata.size())) {
LogWarn("[hw] mpp extradata not annexb");
}
SendExtraData(extradata.data(), extradata.size());
}
opened_ = true;
return OkStatus();
}
Status Send(const DecodePacket& packet) override {
if (!opened_ || !ctx_ || !mpi_) return FailStatus("decoder not opened");
if (!packet.data || packet.size == 0) return FailStatus("empty packet");
MppPacket src = nullptr;
MPP_RET ret = mpp_packet_init(&src, const_cast<uint8_t*>(packet.data), packet.size);
if (ret != MPP_OK || !src) return FailStatus("mpp_packet_init failed");
MppPacket mpp_pkt = nullptr;
ret = mpp_packet_copy_init(&mpp_pkt, src);
mpp_packet_deinit(&src);
if (ret != MPP_OK || !mpp_pkt) return FailStatus("mpp_packet_copy_init failed");
mpp_packet_set_pos(mpp_pkt, mpp_packet_get_data(mpp_pkt));
mpp_packet_set_length(mpp_pkt, packet.size);
mpp_packet_set_pts(mpp_pkt, static_cast<int64_t>(packet.pts));
mpp_packet_clr_eos(mpp_pkt);
bool pkt_done = false;
MPP_RET last_put = MPP_NOK;
int put_tries = 0;
while (!pkt_done && put_tries++ < 200) {
MPP_RET put_ret = mpi_->decode_put_packet(ctx_, mpp_pkt);
if (put_ret == MPP_OK) {
pkt_done = true;
break;
}
last_put = put_ret;
DrainFrames();
usleep(2000);
}
DrainFrames();
mpp_packet_deinit(&mpp_pkt);
if (!pkt_done && last_put != MPP_ERR_BUFFER_FULL) {
return FailStatus("decode_put_packet failed");
}
return OkStatus();
}
Result<std::shared_ptr<Frame>> Receive() override {
if (!frames_.empty()) {
auto frame = frames_.front();
frames_.pop_front();
return frame;
}
if (!opened_ || !ctx_ || !mpi_) return MakeError<std::shared_ptr<Frame>>("decoder not opened");
DrainFrames();
if (!frames_.empty()) {
auto frame = frames_.front();
frames_.pop_front();
return frame;
}
return MakeError<std::shared_ptr<Frame>>("no_frame");
}
void Close() override {
frames_.clear();
if (frame_group_) {
mpp_buffer_group_put(frame_group_);
frame_group_ = nullptr;
}
if (ctx_) {
mpp_destroy(ctx_);
ctx_ = nullptr;
mpi_ = nullptr;
}
opened_ = false;
}
private:
void DrainFrames() {
for (int n = 0; n < 8; ++n) {
MppFrame frm = nullptr;
MPP_RET ret = mpi_->decode_get_frame(ctx_, &frm);
if (ret == MPP_ERR_TIMEOUT) return;
if (ret != MPP_OK) return;
if (!frm) return;
if (mpp_frame_get_info_change(frm)) {
frame_ = frm;
HandleInfoChange();
frame_ = nullptr;
mpp_frame_deinit(&frm);
continue;
}
auto out = MakeFrameFromMpp(frm);
if (out) frames_.push_back(out);
mpp_frame_deinit(&frm);
}
}
void HandleInfoChange() {
if (!ctx_ || !mpi_ || !frame_) return;
MppBufferGroup group = frame_group_;
size_t buf_size = mpp_frame_get_buf_size(frame_);
if (!group) {
auto try_group = [&](MppBufferType t) -> bool {
return mpp_buffer_group_get(&group, t, MPP_BUFFER_INTERNAL, MODULE_TAG, __FUNCTION__) == MPP_OK;
};
if (!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_DMA_HEAP | MPP_BUFFER_FLAGS_CACHABLE | MPP_BUFFER_FLAGS_DMA32)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_DMA_HEAP | MPP_BUFFER_FLAGS_DMA32)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_DMA_HEAP | MPP_BUFFER_FLAGS_CACHABLE)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_DMA_HEAP)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_DRM | MPP_BUFFER_FLAGS_CACHABLE)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_DRM)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_ION | MPP_BUFFER_FLAGS_CACHABLE)) &&
!try_group(static_cast<MppBufferType>(MPP_BUFFER_TYPE_ION))) {
LogError("[hw] mpp info_change: failed to alloc frame buffer group");
return;
}
frame_group_ = group;
} else {
mpp_buffer_group_clear(group);
}
mpp_buffer_group_limit_config(group, buf_size, 24);
mpi_->control(ctx_, MPP_DEC_SET_EXT_BUF_GROUP, group);
mpi_->control(ctx_, MPP_DEC_SET_INFO_CHANGE_READY, nullptr);
}
std::shared_ptr<Frame> MakeFrameFromMpp(MppFrame frm) {
MppBuffer buf = mpp_frame_get_buffer(frm);
if (!buf) return nullptr;
mpp_buffer_inc_ref(buf);
auto owner = std::shared_ptr<void>(buf, [](void* p) { mpp_buffer_put(reinterpret_cast<MppBuffer>(p)); });
int width = mpp_frame_get_width(frm);
int height = mpp_frame_get_height(frm);
int hor_stride = mpp_frame_get_hor_stride(frm);
int ver_stride = mpp_frame_get_ver_stride(frm);
MppFrameFormat mpp_fmt = mpp_frame_get_fmt(frm);
PixelFormat fmt = PixelFormat::UNKNOWN;
int plane_count = 0;
if (mpp_fmt == MPP_FMT_YUV420SP
#ifdef MPP_FMT_YUV420SP_10BIT
|| mpp_fmt == MPP_FMT_YUV420SP_10BIT
#endif
) {
fmt = PixelFormat::NV12;
plane_count = 2;
} else if (mpp_fmt == MPP_FMT_YUV420P
#ifdef MPP_FMT_YUV420P10
|| mpp_fmt == MPP_FMT_YUV420P10
#endif
) {
fmt = PixelFormat::YUV420;
plane_count = 3;
}
if (plane_count == 0) return nullptr;
auto frame = std::make_shared<Frame>();
frame->width = width;
frame->height = height;
frame->format = fmt;
frame->stride = hor_stride;
frame->plane_count = plane_count;
frame->SetDmaFd(mpp_buffer_get_fd(buf));
frame->data = static_cast<uint8_t*>(mpp_buffer_get_ptr(buf));
frame->data_size = mpp_buffer_get_size(buf);
frame->SetOwner(owner);
frame->pts = static_cast<uint64_t>(mpp_frame_get_pts(frm));
if (fmt == PixelFormat::NV12 && plane_count == 2) {
int y_size = hor_stride * ver_stride;
frame->planes[0] = {frame->data, hor_stride, y_size, 0};
frame->planes[1] = {frame->data + y_size, hor_stride, y_size / 2, y_size};
} else if (fmt == PixelFormat::YUV420 && plane_count == 3) {
int y_size = hor_stride * ver_stride;
int uv_stride = hor_stride / 2;
int uv_h = ver_stride / 2;
int u_size = uv_stride * uv_h;
frame->planes[0] = {frame->data, hor_stride, y_size, 0};
frame->planes[1] = {frame->data + y_size, uv_stride, u_size, y_size};
frame->planes[2] = {frame->data + y_size + u_size, uv_stride, u_size, y_size + u_size};
}
frame->SyncBufferFromFrame();
return frame;
}
bool SendExtraData(const uint8_t* data, size_t size) {
if (!ctx_ || !mpi_ || !data || size == 0) return false;
MppPacket src = nullptr;
MPP_RET ret = mpp_packet_init(&src, const_cast<uint8_t*>(data), size);
if (ret != MPP_OK || !src) return false;
MppPacket pkt = nullptr;
ret = mpp_packet_copy_init(&pkt, src);
mpp_packet_deinit(&src);
if (ret != MPP_OK || !pkt) return false;
mpp_packet_set_pos(pkt, mpp_packet_get_data(pkt));
mpp_packet_set_length(pkt, size);
mpp_packet_set_pts(pkt, 0);
mpp_packet_set_extra_data(pkt);
int tries = 0;
while (tries++ < 200) {
MPP_RET put_ret = mpi_->decode_put_packet(ctx_, pkt);
if (put_ret == MPP_OK) {
mpp_packet_deinit(&pkt);
return true;
}
MppFrame tmp = nullptr;
if (mpi_->decode_get_frame(ctx_, &tmp) == MPP_OK && tmp) {
if (mpp_frame_get_info_change(tmp)) {
frame_ = tmp;
HandleInfoChange();
frame_ = nullptr;
}
mpp_frame_deinit(&tmp);
}
usleep(2000);
}
mpp_packet_deinit(&pkt);
return false;
}
MppCtx ctx_ = nullptr;
MppApi* mpi_ = nullptr;
MppBufferGroup frame_group_ = nullptr;
MppFrame frame_ = nullptr;
std::deque<std::shared_ptr<Frame>> frames_;
bool opened_ = false;
};
class MppEncoder : public IEncoder {
public:
~MppEncoder() override { Close(); }
Status Open(const SimpleJson& config) override {
Close();
width_ = config.ValueOr<int>("width", 0);
height_ = config.ValueOr<int>("height", 0);
fps_ = config.ValueOr<int>("fps", 25);
gop_ = config.ValueOr<int>("gop", std::max(1, fps_) * 2);
bitrate_bps_ = config.ValueOr<int>("bitrate_kbps", 4000) * 1000;
codec_ = ToLower(config.ValueOr<std::string>("codec", "h264"));
std::string fmt = ToLower(config.ValueOr<std::string>("pixel_format", "nv12"));
if (width_ <= 0 || height_ <= 0) return FailStatus("invalid size");
if (fmt == "nv12") {
mpp_fmt_ = MPP_FMT_YUV420SP;
} else if (fmt == "yuv420") {
mpp_fmt_ = MPP_FMT_YUV420P;
} else {
return FailStatus("unsupported pixel format");
}
if (codec_ == "h265" || codec_ == "hevc") {
coding_ = MPP_VIDEO_CodingHEVC;
} else {
coding_ = MPP_VIDEO_CodingAVC;
}
hor_stride_ = Align16(width_);
ver_stride_ = Align16(height_);
if (mpp_create(&ctx_, &mpi_) != MPP_OK) return FailStatus("mpp_create failed");
if (mpp_init(ctx_, MPP_CTX_ENC, coding_) != MPP_OK) return FailStatus("mpp_init failed");
if (mpp_enc_cfg_init(&cfg_) != MPP_OK) return FailStatus("mpp_enc_cfg_init failed");
if (mpi_->control(ctx_, MPP_ENC_GET_CFG, cfg_) != MPP_OK) return FailStatus("MPP_ENC_GET_CFG failed");
mpp_enc_cfg_set_s32(cfg_, "prep:width", width_);
mpp_enc_cfg_set_s32(cfg_, "prep:height", height_);
mpp_enc_cfg_set_s32(cfg_, "prep:hor_stride", hor_stride_);
mpp_enc_cfg_set_s32(cfg_, "prep:ver_stride", ver_stride_);
mpp_enc_cfg_set_s32(cfg_, "prep:format", mpp_fmt_);
mpp_enc_cfg_set_s32(cfg_, "rc:mode", MPP_ENC_RC_MODE_CBR);
mpp_enc_cfg_set_s32(cfg_, "rc:gop", gop_);
mpp_enc_cfg_set_s32(cfg_, "rc:fps_in_num", fps_);
mpp_enc_cfg_set_s32(cfg_, "rc:fps_in_denorm", 1);
mpp_enc_cfg_set_s32(cfg_, "rc:fps_out_num", fps_);
mpp_enc_cfg_set_s32(cfg_, "rc:fps_out_denorm", 1);
mpp_enc_cfg_set_s32(cfg_, "rc:bps_target", bitrate_bps_);
mpp_enc_cfg_set_s32(cfg_, "rc:bps_max", bitrate_bps_ * 3 / 2);
mpp_enc_cfg_set_s32(cfg_, "rc:bps_min", bitrate_bps_ / 2);
mpp_enc_cfg_set_s32(cfg_, "codec:type", coding_);
if (mpi_->control(ctx_, MPP_ENC_SET_CFG, cfg_) != MPP_OK) return FailStatus("MPP_ENC_SET_CFG failed");
if (coding_ == MPP_VIDEO_CodingAVC || coding_ == MPP_VIDEO_CodingHEVC) {
MppEncHeaderMode header_mode = MPP_ENC_HEADER_MODE_EACH_IDR;
mpi_->control(ctx_, MPP_ENC_SET_HEADER_MODE, &header_mode);
}
RK_S32 timeout = 2000;
mpi_->control(ctx_, MPP_SET_OUTPUT_TIMEOUT, &timeout);
MppPacket hdr = nullptr;
if (mpi_->control(ctx_, MPP_ENC_GET_HDR_SYNC, &hdr) == MPP_OK && hdr) {
auto* data = static_cast<uint8_t*>(mpp_packet_get_pos(hdr));
size_t len = mpp_packet_get_length(hdr);
header_.assign(data, data + len);
mpp_packet_deinit(&hdr);
}
if (mpp_buffer_group_get_internal(&frm_grp_, MPP_BUFFER_TYPE_DRM, NULL) != MPP_OK) {
if (mpp_buffer_group_get_internal(&frm_grp_, MPP_BUFFER_TYPE_NORMAL, NULL) != MPP_OK) {
return FailStatus("mpp_buffer_group_get_internal failed");
}
}
initialized_ = true;
return OkStatus();
}
Status Send(const std::shared_ptr<Frame>& frame) override {
if (!initialized_ || !frame) return FailStatus("encoder not ready");
MppBuffer buf = nullptr;
size_t frame_size = CalcFrameSize();
if (frame->DmaFd() >= 0 && frame->data_size > 0) {
MppBufferInfo info{};
info.type = MPP_BUFFER_TYPE_EXT_DMA;
info.size = frame->data_size;
info.fd = frame->DmaFd();
if (mpp_buffer_import(&buf, &info) != MPP_OK) {
buf = nullptr;
}
}
if (!buf) {
if (!frame->data) return FailStatus("frame has no data");
if (mpp_buffer_get(frm_grp_, &buf, frame_size) != MPP_OK) {
return FailStatus("mpp_buffer_get failed");
}
if (!CopyToBuffer(*frame, buf)) {
mpp_buffer_put(buf);
return FailStatus("copy to buffer failed");
}
}
MppFrame mpp_frame = nullptr;
mpp_frame_init(&mpp_frame);
mpp_frame_set_width(mpp_frame, width_);
mpp_frame_set_height(mpp_frame, height_);
mpp_frame_set_hor_stride(mpp_frame, hor_stride_);
mpp_frame_set_ver_stride(mpp_frame, ver_stride_);
mpp_frame_set_fmt(mpp_frame, mpp_fmt_);
mpp_frame_set_pts(mpp_frame, static_cast<int64_t>(frame->pts));
mpp_frame_set_buffer(mpp_frame, buf);
if (mpi_->encode_put_frame(ctx_, mpp_frame) != MPP_OK) {
mpp_frame_deinit(&mpp_frame);
mpp_buffer_put(buf);
return FailStatus("encode_put_frame failed");
}
mpp_frame_deinit(&mpp_frame);
while (true) {
MppPacket packet = nullptr;
MPP_RET ret = mpi_->encode_get_packet(ctx_, &packet);
if (ret == MPP_ERR_TIMEOUT) {
usleep(2000);
continue;
}
if (ret != MPP_OK || !packet) break;
EncodePacket out;
auto* pos = static_cast<uint8_t*>(mpp_packet_get_pos(packet));
size_t len = mpp_packet_get_length(packet);
out.data.assign(pos, pos + len);
RK_U32 flag = mpp_packet_get_flag(packet);
out.keyframe = (flag & 0x10) != 0; // MPP_PACKET_FLAG_INTRA
// Fallback: detect keyframe by checking for IDR (5) or SPS (7) NAL types
if (!out.keyframe && len >= 5) {
const uint8_t* d = out.data.data();
for (size_t i = 0; i + 4 < len && i < 1000; ++i) {
if (d[i]==0 && d[i+1]==0 && d[i+2]==0 && d[i+3]==1) {
uint8_t nal_type = d[i+4] & 0x1F;
if (nal_type == 5 || nal_type == 7) { // IDR or SPS
out.keyframe = true;
break;
}
}
}
}
int64_t mpp_pts = mpp_packet_get_pts(packet);
out.pts = mpp_pts > 0 ? static_cast<uint64_t>(mpp_pts) : frame->pts;
if (!header_sent_ && !header_.empty()) {
out.extra_data = header_;
header_sent_ = true;
}
packets_.push_back(std::move(out));
mpp_packet_deinit(&packet);
break;
}
mpp_buffer_put(buf);
return OkStatus();
}
Result<EncodePacket> Receive() override {
if (!packets_.empty()) {
auto pkt = std::move(packets_.front());
packets_.pop_front();
return pkt;
}
return MakeError<EncodePacket>("no_packet");
}
std::vector<uint8_t> ExtraData() const override {
return header_;
}
void Close() override {
packets_.clear();
header_.clear();
header_sent_ = false;
if (frm_grp_) {
mpp_buffer_group_put(frm_grp_);
frm_grp_ = nullptr;
}
if (cfg_) {
mpp_enc_cfg_deinit(cfg_);
cfg_ = nullptr;
}
if (ctx_) {
if (mpi_) mpi_->reset(ctx_);
mpp_destroy(ctx_);
ctx_ = nullptr;
mpi_ = nullptr;
}
initialized_ = false;
}
private:
size_t CalcFrameSize() const {
if (mpp_fmt_ == MPP_FMT_YUV420SP || mpp_fmt_ == MPP_FMT_YUV420P) {
return static_cast<size_t>(hor_stride_) * ver_stride_ * 3 / 2;
}
return 0;
}
bool CopyToBuffer(const Frame& frame, MppBuffer buf) const {
auto* dst = static_cast<uint8_t*>(mpp_buffer_get_ptr(buf));
size_t dst_size = mpp_buffer_get_size(buf);
size_t need = CalcFrameSize();
if (!dst || dst_size < need) return false;
std::memset(dst, 0, dst_size);
if (frame.plane_count < 2) return false;
const FramePlane& y = frame.planes[0];
const FramePlane& uv = frame.planes[1];
if (!y.data || !uv.data) return false;
for (int row = 0; row < height_; ++row) {
std::memcpy(dst + row * hor_stride_, y.data + row * y.stride,
std::min(hor_stride_, y.stride));
}
uint8_t* dst_uv = dst + hor_stride_ * ver_stride_;
int uv_rows = ver_stride_ / 2;
if (mpp_fmt_ == MPP_FMT_YUV420SP) {
for (int row = 0; row < uv_rows; ++row) {
std::memcpy(dst_uv + row * hor_stride_, uv.data + row * uv.stride,
std::min(hor_stride_, uv.stride));
}
} else if (frame.plane_count >= 3) {
const FramePlane& v = frame.planes[2];
uint8_t* dst_u = dst_uv;
uint8_t* dst_v = dst_uv + (hor_stride_ / 2) * uv_rows;
for (int row = 0; row < uv_rows; ++row) {
std::memcpy(dst_u + row * (hor_stride_ / 2), uv.data + row * uv.stride,
std::min(hor_stride_ / 2, uv.stride));
std::memcpy(dst_v + row * (hor_stride_ / 2), v.data + row * v.stride,
std::min(hor_stride_ / 2, v.stride));
}
}
return true;
}
MppCtx ctx_ = nullptr;
MppApi* mpi_ = nullptr;
MppEncCfg cfg_ = nullptr;
MppBufferGroup frm_grp_ = nullptr;
MppCodingType coding_ = MPP_VIDEO_CodingAVC;
MppFrameFormat mpp_fmt_ = MPP_FMT_YUV420SP;
int width_ = 0;
int height_ = 0;
int hor_stride_ = 0;
int ver_stride_ = 0;
int fps_ = 25;
int gop_ = 50;
int bitrate_bps_ = 4000000;
std::string codec_;
std::vector<uint8_t> header_;
bool initialized_ = false;
bool header_sent_ = false;
std::deque<EncodePacket> packets_;
};
} // namespace
#endif
#if defined(RK3588_ENABLE_FFMPEG)
namespace {
AVCodecID CodecIdFromConfig(const SimpleJson& config) {
int codec_id = config.ValueOr<int>("codec_id", 0);
if (codec_id > 0) return static_cast<AVCodecID>(codec_id);
std::string codec = ToLower(config.ValueOr<std::string>("codec", ""));
if (codec == "h264") return AV_CODEC_ID_H264;
if (codec == "h265" || codec == "hevc") return AV_CODEC_ID_HEVC;
return AV_CODEC_ID_NONE;
}
class FfmpegDecoder : public IDecoder {
public:
~FfmpegDecoder() override { Close(); }
Status Open(const SimpleJson& config) override {
Close();
AVCodecID codec_id = CodecIdFromConfig(config);
if (codec_id == AV_CODEC_ID_NONE) return FailStatus("unsupported codec");
const AVCodec* codec = avcodec_find_decoder(codec_id);
if (!codec) return FailStatus("decoder not found");
codec_ctx_ = avcodec_alloc_context3(codec);
if (!codec_ctx_) return FailStatus("avcodec_alloc_context3 failed");
codec_ctx_->time_base = AVRational{1, 1000000};
codec_ctx_->pkt_timebase = AVRational{1, 1000000};
auto extradata = ReadByteArray(config, "extradata");
if (!extradata.empty()) {
codec_ctx_->extradata_size = static_cast<int>(extradata.size());
codec_ctx_->extradata = static_cast<uint8_t*>(av_mallocz(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
std::memcpy(codec_ctx_->extradata, extradata.data(), extradata.size());
}
if (avcodec_open2(codec_ctx_, codec, nullptr) < 0) return FailStatus("avcodec_open2 failed");
frame_ = av_frame_alloc();
if (!frame_) return FailStatus("av_frame_alloc failed");
opened_ = true;
return OkStatus();
}
Status Send(const DecodePacket& packet) override {
if (!opened_ || !codec_ctx_) return FailStatus("decoder not opened");
if (!packet.data || packet.size == 0) return FailStatus("empty packet");
AVPacket* pkt = av_packet_alloc();
if (!pkt) return FailStatus("av_packet_alloc failed");
if (av_new_packet(pkt, static_cast<int>(packet.size)) < 0) {
av_packet_free(&pkt);
return FailStatus("av_new_packet failed");
}
std::memcpy(pkt->data, packet.data, packet.size);
pkt->pts = static_cast<int64_t>(packet.pts);
pkt->dts = pkt->pts;
const int ret = avcodec_send_packet(codec_ctx_, pkt);
av_packet_free(&pkt);
if (ret < 0) return FailStatus("avcodec_send_packet failed");
DrainFrames();
return OkStatus();
}
Result<std::shared_ptr<Frame>> Receive() override {
if (!frames_.empty()) {
auto frame = frames_.front();
frames_.pop_front();
return frame;
}
if (!opened_) return MakeError<std::shared_ptr<Frame>>("decoder not opened");
DrainFrames();
if (!frames_.empty()) {
auto frame = frames_.front();
frames_.pop_front();
return frame;
}
return MakeError<std::shared_ptr<Frame>>("no_frame");
}
void Close() override {
frames_.clear();
if (codec_ctx_) {
avcodec_free_context(&codec_ctx_);
}
if (frame_) {
av_frame_free(&frame_);
}
opened_ = false;
}
private:
void DrainFrames() {
while (avcodec_receive_frame(codec_ctx_, frame_) == 0) {
auto out = MakeFrameFromAv(*frame_);
if (out) frames_.push_back(out);
av_frame_unref(frame_);
}
}
std::shared_ptr<Frame> MakeFrameFromAv(const AVFrame& f) {
PixelFormat fmt = PixelFormat::UNKNOWN;
int plane_count = 0;
int width = f.width;
int height = f.height;
if (f.format == AV_PIX_FMT_NV12) {
fmt = PixelFormat::NV12;
plane_count = 2;
} else if (f.format == AV_PIX_FMT_YUV420P || f.format == AV_PIX_FMT_YUVJ420P) {
fmt = PixelFormat::YUV420;
plane_count = 3;
}
if (plane_count == 0) return nullptr;
int buf_size = av_image_get_buffer_size(static_cast<AVPixelFormat>(f.format), width, height, 1);
if (buf_size <= 0) return nullptr;
auto buffer = std::make_shared<std::vector<uint8_t>>(static_cast<size_t>(buf_size));
if (av_image_copy_to_buffer(buffer->data(), buffer->size(), f.data, f.linesize,
static_cast<AVPixelFormat>(f.format), width, height, 1) < 0) {
return nullptr;
}
auto frame = std::make_shared<Frame>();
frame->width = width;
frame->height = height;
frame->format = fmt;
frame->stride = (fmt == PixelFormat::RGB || fmt == PixelFormat::BGR) ? width * 3 : width;
frame->data = buffer->data();
frame->data_size = buffer->size();
frame->plane_count = plane_count;
frame->SetOwner(buffer);
frame->pts = f.pts == AV_NOPTS_VALUE ? 0 : static_cast<uint64_t>(f.pts);
if (fmt == PixelFormat::NV12 && plane_count == 2) {
int y_size = width * height;
frame->planes[0] = {buffer->data(), width, y_size, 0};
frame->planes[1] = {buffer->data() + y_size, width, y_size / 2, y_size};
} else if (fmt == PixelFormat::YUV420 && plane_count == 3) {
int y_size = width * height;
int uv_stride = width / 2;
int uv_h = height / 2;
int u_size = uv_stride * uv_h;
frame->planes[0] = {buffer->data(), width, y_size, 0};
frame->planes[1] = {buffer->data() + y_size, uv_stride, u_size, y_size};
frame->planes[2] = {buffer->data() + y_size + u_size, uv_stride, u_size, y_size + u_size};
}
frame->SyncBufferFromFrame();
return frame;
}
AVCodecContext* codec_ctx_ = nullptr;
AVFrame* frame_ = nullptr;
std::deque<std::shared_ptr<Frame>> frames_;
bool opened_ = false;
};
class FfmpegEncoder : public IEncoder {
public:
Status Open(const SimpleJson& /*config*/) override { return FailStatus("ffmpeg encoder not implemented"); }
Status Send(const std::shared_ptr<Frame>& /*frame*/) override { return FailStatus("ffmpeg encoder not implemented"); }
Result<EncodePacket> Receive() override { return MakeError<EncodePacket>("ffmpeg encoder not implemented"); }
std::vector<uint8_t> ExtraData() const override { return {}; }
void Close() override {}
};
} // namespace
#endif
Status Rk3588Decoder::Open(const SimpleJson& config) {
Close();
std::string backend = ReadBackend(config);
if (backend == "mpp") {
#if defined(RK3588_ENABLE_MPP)
impl_ = std::make_unique<MppDecoder>();
#else
return FailStatus("mpp not enabled");
#endif
} else if (backend == "ffmpeg") {
#if defined(RK3588_ENABLE_FFMPEG)
impl_ = std::make_unique<FfmpegDecoder>();
#else
return FailStatus("ffmpeg not enabled");
#endif
} else {
return FailStatus("decoder backend not selected");
}
return impl_->Open(config);
}
Status Rk3588Decoder::Send(const DecodePacket& packet) {
if (!impl_) return FailStatus("decoder not initialized");
return impl_->Send(packet);
}
Result<std::shared_ptr<Frame>> Rk3588Decoder::Receive() {
if (!impl_) return MakeError<std::shared_ptr<Frame>>("decoder not initialized");
return impl_->Receive();
}
void Rk3588Decoder::Close() {
if (impl_) impl_->Close();
impl_.reset();
}
Status Rk3588Encoder::Open(const SimpleJson& config) {
Close();
std::string backend = ReadBackend(config);
if (backend == "mpp") {
#if defined(RK3588_ENABLE_MPP)
impl_ = std::make_unique<MppEncoder>();
#else
return FailStatus("mpp not enabled");
#endif
} else if (backend == "ffmpeg") {
#if defined(RK3588_ENABLE_FFMPEG)
impl_ = std::make_unique<FfmpegEncoder>();
#else
return FailStatus("ffmpeg not enabled");
#endif
} else {
return FailStatus("encoder backend not selected");
}
return impl_->Open(config);
}
Status Rk3588Encoder::Send(const std::shared_ptr<Frame>& frame) {
if (!impl_) return FailStatus("encoder not initialized");
return impl_->Send(frame);
}
Result<EncodePacket> Rk3588Encoder::Receive() {
if (!impl_) return MakeError<EncodePacket>("encoder not initialized");
return impl_->Receive();
}
std::vector<uint8_t> Rk3588Encoder::ExtraData() const {
if (!impl_) return {};
return impl_->ExtraData();
}
void Rk3588Encoder::Close() {
if (impl_) impl_->Close();
impl_.reset();
}
} // namespace rk3588