241 lines
6.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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>
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);
}
}
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);
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__