近期看了一些关于ffmpeg 推流的文章,但是文章中都是基于ffmpeg命令行实现的,觉得不是很灵活,不好和easydarwin 集成到一起工作。于是自己基于ffmpeg 的lib 实现了,采集,编码,推流的过程
ffmpeg 可以支持多种方式的采集,如windows 下的dshow,采集桌面的
gdigrab,linux下有video4linux2 等方式采集。我在windows 下实现的是dshow 设备的采集。
实现的过程如下
1、使用avformat_alloc_context分配一个AVFormatContext 一个结构。
2、使用av_find_input_format 查找dshow 的输入是否存在 如AVInputFormat *ifmt = av_find_input_format("dshow"),此时如果dshow 没有被编译进ffmpeg ,ifmt 等于NULL
3、使用avformat_open_input 打开输入流 avformat_open_input(&pFormatCtx, strUtf8.c_str(), ifmt, &options) ,注意此处,输入的设备名称一定要utf 8 编码的设备名称,否则在中文设备名称下会导致打开设备失败。
4、avformat_find_stream_info 解析流名称,找到输出流的一些格式信息,这个和用ffmpeg 打开视频文件类似。
5、 avcodec_find_decoder 基本上来说是解析具体的图像帧格式,一般是YUV420 或者YUV422 格式,
6、 分配YUV数据的缓冲区 pFrameYUV = av_frame_alloc()
7、初始化 h264 编码器等 pCodecOut = avcodec_find_encoder(AV_CODEC_ID_H264); pCodecOutCtx = avcodec_alloc_context3(pCodecOut);
h264 编码器的具体参数设置如下
av_opt_set(pCodecOutCtx->priv_data, "preset", "veryfast", 0);
av_opt_set(pCodecOutCtx->priv_data, "tune", "zerolatency", 0);
av_opt_set(pCodecOutCtx->priv_data,"profile","baseline",0);
8、打开编码器准备编码
当然如果摄像头输出的YUV422 或RGB 等图像帧时 需要对 使用 sws_scale API 进行转换
9、循环调用 av_read_frame 读取图像,然后使用 avcodec_encode_video2进行编码
10、使用write_frame 写入编码好的视频帧,如果是保存文件,就写入文件,如果是流媒体协议则向网络发送数据,这里我们使用的EasyDarwin 的推流功能,因此只要调用了
write_frame 就向 EasyDarwin 发送视频数据了
输出方式的设定:
ffmpeg 多种方式输出,由于EasyDarwin 支持RTSP 协议推流转发,因此输出采用了RTSP 推流输出。
关键API 如下:
avformat_alloc_output_context2(&FormatOutCtx, NULL, “rtsp”, 输出的URL);
avio_open(&m_FormatOutCtx->pb, strOut.c_str(), AVIO_FLAG_READ_WRITE)
如果连接EasyDarwin 服务器失败时 avio_open 返回值小于0 .
最后我们可以用 EasyDarwin 自带的EasyPlayer 或者vlc 观看推送的实时流了。