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.gz、yasm-1.3.0.tar.gz
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