基于Qt和ffmpeg的抓屏rtsp服务(一)
程序员文章站
2022-07-02 09:46:48
...
章节目录
实现一个基于ffmpeg的rtsp抓屏服务
一、服务实现图
1、整体图
2、 采集图
3、 编码图
4、 传输
二、各个模块简要说明
1、模块讲解
上面图1
在上面的1、中,主要是整个程序的主要流程走向,使用ffmpeg **gdigrab**进行采集,ffmpeg中的libx264进行编码,
由于直接使用ES流进行传输。所以,封装这层先不做过多的处理。传输,是网络上找的一个rtsp服务,最终会使用**vlc**进行验证。
上面图2、
图2中,有几个函数avdevice_register_all,是需要使用到采集的gdi设备,所以需要该函数进行注册。使用av_dict_set_int来设
置编码参数,
图2中av_dict_set_int,设置抓屏参数
部分采集编码:
AVDictionary *options = nullptr;
av_dict_set_int(&options, "framerate", 25, 1);
av_dict_set_int(&options, "draw_mouse", 1, 1);
av_dict_set_int(&options, "offset_x", 0, 1);
av_dict_set_int(&options, "offset_y", 0, 1);
av_dict_set(&options, "video_size", video_size, 1);
AVInputFormat* pInputFmt = av_find_input_format("gdigrab");
AVFormatContext* pFmtCtx = avformat_alloc_context();
avformat_open_input(&pFmtCtx , "desktop", pInputFmt, &options);
if (avformat_find_stream_info(pFmtCtx , nullptr) < 0)
{
printf("Couldn't find stream info.\n");
avformat_close_input(&pFmtCtx);
pFmtCtx = nullptr;
return false;
}
int video_index = -1;
for (unsigned int i = 0; i < pFmtCtx ->nb_streams; i++)
{
if (pFmtCtx ->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
video_index = i;
}
}
if (video_index < 0)
{
printf("Couldn't find video stream.\n");
avformat_close_input(&pFmtCtx );
pFmtCtx = nullptr;
return false;
}
AVCodec* codec = avcodec_find_decoder(pFmtCtx->streams[video_index]->codecpar->codec_id);
if (!codec)
{
avformat_close_input(&pFmtCtx );
pFmtCtx = nullptr;
return false;
}
m_pCodecCtx = avcodec_alloc_context3(codec);
if (!m_pCodecCtx )
{
return false;
}
avcodec_parameters_to_context(m_pCodecCtx , pFmtCtx->streams[video_index]->codecpar);
if (avcodec_open2(m_pCodecCtx , codec, nullptr) != 0)
{
avcodec_close(m_pCodecCtx );
codec_context_ = nullptr;
avformat_close_input(&pFmtCtx);
pFmtCtx= nullptr;
return false;
}
部分编码
AVCodec *codec = nullptr;
//codec = avcodec_find_encoder(AV_CODEC_ID_H264);
codec = avcodec_find_encoder_by_name("libx264");
if (!codec)
{
std::cout << "H.264 Encoder not found.\n" << std::endl;
return false;
}
codec_context_ = avcodec_alloc_context3(codec);
if (!codec_context_)
{
std::cout << "avcodec_alloc_context3() failed." << std::endl;
return false;
}
codec_context_->width = av_config_.video.width;
codec_context_->height = av_config_.video.height;
codec_context_->time_base = { 1, (int)av_config_.video.framerate };
codec_context_->framerate = { (int)av_config_.video.framerate, 1 };
codec_context_->gop_size = av_config_.video.gop;
codec_context_->max_b_frames = 0;
codec_context_->pix_fmt = AV_PIX_FMT_YUV420P;
// rc control mode: abr
codec_context_->bit_rate = av_config_.video.bitrate;
// cbr mode config
codec_context_->rc_min_rate = av_config_.video.bitrate;
codec_context_->rc_max_rate = av_config_.video.bitrate;
codec_context_->rc_buffer_size = (int)av_config_.video.bitrate;
codec_context_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if (codec->id == AV_CODEC_ID_H264)
{
av_opt_set(codec_context_->priv_data, "preset", "ultrafast", 0); //ultrafast
}
av_opt_set(codec_context_->priv_data, "tune", "zerolatency", 0);
av_opt_set_int(codec_context_->priv_data, "forced-idr", 1, 0);
av_opt_set_int(codec_context_->priv_data, "avcintra-class", -1, 0);
if (avcodec_open2(codec_context_, codec, NULL) != 0)
{
std::cout << "avcodec_open2() failed." << std::endl;
return false;
}
部分代码如上
① 采集流程
上面主要是获取设备信息,并打开解码器
后面通过av_read_frame读取流,avcodec_send_packet/avcodec_receive_frame来进行解码流
同时,将获取到的数据进行编码成h264,
② 编码流程
编码流程中需要注意,数据是通过采集中获取到的数据,AVframe,需要先将AVFrame转为yuvAVFrame,然后通过接口avcodec_send_frame/avcodec_receive_packet,avcodec_receive_packet中的数据,得到的是AVPacket的数据,需要去掉其中h264中的起始码,传给rtsp服务,rtsp服务就能将数据传输给用户端。
③ 封装
由于此处直接使用ES流,不做封装,直接将编码器中的数据进行传输。
④ 传输
传输参考下面的3.rtsp服务
2、引用参考部分
- 技术视频参考:https://ke.qq.com/course/3202131?flowToken=1040952
- 参考文献:最简单的基于FFmpeg的AVDevice例子(屏幕录制)
- rtsp服务参考: https://blog.csdn.net/weixin_42462202/article/details/98956346
- 参考代码:https://github.com/PHZ76/DesktopSharing
3、总结
本章节只讲述整个服务器相关的流程,实现代码请关注后续章节!
上一篇: 变量的解构赋值,数组解构/对象解构/字符串解构/函数解构
下一篇: 新手入门makefile教程