241 lines
6.6 KiB
C++
241 lines
6.6 KiB
C++
#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 承载 Mono8(Y=灰度,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 → NV12:Y 平面逐行拷贝(源 stride=width → 目标 hor_stride),UV 平面填 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__
|