GrabBag/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp

230 lines
6.1 KiB
C++
Raw Normal View History

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