230 lines
6.1 KiB
C++
230 lines
6.1 KiB
C++
#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);
|
||
}
|