基于Qt和ffmpeg的抓屏rtsp服务(一)

实现一个基于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,设置抓屏参数

小编整理了一些学习资料、教学视频和学习路线图共享在群文件,资料包括《Andoird音视频开发必备手册+音视频最新学习视频+大厂面试真题+2022最新学习路线图+项目实战源码》等等(C/C++,Linux,FFmpeg ,webRTC, rtmp, hls, rtsp, ffplay, srs)

关注+私信1免费分享2022最新最全学习提升资料包,资料内容包括《Andoird音视频开发必备手册+音视频最新学习视频+大厂面试真题+2022最新学习路线图(C/C++,Linux,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)等等 

 部分采集编码:

	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服务

总结

本章节只讲述整个服务器相关的流程

 

猜你喜欢

转载自blog.csdn.net/yinshipin007/article/details/125153842