#include "CVrFFMediaPusher.h" #include "VrError.h" #include "VrLog.h" #include #if !defined(FFMEDIA_PLATFORM_DUMMY) #include #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 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 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(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 encInput = m_memReader; if (needRga) { ImagePara nv12Para(config.width, config.height, config.width, config.height, V4L2_PIX_FMT_NV12); m_rga = std::make_shared(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( 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(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(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(now).count(); } m_lastPtsUs = ptsUs; int ret = m_memReader->setInputBuffer(const_cast(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 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); }