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