如何使用IMFTransform将摄像头NV12数据转换为分片1-3秒的m3u8 ts文件?
使用IMFTransform将NV12转成HLS(TS分片+M3U8)的实现方案
核心流程梳理
- 编码器初始化与配置:选择支持NV12输入的H.264/H.265 IMFTransform编码器(优先硬件编码器),配置输入输出媒体类型,确保输入匹配摄像头NV12的分辨率、帧率,输出设置合适的比特率、关键帧间隔(GOP建议设为分片时长的整数倍,比如3秒)。
- NV12帧编码处理:将读取到的NV12裸数据封装为
IMFMediaBuffer,通过IMFTransform::ProcessInput送入编码器,再调用ProcessOutput获取编码后的NALU数据。 - TS打包与分片控制:把NALU按MPEG-TS规则打包(生成PAT/PMT表,每个TS包188字节),通过帧时间戳累计时长,达到1-3秒阈值时,关闭当前TS文件、更新M3U8索引,再创建新的TS分片。
- M3U8索引维护:按照HLS规范生成索引文件,包含版本号、目标时长,以及每个分片的
EXTINF标签(标注时长)和文件路径。
关键细节实现
1. 编码器输入类型配置
IMFMediaType* pInputType = nullptr; MFCreateMediaType(&pInputType); // 设置媒体类型为视频 pInputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); // 指定输入子类型为NV12 pInputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12); // 匹配摄像头输出的分辨率 pInputType->SetUINT32(MF_MT_FRAME_SIZE_WIDTH, 1920); pInputType->SetUINT32(MF_MT_FRAME_SIZE_HEIGHT, 1080); // 设置帧率(与摄像头一致) pInputType->SetUINT32(MF_MT_FRAME_RATE_NUMERATOR, 30); pInputType->SetUINT32(MF_MT_FRAME_RATE_DENOMINATOR, 1); // 将配置好的输入类型绑定到编码器 pTransform->SetInputType(0, pInputType, 0); pInputType->Release();
2. 分片时长控制逻辑
// 时间戳单位:100纳秒(Media Foundation标准) LONGLONG currentFrameTimestamp = ...; // 当前NV12帧的时间戳 static LONGLONG segmentStartTs = currentFrameTimestamp; // 分片阈值设为2秒(可在1-3秒区间调整) const LONGLONG segmentThreshold = 2 * 10000000; if (currentFrameTimestamp - segmentStartTs >= segmentThreshold) { // 结束当前TS分片,写入收尾数据 FinalizeCurrentTS(); // 更新M3U8索引,添加当前分片信息 AppendToM3U8(segmentFilePath, (currentFrameTimestamp - segmentStartTs) / 10000000.0); // 初始化新分片 CreateNewTS(); segmentStartTs = currentFrameTimestamp; }
3. TS打包注意点
- 每个TS分片开头必须包含PAT、PMT表,且第一个NALU必须是关键帧,保证分片可独立播放。
- 编码后的NALU需要按TS包规则拆分,不足188字节的部分用填充字节补全。
常见坑点规避
- 编码器缓存处理:部分硬件编码器会缓存2-3帧才输出,分片切换前需调用
IMFTransform::Flush,确保缓存帧全部写入当前分片。 - 时间戳连续性:如果摄像头输出无时间戳,需自己按帧率生成连续的时间戳,否则会导致分片时长计算错误、播放卡顿。
- HLS兼容性:M3U8中的
EXTINF时长建议保留一位小数,分片文件名用连续序号(如seg_001.ts),方便播放器索引。
内容的提问来源于stack exchange,提问作者liang.good
相关产品推荐
相关产品推荐

