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

这节课我们开始利用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(){TRACE("runFFmpeg...\n");AVCodec *vDecodec;AVCodecContext *vDecodeCtx = NULL;AVFormatContext *inFormatCtx = NULL;AVPacket normalPkt;const char *inFileName;int ret;int videoIndex = -1;	struct SwsContext* bgrSwsCtx = NULL;struct SwsContext* yuvSwsCtx = NULL;uint8_t* bgrBuff = NULL;uint8_t* yuvBuff = NULL;cv::Mat srcMat;int64_t startTime = 0;int64_t currentTime = 0;inRtmpURL = "rtmp://192.168.0.100/vod/xhn.mp4";CW2A  inputURL(inRtmpURL);char inputStr[1000] = { 0 };sprintf(inputStr, "%s", inputURL);inFileName = inputStr;	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;}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 bgrFrame = { 0 };bgrFrame.width = 640;bgrFrame.height = 480;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);AVFrame *deVideoFrame = av_frame_alloc();while (isRunning){ret = av_read_frame(inFormatCtx, &normalPkt);if (ret < 0){break;}	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);			}}av_dict_free(&options);avformat_close_input(&inFormatCtx);isRunning = false;	return 0;
}

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

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

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

相关文章

蓝牙物联网智能门控系统设计方案

随着电子信息技术的飞速发展&#xff0c;物联网技术提升到国家战略高度&#xff0c;研发和应用进程加速并不断取得实质性进展。物联网核心技术包括传感测试技术、网络通信技术、云计算等&#xff0c;具有广域覆盖、大容量、超低功耗和低成本等特点&#xff0c;目前在远程监控、…

Git使用教程 gittutorial

该教程对该文章的翻译&#xff1a;https://git-scm.com/docs/gittutorial 本文介绍怎用使用 Git 导入新的工程、修改文件及如何其他人同步开发。 首先&#xff0c; 可以使用以下指令获取文档帮助 git help log笔者注&#xff1a;不建议看这个文档&#xff0c;标准的语法介绍…

《Spring Cloud学习笔记:微服务保护Sentinel》

Review 解决了服务拆分之后的服务治理问题&#xff1a;Nacos解决了服务治理问题OpenFeign解决了服务之间的远程调用问题网关与前端进行交互&#xff0c;基于网关的过滤器解决了登录校验的问题 流量控制&#xff1a;避免因为突发流量而导致的服务宕机。 隔离和降级&#xff1a…

前后端分离下的鸿鹄电子招投标系统:使用Spring Boot、Mybatis、Redis和Layui实现源码与立项流程

在数字化时代&#xff0c;采购管理也正经历着前所未有的变革。全过程数字化采购管理成为了企业追求高效、透明和规范的关键。该系统通过Spring Cloud、Spring Boot2、Mybatis等先进技术&#xff0c;打造了从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通过…

【头歌实训】PySpark Streaming 数据源

文章目录 第1关&#xff1a;MySQL 数据源任务描述相关知识PySpark JDBC 概述PySpark JDBCPySpark Streaming JDBC 编程要求测试说明答案代码 第2关&#xff1a;Kafka 数据源任务描述相关知识Kafka 概述Kafka 使用基础PySpark Streaming Kafka 编程要求测试说明答案代码 第1关&a…

netty trojan

参考代码&#xff1a;https://github.com/kdyzm/trojan-client-netty 参考博客&#xff1a; github代码作者的博客&#xff1a;https://blog.kdyzm.cn/post/71 trojan-go介绍&#xff1a;https://p4gefau1t.github.io/trojan-go/developer/trojan/ trojan协议介绍&#xff1a;h…

2014年第三届数学建模国际赛小美赛A题吹口哨解题全过程文档及程序

2014年第三届数学建模国际赛小美赛 A题 吹口哨 原题再现&#xff1a; 哨子是一种小装置&#xff0c;当空气被迫通过开口时会发出声音。哨声的巨大而引人注目&#xff0c;使其对警察和体育裁判来说至关重要。当救生员、迷路的露营者或犯罪受害者使用它们时&#xff0c;它们可以…

创建springboot项目

SpringBoot 就相当于不需要配置文件的SpringSpringMVC。 常用的框架和第三方库都已经配置好了。 maven安装配置 管理项目依赖库的 maven的安装教程网上有很多&#xff0c;这里简单记录一下。 官网下载maven后并解压。 在其目录下添加一个目录repository 然后在conf目录下…

怎么制作有时效的文件二维码?二维码加密、有效期的设置技巧

在制作文件二维码的时候&#xff0c;如何设置文件的有效期呢&#xff1f;一般将文件生成二维码查看能够避免在微信或者QQ云端保存有时间限制的问题&#xff0c;而且扫码阅读文件或者下载文件也更加的方便。那么如果我们想要做一个文件类型的二维码&#xff0c;但是想要设置或者…

QT QString中mid()、left()、right()函数

mid函数原型&#xff1a; QString QString::mid(int position, int n -1) const 返回一个从position开始&#xff0c;长度为n的QString 类型的子串。position不能超出字符串长度&#xff0c;否则返回null&#xff1b;当从position开始的子串长度不够n或n为-1&#xff08;缺省…

list集合

List集合 List集合的概述 有序集合&#xff08;也称之为序列&#xff09;&#xff0c;用户可以精确的控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素&#xff0c;并搜索列表中的元素 与 Set 集合不同&#xff0c;列表通常允许重复的元素 List 集合的特点 有…

Redis分布式缓存之主从哨兵分片集群

Redis主从 数据同步原理 Redis哨兵 Redis分片集群 集群伸缩&#xff1a;在集群中插入或删除某个节点 集群故障转移

vue2、vue3状态管理之vuex、pinia

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、状态管理之vuex1.1 State调用&#xff1a;1.2 Mutation在vuex中定义&#xff1a;在组件中使用&#xff1a; 1.3 Action在vuex中定义&#xff1a;将上面的减…

性能测试-jemeter:安装 / 基础使用

一、理解jemeter 官网-Apache JMeter-Apache JMeter™ JMeter是一款开源的性能测试工具&#xff0c;主要用于模拟大量用户并发访问目标服务器&#xff0c;以评估服务器的性能和稳定性。 JMeter可以执行以下任务序号用途描述1性能测试通过模拟多个用户在同一时间对服务器进行…

【Graylog】通过Pipelines在Graylog生成IP地理位置信息

序 在当今数字化时代&#xff0c;随着网络攻击的不断增加和全球化的用户活动&#xff0c;了解IP地址的地理位置信息变得越来越重要。对于网络安全和营销策略来说&#xff0c;掌握IP地址的地理信息可以带来许多好处。 接下里将介绍如何通过Graylog的Pipelines功能&#xff0c;…

linux开放tomcat 8080端口

1、查看8080是否开放 firewall-cmd --query-port8080/tcp查看已开启的端口 firewall-cmd --list-ports开启防火墙 systemctl start firewalld2、永久开放8080端口 firewall-cmd --zonepublic --add-port8080/tcp --permanent3、重加载&#xff08;重启防火墙&#xff09; …

如何开发一个类似美团小程序商家入驻

上线类似美团的小程序可以推动商业生态系统的优化和升级。小程序可以连接商家、用户和平台&#xff0c;促进信息流通和交易&#xff0c;提高整个生态系统的效率和效益。今天&#xff0c;我们分享如何开发一个类似美团小程序商家入驻的平台。大家点个关注点个赞&#xff0c;我们…

本地登陆页面:对本地搜索词进行排名的策略

位置着陆页没有得到足够的尊重。 你用你的姓名、地址、电话号码和工作时间来设置它们。也许您嵌入了用于行车路线的 Google 地图。 也许你写了一些没人会读的副本&#xff0c;如果你有多个位置&#xff0c;你在每一页上重复相同的副本&#xff0c;只是更改位置名称。 如果你…

408数据结构错题知识点拾遗

个人向错题相关部分整理&#xff0c;涵盖真题、模拟、课后习题等。 408相关&#xff1a; 408数据结构错题知识点拾遗 408数据结构常考算法基础训练等待完善 408计算机组成原理错题知识点拾遗408操作系统错题知识点拾遗等待完善408计算机网络错题知识点拾遗 408计算机网络各层协…

DRF从入门到精通五(路由组件、认证组件、权限组件、频率组件及认证、权限源码分析)

文章目录 一、路由组件REST framework提供了两个routeraction装饰器 二、认证组件(Authentication)三、权限组件(Permissions)内置权限类 四、频率组件(Throttling)五、权限组件源码分析六、认证组件源码分析 一、路由组件 对于视图集ViewSetMixin&#xff0c;我们除了可以自己…