#include "hw/rk3588_defaults.h" #include #include #include #include #include #include #include "utils/logger.h" #if defined(RK3588_ENABLE_MPP) extern "C" { #include #include #include #include } #include #endif #if defined(RK3588_ENABLE_FFMPEG) extern "C" { #include #include } #endif namespace rk3588 { namespace { std::string ToLower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return s; } std::vector ReadByteArray(const SimpleJson& config, const std::string& key) { std::vector 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(iv)); } return out; } std::string ReadBackend(const SimpleJson& config) { std::string backend = ToLower(config.ValueOr("backend", "")); if (!backend.empty()) return backend; if (config.ValueOr("use_mpp", false)) return "mpp"; if (config.ValueOr("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("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(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(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> Receive() override { if (!frames_.empty()) { auto frame = frames_.front(); frames_.pop_front(); return frame; } if (!opened_ || !ctx_ || !mpi_) return MakeError>("decoder not opened"); DrainFrames(); if (!frames_.empty()) { auto frame = frames_.front(); frames_.pop_front(); return frame; } return MakeError>("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(MPP_BUFFER_TYPE_DMA_HEAP | MPP_BUFFER_FLAGS_CACHABLE | MPP_BUFFER_FLAGS_DMA32)) && !try_group(static_cast(MPP_BUFFER_TYPE_DMA_HEAP | MPP_BUFFER_FLAGS_DMA32)) && !try_group(static_cast(MPP_BUFFER_TYPE_DMA_HEAP | MPP_BUFFER_FLAGS_CACHABLE)) && !try_group(static_cast(MPP_BUFFER_TYPE_DMA_HEAP)) && !try_group(static_cast(MPP_BUFFER_TYPE_DRM | MPP_BUFFER_FLAGS_CACHABLE)) && !try_group(static_cast(MPP_BUFFER_TYPE_DRM)) && !try_group(static_cast(MPP_BUFFER_TYPE_ION | MPP_BUFFER_FLAGS_CACHABLE)) && !try_group(static_cast(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 MakeFrameFromMpp(MppFrame frm) { MppBuffer buf = mpp_frame_get_buffer(frm); if (!buf) return nullptr; mpp_buffer_inc_ref(buf); auto owner = std::shared_ptr(buf, [](void* p) { mpp_buffer_put(reinterpret_cast(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->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(mpp_buffer_get_ptr(buf)); frame->data_size = mpp_buffer_get_size(buf); frame->SetOwner(owner); frame->pts = static_cast(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(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> frames_; bool opened_ = false; }; class MppEncoder : public IEncoder { public: ~MppEncoder() override { Close(); } Status Open(const SimpleJson& config) override { Close(); width_ = config.ValueOr("width", 0); height_ = config.ValueOr("height", 0); fps_ = config.ValueOr("fps", 25); gop_ = config.ValueOr("gop", std::max(1, fps_) * 2); bitrate_bps_ = config.ValueOr("bitrate_kbps", 4000) * 1000; codec_ = ToLower(config.ValueOr("codec", "h264")); std::string fmt = ToLower(config.ValueOr("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(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) 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(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(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(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 Receive() override { if (!packets_.empty()) { auto pkt = std::move(packets_.front()); packets_.pop_front(); return pkt; } return MakeError("no_packet"); } std::vector 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(hor_stride_) * ver_stride_ * 3 / 2; } return 0; } bool CopyToBuffer(const Frame& frame, MppBuffer buf) const { auto* dst = static_cast(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 header_; bool initialized_ = false; bool header_sent_ = false; std::deque packets_; }; } // namespace #endif #if defined(RK3588_ENABLE_FFMPEG) namespace { AVCodecID CodecIdFromConfig(const SimpleJson& config) { int codec_id = config.ValueOr("codec_id", 0); if (codec_id > 0) return static_cast(codec_id); std::string codec = ToLower(config.ValueOr("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(extradata.size()); codec_ctx_->extradata = static_cast(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(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(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> Receive() override { if (!frames_.empty()) { auto frame = frames_.front(); frames_.pop_front(); return frame; } if (!opened_) return MakeError>("decoder not opened"); DrainFrames(); if (!frames_.empty()) { auto frame = frames_.front(); frames_.pop_front(); return frame; } return MakeError>("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 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(f.format), width, height, 1); if (buf_size <= 0) return nullptr; auto buffer = std::make_shared>(static_cast(buf_size)); if (av_image_copy_to_buffer(buffer->data(), buffer->size(), f.data, f.linesize, static_cast(f.format), width, height, 1) < 0) { return nullptr; } auto frame = std::make_shared(); 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(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> 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*/) override { return FailStatus("ffmpeg encoder not implemented"); } Result Receive() override { return MakeError("ffmpeg encoder not implemented"); } std::vector 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(); #else return FailStatus("mpp not enabled"); #endif } else if (backend == "ffmpeg") { #if defined(RK3588_ENABLE_FFMPEG) impl_ = std::make_unique(); #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> Rk3588Decoder::Receive() { if (!impl_) return MakeError>("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(); #else return FailStatus("mpp not enabled"); #endif } else if (backend == "ffmpeg") { #if defined(RK3588_ENABLE_FFMPEG) impl_ = std::make_unique(); #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) { if (!impl_) return FailStatus("encoder not initialized"); return impl_->Send(frame); } Result Rk3588Encoder::Receive() { if (!impl_) return MakeError("encoder not initialized"); return impl_->Receive(); } std::vector Rk3588Encoder::ExtraData() const { if (!impl_) return {}; return impl_->ExtraData(); } void Rk3588Encoder::Close() { if (impl_) impl_->Close(); impl_.reset(); } } // namespace rk3588