GrabBag/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp

230 lines
6.1 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 "CVrFFMediaPusher.h"
#include "VrError.h"
#include "VrLog.h"
#include <chrono>
#if !defined(FFMEDIA_PLATFORM_DUMMY)
#include <linux/videodev2.h>
#endif
bool IVrFFMediaPusher::CreateObject(IVrFFMediaPusher** ppPusher)
{
if (!ppPusher) return false;
try { *ppPusher = new CVrFFMediaPusher(); return true; }
catch (...) { return false; }
}
CVrFFMediaPusher::CVrFFMediaPusher() = default;
CVrFFMediaPusher::~CVrFFMediaPusher()
{
try { UnInit(); } catch (...) {}
}
#if defined(FFMEDIA_PLATFORM_DUMMY)
// =================== Windows / 非 RK3588 占位实现 ===================
// 实际编码/推流仅在 RK3588 + ffmedia_release 下可用
// Windows 仅保证编译通过和接口存在
int CVrFFMediaPusher::Init(const VrFFPushConfig& config)
{
std::lock_guard<std::mutex> lk(m_mutex);
m_config = config;
m_bInited = true;
LOG_INFO("FFMediaPusher: Init (dummy on this platform)\n");
return SUCCESS;
}
int CVrFFMediaPusher::Start()
{
if (!m_bInited.load()) return ERR_CODE(DEV_NO_OPEN);
m_bStarted = true;
LOG_INFO("FFMediaPusher: Start (dummy)\n");
return SUCCESS;
}
int CVrFFMediaPusher::PushFrame(const void* /*data*/, size_t /*size*/, int64_t /*ptsUs*/)
{
return m_bStarted.load() ? SUCCESS : ERR_CODE(DEV_NO_OPEN);
}
int CVrFFMediaPusher::Stop()
{
m_bStarted = false;
return SUCCESS;
}
int CVrFFMediaPusher::UnInit()
{
Stop();
m_bInited = false;
return SUCCESS;
}
#else
// =================== RK3588 真正实现 ===================
static uint32_t ToV4l2Pix(EFFPushPixelFormat fmt)
{
switch (fmt)
{
case EFFPushPixelFormat::NV12: return V4L2_PIX_FMT_NV12;
case EFFPushPixelFormat::YUV420P: return V4L2_PIX_FMT_YUV420;
case EFFPushPixelFormat::BGR24: return V4L2_PIX_FMT_BGR24;
case EFFPushPixelFormat::RGB24: return V4L2_PIX_FMT_RGB24;
case EFFPushPixelFormat::BGRA32: return V4L2_PIX_FMT_BGR32;
case EFFPushPixelFormat::RGBA32: return V4L2_PIX_FMT_RGBA32;
}
return V4L2_PIX_FMT_NV12;
}
static EncodeType ToFFEncode(EFFEncodeType t)
{
switch (t)
{
case EFFEncodeType::H264: return ENCODE_TYPE_H264;
case EFFEncodeType::H265: return ENCODE_TYPE_H265;
case EFFEncodeType::MJPEG: return ENCODE_TYPE_MJPEG;
}
return ENCODE_TYPE_H264;
}
int CVrFFMediaPusher::Init(const VrFFPushConfig& config)
{
std::lock_guard<std::mutex> lk(m_mutex);
if (m_bInited.load()) return SUCCESS;
m_config = config;
const uint32_t inPix = ToV4l2Pix(config.pixelFormat);
const bool needRga = (config.pixelFormat != EFFPushPixelFormat::NV12);
// 1) 内存输入源
ImagePara inPara(config.width, config.height, config.width, config.height, inPix);
m_memReader = std::make_shared<ModuleMemReader>(inPara);
if (m_memReader->init() != 0)
{
LOG_ERROR("FFMediaPusher: memReader init fail\n");
return ERR_CODE(DEV_OPEN_ERR);
}
// 2) 可选RGA 转 NV12
std::shared_ptr<ModuleMedia> encInput = m_memReader;
if (needRga)
{
ImagePara nv12Para(config.width, config.height, config.width, config.height, V4L2_PIX_FMT_NV12);
m_rga = std::make_shared<ModuleRga>(inPara, nv12Para, RGA_ROTATE_NONE);
m_rga->setProductor(m_memReader);
if (m_rga->init() != 0)
{
LOG_ERROR("FFMediaPusher: rga init fail\n");
return ERR_CODE(DEV_OPEN_ERR);
}
encInput = m_rga;
}
// 3) MPP 硬件编码
m_encoder = std::make_shared<ModuleMppEnc>(
ToFFEncode(config.encodeType),
config.fps,
config.gop,
config.bitrateKbps,
(config.rcMode == EFFRcMode::CBR) ? ENCODE_RC_MODE_CBR : ENCODE_RC_MODE_VBR,
ENCODE_QUALITY_BEST,
ENCODE_PROFILE_HIGH
);
m_encoder->setProductor(encInput);
m_encoder->setDuration(1000000 / std::max<uint32_t>(1, config.fps));
if (m_encoder->init() != 0)
{
LOG_ERROR("FFMediaPusher: encoder init fail\n");
return ERR_CODE(DEV_OPEN_ERR);
}
// 4) RTSP 推流
m_rtspServer = std::make_shared<ModuleRtspServer>(config.rtspPath.c_str(), config.rtspPort);
if (!config.authUser.empty())
{
m_rtspServer->setAuthInfo(config.authRealm, config.authUser, config.authPass);
}
m_rtspServer->setProductor(m_encoder);
if (m_rtspServer->init() != 0)
{
LOG_ERROR("FFMediaPusher: rtsp server init fail\n");
return ERR_CODE(DEV_OPEN_ERR);
}
m_bInited = true;
return SUCCESS;
}
int CVrFFMediaPusher::Start()
{
if (!m_bInited.load()) return ERR_CODE(DEV_NO_OPEN);
if (m_bStarted.load()) return SUCCESS;
if (!m_memReader) return ERR_CODE(DEV_CTRL_ERR);
m_memReader->start();
m_bStarted = true;
return SUCCESS;
}
int CVrFFMediaPusher::PushFrame(const void* data, size_t size, int64_t ptsUs)
{
if (!m_bStarted.load()) return ERR_CODE(DEV_NO_OPEN);
if (!m_memReader) return ERR_CODE(DEV_CTRL_ERR);
if (ptsUs == 0)
{
auto now = std::chrono::steady_clock::now().time_since_epoch();
ptsUs = std::chrono::duration_cast<std::chrono::microseconds>(now).count();
}
m_lastPtsUs = ptsUs;
int ret = m_memReader->setInputBuffer(const_cast<void*>(data), size, -1, ptsUs);
if (ret != 0)
{
LOG_DEBUG("FFMediaPusher: setInputBuffer fail %d\n", ret);
return ret;
}
m_memReader->waitProcess(2000);
return SUCCESS;
}
int CVrFFMediaPusher::Stop()
{
if (!m_bStarted.load()) return SUCCESS;
if (m_memReader)
{
m_memReader->setProcessStatus(ModuleMemReader::PROCESS_STATUS_EXIT);
m_memReader->stop();
}
m_bStarted = false;
return SUCCESS;
}
int CVrFFMediaPusher::UnInit()
{
Stop();
std::lock_guard<std::mutex> lk(m_mutex);
m_rtspServer.reset();
m_encoder.reset();
m_rga.reset();
m_memReader.reset();
m_bInited = false;
return SUCCESS;
}
#endif // FFMEDIA_PLATFORM_DUMMY
std::string CVrFFMediaPusher::GetRtspUrl(const std::string& localIp) const
{
char buf[256];
snprintf(buf, sizeof(buf), "rtsp://%s:%u%s",
localIp.c_str(), m_config.rtspPort, m_config.rtspPath.c_str());
return std::string(buf);
}