diff --git a/Module/FFMediaStream/FFMediaStream.pro b/Module/FFMediaStream/FFMediaStream.pro new file mode 100644 index 00000000..22c43c08 --- /dev/null +++ b/Module/FFMediaStream/FFMediaStream.pro @@ -0,0 +1,72 @@ +# FFMediaStream.pro +# 基于 ffmedia_release (Rockchip MPP/RGA/RTSP) 的二次封装 +# 提供 RTSP 推流 (IVrFFMediaPusher) 和 RTSP 拉流 (IVrFFMediaPuller) 接口 + +CONFIG -= qt +TEMPLATE = lib +DEFINES += FFMEDIASTREAM_LIBRARY + +CONFIG += c++17 + +# 全平台使用动态库 +CONFIG += shared + +win32-msvc { + QMAKE_CXXFLAGS += /utf-8 + QMAKE_CFLAGS += /utf-8 +} + +# 头文件路径 +INCLUDEPATH += ./Inc +INCLUDEPATH += ./_Inc +INCLUDEPATH += $$PWD/../../Utils/VrCommon/Inc +INCLUDEPATH += $$PWD/../../Utils/VrUtils/Inc + +# ffmedia 头文件 +INCLUDEPATH += $$PWD/../../SDK/ffmedia_release/include +INCLUDEPATH += $$PWD/../../SDK/ffmedia_release/include/base +INCLUDEPATH += $$PWD/../../SDK/ffmedia_release/include/module + +SOURCES += \ + Src/CVrFFMediaPusher.cpp \ + Src/CVrFFMediaPuller.cpp + +HEADERS += \ + Inc/IVrFFMediaPusher.h \ + Inc/IVrFFMediaPuller.h \ + _Inc/CVrFFMediaPusher.h \ + _Inc/CVrFFMediaPuller.h + +# 链接 +win32:CONFIG(debug, debug|release) { + LIBS += -L../../Utils/VrUtils/debug -lVrUtils +}else:win32:CONFIG(release, debug|release){ + LIBS += -L../../Utils/VrUtils/release -lVrUtils +}else:unix:!macx { + LIBS += -L../../Utils/VrUtils -lVrUtils + + # ffmedia 主库(仅 ARM/Linux 真正可用,Windows 下纯占位) + FFMEDIA_LIB_DIR = $$PWD/../../SDK/ffmedia_release/lib + RKNN_LIB_DIR = $$PWD/../../SDK/ffmedia_release/inference_examples/lib/RK3588 + LIBS += -L$${FFMEDIA_LIB_DIR} -lff_media + QMAKE_LFLAGS += -Wl,-rpath-link,$${FFMEDIA_LIB_DIR} + QMAKE_LFLAGS += -Wl,-rpath-link,$${RKNN_LIB_DIR} + + # 系统依赖(RK3588):rga / mpp / drm。 + # 这些库由 firefly 系统镜像自带,无需提供。 + LIBS += -lrga -lrockchip_mpp -ldrm -lpthread -ldl +} + +# Windows 下提供编译占位(实现内部用 #ifdef __linux__ 守卫具体调用) +win32 { + DEFINES += FFMEDIA_PLATFORM_DUMMY + LIBS += -lws2_32 +} + +unix:!macx { + message("build FFMediaStream for unix (ffmedia_release)") + QMAKE_CXXFLAGS += -fPIC + target.path = /usr/lib +} + +!isEmpty(target.path): INSTALLS += target diff --git a/Module/FFMediaStream/Inc/IVrFFMediaPuller.h b/Module/FFMediaStream/Inc/IVrFFMediaPuller.h new file mode 100644 index 00000000..d9c1099b --- /dev/null +++ b/Module/FFMediaStream/Inc/IVrFFMediaPuller.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include +#include + +// DLL 导出宏(FFMEDIASTREAM_LIBRARY 在 .pro 中已定义) +#ifdef _WIN32 +# ifdef FFMEDIASTREAM_LIBRARY +# define VR_FFMEDIA_API __declspec(dllexport) +# else +# define VR_FFMEDIA_API __declspec(dllimport) +# endif +#else +# define VR_FFMEDIA_API +#endif + +/** + * @brief 拉流帧输出格式 + */ +enum class EFFPullPixelFormat +{ + NV12 = 0, // 原生硬解输出,零拷贝最快 + RGBA32, // 适合 QImage::Format_RGBA8888 直接显示 + BGR24, // 适合 OpenCV +}; + +/** + * @brief 拉流参数 + */ +struct VrFFPullConfig +{ + std::string rtspUrl; // rtsp://user:pass@host:port/path + bool useTcp{true}; // true=TCP 模式,false=UDP(低延迟但容易丢包) + bool enableVideo{true}; + bool enableAudio{false}; + + // 解码后输出:是否做 RGA 转换到 RGBA32 / BGR24,以及目标分辨率 + EFFPullPixelFormat outputFormat{EFFPullPixelFormat::RGBA32}; + unsigned int outputWidth{0}; // 0 = 跟随输入 + unsigned int outputHeight{0}; +}; + +/** + * @brief 拉流回调帧 + */ +struct VrFFPulledFrame +{ + const void* data{nullptr}; + size_t size{0}; + unsigned int width{0}; + unsigned int height{0}; + unsigned int stride{0}; // 一行字节数(RGBA=width*4,BGR=width*3,NV12=hstride) + EFFPullPixelFormat format{EFFPullPixelFormat::RGBA32}; + int64_t ptsUs{0}; // PTS +}; + +using FFPulledFrameCallback = std::function; + +/** + * @brief IVrFFMediaPuller + * + * 基于 ffmedia_release 的 RTSP 拉流(RTSP -> MPP 解码 -> RGA 转换 -> 内存回调)。 + * + * 典型用法: + * IVrFFMediaPuller* puller = nullptr; + * IVrFFMediaPuller::CreateObject(&puller); + * puller->Init(cfg); + * puller->SetFrameCallback([](const VrFFPulledFrame& f){ ... }); + * puller->Start(); + * ... + * puller->Stop(); + * delete puller; + */ +class VR_FFMEDIA_API IVrFFMediaPuller +{ +public: + virtual ~IVrFFMediaPuller() = default; + + static bool CreateObject(IVrFFMediaPuller** ppPuller); + + virtual int Init(const VrFFPullConfig& config) = 0; + + virtual void SetFrameCallback(FFPulledFrameCallback callback) = 0; + + virtual int Start() = 0; + virtual int Stop() = 0; + virtual int UnInit() = 0; + + /** + * @brief 当前是否处于拉流状态 + */ + virtual bool IsStreaming() const = 0; +}; diff --git a/Module/FFMediaStream/Inc/IVrFFMediaPusher.h b/Module/FFMediaStream/Inc/IVrFFMediaPusher.h new file mode 100644 index 00000000..d907b8e2 --- /dev/null +++ b/Module/FFMediaStream/Inc/IVrFFMediaPusher.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include +#include +#include + +// DLL 导出宏(FFMEDIASTREAM_LIBRARY 在 .pro 中已定义) +#ifdef _WIN32 +# ifdef FFMEDIASTREAM_LIBRARY +# define VR_FFMEDIA_API __declspec(dllexport) +# else +# define VR_FFMEDIA_API __declspec(dllimport) +# endif +#else +# define VR_FFMEDIA_API +#endif + +/** + * @brief 推流帧像素格式 + */ +enum class EFFPushPixelFormat +{ + NV12 = 0, // 4:2:0 半平面 (Y + UV) + YUV420P, // 4:2:0 平面 + BGR24, // OpenCV 默认 + RGB24, + BGRA32, + RGBA32, +}; + +/** + * @brief 编码类型 + */ +enum class EFFEncodeType +{ + H264 = 0, + H265, + MJPEG, +}; + +/** + * @brief 码率控制模式 + */ +enum class EFFRcMode +{ + CBR = 0, + VBR, +}; + +/** + * @brief 推流参数 + */ +struct VrFFPushConfig +{ + // 帧参数 + unsigned int width{1920}; + unsigned int height{1080}; + EFFPushPixelFormat pixelFormat{EFFPushPixelFormat::NV12}; + + // 编码参数 + EFFEncodeType encodeType{EFFEncodeType::H264}; + unsigned int fps{30}; + unsigned int gop{60}; + unsigned int bitrateKbps{4096}; // 码率 (kbps) + EFFRcMode rcMode{EFFRcMode::CBR}; + + // RTSP 服务参数 + std::string rtspPath{"/live/stream"}; // URL 路径 + unsigned short rtspPort{8554}; + std::string authRealm; + std::string authUser; + std::string authPass; +}; + +/** + * @brief IVrFFMediaPusher + * + * 基于 ffmedia_release 的 RTSP 推流(内存输入 -> RGA 格式转换 -> MPP 编码 -> RTSP)。 + * + * 典型用法: + * IVrFFMediaPusher* pusher = nullptr; + * IVrFFMediaPusher::CreateObject(&pusher); + * pusher->Init(cfg); + * pusher->Start(); + * pusher->PushFrame(data, size); + * ... + * pusher->Stop(); + * delete pusher; + */ +class VR_FFMEDIA_API IVrFFMediaPusher +{ +public: + virtual ~IVrFFMediaPusher() = default; + + // 工厂 + static bool CreateObject(IVrFFMediaPusher** ppPusher); + + /** + * @brief 初始化推流链路 + * @return 0 成功 + */ + virtual int Init(const VrFFPushConfig& config) = 0; + + /** + * @brief 启动推流(建立 RTSP 服务,等待客户端拉流) + */ + virtual int Start() = 0; + + /** + * @brief 喂入一帧待编码的图像 + * @param data 帧数据指针 + * @param size 帧数据大小(字节) + * @param ptsUs PTS,单位微秒;传 0 由内部生成 + * @return 0 成功 + */ + virtual int PushFrame(const void* data, size_t size, int64_t ptsUs = 0) = 0; + + /** + * @brief 停止推流 + */ + virtual int Stop() = 0; + + /** + * @brief 反初始化 + */ + virtual int UnInit() = 0; + + /** + * @brief 获取当前推流 URL(含 IP/端口/路径) + */ + virtual std::string GetRtspUrl(const std::string& localIp) const = 0; +}; diff --git a/Module/FFMediaStream/Src/CVrFFMediaPuller.cpp b/Module/FFMediaStream/Src/CVrFFMediaPuller.cpp new file mode 100644 index 00000000..bc940f4e --- /dev/null +++ b/Module/FFMediaStream/Src/CVrFFMediaPuller.cpp @@ -0,0 +1,203 @@ +#include "CVrFFMediaPuller.h" +#include "VrError.h" +#include "VrLog.h" + +#if !defined(FFMEDIA_PLATFORM_DUMMY) +#include +#endif + +bool IVrFFMediaPuller::CreateObject(IVrFFMediaPuller** ppPuller) +{ + if (!ppPuller) return false; + try { *ppPuller = new CVrFFMediaPuller(); return true; } + catch (...) { return false; } +} + +CVrFFMediaPuller::CVrFFMediaPuller() = default; + +CVrFFMediaPuller::~CVrFFMediaPuller() +{ + try { UnInit(); } catch (...) {} +} + +void CVrFFMediaPuller::SetFrameCallback(FFPulledFrameCallback callback) +{ + std::lock_guard lk(m_mutex); + m_callback = std::move(callback); +} + +bool CVrFFMediaPuller::IsStreaming() const +{ + return m_bStarted.load(); +} + +#if defined(FFMEDIA_PLATFORM_DUMMY) + +// =================== Windows / 非 RK3588 占位实现 =================== + +int CVrFFMediaPuller::Init(const VrFFPullConfig& config) +{ + std::lock_guard lk(m_mutex); + m_config = config; + m_bInited = true; + LOG_INFO("FFMediaPuller: Init (dummy)\n"); + return SUCCESS; +} + +int CVrFFMediaPuller::Start() +{ + if (!m_bInited.load()) return ERR_CODE(DEV_NO_OPEN); + m_bStarted = true; + LOG_INFO("FFMediaPuller: Start (dummy)\n"); + return SUCCESS; +} + +int CVrFFMediaPuller::Stop() +{ + m_bStarted = false; + return SUCCESS; +} + +int CVrFFMediaPuller::UnInit() +{ + Stop(); + m_bInited = false; + return SUCCESS; +} + +#else + +// =================== RK3588 真正实现 =================== + +static uint32_t ToV4l2OutPix(EFFPullPixelFormat fmt) +{ + switch (fmt) + { + case EFFPullPixelFormat::NV12: return V4L2_PIX_FMT_NV12; + case EFFPullPixelFormat::RGBA32: return V4L2_PIX_FMT_RGBA32; + case EFFPullPixelFormat::BGR24: return V4L2_PIX_FMT_BGR24; + } + return V4L2_PIX_FMT_RGBA32; +} + +static unsigned int PixelBpp(EFFPullPixelFormat fmt) +{ + switch (fmt) + { + case EFFPullPixelFormat::NV12: return 1; // 平均 1.5 byte/px,但 stride 用 width + case EFFPullPixelFormat::RGBA32: return 4; + case EFFPullPixelFormat::BGR24: return 3; + } + return 4; +} + +int CVrFFMediaPuller::Init(const VrFFPullConfig& config) +{ + std::lock_guard lk(m_mutex); + if (m_bInited.load()) return SUCCESS; + + m_config = config; + + // 1) RTSP 拉流 + m_rtspClient = std::make_shared( + config.rtspUrl.c_str(), + config.useTcp ? RTSP_STREAM_TYPE_TCP : RTSP_STREAM_TYPE_UDP, + config.enableVideo, + config.enableAudio + ); + if (m_rtspClient->init() != 0) + { + LOG_ERROR("FFMediaPuller: rtspClient init fail (%s)\n", config.rtspUrl.c_str()); + return ERR_CODE(DEV_OPEN_ERR); + } + + // 2) MPP 解码 + ImagePara inPara = m_rtspClient->getOutputImagePara(); + m_decoder = std::make_shared(inPara); + m_decoder->setProductor(m_rtspClient); + if (m_decoder->init() != 0) + { + LOG_ERROR("FFMediaPuller: mppdec init fail\n"); + return ERR_CODE(DEV_OPEN_ERR); + } + + // 3) RGA 转目标格式 + ImagePara decOut = m_decoder->getOutputImagePara(); + const uint32_t outFmt = ToV4l2OutPix(config.outputFormat); + const unsigned int outW = config.outputWidth ? config.outputWidth : decOut.width; + const unsigned int outH = config.outputHeight ? config.outputHeight : decOut.height; + ImagePara outPara(outW, outH, outW, outH, outFmt); + + m_rga = std::make_shared(decOut, outPara, RGA_ROTATE_NONE); + m_rga->setBufferType(VideoBuffer::MALLOC_BUFFER); + m_rga->setProductor(m_decoder); + + // 注册输出回调 + m_rga->setOutputDataCallback( + this, + [](void* ctx, std::shared_ptr buffer) { + if (!ctx || !buffer) return; + CVrFFMediaPuller* self = static_cast(ctx); + FFPulledFrameCallback cb; + { + std::lock_guard lk(self->m_mutex); + cb = self->m_callback; + } + if (!cb) return; + + VideoBuffer* vbuf = static_cast(buffer.get()); + ImagePara para = vbuf->getImagePara(); + VrFFPulledFrame frame; + frame.data = vbuf->getData(); + frame.size = vbuf->getActiveSize(); + frame.width = para.width; + frame.height = para.height; + frame.stride = para.hstride * PixelBpp(self->m_config.outputFormat); + frame.format = self->m_config.outputFormat; + frame.ptsUs = vbuf->getPUstimestamp(); + + try { cb(frame); } catch (...) {} + } + ); + + if (m_rga->init() != 0) + { + LOG_ERROR("FFMediaPuller: rga init fail\n"); + return ERR_CODE(DEV_OPEN_ERR); + } + + m_bInited = true; + return SUCCESS; +} + +int CVrFFMediaPuller::Start() +{ + if (!m_bInited.load()) return ERR_CODE(DEV_NO_OPEN); + if (m_bStarted.load()) return SUCCESS; + if (!m_rtspClient) return ERR_CODE(DEV_CTRL_ERR); + + m_rtspClient->start(); + m_bStarted = true; + return SUCCESS; +} + +int CVrFFMediaPuller::Stop() +{ + if (!m_bStarted.load()) return SUCCESS; + if (m_rtspClient) m_rtspClient->stop(); + m_bStarted = false; + return SUCCESS; +} + +int CVrFFMediaPuller::UnInit() +{ + Stop(); + std::lock_guard lk(m_mutex); + m_rga.reset(); + m_decoder.reset(); + m_rtspClient.reset(); + m_bInited = false; + return SUCCESS; +} + +#endif // FFMEDIA_PLATFORM_DUMMY diff --git a/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp b/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp new file mode 100644 index 00000000..a17fde1a --- /dev/null +++ b/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp @@ -0,0 +1,229 @@ +#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); +} diff --git a/Module/FFMediaStream/_Inc/CVrFFMediaPuller.h b/Module/FFMediaStream/_Inc/CVrFFMediaPuller.h new file mode 100644 index 00000000..5c70a796 --- /dev/null +++ b/Module/FFMediaStream/_Inc/CVrFFMediaPuller.h @@ -0,0 +1,39 @@ +#pragma once + +#include "IVrFFMediaPuller.h" +#include +#include +#include + +#if !defined(FFMEDIA_PLATFORM_DUMMY) +#include "module/vi/module_rtspClient.hpp" +#include "module/vp/module_mppdec.hpp" +#include "module/vp/module_rga.hpp" +#endif + +class CVrFFMediaPuller : public IVrFFMediaPuller +{ +public: + CVrFFMediaPuller(); + ~CVrFFMediaPuller() override; + + int Init(const VrFFPullConfig& config) override; + void SetFrameCallback(FFPulledFrameCallback callback) override; + int Start() override; + int Stop() override; + int UnInit() override; + bool IsStreaming() const override; + +private: + VrFFPullConfig m_config; + std::atomic m_bStarted{false}; + std::atomic m_bInited{false}; + FFPulledFrameCallback m_callback; + std::mutex m_mutex; + +#if !defined(FFMEDIA_PLATFORM_DUMMY) + std::shared_ptr m_rtspClient; + std::shared_ptr m_decoder; + std::shared_ptr m_rga; +#endif +}; diff --git a/Module/FFMediaStream/_Inc/CVrFFMediaPusher.h b/Module/FFMediaStream/_Inc/CVrFFMediaPusher.h new file mode 100644 index 00000000..6c4e7181 --- /dev/null +++ b/Module/FFMediaStream/_Inc/CVrFFMediaPusher.h @@ -0,0 +1,42 @@ +#pragma once + +#include "IVrFFMediaPusher.h" +#include +#include +#include + +// ffmedia 头(仅在 Linux/RK3588 平台真正可用) +#if !defined(FFMEDIA_PLATFORM_DUMMY) +#include "module/vi/module_memReader.hpp" +#include "module/vp/module_mppenc.hpp" +#include "module/vo/module_rtspServer.hpp" +#include "module/vp/module_rga.hpp" +#endif + +class CVrFFMediaPusher : public IVrFFMediaPusher +{ +public: + CVrFFMediaPusher(); + ~CVrFFMediaPusher() override; + + int Init(const VrFFPushConfig& config) override; + int Start() override; + int PushFrame(const void* data, size_t size, int64_t ptsUs = 0) override; + int Stop() override; + int UnInit() override; + std::string GetRtspUrl(const std::string& localIp) const override; + +private: + VrFFPushConfig m_config; + std::atomic m_bStarted{false}; + std::atomic m_bInited{false}; + std::mutex m_mutex; + int64_t m_lastPtsUs{0}; + +#if !defined(FFMEDIA_PLATFORM_DUMMY) + std::shared_ptr m_memReader; + std::shared_ptr m_rga; // 输入格式转 NV12(若非 NV12) + std::shared_ptr m_encoder; + std::shared_ptr m_rtspServer; +#endif +}; diff --git a/SDK/ffmedia_release b/SDK/ffmedia_release new file mode 160000 index 00000000..6fe74d3f --- /dev/null +++ b/SDK/ffmedia_release @@ -0,0 +1 @@ +Subproject commit 6fe74d3fd783faa8725cba9be5f8487e9d92e6d2