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

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

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;

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

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;}//获取音视频流通道IDfor (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服务器成功并成功拿到视频数据。

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);

再次运行,效果立现:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/228065.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2007~2016 年税调经纬度及其所处的省市区县乡镇数据

之前给大家分享过一份税调企业经纬度及其所处的省市区县数据: 2007~2016 年税调企业地理信息数据(含经纬度及其所处的省市区县):https://rstata.duanshu.com/#/course/76d38022cd004b09b2aa09647936beb0 最近有培训班的小伙伴提出是否能根据税调企业经纬度来判断其所属的乡…

Java:IO流——字节流和字符流

目录 IO流的基本概念 IO流体系结构 FileOutputStream字节输出流 构造方法 成员方法 细节 关流 FileInputStream字节输入流 构造方法及成员方法 read不带参数代码示例 read带参数代码示例​编辑 将字节数组或字符数组转成字符串 FileReader 字符输入流 构造方法和…

计算机速成课Crash Course - 14. 数据结构

今天继续计算机速成课Crash Course的系列讲解。 更多技术文章&#xff0c;公众号 “摸鱼IT” 锁定 -上午11点 - &#xff0c;感谢大家关注、转发、点赞&#xff01; 计算机速成课Crash Course - 13. 算法入门 14. 数据结构 上集讲了一些经典算法,比如给数组排序&#xff0c;…

回首2023: 程序员跳出舒适圈

1 前言 今天的冬日暖阳高照&#xff0c;照耀着我穿着羽绒服的身体&#xff0c;让我感到火一般的燥热&#xff0c;仿佛错觉中已经到了阳春三月。刚刚把孩子洗好&#xff0c;我坐在电脑前&#xff0c;准备整理一下思绪&#xff0c;回顾一下2023年的生活和工作。 2 2023 回顾 回…

Java毕业设计—springboot健身房管理系统

一、项目背景介绍&#xff1a; 随着人们生活水平的提高和健康意识的增强&#xff0c;健身行业逐渐兴起并迅速发展。而现代化的健身房管理系统已经成为健身房发展的必备工具之一。传统的健身房管理方式已经无法满足现代化健身房的需求&#xff0c;需要一种更加高效、智能、安全…

CentOS虚拟机硬盘管理

CentOS虚拟机硬盘管理 一、创建虚拟机时分配硬盘 创建虚拟机时&#xff0c;在下图这个页面需要重新选择一下硬盘&#xff0c;可以对硬盘进行配置。 默认自动分区 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e9ce72af3d934e75be95f7f86860e92b.png 选择确认分…

类的加载顺序问题-demo展示

面试的的时候经常会被问到包含静态代码块、实例代码块和构造器等代码结构的加载顺序问题&#xff0c;下面借用一个面试题&#xff0c;回顾一下类的代码加载顺序。 public class AooTest {public static void main(String[] args) {AooTest.f1();}static AooTest test1 new Ao…

免费的云服务器推荐~三丰云

对于许多初创企业和小型公司来说&#xff0c;寻找一个经济实惠且可靠的云服务提供商是至关重要的。在这方面&#xff0c;三丰云以其免费虚拟主机和云服务器吸引了大量用户。 1. 注册与界面 注册三丰云的账户过程简单明了&#xff0c;只需按照步骤填写必要信息即可。其界面设计…

Centos7:Jenkins+gitlab+node项目启动(1)

Centos7&#xff1a;Jenkinsgitlabnode项目启动(1) Centos7&#xff1a;Jenkinsgitlabnode项目启动(1)-CSDN博客 Centos7&#xff1a;Jenkinsgitlabnode项目启动(2) Centos7&#xff1a;Jenkinsgitlabnode项目启动(2)-CSDN博客 Centos7&#xff1a;Jenkinsgitlabnode项目启…

逻辑卷使用和扩容

1.逻辑卷管理LVM &#xff08; Logical Volume Manager&#xff09; 是 Linux 下对硬盘分区的一种管理机制 /boot分区用于存放引导文件&#xff0c;不能基于lvm创建 2.分区缺点&#xff1a; 无法动态扩容 必须使用连续的空间 没有备份 逻辑卷 lvm&#xff1a; 可以动态扩容…

Python高级用法:生成器(generator)

生成器&#xff08;generator&#xff09; 生成器是一种返回生成序列的方法&#xff0c;与直接使用列表等方式返回序列的方式不同的是&#xff0c;他的生成可以是无限的。 生成器可以与next搭配使用&#xff0c;可以被看作是一种特殊的迭代器。 yield语句 yield一般与循环相…

单集群400TB,OceanBase稳定支撑快手核心业务场景

一款日均超过千万人访问的短视频 App 快手&#xff0c;面对高并发流量如何及时有效地处理用户请求&#xff1f;通过在后端配置多套 MySQL 集群来支撑高流量访问&#xff0c;以解决大数据量存储和性能问题&#xff0c;这种传统的 MySQL 分库分表方案有何问题&#xff1f;快手对分…

【C++】STL 容器 - set 集合容器 ① ( set 集合容器简介 | set 集合容器操作的时间复杂度 | set 集合容器常用操作 )

文章目录 一、set 集合容器1、set 集合容器简介2、set 集合容器操作的时间复杂度3、set 集合容器常用操作 二、代码示例 - set 集合容器1、代码示例2、执行结果 一、set 集合容器 1、set 集合容器简介 C 语言中的 STL 容器中的 set 容器 , 是 " 集合容器 " , 容器中…

在Go语言中处理HTTP文件上传

大家好&#xff0c;我是你们可爱又迷人的编程小助手&#xff0c;今天要带你们一起探讨在Go语言中如何处理HTTP文件上传&#xff0c;让我们把这场技术之旅变得轻松有趣吧&#xff01; 首先&#xff0c;想象一下这个场景&#xff1a;你是一个网站的开发者&#xff0c;用户们急切…

使用 SSH 方式实现 Git 远程连接GitHub

git是目前世界上最先进的分布式版本控制系统&#xff0c;相比于SVN&#xff0c;分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在&#xff0c;也就是有没有联网都可以正常工作&#xff01;当有网络的时候&#xff0c;再把本地提交推送一下就完成了同步&…

MongoDB Certified Associate Developer 认证考试心得

介绍 前段时间通过了 MongoDB Associate Developer 考试&#xff0c;也记下了一些心得&#xff0c;结果忘记发出来了&#xff0c;现在重新整理下。通过考试后证书是这样的: MongoDB 目前有两个认证证书 1. MongoDB Associate Developer 认证掌握使用MongoDB 来构建现代应用…

【一分钟】ThinkPHP v6.0 (poc-yaml-thinkphp-v6-file-write)环境复现及poc解析

写在前面 一分钟表示是非常短的文章&#xff0c;只会做简单的描述。旨在用较短的时间获取有用的信息 环境下载 官方环境下载器&#xff1a;https://getcomposer.org/Composer-Setup.exe 下载文档时可以设置代理&#xff0c;不然下载不上&#xff0c;你懂的 下载成功 cmd cd…

Linux环境grep搜索方法记录

1 grep grep 命令&#xff0c;用来搜索字符串所在位置&#xff0c;可以具体到不同文件&#xff0c;不同行&#xff1b; 在Linux 下&#xff0c;查看命令释义如下 zhaocubuntu2004:~$ grep --help Usage: grep [OPTION]... PATTERNS [FILE]... Search for PATTERNS in each FI…

C#获取windows系统资源使用情况

1.前言 之前有一篇博客介绍如何获取Linux服务器上的资源使用情况《Java 获取服务器资源&#xff08;内存、负载、磁盘容量&#xff09;》&#xff0c;这里介绍如何通过C#获取Window系统的资源使用。 2.获取服务器资源 2.1.内存 [DllImport("kernel32.dll")][retu…

Maven项目提示Ignored pom.xml问题

1 环境 &#xff08;1&#xff09;IDEA开发工具&#xff1a;2022.2.1 &#xff08;2&#xff09;JDK&#xff1a;Java17&#xff08;Spring6要求JDK最低版本是Java17&#xff09; &#xff08;3&#xff09;Spring&#xff1a;6.1.2 &#xff08;4&#xff09;Maven 3.8.8 2 …