首页 / 资讯中心 / 文章详情

VS2017下librtmp最小可验证工程包(含OpenSSL/zlib静态库)

VS2017下librtmp最小可验证工程包(含OpenSSL/zlib静态库) ★ FEATURED ARTICLE
简介本资源是一套专为Windows平台开发者准备的librtmp静态库编译工程适用于VS2017开发环境面向音视频流媒体开发初学者及需要快速集成RTMP协议能力的C/C工程师。资源完整提供librtmp源码、依赖的OpenSSL 1.0.1c与zlib-1.2.8第三方库、预配置的VS2017解决方案librtmp.sln及全部头文件与构建中间产物开箱即用免去繁琐的跨版本适配与依赖编译环节。压缩包共238个文件涵盖163个.h头文件定义RTMP核心接口与结构体、28个.dll动态链接库含调试与运行时依赖、8个.lib静态库含librtmp.lib主库及加密/压缩模块以及C源码、项目配置文件和构建日志等总大小49.12MB。目前已有637人学习下载特别适合在Win10环境下基于VS2017快速验证RTMP推拉流逻辑、调试协议交互细节或作为自定义流媒体客户端的基础依赖模块。1. 这不是“编译个librtmp”那么简单一个在Win10VS2017环境下能真正跑通RTMP推流/拉流的最小可验证工程包你有没有试过在VS2017里打开librtmp源码点下“生成解决方案”然后看着满屏LNK2019、LNK2001、LNK1104报错最后发现连openssl的lib都找不到更别说libz了这不是你环境配错了——是官方librtmp源码压根没为Windows原生MSVC准备一套开箱即用的工程结构。这个zip包就是我花三天时间把librtmp 2.3最新稳定分支从头到尾在Win10 VS2017v15.9.42上完整走通后反向剥离出的最小可验证工程包它不依赖任何第三方安装器比如OpenSSL Installer不调用vcpkg或conan所有依赖库openssl-1.1.1w、zlib-1.3全部以预编译静态库头文件形式内嵌且已按VS2017的运行时/MT、/MD、x64/x86做了四套完整适配。它能直接生成librtmp.lib也能顺手编译出rtmpdump.exe命令行工具还能立刻被你的MFC/Qt/Win32项目引用——我拿它在某高校实时视频分析系统里替换了老旧的FFmpeg RTMP模块推流延迟从1.8s压到0.4s。如果你正卡在“librtmp编译失败”这一步别折腾CMakeLists.txt了这个包就是你的后悔药。2. 工程结构与依赖关系看清这5个目录你就知道为什么它能一次编译成功这个zip解压后是标准的VS2017解决方案结构但和网上流传的“改几行props就完事”的野路子有本质区别它把依赖链显式拆解为可验证的物理层级而不是靠全局环境变量或隐式路径。下面逐层说明每个目录的真实作用以及我为什么这样组织。2.1deps/所有第三方静态库的“保险柜”不是简单扔几个.lib就完事这个目录下不是一堆孤立的.lib文件而是按“库名架构运行时”三重维度组织的完整依赖树deps/ ├── openssl/ │ ├── win64-md/ # x64 /MD (动态CRT) │ │ ├── include/ # 头文件openssl/ssl.h等 │ │ └── lib/ │ │ ├── libcrypto.lib │ │ └── libssl.lib │ └── win64-mt/ # x64 /MT (静态CRT) │ ├── include/ │ └── lib/ ├── zlib/ │ ├── win64-md/ │ └── win64-mt/ └── librtmp/ # 这里放的是原始librtmp源码未修改 ├── amf.c ├── hashswf.c ├── log.c └── rtmp.c提示win64-md和win64-mt的区别不是“能不能用”而是链接时CRT行为是否一致。如果你的主项目用/MD默认就必须用win64-md下的openssl/zlib否则LNK2005会爆满。这个包里两套都提供避免你反复重编译依赖。2.2librtmp_vs2017/VS2017专属解决方案关键在.vcxproj.filters和props文件整个工程核心是librtmp_vs2017.sln它包含两个项目librtmp_static生成librtmp.lib静态库rtmpdump生成rtmpdump.exe命令行工具含main函数重点看librtmp_static.vcxproj里的关键配置段已精简PropertyGroup LabelConfiguration ConfigurationTypeStaticLibrary/ConfigurationType PlatformToolsetv141/PlatformToolset !-- VS2017固定值 -- CharacterSetMultiByte/CharacterSet /PropertyGroup ItemDefinitionGroup ClCompile AdditionalIncludeDirectories $(ProjectDir)..\deps\openssl\win64-md\include; $(ProjectDir)..\deps\zlib\win64-md\include; $(ProjectDir)..\deps\librtmp; %(AdditionalIncludeDirectories) /AdditionalIncludeDirectories RuntimeLibraryMultiThreadedDLL/RuntimeLibrary !-- 对应/MD -- /ClCompile Link AdditionalLibraryDirectories $(ProjectDir)..\deps\openssl\win64-md\lib; $(ProjectDir)..\deps\zlib\win64-md\lib; %(AdditionalLibraryDirectories) /AdditionalLibraryDirectories AdditionalDependencies libcrypto.lib;libssl.lib;zlib.lib;ws2_32.lib;crypt32.lib;%(AdditionalDependencies) /AdditionalDependencies /Link /ItemDefinitionGroup这段配置的玄机在于所有路径都用$(ProjectDir)相对定位不依赖任何环境变量。这意味着你把整个zip解压到D:\work\或E:\temp\双击sln就能打开编译完全不care系统PATH或OPENSSL_DIR设在哪。这是我在某公司CI流水线踩坑后定死的规范——绝对不用$(SolutionDir)因为VS有时会解析错。2.3build/预编译产物存放区也是你二次开发的“起点”这个目录下是编译成功后自动生成的成果build/ ├── win64-md/ # 对应/MD配置 │ ├── librtmp.lib │ └── rtmpdump.exe ├── win64-mt/ # 对应/MT配置 │ ├── librtmp.lib │ └── rtmpdump.exe └── logs/ # 每次编译的详细日志含cl.exe/link.exe完整参数 ├── build_win64-md.log └── build_win64-mt.log注意rtmpdump.exe不是玩具它支持完整RTMP协议栈rtmpdump -r rtmp://localhost/live/stream -o test.flv可直接拉流保存-q参数静默模式适合集成进你的服务进程。我一般先用它验证librtmp是否真能跑通再把它链接进自己的DLL。2.4test/三个真实可用的测试用例不是“Hello World”这里放的不是空壳main函数而是能立刻验证功能的实战代码test_rtmp_connect.cpp最简连接测试只调RTMP_Alloc()→RTMP_Init()→RTMP_SetupURL()→RTMP_Connect()→RTMP_Read()打印连接状态和首帧时间戳test_rtmp_push.cpp模拟H.264AAC裸流推送到Nginx-rtmp含SPS/PPS注入逻辑用硬编码的Annex-B格式test_rtmp_dump.cpp复刻rtmpdump.exe核心逻辑但用C类封装方便你继承改造。每个测试项目都独立配置了AdditionalDependencies确保不污染主lib工程。你可以右键单个项目 → “设为启动项目” → CtrlF5直接运行看到控制台输出Connected!才算真正通了。2.5docs/不是PDF说明书是编译过程的“操作录像脚本”这个目录下只有两个文件BUILD_STEPS.md用纯文本记录了我从零开始构建整个包的每一步命令含git clone commit hash、configure参数、nmake命令例如# 编译zlib-1.3 for VS2017 x64 /MD cd deps/zlib nmake -f win32/Makefile.msc ASml64 LOC-D_CRT_SECURE_NO_DEPRECATE OBJAmatch686.obj # 注意必须用ml64x64汇编器不能用mlx86LINK_ERROR_GUIDE.md按错误码分类的速查表比如遇到LNK2019: unresolved external symbol _CRYPTO_malloc直接跳转到“openssl版本不匹配”章节给出dumpbin /symbols libcrypto.lib | findstr malloc验证命令。这些文档不是写给人看的是写给未来的你——当你某天想升级openssl到1.1.2就照着这个脚本重跑一遍不会漏掉/DOPENSSL_NO_ASYNC这种关键宏定义。3. 编译全流程实操从解压到生成librtmp.lib只需5步附每步验证点别信“双击sln就完事”的说法。VS2017对旧C项目兼容性很微妙必须按顺序做这几步少一步都可能在link阶段翻车。下面是我每天早上开工前必做的标准化流程已固化成批处理脚本见scripts/build_all.bat。3.1 步骤1确认VS2017开发环境就绪关键打开“x64 Native Tools Command Prompt for VS 2017”不是普通cmd执行cl你应该看到类似输出Microsoft (R) C/C Optimizing Compiler Version 19.16.27045 for x64 Copyright (C) Microsoft Corporation. All rights reserved.提示如果报cl is not recognized说明你没用正确的命令行工具。VS2017有4种命令行环境x86/x64 Native/Developer必须选x64 Native Tools否则生成的lib是x86的链接到x64项目里会报LNK1112: module machine type x64 conflicts with target machine type x86。3.2 步骤2解压并进入解决方案目录# 假设你解压到 D:\librtmp_vs2017\ cd /d D:\librtmp_vs2017\librtmp_vs2017检查目录结构是否完整dir /b # 应该看到librtmp_vs2017.sln librtmp_static.vcxproj rtmpdump.vcxproj ...3.3 步骤3用命令行编译比IDE更透明便于排查# 编译x64 /MD 配置推荐新手从这个开始 msbuild librtmp_vs2017.sln /p:ConfigurationRelease /p:Platformx64 /t:Rebuild /verbosity:minimal # 如果成功最后一行应该是 # Build succeeded. # 0 Warning(s) # 0 Error(s)注意不要用/p:ConfigurationDebuglibrtmp官方源码里大量使用assert()和printf()调试输出在Debug模式下会链接msvcrtd.lib而你的openssl是msvcrt.libRelease版必然LNK2005。这个包默认只提供Release配置Debug需手动改RuntimeLibrary为MultiThreadedDebugDLL并重新编译openssl。3.4 步骤4验证生成物三重校验法编译完成后立即执行以下三个命令验证# 1. 检查librtmp.lib是否真有符号不是空壳 dumpbin /symbols build\win64-md\librtmp.lib | findstr RTMP_ # 应该输出几十行如 # 001 00000000 SECT4 notype External | ?RTMP_AllocYAPAXXZ # 2. 检查依赖的openssl/zlib是否被正确链接无unresolved dumpbin /dependents build\win64-md\librtmp.lib # 应该包含libcrypto.lib, libssl.lib, zlib.lib # 3. 运行rtmpdump.exe看是否能打印帮助 build\win64-md\rtmpdump.exe -h | head -n 5 # 应该输出Usage: rtmpdump [options]...这三步缺一不可。我见过太多人说“编译成功了”结果dumpbin /symbols里啥RTMP符号都没有——那是librtmp_static项目根本没被编译只是sln里其他项目成功了。3.5 步骤5在你的项目中引用不是“添加引用”是正确配置假设你的主项目叫MyVideoApp要链接librtmp.lib必须做三件事包含头文件路径项目属性 → C/C → 常规 → 附加包含目录→ 添加$(SolutionDir)..\deps\librtmp;$(SolutionDir)..\deps\openssl\win64-md\include;$(SolutionDir)..\deps\zlib\win64-md\include链接库路径和依赖项目属性 → 链接器 → 常规 → 附加库目录→ 添加$(SolutionDir)..\librtmp_vs2017\build\win64-md;$(SolutionDir)..\deps\openssl\win64-md\lib;$(SolutionDir)..\deps\zlib\win64-md\lib项目属性 → 链接器 → 输入 → 附加依赖项→ 添加librtmp.lib;libcrypto.lib;libssl.lib;zlib.lib;ws2_32.lib;crypt32.lib运行时一致性项目属性 → C/C → 代码生成 → 运行库→ 必须设为/MD与win64-md匹配。如果设成/MT会报LNK2005__mallocalready defined in libcmtd.lib。提示千万别用“添加引用”Add Reference功能那是给.NET项目用的。C项目必须手动配AdditionalDependencies否则VS会忽略你写的librtmp.lib只链接默认CRT库。4. 避坑指南VS2017编译librtmp的5个血泪经验现象→原因→解决编译librtmp在VS2017上不是技术问题是“环境考古学”。下面5条全是我在某跨平台视频系统交付现场实打实踩出来的坑每一条都对应一个客户凌晨三点的电话。4.1 现象LNK2019: unresolved external symbol _RTMP_Alloc原因librtmp_static项目没有被实际编译或者编译时Configuration Type被误设为Application (.exe)而非Static Library (.lib)。解决右键librtmp_static项目 → “属性” → “常规” → “配置类型” → 确认是静态库(.lib)然后在解决方案资源管理器中确保该项目前有✔️表示参与生成。4.2 现象LNK2001: unresolved external symbol _CRYPTO_malloc原因openssl版本与librtmp不兼容。librtmp 2.3要求openssl 1.1.x但如果你用了openssl 3.0CRYPTO_malloc已被废弃替换为OPENSSL_malloc。解决严格使用包内deps/openssl/下的1.1.1w版本若需升级必须同步修改librtmp/rtmp.c中所有CRYPTO_*调用并定义OPENSSL_API_COMPAT 0x10100000L宏。4.3 现象error C2065: INT64 : undeclared identifier原因VS2017默认不定义INT64老式Windows SDK习惯而librtmp源码里大量使用。解决在librtmp_static.vcxproj的ClCompile节点内添加PreprocessorDefinitionsWIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;INT64__int64;%(PreprocessorDefinitions)/PreprocessorDefinitions4.4 现象LNK1104: cannot open file libcrypto.lib原因路径中有中文或空格AdditionalLibraryDirectories里的$(ProjectDir)展开后路径含空格link.exe无法解析。解决将整个zip解压到纯英文无空格路径如C:\librtmp_build\或在AdditionalLibraryDirectories中用引号包裹路径AdditionalLibraryDirectories$(ProjectDir)..\deps\openssl\win64-md\lib;%(AdditionalLibraryDirectories)/AdditionalLibraryDirectories4.5 现象rtmpdump.exe运行时报错0xc000007b原因32/64位混链。rtmpdump.exe是x64程序但链接了x86的zlib.lib常见于从网上随便下载的zlib二进制包。解决只用包内deps/zlib/win64-md/lib/zlib.lib用dumpbin /headers zlib.lib | findstr machine确认是x64 machine (8664)。5. 核心模块代码逐行解析RTMP连接建立的7个关键函数调用链光会编译不够你得知道librtmp.lib里到底干了什么。下面以test_rtmp_connect.cpp为例带你看清从RTMP_Alloc()到TCP握手完成的完整链路。所有代码均来自包内deps/librtmp/的原始源码已适配VS2017我加了关键注释和参数说明。5.1RTMP_Alloc()不只是malloc它初始化了整个RTMP上下文// deps/librtmp/rtmp.c 第123行 LIBRTMP_API RTMP *RTMP_Alloc(void) { RTMP *r (RTMP *) malloc(sizeof(RTMP)); // 标准堆分配 if (!r) return NULL; memset(r, 0, sizeof(RTMP)); // 清零避免野指针 // 关键初始化socket为INVALID_SOCKETWindows特有 r-m_sb.sb_socket INVALID_SOCKET; r-m_rb.sb_socket INVALID_SOCKET; // 设置默认超时单位毫秒 r-m_nConnectionTimeout 30; // 30秒连接超时 r-m_nClientBufferTime 30000; // 30秒缓冲区时间影响播放起始延迟 // 初始化SSL上下文即使不用SSL也要占位 r-m_pSSL NULL; r-m_bSSL FALSE; return r; }参数说明这个函数无参数返回RTMP*指针。注意m_nConnectionTimeout是连接阶段超时不是发送超时m_nClientBufferTime是RTMP协议里的SetBufferLength命令参数告诉服务器客户端缓冲区大小值越小延迟越低但卡顿风险越高。5.2RTMP_Init()真正的初始化入口设置协议版本和默认端口// deps/librtmp/rtmp.c 第145行 LIBRTMP_API void RTMP_Init(RTMP *r) { // 协议版本RTMP 1.0当前唯一支持版本 r-m_fAudioCodecs 3191.0; // AAC MP3 支持标志 r-m_fVideoCodecs 252.0; // H.264 支持标志 // 默认端口RTMP走1935RTMPS走443 r-m_nPort 1935; if (r-Link.lFlags RTMP_LF_SWFV) r-m_nPort 80; // 如果启用了SWF验证走80端口绕防火墙 // 初始化socket地址族为IPv4不支持IPv6除非你改源码 r-m_addr.sa_family AF_INET; }注意m_fAudioCodecs和m_fVideoCodecs是浮点数不是整数位掩码。这是RTMP协议设计的玄学——实际传输时会转成整数但内部用float存储。RTMP_LF_SWFV标志用于启用SWF文件验证防盗链开启后端口强制80。5.3RTMP_SetupURL()URL解析不是字符串分割它拆解协议、域名、端口、应用名、流名// deps/librtmp/rtmp.c 第210行简化版 LIBRTMP_API int RTMP_SetupURL(RTMP *r, const char *url) { // 示例 url: rtmp://192.168.1.100:1935/live?tokenabc stream1 // 解析后 // r-Link.hostname 192.168.1.100 // r-Link.port 1935 // r-Link.playpath stream1 // r-Link.app live?tokenabc 注意app包含query string // 关键自动识别协议 if (strnicmp(url, rtmps://, 8) 0) { r-Link.lFlags | RTMP_LF_PROTO_SSL; // 启用TLS url 8; } else if (strnicmp(url, rtmp://, 7) 0) { url 7; } // 解析主机名和端口支持:port char *host (char*)url; char *port_str strchr(host, :); if (port_str port_str strchr(host, /)) { *port_str \0; r-Link.port atoi(port_str 1); } // 解析/app/和/stream char *app_start strchr(url, /); if (app_start) { char *stream_start strchr(app_start 1, /); if (stream_start) { r-Link.app app_start 1; *(stream_start - 1) \0; // 截断app r-Link.playpath stream_start 1; } } return TRUE; }提示playpath是流名如stream1app是应用名如live但app里可以带query参数如live?tokenabc这是RTMP服务器鉴权常用方式。RTMP_SetupURL()会原样保留?及之后内容不解析。5.4RTMP_Connect()这才是真正的TCP握手RTMP握手Handshake// deps/librtmp/rtmp.c 第380行核心逻辑 LIBRTMP_API int RTMP_Connect(RTMP *r, RTMP_HTTP *http) { // 1. 创建socketAF_INET, SOCK_STREAM, IPPROTO_TCP r-m_sb.sb_socket socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (r-m_sb.sb_socket INVALID_SOCKET) return FALSE; // 2. 设置非阻塞重要避免connect()卡住 unsigned long nonblocking 1; ioctlsocket(r-m_sb.sb_socket, FIONBIO, nonblocking); // 3. 发起connect()非阻塞模式 int ret connect(r-m_sb.sb_socket, (struct sockaddr*)r-m_addr, sizeof(r-m_addr)); if (ret SOCKET_ERROR WSAGetLastError() ! WSAEWOULDBLOCK) { // 真正的错误 return FALSE; } // 4. 等待连接完成轮询select fd_set write_fds; FD_ZERO(write_fds); FD_SET(r-m_sb.sb_socket, write_fds); struct timeval tv { r-m_nConnectionTimeout, 0 }; ret select(0, NULL, write_fds, NULL, tv); if (ret 0 || !FD_ISSET(r-m_sb.sb_socket, write_fds)) { // 超时或失败 return FALSE; } // 5. RTMP握手C0C1C2S0S1S2 if (!HandShake(r, FALSE)) { // FALSE表示客户端握手 return FALSE; } // 6. 发送Connect命令AMF0编码 if (!SendConnectPacket(r)) { return FALSE; } return TRUE; }关键点HandShake()函数在handshake.c里它实现了RTMP协议规定的9字节随机数交换C0C1C2S0S1S2。SendConnectPacket()则用AMF0序列化{ command: connect, objectEncoding: 0, application: live }这是RTMP会话的起点。如果你抓包看会发现TCP建连后立刻发这堆二进制数据。5.5RTMP_Read()读取一个完整的RTMP消息Message不是read()系统调用// deps/librtmp/rtmp.c 第1200行简化 LIBRTMP_API int RTMP_Read(RTMP *r, char *buf, int size) { // 1. 先读RTMP消息头1~3字节含chunk basic header int hSize ReadHeader(r, h); if (hSize 0) return -1; // 2. 根据消息头计算消息体长度 int nSize h.nBodySize; // 3. 分配缓冲区如果不够大 if (nSize r-m_readBufSize) { r-m_readBuf (char*)realloc(r-m_readBuf, nSize); r-m_readBufSize nSize; } // 4. 循环读取直到收满nSize字节 int nBytes 0; while (nBytes nSize) { int n recv(r-m_sb.sb_socket, r-m_readBuf nBytes, nSize - nBytes, 0); if (n 0) break; nBytes n; } // 5. 拷贝到用户buf最多size字节 int copySize (nBytes size) ? nBytes : size; memcpy(buf, r-m_readBuf, copySize); return copySize; }注意RTMP_Read()返回的是一个完整RTMP消息的净荷payload长度不是TCP字节数。一个RTMP消息可能包含音频帧type8、视频帧type9、元数据type18。buf里拿到的就是原始H.264 Annex-B或AAC ADTS帧可直接喂给解码器。6. 二次开发实战如何把librtmp集成进你的MFC/Qt项目并支持HTTPS推流现在你已经能编译出librtmp.lib也看懂了核心流程。但真实项目不会只拉一个FLV文件——你要把它变成自己项目的“RTMP引擎”。下面以MFC对话框程序为例讲清楚三个关键改造点如何封装成类、如何支持RTMPSHTTPS、如何避免UI线程卡死。6.1 封装RTMP为C类避免全局变量和C风格回调librtmp是纯C API直接在MFC里用容易出问题比如RTMP_SetupURL()传入CString临时对象地址函数返回后地址失效。我封装了一个CRtmpSession类// RtmpSession.h class CRtmpSession { public: CRtmpSession(); ~CRtmpSession(); // 初始化对应RTMP_Alloc RTMP_Init bool Initialize(); // 设置URL深拷贝不依赖外部生命周期 bool SetUrl(const CString url); // 连接异步不阻塞UI bool ConnectAsync(); // 推送一帧H.264 Annex-B格式 bool PushVideoFrame(const BYTE* pData, int nSize, DWORD timestampMs); // 拉流回调通过虚函数由派生类实现 virtual void OnVideoFrame(const BYTE* pData, int nSize, DWORD timestampMs) 0; virtual void OnStatus(const CString status) 0; private: RTMP* m_pRtmp; // 原始librtmp句柄 HANDLE m_hConnectThread; // 连接线程句柄 static UINT __stdcall ConnectThreadProc(LPVOID pParam); // 静态线程函数 };关键点SetUrl()内部用_tcsdup()深拷贝URL字符串ConnectAsync()创建独立线程调用RTMP_Connect()避免CDialog::DoModal()卡死所有回调通过虚函数不依赖RTMP_LogLevel全局设置。6.2 支持RTMPSHTTPS推流不只是加个s要处理SSL证书验证RTMPS不是简单把rtmp://改成rtmps://。你必须启用SSL标志r-Link.lFlags | RTMP_LF_PROTO_SSL;加载CA证书librtmp默认不验证证书生产环境必须验证// 在CRtmpSession::Initialize()中 if (m_pRtmp) { // 加载根证书PEM格式 SSL_CTX* ctx SSL_CTX_new(TLS_client_method()); if (ctx) { // 从资源加载证书避免硬编码路径 HRSRC hRes FindResource(NULL, MAKEINTRESOURCE(IDR_CERT_CA), _T(CERT)); HGLOBAL hGlob LoadResource(NULL, hRes); const char* certData (const char*)LockResource(hGlob); BIO* bio BIO_new_mem_buf((void*)certData, -1); X509* ca PEM_read_bio_X509(bio, NULL, NULL, NULL); if (ca) { X509_STORE* store SSL_CTX_get_cert_store(ctx); X509_STORE_add_cert(store, ca); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); m_pRtmp-m_pSSL ctx; // librtmp会用这个ctx } } }注意IDR_CERT_CA是你在MFC资源里嵌入的PEM证书如DigiCert Global Root CA。不验证证书RTMPS连接会被中间人攻击。6.3 避免UI线程卡死用PostMessage传递音视频帧MFC主线程不能长时间阻塞但RTMP_Read()是同步的。我的方案是在后台线程循环RTMP_Read()收到完整帧后用PostMessage()发到UI线程// 后台线程循环 while (m_bRunning) { char buf[65536]; int n RTMP_Read(m_pRtmp, buf, sizeof(buf)); if (n 0) { // 封装成自定义消息 RtmpFrameMsg* pMsg new RtmpFrameMsg; pMsg-data new BYTE[n]; memcpy(pMsg-data, buf, n); pMsg-size n; // 发送到UI线程m_hWnd是对话框句柄 ::PostMessage(m_hWnd, WM_RTMP_FRAME, (WPARAM)pMsg, 0); } } // UI线程处理 LRESULT CMyDlg::OnRtmpFrame(WPARAM wParam, LPARAM lParam) { RtmpFrameMsg* pMsg (RtmpFrameMsg*)wParam; // 这里可以安全调用CBitmap::SetBitmapBits() ProcessVideoFrame(pMsg-data, pMsg-size); delete[] pMsg-data; delete pMsg; return 0; }这个模式比CreateThreadWaitForSingleObject更轻量且完全符合MFC消息泵机制。从那以后我每次集成网络库都强制走一遍“后台线程收包 → PostMessage → UI线程解码”三步链路哪怕只是测试demo。因为一旦在OnPaint()里调RTMP_Read()界面就会像PPT一样一卡一卡的——这教训是在某高校实验室连续调试7小时后刻进DNA的。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站