如何将CMSampleBuffer数据传入FFmpeg的avformat_open_input()?苹果摄像头UDP推流求助
问题2:苹果设备摄像头推流至UDP的解决方案思路
既然你已经实现了文件推流,实时摄像头推流的核心差异就是数据源从本地文件变成了摄像头的实时CMSampleBuffer,整体流程可以拆分为以下关键步骤:
一、用AVFoundation获取摄像头的CMSampleBuffer
搭建苹果原生的摄像头采集流程:
- 创建
AVCaptureSession,设置会话预设(比如AVCaptureSessionPreset1280x720)。 - 添加
AVCaptureDeviceInput(前置/后置摄像头)到会话。 - 创建
AVCaptureVideoDataOutput,设置像素格式为NV12(和FFmpeg兼容性更好),并设置代理AVCaptureVideoDataOutputSampleBufferDelegate,在captureOutput:didOutputSampleBuffer:fromConnection:回调中获取实时的CMSampleBuffer。 - 启动
AVCaptureSession开始采集。
二、CMSampleBuffer转FFmpeg AVFrame
在摄像头回调中处理帧数据:
- 提取像素缓冲区:
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - 锁定缓冲区:
CVPixelBufferLockBaseAddress(pixelBuffer, 0); - 获取Y、UV平面的地址和行间距:
void *yPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); void *uvPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); int yStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); int uvStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); - 用FFmpeg的
sws_context完成格式转换,填充到AVFrame:// 假设已初始化sws_ctx sws_scale(sws_ctx, (const uint8_t *const[]){yPlane, uvPlane}, (const int[]){yStride, uvStride}, 0, height, frame->data, frame->linesize); - 设置时间戳:把
CMSampleBuffer的presentationTimeStamp(CMTime)转换成FFmpeg的时间基:CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); frame->pts = av_rescale_q(CMTimeGetSeconds(pts) * AV_TIME_BASE, AV_TIME_BASE_Q, stream->time_base); - 解锁缓冲区:
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
三、FFmpeg编码与UDP推流
这部分和文件推流逻辑类似,但要适配实时场景:
- 初始化输出上下文:
选AVFormatContext *fmt_ctx = NULL; const char *output_url = "udp://192.168.1.100:5000"; avformat_alloc_output_context2(&fmt_ctx, NULL, "mpegts", output_url);mpegts格式是因为UDP是无连接协议,TS的分片特性更适合应对丢包场景。 - 创建并配置视频流:
- 添加视频流:
AVStream *stream = avformat_new_stream(fmt_ctx, NULL); - 设置流参数:分辨率、帧率、比特率、像素格式(
AV_PIX_FMT_YUV420P)、编码器ID(AV_CODEC_ID_H264)。 - 打开编码器时设置低延迟参数:比如把
preset设为ultrafast,tune设为zerolatency。
- 添加视频流:
- 写入头信息:
avformat_write_header(fmt_ctx, NULL); - 实时编码推流:在摄像头回调中,把转换好的
AVFrame送入编码器,得到AVPacket后调整时间戳(av_packet_rescale_ts),再用av_interleaved_write_frame(fmt_ctx, &packet)写入输出,完成推流。
四、关键注意事项
- 线程安全:AVFoundation的回调默认在非主线程,FFmpeg操作要确保线程安全,或者把
CMSampleBuffer放到串行队列中处理,避免并发冲突。 - 延迟控制:除了编码器参数,还要注意采集队列的长度,避免缓存过多帧导致延迟。
- 资源清理:停止推流时,要先停止
AVCaptureSession,再关闭FFmpeg的编码器、输出上下文,释放所有资源避免内存泄漏。
内容的提问来源于stack exchange,提问作者Shahrear Mahmud
相关产品推荐
相关产品推荐

