第2课 用FFmpeg读取rtmp流并显示视频

这篇具有很好参考价值的文章主要介绍了第2课 用FFmpeg读取rtmp流并显示视频。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 本课对应源文件下载链接:

https://download.csdn.net/download/XiBuQiuChong/88680079

这节课我们开始利用ffmpeg和opencv来实现一个rtmp播放器。播放器的最基本功能其实就两个:显示画面和播放声音。在实现这两个功能前,我们需要先用ffmpeg连接到rtmp服务器,当然也可以打开一个文件。

1.压缩备份上节课工程文件夹为demo.rar,并修改工程文件夹demo为demo2,及时备份源文件并在原基础上继续迭代开发是一种好习惯。

2.打开fmlp.cpp,修改其中的删除原来init函数中的代码,并加入以下代码:

runFFmpegHandle = CreateThread(NULL, 0, runFFmpegThreadProc, (LPVOID)this, 0, NULL);

如果把MFC对话框相关代码看作主线程函数的话,上述代码的作用是新建一个线程,并在新的线程中执行与ffmpeg及opencv有关的操作。这样做的好处就是实现了“各司其责”,MFC所在的主线程主要用来处理UI(界面)方面的工作,ffmpeg及opencv子线程主要用来处理网络连接、图形处理等方面的工作,互不影响,简洁高效。

3.因为我们需要连接rtmp服务器,所以我们需要在fmlp.h中增加一个字符串类型的rtmp地址;另外还需要定义子线程句柄及相关函数:

CString inRtmpURL;
HANDLE runFFmpegHandle;
static DWORD WINAPI runFFmpegThreadProc(LPVOID lpParam);
int runFFmpeg();
BOOL isRunning = false;

第2课 用FFmpeg读取rtmp流并显示视频,ffmpeg,opencv,c++

4.在fmlp.cpp中加入对应的函数,调试输出“runFFmpeg...”则表示子线程正常运行。

第2课 用FFmpeg读取rtmp流并显示视频,ffmpeg,opencv,c++

5.FFmpeg作为开源的跨平台音视频处理工具,提供了丰富的API来处理音视频文件。下面是利用FFmpeg API播放rtmp或rtsp流或文件的工作流程:

(1)打开输入文件:使用avformat_open_input函数打开输入文件,该函数会自动检测文件格式并初始化相应的解码器。

(2)查找流信息:使用avformat_find_stream_info函数查找输入文件中的音视频流信息,包括编码格式、帧率、分辨率等。

(3)查找解码器:根据流信息中的编码格式,使用avcodec_find_decoder函数查找相应的解码器。

(4)打开解码器:使用avcodec_open2函数打开解码器,准备解码音视频数据。

(5)取数据包:使用av_read_frame函数读取音视频数据包,每个数据包包含一个或多个音视频帧。

(6)解码数据包:对于音频数据包,使用avcodec_send_packet和avcodec_receive_frame函数解码。

(7)处解码后的数据:对于音频数据,可以进行音频处理,如音频播放、音频重采样等;对于视频数据,可以进行视频处理,如视频叠加水印、视频滤镜效果等。

(8)编码数据包:对于音频数据,可以使用avcodec_send_frame和avcodec_receive_packet函数进行编码。

(9)写入数据包:使用av_write_frame函数将编码后的数据包写入输出文件或使用av_interleaved_write_frame函数将编码后的数据包推送到rmtp流服务器。

(10)关闭解码器和输入文件:使用avcodec_close函数关闭解码器,使用avformat_close_input函数关闭输入文件。

(11)释放资源:使用avformat_free_context函数释放AVFormatContext结构体和相关资源。

根据上述流程,我们就可以在runFFmpeg函数中正式开始我们的工作了:


int fmlp::runFFmpeg(){

	//返回值
	int ret;
	//rtmp地址,也可以是本地文件
	const char *inFileName = "rtmp://192.168.0.100/vod/sample.mp4";

	//输入文件上下文结构体
	AVFormatContext *inFormatCtx = NULL;

	//视频解码相关
	int videoIndex = -1;
	AVCodec *vDecodec;
	AVCodecContext *vDecodeCtx = NULL;
	//图像转换上下文结构体
	struct SwsContext* bgrSwsCtx = NULL;
	struct SwsContext* yuvSwsCtx = NULL;
	//图像数据数组
	uint8_t* bgrBuff = NULL;
	//读取的数据包
	AVPacket normalPkt;
	//Mat对象
	cv::Mat srcMat;


	//开始时间和当前时间
	int64_t startTime = 0;
	int64_t currentTime = 0;

	//FFmpeg初始化
	av_register_all();
	avcodec_register_all();
	avformat_network_init();


	inFormatCtx = avformat_alloc_context();
	AVDictionary* options = NULL;
	av_dict_set(&options, "buffer_size", "10240", 0);
	av_dict_set(&options, "max_delay", "1000", 0);
	av_dict_set(&options, "max_analyze_duration", "10000", 0);
	av_dict_set(&options, "probesize", "20480", 0);
	av_dict_set(&options, "stimeout", "5000", 0);
	av_dict_set(&options, "listen_time", "5000", 0);
	av_dict_set(&options, "initial_timeout", "5000", 0);
	av_dict_set(&options, "preset", "ultrafast", 0);
	av_dict_set(&options, "tune", "zerolatency", 0);

	if ((ret = avformat_open_input(&inFormatCtx, inFileName, 0, &options)) < 0)
	{
		TRACE("无法打开输入流.\n");
		return -1;
	}

	if (ret == 0){
		isRunning = true;
	}
	else{
		isRunning = false;
	}

	if ((ret = avformat_find_stream_info(inFormatCtx, 0)) < 0)
	{
		TRACE("查找输入流信息失败.\n");
		return -1;
	}
	//获取音视频流通道ID
	for (int i = 0; i < inFormatCtx->nb_streams; i++){

		if (inFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoIndex = i;
		}
		
	}

	TRACE("视频流通道索引%d\n", videoIndex);
	//初始化并打开视频解码器
	vDecodec = avcodec_find_decoder(inFormatCtx->streams[videoIndex]->codecpar->codec_id);
	vDecodeCtx = avcodec_alloc_context3(vDecodec);
	avcodec_parameters_to_context(vDecodeCtx, inFormatCtx->streams[videoIndex]->codecpar);
	avcodec_open2(vDecodeCtx, vDecodec, 0);

	av_dump_format(inFormatCtx, 0, inFileName, 0);

	//解码后的原始视频帧
	AVFrame *deVideoFrame = av_frame_alloc();
	//缩放后的视频帧
	AVFrame bgrFrame = { 0 };
	bgrFrame.width = 960;
	bgrFrame.height = 540;
	bgrFrame.format = AV_PIX_FMT_BGR24;
	int bgrFrameSize = av_image_get_buffer_size((AVPixelFormat)bgrFrame.format, bgrFrame.width, bgrFrame.height, 1);
	bgrBuff = (uint8_t*)av_malloc(bgrFrameSize);
	av_image_fill_arrays(bgrFrame.data, bgrFrame.linesize, bgrBuff, (AVPixelFormat)bgrFrame.format, bgrFrame.width, bgrFrame.height, 1);
	//获取图像转换上下文
	bgrSwsCtx = sws_getContext(vDecodeCtx->width, vDecodeCtx->height, vDecodeCtx->pix_fmt, bgrFrame.width, bgrFrame.height, (AVPixelFormat)bgrFrame.format, SWS_BICUBIC, NULL, NULL, NULL);

	//获取开始时间
	startTime = av_gettime();
	while (isRunning)
	{
		ret = av_read_frame(inFormatCtx, &normalPkt);
		if (ret < 0){
			break;
		}

		//当数据包时间快于当前时间则延当延时
		currentTime = (av_gettime() - startTime) / 1000;
		if (normalPkt.pts > currentTime){
			Sleep(normalPkt.pts - currentTime);
		}

		if (normalPkt.stream_index == videoIndex)
		{
			ret = avcodec_send_packet(vDecodeCtx, &normalPkt);
			ret = avcodec_receive_frame(vDecodeCtx, deVideoFrame);
			av_packet_unref(&normalPkt);
			ret = sws_scale(bgrSwsCtx, (const uint8_t* const*)deVideoFrame->data, deVideoFrame->linesize, 0, deVideoFrame->height, bgrFrame.data, bgrFrame.linesize);
			srcMat = cv::Mat(bgrFrame.height, bgrFrame.width, CV_8UC3, bgrFrame.data[0]);
			imshow("viceo", srcMat);
			cv::waitKey(10);
			//mainDlg->drawMatOfPlay(srcMat);
			//av_frame_unref(deVideoFrame);
		}
	}


	av_dict_free(&options);
	avformat_close_input(&inFormatCtx);
	isRunning = false;
	return 0;
}

6.先不用管那么多,先运行起来看看效果吧。如果能弹出窗口显示图像则表示连接rtmp服务器成功并成功拿到视频数据。

第2课 用FFmpeg读取rtmp流并显示视频,ffmpeg,opencv,c++

7.上面的视频显示是利用openCV的内置函数来imshow来实现的,会弹出一个新的窗口,这样会显得会怪异。为了让视频显示在MFC对话框中,需要先在对话框中添加一个名为IDC_playPic的Picture Control 控件,并加入显示函数:

void CdemoDlg::drawMatOfPlay(cv::Mat &img)
{

	CDC *tmpCDC;
	CRect tmpRect;
	cv::Mat scaleMat;
	tmpCDC = myWnd->GetDlgItem(IDC_playPic)->GetDC();
	myWnd->GetDlgItem(IDC_playPic)->GetClientRect(&tmpRect);
	cv::resize(img, scaleMat, cv::Size(tmpRect.Width(), tmpRect.Height()));
	switch (scaleMat.channels())
	{
	case 1:
		cv::cvtColor(scaleMat, scaleMat, CV_GRAY2BGRA);
		break;
	case 3:
		cv::cvtColor(scaleMat, scaleMat, CV_BGR2BGRA);
		break;
	default:
		break;
	}
	int pixelBytes = scaleMat.channels()*(scaleMat.depth() + 1);
	BITMAPINFO bitInfo;
	bitInfo.bmiHeader.biBitCount = 8 * pixelBytes;
	bitInfo.bmiHeader.biWidth = scaleMat.cols;
	bitInfo.bmiHeader.biHeight = -scaleMat.rows;
	bitInfo.bmiHeader.biPlanes = 1;
	bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitInfo.bmiHeader.biCompression = BI_RGB;
	bitInfo.bmiHeader.biClrImportant = 0;
	bitInfo.bmiHeader.biClrUsed = 0;
	bitInfo.bmiHeader.biSizeImage = 0;
	bitInfo.bmiHeader.biXPelsPerMeter = 0;
	bitInfo.bmiHeader.biYPelsPerMeter = 0;
	StretchDIBits(
		tmpCDC->GetSafeHdc(),
		0, 0, tmpRect.Width(), tmpRect.Height(),
		0, 0, tmpRect.Width(), tmpRect.Height(),
		scaleMat.data,
		&bitInfo,
		DIB_RGB_COLORS,
		SRCCOPY
		);
	ReleaseDC(tmpCDC);
}

8.在fmlp.cpp中调用显示函数:

//imshow("viceo", srcMat);
//cv::waitKey(10);
mainDlg->drawMatOfPlay(srcMat);
av_frame_unref(deVideoFrame);

再次运行,效果立现:

第2课 用FFmpeg读取rtmp流并显示视频,ffmpeg,opencv,c++文章来源地址https://www.toymoban.com/news/detail-766319.html

到了这里,关于第2课 用FFmpeg读取rtmp流并显示视频的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 音视频开发---ffmpeg rtmp推流

    推流是将输入视频数据推送至流媒体服务器, 输入视频数据可以是本地视频文件(avi,mp4,flv......),也可以是内存视频数据,或者摄像头等系统设备,也可以是网络流URL。本篇介绍将本地视频文件通过FFmpeg编程以RTMP直播流的形式推送至RTMP流媒体服务器的方法。 推流的网络拓扑

    2024年02月16日
    浏览(82)
  • ffmpeg+nginx-rtmp转发视频流

    本篇博客最早发布于实验室公共博客,但已无人维护,现迁移至个人博客 画了好几天图,实在有些乏力,找点有意思的事情做做 觉得 视频流传输 挺有意思,B站找了些视频,但感觉有些大同小异,讲得不是很清楚 FFmpeg/RTMP/webRTC丨90分钟搞定直播逻辑-推流-流媒体服务器-拉流

    2024年02月11日
    浏览(38)
  • 【音视频 ffmpeg 学习】 RTMP推流 mp4文件

    1.RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议。 2.RTMP协议中基本的数据单元称为消息(Message)。 3.当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。 (1). linux 环境准备 安装nginx 和 rtmp模块 下载nginx安装包 下载

    2024年02月03日
    浏览(67)
  • OpenCV+FFmpeg 实现人脸检测Rtmp直播推流(Python快速实现)

    windows平台笔记本摄像头视频采集、人脸识别,识别后将视频推流到RTMP流媒体服务器,在任意客户端可以进行RTMP拉流播放。 效果如图: 使用VLC播放器进行拉流。 需要先安装OpenCV的python包以及FFmpeg。 对于ffmpeg有两种调用方式,但这两种方式都需要先安装ffmpeg,调用的具体区别

    2024年02月12日
    浏览(42)
  • Docker RTMP服务器搭建与视频流推送示例(流媒体服务器tiangolo/nginx-rtmp,推流客户端ffmpeg)

    在这篇文章中,我将详述如何搭建一个RTMP(Real-Time Messaging Protocol)服务器,并使用ffmpeg技术进行本地视频的推流。最后,我们将使用VLC播放器来播放这个RTMP流。 首先,我们需要搭建一个RTMP服务器。为了方便起见,我们将选择Docker作为服务器的环境。Docker的轻量化和可移植

    2024年01月17日
    浏览(57)
  • ffmpeg获取rtsp h265,用ffmpeg从RTSP服务器拉流并保存各种格式文件

    ffmpeg:FFmpeg的名称来自MPEG视频编码标准,前面的“FF”代表“Fast Forward,是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。 库的组成: libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音

    2024年01月22日
    浏览(55)
  • 第4课 FFmpeg读取本地mp4文件并显示

    在上节课,我们使用FFmpeg实现了一个最简单的rtmp播放器,它看起来工作正常。这节课,我们尝试让它来播放本地的mp4文件试试。 1.压缩备份上节课工程文件夹为demo3.rar,并修改工程文件夹demo3为demo4,重要的事情再说一遍:及时备份源文件并在原基础上继续迭代开发是一种好

    2024年02月03日
    浏览(51)
  • 使用opencv及FFmpeg编辑视频

    帮朋友做了一个小作业,具体实现分为几个过程: 将两个mp4格式视频融合到一起 为新视频添加声音 其中一个视频为背景。 此时保存的视频没有声音。 从官网下载安装包,跳转到链接: 我使用的是第一个压缩包。剪切到自定义位置,并设置好系统路径。 我这里的路径是 \\\'

    2024年02月07日
    浏览(37)
  • 使用nginx和ffmpeg搭建HTTP FLV流媒体服务器(摄像头RTSP视频流->RTMP->http-flv)

    名词解释   RTSP (Real-Time Streaming Protocol) 是一种网络协议,用于控制实时流媒体的传输。它是一种应用层协议,通常用于在客户端和流媒体服务器之间建立和控制媒体流的传输。RTSP允许客户端向服务器发送请求,如播放、暂停、停止、前进、后退等,以控制媒体流的播放和

    2024年02月16日
    浏览(57)
  • ffmpeg与opencv-python处理视频

    1.下载 FFmpeg 访问FFmpeg官方网站。 选择 “Windows builds from gyan.dev” 链接,这会带您到一个包含最新版本 FFmpeg Windows 构建的页面。 选择一个适合您系统的版本(例如,32位或64位),并下载 ZIP 文件。 2.解压文件 将下载的 ZIP 文件解压到选择的文件夹中。 3.添加 FFmpeg 到您的环境

    2024年01月22日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包