FFmpeg获取实时RTSP流 (并转为Qt、OpenCv进行播放)

RTSP简介

     RTSP(Real Time Streaming Protocol),RFC2326,实时流传输协议,是TCP/IP协议体系中的一个应用层协议,由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC标准。该协议定义了一对多应用程序如何有效地通过IP网络传送多媒体数据。RTSP在体系结构上位于RTP和RTCP之上,它使用TCP或UDP完成数据传输。HTTP与RTSP相比,HTTP请求由客户机发出,服务器作出响应;使用RTSP时,客户机和服务器都可以发出请求,即RTSP可以是双向的。RTSP是用来控制声音或影像的多媒体串流协议,并允许同时多个串流需求控制,传输时所用的网络通讯协定并不在其定义的范围内,服务器端可以自行选择使用TCP或UDP来传送串流内容,它的语法和运作跟HTTP 1.1类似,但并不特别强调时间同步,所以比较能容忍网络延迟。而前面提到的允许同时多个串流需求控制(Multicast),除了可以降低服务器端的网络用量,更进而支持多方视讯会议(Video Conference)。因为与HTTP1.1的运作方式相似,所以代理服务器〈Proxy〉的快取功能〈Cache〉也同样适用于RTSP,并因RTSP具有重新导向功能,可视实际负载情况来转换提供服务的服务器,以避免过大的负载集中于同一服务器而造成延迟。

     具体了解:RTSP

     
     
     

FFMPEG

     Linux系统下进行编译安装

          1、安装所需安装包:ffmpeg-4.0.tar.gzyasm-1.3.0.tar.gz

               ffmpeg下载链接

               yasm下载链接 - (yasm是一款汇编器,并完全重写了nasm的汇编环境,接收nasm和gas语法,支持x86和amd64指令集。)

          2、安装yasm (终端下执行)

               tar -zxzf yasm-1.3.0.tar.gz
               cd yasm-1.3.0/
               ./configure
               make
               make install

          3、安装ffmpeg (终端下执行)

               tar -zxvf ffmpeg-4.0.tar.gz
               cd ffmpeg-4.0/
               ./configure --enable-shared --prefix=/monchickey/ffmpeg
               make
               make install

               编译参数都是默认的,直接安装到系统中即可。编译过程有点长,耐心等待完成之后执行 cd /monchickey/ffmpeg/ 进入安装目录,查看一下发现有bin,include,lib,share这4个目录,其中bin是ffmpeg主程序二进制目录,include是C/C++头文件目录,lib是编译好的库文件目录,share是文档目录。

     ffmpeg获取rtsp流 (C++)

// 包含头文件
extern "C"
{
    
    
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

int main()
{
    
    
	// rtsp流地址
	const char RtspAddress[] = {
    
    "rtsp://xxxxxxx"};
	
	// ffmpeg注册
    av_register_all();
    avformat_network_init();
    
	// 初始化上下文
    AVFormatContext *avFormatContext = avformat_alloc_context();

	// 画质优化
    AVDictionary *avDictionary = nullptr;
    av_dict_set(&avDictionary, "buffer_size", "1024000", 0);	// 设置图像最大buf长度
    av_dict_set(&avDictionary, "max_delay", "500000", 0);		// 设置图像最大缓冲
    av_dict_set(&avDictionary, "rtsp_transport", "tcp", 0);  	// 设置使用tcp传输,因udp在1080p下会丢包导致花屏

	// 打开rtsp
    int result = avformat_open_input(&avFormatContext,RtspAddress,nullptr,&avDictionary);
    if(result < 0)
    {
    
    
    	std::cout << "开打rtsp错误:" << result << std::endl;
        return -1;
    }

	// 查找视频码流信息
    result = avformat_find_stream_info(avFormatContext,nullptr);
    if(result < 0)
    {
    
    
        std::cout << "查找视频码流错误:" << result << std::endl;
        return -2;
    }
    
	// 查找视频码流
    int VideoStream = -1;
    for(int index = 0;index<avFormatContext->nb_streams;++index)
    {
    
    
        if(avFormatContext->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
    
    
            VideoStream = index;
            break;
        }
    }
    if(VideoStream == -1)
    {
    
    
        std::cout << "查找视频码流失败。" << std::endl;
        return -3;
    }

	// 查找视频解码器
    AVCodecContext *avCodecContext = avFormatContext->streams[VideoStream]->codec;
    AVCodec *avCodec = avcodec_find_decoder(avCodecContext->codec_id);
    if(!avCodec)
    {
    
    
        std::cout << "查找视频解码器。" << std::endl;
        return -4;
    }

	// 打开解码器
	result = avcodec_open2(avCodecContext,avCodec,NULL);
    if(result < 0)
    {
    
    
        std::cout << "打开解码器错误:" << result << std::endl;
        return -5;
    }
    
    // 创建初始化解码包
	AVPacket *avPacket = av_packet_alloc();
    av_init_packet(avPacket);

	// 创建源、目标帧
	AVFrame *sFrame = av_frame_alloc();
    AVFrame *dFrame = av_frame_alloc();

	// 获取图像尺寸并分配出内存
    int PictureSize = avpicture_get_size(AV_PIX_FMT_RGB32, avCodecContext->width,avCodecContext->height);
    unsigned char *pBuffer = (unsigned char *) av_malloc(PictureSize * sizeof(unsigned char));

	//	图像填充
    avpicture_fill(	(AVPicture *)dFrame, 
    				pBuffer, 
    				AV_PIX_FMT_RGB32, 
    				avCodecContext->width, 
    				avCodecContext->height);

	// 设置数据转换参数
	struct SwsContext *swsContext = sws_getContext(	avCodecContext->width, 
													avCodecContext->height,
													avCodecContext->pix_fmt,
													avCodecContext->width, 
													avCodecContext->height, 
													AV_PIX_FMT_RGB32, 
													SWS_BICUBIC,
													NULL, NULL, NULL);

	// 视频解码状态
	int DecodePictureState = -1;

	// 循环读取处理(此处死循环在实际生产中,可做出其他控制)
	while(1)
	{
    
    
		// 读取一包数据
		result = av_read_frame(avFormatContext,avPacket);
	
		// 若该包数据为视频流
		if (avPacket->stream_index == VideoStream)
        {
    
    
        	// 解码一帧图像
        	result = avcodec_decode_video2(avCodecContext,sFrame,&DecodePictureState,avPacket);
            if (result < 0)
            {
    
    
                std::cout << "decode error :" << result << std::endl;
                break;
            }
				
			// 解码成功
			if (DecodePictureState)
            {
    
    
            	// 切片
                sws_scale(	swsContext, 
                			(const uint8_t* const*)sFrame->data, 
                			sFrame->linesize,
                			0, 
               				avCodecContext->height, 
                			dFrame->data, 
                			dFrame->linesize);
                			
				/ 转cv::Mat
				cv::Mat mat;
				int width  = sFrame->width;
				int height = sFrame->height;
				if (mat.rows != height || mat.cols != width || mat.type() != CV_8UC3)
				{
    
    
					mat = cv::Mat(height, width, CV_8UC3);
				}
				int cvLinesizes[1];
				cvLinesizes[0] = mat.step1();
				SwsContext* conversion = sws_getContext(width, 
														height, 
														(AVPixelFormat) sAVFrame->format, 
														width, 
														height, 
														AVPixelFormat::AV_PIX_FMT_BGR24,
														SWS_FAST_BILINEAR, 
														NULL, NULL, NULL);
				sws_scale(	conversion, 
							sAVFrame->data, 
							sAVFrame->linesize, 
							0, 
							height, 
							&mat.data, 
							cvLinesizes);
				sws_freeContext(conversion);
				//cv::imshow("mat",mat);
				/
                			
				/ 转QImage
				QImage qimage((uchar *)pBuffer,avCodecContext->width,avCodecContext->height,QImage::Format_RGB32);
                if(!qimage.isNull())
                {
    
    
                	// 转换结束,可做其他操作..
                }
                /
                			
				// 至此已经结束,可利用解码后图像数据做其他操作..
            }
        }
	}
	
	// 释放图像优化
	if(avDictionary)
	{
    
    
		av_dict_free(&avDictionary);
	}
    avDictionary = nullptr;

    // 释放图片内存
    if(pBuffer)
    {
    
    
    	delete []pBuffer;
	}
	pBuffer = nullptr;

	// 释放解码环境
	if(avCodecContext)
    {
    
    
        avcodec_close(avCodecContext);
        avCodecContext = NULL;
    }
    
    // 释放上下文
    if(avFormatContext)
    {
    
    
        avformat_close_input(&avFormatContext);
        avFormatContext = NULL;
    }
    
    // 释放网络
    avformat_network_deinit();

	return 0;
}


另附“OpenCv之RTSP播放、截图、录制 —— CV、Qt播放”

     阅读文章

关注

微信公众号搜索"Qt_io_"或"Qt开发者中心"了解更多关于Qt、C++开发知识.。

笔者 - jxd

猜你喜欢

转载自blog.csdn.net/automoblie0/article/details/107533863