241 lines
6.6 KiB
C++
Raw Normal View History

2026-06-26 17:55:15 +08:00
#include "MjpegEncoder.h"
#include <cstring>
#if !defined(__linux__)
// =================== 非 Linux 占位:始终失败,调用方回退原始数据 ===================
MppMjpegEncoder::~MppMjpegEncoder() = default;
bool MppMjpegEncoder::EncodeMono8(const uint8_t*, size_t, int, int, std::vector<uint8_t>&)
{
return false;
}
void MppMjpegEncoder::SetQuality(int q)
{
if (q < 1) q = 1; if (q > 99) q = 99;
m_quality = q;
}
#else
#include "VrLog.h"
#include "rk_type.h"
#include "rk_mpi.h"
#include "mpp_frame.h"
#include "mpp_packet.h"
#include "mpp_buffer.h"
#include "rk_venc_cfg.h"
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
2026-06-26 17:55:15 +08:00
namespace {
inline int alignTo(int v, int a) { return (v + a - 1) & ~(a - 1); }
bool encodeMono8JpegSoftware(const uint8_t* mono,
size_t monoSize,
int width,
int height,
int quality,
std::vector<uint8_t>& jpeg)
{
if (!mono || width <= 0 || height <= 0)
return false;
if (monoSize < static_cast<size_t>(width) * static_cast<size_t>(height))
return false;
cv::Mat gray(height, width, CV_8UC1, const_cast<uint8_t*>(mono));
std::vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(quality);
return cv::imencode(".jpg", gray, jpeg, params);
}
2026-06-26 17:55:15 +08:00
}
MppMjpegEncoder::~MppMjpegEncoder()
{
std::lock_guard<std::mutex> lk(m_mutex);
release();
}
void MppMjpegEncoder::SetQuality(int q)
{
if (q < 1) q = 1;
if (q > 99) q = 99;
std::lock_guard<std::mutex> lk(m_mutex);
if (q != m_quality)
{
m_quality = q;
release(); // 强制下一帧用新质量重建编码器
}
}
void MppMjpegEncoder::release()
{
if (m_ctx)
{
mpp_destroy(static_cast<MppCtx>(m_ctx));
m_ctx = nullptr;
m_mpi = nullptr;
}
if (m_grp)
{
mpp_buffer_group_put(static_cast<MppBufferGroup>(m_grp));
m_grp = nullptr;
}
m_w = m_h = m_hor = m_ver = 0;
}
bool MppMjpegEncoder::ensureInited(int width, int height)
{
if (m_ctx && m_w == width && m_h == height)
return true;
release();
m_w = width;
m_h = height;
m_hor = alignTo(width, 16);
m_ver = alignTo(height, 16);
MppCtx ctx = nullptr;
MppApi* mpi = nullptr;
if (mpp_create(&ctx, &mpi) != MPP_OK || !ctx || !mpi)
{
LOG_ERROR("[MJPEG] mpp_create fail\n");
release();
return false;
}
m_ctx = ctx;
m_mpi = mpi;
if (mpp_init(ctx, MPP_CTX_ENC, MPP_VIDEO_CodingMJPEG) != MPP_OK)
{
LOG_ERROR("[MJPEG] mpp_init fail\n");
release();
return false;
}
MppEncCfg cfg = nullptr;
if (mpp_enc_cfg_init(&cfg) != MPP_OK || !cfg)
{
LOG_ERROR("[MJPEG] enc_cfg_init fail\n");
release();
return false;
}
// 输入帧格式:以 NV12 承载 Mono8Y=灰度UV=128输出标准 JPEG
mpp_enc_cfg_set_s32(cfg, "prep:width", m_w);
mpp_enc_cfg_set_s32(cfg, "prep:height", m_h);
mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", m_hor);
mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", m_ver);
mpp_enc_cfg_set_s32(cfg, "prep:format", MPP_FMT_YUV420SP);
mpp_enc_cfg_set_s32(cfg, "codec:type", MPP_VIDEO_CodingMJPEG);
// MJPEG 采用固定质量q_factor 越大越清晰、体积越大1~99
mpp_enc_cfg_set_s32(cfg, "rc:mode", MPP_ENC_RC_MODE_FIXQP);
mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", m_quality);
mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", 1);
mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", 99);
const int ret = mpi->control(ctx, MPP_ENC_SET_CFG, cfg);
mpp_enc_cfg_deinit(cfg);
if (ret != MPP_OK)
{
LOG_ERROR("[MJPEG] MPP_ENC_SET_CFG fail %d\n", ret);
release();
return false;
}
MppBufferGroup grp = nullptr;
if (mpp_buffer_group_get_internal(&grp, MPP_BUFFER_TYPE_DRM) != MPP_OK)
{
LOG_ERROR("[MJPEG] buffer group get fail\n");
release();
return false;
}
m_grp = grp;
LOG_INFO("[MJPEG] encoder ready %dx%d stride=%dx%d q=%d\n",
m_w, m_h, m_hor, m_ver, m_quality);
return true;
}
bool MppMjpegEncoder::EncodeMono8(const uint8_t* mono, size_t monoSize,
int width, int height,
std::vector<uint8_t>& jpeg)
{
if (!mono || width <= 0 || height <= 0)
return false;
if (monoSize < static_cast<size_t>(width) * static_cast<size_t>(height))
return false;
std::lock_guard<std::mutex> lk(m_mutex);
if (encodeMono8JpegSoftware(mono, monoSize, width, height, m_quality, jpeg))
return true;
LOG_WARN("[MJPEG] software imencode failed %dx%d q=%d, try MPP\n",
width, height, m_quality);
2026-06-26 17:55:15 +08:00
if (!ensureInited(width, height))
return false;
MppApi* mpi = static_cast<MppApi*>(m_mpi);
MppCtx ctx = static_cast<MppCtx>(m_ctx);
MppBufferGroup grp = static_cast<MppBufferGroup>(m_grp);
const int frameSize = m_hor * m_ver * 3 / 2;
MppBuffer buf = nullptr;
if (mpp_buffer_get(grp, &buf, frameSize) != MPP_OK || !buf)
return false;
uint8_t* dst = static_cast<uint8_t*>(mpp_buffer_get_ptr(buf));
if (!dst)
{
mpp_buffer_put(buf);
return false;
}
// Mono8 → NV12Y 平面逐行拷贝(源 stride=width → 目标 hor_strideUV 平面填 128灰度
uint8_t* dstY = dst;
uint8_t* dstUV = dst + static_cast<size_t>(m_hor) * m_ver;
for (int y = 0; y < m_h; ++y)
std::memcpy(dstY + static_cast<size_t>(y) * m_hor,
mono + static_cast<size_t>(y) * m_w, m_w);
std::memset(dstUV, 128, static_cast<size_t>(m_hor) * (m_ver / 2));
MppFrame frame = nullptr;
mpp_frame_init(&frame);
mpp_frame_set_width(frame, m_w);
mpp_frame_set_height(frame, m_h);
mpp_frame_set_hor_stride(frame, m_hor);
mpp_frame_set_ver_stride(frame, m_ver);
mpp_frame_set_fmt(frame, MPP_FMT_YUV420SP);
mpp_frame_set_buffer(frame, buf);
MppPacket pkt = nullptr;
const int ret = mpi->encode(ctx, frame, &pkt);
mpp_frame_deinit(&frame);
mpp_buffer_put(buf);
if (ret != MPP_OK || !pkt)
return false;
void* p = mpp_packet_get_data(pkt);
size_t len = mpp_packet_get_length(pkt);
bool ok = false;
if (p && len > 0)
{
const uint8_t* bytes = static_cast<const uint8_t*>(p);
jpeg.assign(bytes, bytes + len);
ok = true;
}
mpp_packet_deinit(&pkt);
return ok;
}
#endif // __linux__