使用ffmpeg播放rtsp视频流

获取IPC摄像机视频流一般使用GB28181或者RTSP协议,这两款协议是比较常见的;两者都有开源的库,下面介绍如何使用RTSP获取进行IPC视频流;

  • 准备库

ffmepg是个开源的库,该库集成了rtsp协议,可以直接使用;首先需要编译该库,看使用的编译的平台或者编译工具,编译对应的库即可,如何编译,网上有很多方法,可以参考下,我们代码里面也会提供VS2017编译的库,可以直接使用;

window播放使用SDL库,怎么编译使用网上可以参考下;我们代码里面也会提供VS2017编译的库,可以直接使用;

  • 新建工程

这里使用的是VS2017进行编译;

(1)、新建工程,选择MFC应用程序;

(2)、选择基于对话框

(3)、配置编译环境

  • 代码实施

// rtspplayDlg.cpp: 实现文件
//#include "stdafx.h"
#include "rtspplay.h"
#include "rtspplayDlg.h"
#include "afxdialogex.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CrtspplayDlg 对话框CrtspplayDlg::CrtspplayDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_RTSPPLAY_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CrtspplayDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_STATIC_VIDEO, m_staVideo);
}BEGIN_MESSAGE_MAP(CrtspplayDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDOK, &CrtspplayDlg::OnBnClickedOk)ON_BN_CLICKED(IDCANCEL, &CrtspplayDlg::OnBnClickedCancel)ON_BN_CLICKED(IDC_BUTTON_STOPPLAY, &CrtspplayDlg::OnBnClickedButtonStopplay)
END_MESSAGE_MAP()// CrtspplayDlg 消息处理程序BOOL CrtspplayDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);			// 设置大图标SetIcon(m_hIcon, FALSE);		// 设置小图标WSADATA Ws;if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0) {return FALSE;}static BOOL sbInitFfmpeg = FALSE;if (FALSE == sbInitFfmpeg) {avformat_network_init();sbInitFfmpeg = TRUE;}return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CrtspplayDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。void CrtspplayDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CrtspplayDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}void CrtspplayDlg::OnBnClickedOk()
{if (mbRunning == FALSE) {mbRunning = TRUE;m_pVideoPlayWorker = AfxBeginThread((AFX_THREADPROC)VideoPlayThread, (LPVOID)this);((CButton*)GetDlgItem(IDOK))->EnableWindow(FALSE);}else {AfxMessageBox(L"正在播放");}//CDialogEx::OnOK();
}void CrtspplayDlg::OnBnClickedCancel()
{if (mbRunning == TRUE) {AfxMessageBox(L"请先停止播放");return;}CDialogEx::OnCancel();
}DWORD __cdecl CrtspplayDlg::VideoPlayThread(LPVOID pParam)
{CrtspplayDlg *pd = (CrtspplayDlg*)pParam;pd->RtspPlay();return 0;
}// Rtsp播放线程
void CrtspplayDlg::RtspPlay()
{int nTimeoutCnt = 0;CStringW strUrl;GetDlgItem(IDC_EDIT_RTSPADDR)->GetWindowText(strUrl);;//char filepath[] = "rtsp://xmygkj:7290@192.168.199.45/streaming?transportmode=unicast&amp;profile=Profile_13";//char filepath[] = "rtsp://xmygkj:7290@192.168.199.61/streaming?transportmode=unicast&amp;profile=Profile_17";//初始化m_pFormatCtx = avformat_alloc_context();AVDictionary* options = NULL;//av_dict_set(&options, "buffer_size", "1024000", 0); //设置缓存大小,1080p可将值跳到最大av_dict_set(&options, "rtsp_transport", "udp", 0); //以udp的方式打开,av_dict_set(&options, "stimeout", "5000000", 0); //设置超时断开链接时间,单位usav_dict_set(&options, "max_delay", "500000", 0); //设置最大时延m_pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));USES_CONVERSION;//打开网络流或文件流if (avformat_open_input(&m_pFormatCtx, W2A(strUrl), NULL, &options) != 0) {AfxMessageBox(L"打开网络流失败\n");return;}//查找码流信息if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0) {AfxMessageBox(L"查找码流信息失败\n");return;}//查找码流中是否有视频流int videoindex = -1;for (unsigned i = 0; i < m_pFormatCtx->nb_streams; i++) {if (m_pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {videoindex = i;break;}}if (videoindex == -1) {AfxMessageBox(L"找不到码流信息\n");return;}if (FALSE == InitFfmpeg()) {return;}//保存一段的时间视频,写到文件中while (mbRunning) {if (av_read_frame(m_pFormatCtx, m_pPacket) >= 0) {nTimeoutCnt = 0;int ret = 0;ret = avcodec_send_packet(m_pCodecCtx, m_pPacket);ret = avcodec_receive_frame(m_pCodecCtx, m_pFrame);if ((ret == 0) && (m_pCodecCtx->width > 0) && (m_pCodecCtx->height > 0)) {if (m_img_convert_ctx == NULL) {m_img_convert_ctx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt,m_stSdlRect.w, m_stSdlRect.h, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);m_nWidth = m_pCodecCtx->width;m_nHeight = m_pCodecCtx->height;}else {if ((m_pCodecCtx->width != m_nWidth) || (m_pCodecCtx->height != m_nHeight)) {if (m_img_convert_ctx != NULL) {sws_freeContext(m_img_convert_ctx);m_img_convert_ctx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt,m_stSdlRect.w, m_stSdlRect.h, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);m_nWidth = m_pCodecCtx->width;m_nHeight = m_pCodecCtx->height;}}}sws_scale(m_img_convert_ctx, (const uint8_t* const*)m_pFrame->data, m_pFrame->linesize, \0, m_pCodecCtx->height, m_pFrameYUV->data, m_pFrameYUV->linesize);SDL_UpdateYUVTexture(m_pSdlTexture, &m_stSdlRect,m_pFrameYUV->data[0], m_pFrameYUV->linesize[0],m_pFrameYUV->data[1], m_pFrameYUV->linesize[1],m_pFrameYUV->data[2], m_pFrameYUV->linesize[2]);SDL_RenderClear(m_pSdlRenderer);//SDL_RenderCopy( sdlRenderer, sdlTexture, &m_stSdlRect, &m_stSdlRect );  SDL_RenderCopy(m_pSdlRenderer, m_pSdlTexture, NULL, &m_stSdlRect);SDL_Rect sRect;memcpy(&sRect, &m_stSdlRect, sizeof(SDL_Rect));SDL_SetRenderDrawColor(m_pSdlRenderer, 0x00, 0x00, 0x00, 0x00);SDL_RenderPresent(m_pSdlRenderer);}av_packet_unref(m_pPacket);}else {Sleep(500);nTimeoutCnt++;if (nTimeoutCnt > 5) {AfxMessageBox(L"超时断开");}mbRunning = FALSE;}}SDL_RenderClear(m_pSdlRenderer);SDL_SetRenderDrawColor(m_pSdlRenderer, 0x00, 0x00, 0x00, 0x00);SDL_RenderPresent(m_pSdlRenderer);ReleaseFfmpeg();mbRunning = FALSE;
}BOOL CrtspplayDlg::InitFfmpeg()
{m_pCodec = (AVCodec*)avcodec_find_decoder(AV_CODEC_ID_H264);if (m_pCodec == NULL) {AfxMessageBox(L"找不到编码器");return FALSE;}m_pCodecCtx = avcodec_alloc_context3(m_pCodec);m_pCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;if (avcodec_open2(m_pCodecCtx, m_pCodec, nullptr) < 0) {AfxMessageBox(L"打开编码器失败");return FALSE;}m_pFrame = av_frame_alloc();m_pParser = av_parser_init(AV_CODEC_ID_H264);CRect staRect;m_staVideo.GetWindowRect(&staRect);m_stSdlRect.w = staRect.right - staRect.left;m_stSdlRect.h = staRect.bottom - staRect.top;m_pFrameYUV = av_frame_alloc();m_pOutBuffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, m_stSdlRect.w, m_stSdlRect.h, 1));av_image_fill_arrays(m_pFrameYUV->data, m_pFrameYUV->linesize, m_pOutBuffer, AV_PIX_FMT_YUV420P, m_stSdlRect.w, m_stSdlRect.h, 1);SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);//显示在MFC控件上m_pScreen = SDL_CreateWindowFrom(m_staVideo.GetSafeHwnd());if (!m_pScreen) {AfxMessageBox(L"创建窗体失败");return FALSE;}m_pSdlRenderer = SDL_CreateRenderer(m_pScreen, -1, 0);if (!m_pSdlRenderer) {AfxMessageBox(L"无法创建窗体");return FALSE;}//IYUV: Y + U + V  (3 planes)//YV12: Y + V + U  (3 planes)m_pSdlTexture = SDL_CreateTexture(m_pSdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, m_stSdlRect.w, m_stSdlRect.h);if (!m_pSdlTexture) {AfxMessageBox(L"创建纹理失败");return FALSE;}return TRUE;
}void CrtspplayDlg::ReleaseFfmpeg()
{//FIX Small Bug//SDL Hide Window When it finishedif (m_pFormatCtx != NULL) {avformat_close_input(&m_pFormatCtx);av_free(m_pFormatCtx);m_pFormatCtx = NULL;}if (NULL == m_pPacket) {av_free(m_pPacket);m_pPacket = NULL;}if (m_pSdlRenderer != NULL) {SDL_DestroyRenderer(m_pSdlRenderer);m_pSdlRenderer = NULL;}if (m_pSdlTexture != NULL) {SDL_DestroyTexture(m_pSdlTexture);m_pSdlTexture = NULL;}if (m_pScreen != NULL) {SDL_DestroyWindow(m_pScreen);m_pScreen = NULL;}if (m_img_convert_ctx != NULL) {sws_freeContext(m_img_convert_ctx);m_img_convert_ctx = NULL;}if (NULL != m_pFrameYUV) {av_frame_free(&m_pFrameYUV);m_pFrameYUV = NULL;}if (NULL != m_pCodecCtx) {avcodec_close(m_pCodecCtx);avcodec_free_context(&m_pCodecCtx);m_pCodecCtx = NULL;}if (m_pOutBuffer != NULL) {av_free(m_pOutBuffer);m_pOutBuffer = NULL;}
}void CrtspplayDlg::OnBnClickedButtonStopplay()
{mbRunning = FALSE;((CButton*)GetDlgItem(IDOK))->EnableWindow(TRUE);
}

  • 成果

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

python之数据结构与算法(数据结构篇)-- 队列

一、队列的概念 1.“队列”这个概念&#xff0c;其实可能我们在进行排队的时候是差不多的。 2.例如&#xff0c;在结账时&#xff0c;顾客按照到达顺序排队等待付款&#xff0c;这是现实生活中的一个队列示例。同样地&#xff0c;在计算机程序中&#xff0c;当多个任务需要按顺…

网卡多队列介绍

一、多队列网卡 1.1 什么是多队列网卡 多队列网卡&#xff08;Multi-Queue NIC&#xff09;支持将网络流量分发到多个处理队列中&#xff0c;以提高系统的网络处理性能和吞吐量。传统上&#xff0c;单队列网卡只有一个硬件队列用于处理所有的输入和输出流量&#xff0c;这可能…

文本语义分块、RAG 系统的分块难题:小型语言模型如何找到最佳断点?

文本语义分块、RAG 系统的分块难题&#xff1a;小型语言模型如何找到最佳断点&#xff1f; 转自jina最新的关于文本语义分块的分享和模型 之前我们聊过 RAG 里文档分块 (Chunking) 的挑战&#xff0c;也介绍了 迟分 (Late Chunking) 的概念&#xff0c;它可以在向量化的时候减…

linux 安装php扩展:xlswriter

这里以xlswriter扩展为例 进入官方扩展&#xff1a;https://pecl.php.net查询自己php对应版本的扩展包 下载扩展 wget https://pecl.php.net/get/xlswriter-1.5.5.tgz 解压扩展 tar -zxvf xlswriter-1.5.5.tgz 进入扩展目录 cd xlswriter-1.5.5 查找对应php版本的phpiz…

IPC原理

消息队列 什么是消息队列 消息队列接口 msgget msgctl ipcs -p msgsnd msgrcv code示例 发送到消息队列&#xff1a; #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include…

成为编程高手 day16

今天在学结构体 网上说可以用点号找到对应的地方&#xff0c;但是发现不可以&#xff0c;后来上网查资料 vs里面需要把cpp改成c才可以 我爱说实话&#xff0c;这玩意咋这么长。

U8C表体存货或编码相关的字段赋值不上

涉及到表体有存货相关字段的单据&#xff0c;如果更改源码或者客开的时候使用VO赋值&#xff0c;跟代码VO的存货字段有值&#xff0c;但是在前台不显示有值&#xff0c;或者说在前台显示没有值。 需要你在使用this.getBillCardPanel().setBillValueVO(oldVO);或者ui.getBillCa…

实战攻略 | ClickHouse优化之FINAL查询加速

【本文作者&#xff1a;擎创科技资深研发 禹鼎侯】 查询时为什么要加FINAL 我们在使用ClickHouse存储数据时&#xff0c;通常会有一些去重的需求&#xff0c;这时候我们可以使用ReplacingMergeTree引擎。这个引擎允许你存储重复数据&#xff0c;但是在merge的时候会根据order …

albert模型实现微信公众号虚假新闻分类

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…

nginx系列--(一)--调试环境搭建

辅助脚本&#xff1a; #!/bin/bash mkdir -p $(pwd)/nginxhome # 生成 Makefile,--prefix need a absolute path --with-stream表示要包括stream模块 auto/configure --prefix$(pwd)/nginxhome --with-stream # lsof -i tcp:10086 && fuser -k 10086/tcp ||true # 定…

Flutter开发者必备面试问题与答案06

Flutter开发者必备面试问题与答案06 视频 https://youtu.be/a1NAfSQrpIg https://www.bilibili.com/video/BV1g71KYREBN/ 前言 原文 Flutter 完整面试问题及答案06 本文是 flutter 面试问题的第六讲。 正文 51. 定义什么是 App State &#xff1f; 在 Flutter 中&#xf…

ElasticSearch学习篇16_《检索技术核心20讲》进阶篇之空间检索

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243&#xff0c;文档形式记录笔记。 相关问题&#xff1a; 查询范围固定的需求 直接计算两点之间距离区域二进制编码GeoHash编码 查询范围不固定的需求 GeoHash编码索引结构设计 基于…

element-plus按需引入报错IconsResolver is not a function

官网文档&#xff1a;element-plus-best-practices/vite.config.ts at db2dfc983ccda5570033a0ac608a1bd9d9a7f658 sxzz/element-plus-best-practices GitHubElement Plus Best Practices 最佳实践. Contribute to sxzz/element-plus-best-practices development by creating…

恢复Ubuntu+Windows10双系统安装前状态及分区还原详细步骤

1、恢复到安装 Ubuntu 之前的状态&#xff0c;先看看系统属性 2、选择 运行 3、 输入 msinfo32 回车 4、注意查看 BIOS 模式这一栏&#xff0c;UEFI&#xff0c;这里我们以UEFI系统为例 5、下来就可以开始进行 Ubuntu 的移除操作了 6、从Windows打开网页搜索磁盘精灵&#xff0…

SQL实战训练之,力扣:1843. 可疑银行账户

目录 一、力扣原题链接 二、题目描述 三、建表语句 四、题目分析 五、SQL解答 六、最终答案 七、验证 八、知识点 一、力扣原题链接 1843. 可疑银行账户 二、题目描述 表: Accounts ---------------------- | Column Name | Type | ---------------------- | acco…

酒店管理系统|基于java和小程序的酒店管理小程序系统设计与实现(源码+数据库+文档)

酒店管理小程序系统 目录 基于java和小程序的酒店管理小程序系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师…

Template Method(模板方法)

1)意图 定义一个操作中的算法骨架&#xff0c;而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 2)结构 模板方法模式的结构图如图7-47 所示。 其中: AbstractClass(抽象类) 定义抽象的原语操作&#xff0c;具体…

TrafficPeak | TB级云原生数据平台释放全新洞察力

Hydrolix是一款专为TB级工作负载设计的云原生数据平台&#xff0c;旨在消除现有解决方案中广泛存在的数据焦虑、技术障碍和限制等问题&#xff0c;为用户提供不受限制的实时可观察性。 最近&#xff0c;Hydrolix与Akamai联手&#xff0c;基于Akamai Connected Cloud提供完全托管…

C++ | Leetcode C++题解之第540题有序数组中的单一元素

题目&#xff1a; 题解&#xff1a; class Solution { public:int singleNonDuplicate(vector<int>& nums) {int low 0, high nums.size() - 1;while (low < high) {int mid (high - low) / 2 low;mid - mid & 1;if (nums[mid] nums[mid 1]) {low mid…

IT架构管理

目录 总则 IT架构管理目的 明确组织与职责 IT架构管理旨在桥接技术实施与业务需求之间的鸿沟&#xff0c;通过深入理解业务战略和技术能力&#xff0c;推动技术创新以支持业务增长&#xff0c;实现技术投资的最大价值。 设定目标与范围 IT架构管理的首要目的是确立清晰的组织…