使用ffmpeg播放rtsp视频流

news2024/12/26 11:36:45

获取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_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	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); //设置超时断开链接时间,单位us
	av_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 finished
	if (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.coloradmin.cn/o/2238312.html

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

相关文章

【Linux探索学习】第十二弹——初识进程:进程的定义、描述和一些简单的相关操作

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 在前面经过那么多篇的铺垫后&#xff0c;今天我们正式进入Linux学习的第一个重难点——进程&#xff0c;理解进程对于我们学习操作系统的其…

AI教育革命:个性化学习的新篇章

内容概要 在 教育 领域&#xff0c;人工智能 的崭露头角带来了前所未有的变化。如今&#xff0c;个性化学习 已不再是一个遥不可及的梦想&#xff0c;而是通过 AI 技术真正实现的可能。借助先进的数据分析能力&#xff0c;教师可以实时跟踪和评估每位学生的学习进度&#xff0…

ASMR助眠声音视频素材去哪找 吃播助眠素材网站分享

在快节奏的现代生活中&#xff0c;越来越多的人感到压力山大&#xff0c;许多人开始寻求助眠和放松的方式。而ASMR&#xff08;自发性知觉经络反应&#xff09;助眠声音视频&#xff0c;凭借其独特的声音刺激和放松效果&#xff0c;成为了睡前的“神器”。如果你是一位内容创作…

项目管理中不可或缺的能力

在现代企业中&#xff0c;项目管理是一项至关重要的能力。项目管理需要具备的能力包括&#xff1a;有效的沟通能力、团队协作能力、时间管理能力、风险管理能力、以及问题解决能力。 其中&#xff0c;有效的沟通能力尤为重要&#xff0c;它不仅涉及到信息的传递&#xff0c;还包…

蓝桥杯备考——算法

一、排序 冒泡排序、选择排序、插入排序、 快速排序、归并排序、桶排序 二、枚举 三、二分查找与二分答案 四、搜索&#xff08;DFS&#xff09; DFS&#xff08;DFS基础、回溯、剪枝、记忆化&#xff09; 1.DFS算法&#xff08;深度优先搜索算法&#xff09; 深度优先搜…

【Vue】Vue3.0(十九)Vue 3.0 中一种组件间通信方式-自定义事件

文章目录 一、自定义事件概念及使用场景二、代码解释三、新的示例 一、自定义事件概念及使用场景 概念 在 Vue 3.0 中&#xff0c;自定义事件是一种组件间通信的机制&#xff0c;允许子组件向父组件传递数据或触发父组件中的操作。子组件通过defineEmits函数定义可以触发的事件…

成功解决WSL2上的Ubuntu22.04执行sudo apt-get update指令报错问题

问题&#xff1a;输入sudo apt-get update指令会显示如下报错 问题所在&#xff1a;Temporary failure in name resolution 显然是系统无法解析域名。这可能是 DNS 配置问题。 解决方案&#xff1a; 临时修改 DNS 配置 尝试手动修改 /etc/resolv.conf 文件来使用公共 DNS 服务…

L1G3000 提示工程(Prompt Engineering)

什么是Prompt(提示词)? Prompt是一种灵活、多样化的输入方式&#xff0c;可以用于指导大语言模型生成各种类型的内容。什么是提示工程? 提示工程是一种通过设计和调整输入(Prompts)来改善模型性能或控制其输出结果的技术。 六大基本原则: 指令要清晰提供参考内容复杂的任务拆…

探索Python的Shell力量:Plumbum库揭秘

文章目录 探索Python的Shell力量&#xff1a;Plumbum库揭秘第一部分&#xff1a;背景介绍第二部分&#xff1a;Plumbum是什么&#xff1f;第三部分&#xff1a;如何安装Plumbum&#xff1f;2. 创建管道3. 重定向4. 工作目录操作5. 前台和后台执行 第五部分&#xff1a;场景应用…

点击文本将内容填入tinymce-vue 富文本编辑器的光标处

富文本编辑器组件 <template><div ref"tinymceBox" class"tinymce-box"><Editor id"myEditor" v-model"contentValue" :init"init" :disabled"disabled" blur"inputBlur" click"o…

星海智算:风月ComfyUI_SD3.5

&#xff08;一&#xff09;镜像介绍 1、风月ComfyUI_SD3.5​ 占用69.71G磁盘&#xff0c;为用户预留了近30个G使用。 2、SD3.5​ SD3.5&#xff0c;即Stable Diffusion 3.5&#xff0c;是Stability AI推出的最新图像生成模型&#xff0c;是Stable Diffusion 3.0版本的升级版…

在模方置平建筑失败的原因是什么?

在模方置平建筑失败的原因是什么&#xff1f; 可能是obj拓扑不连续&#xff0c;可以在网格大师使用osgb转obj功能&#xff0c;选择拓扑或者重建。 网格大师是一款能够解决实景三维模型空间参考、原点、瓦块大小不统一&#xff0c;重叠区域处理问题的工具“百宝箱”&#xff0c…

python 语言入门

目录 1.发展历程 2.优缺点 3.环境搭建 3.1.Anaconda 3.2.VSCode 3.3.重装自己的独立环境 4.第一个 python 程序 4.1.创建一个 .py 的文件 4.2.编写 python 代码 ​4.3.运行 python 代码 5.注释 5.1.单行注释 5.2.多行注释 6.转义字符 7.变量 7.1.变量类型 7.2…

C++11 --- 智能指针详解

C11 智能指针 一、智能指针的使用场景分析二、RAII和智能指针的设计思路三、智能指针的本质及衍生的问题四、C标准库的智能指针的使用五、智能指针的原理&#xff08;模拟实现&#xff09;1. auto_ptr的模拟实现2. unique_ptr的模拟实现3. shared_ptr的模拟实现&#xff08;简单…

(实战)WebApi第13讲:怎么把不同表里的东西,包括同一个表里面不同的列设置成不同的实体,所有的给整合到一起?【前端+后端】、前端中点击标签后在界面中显示

一、实现全局跨域&#xff1a;新建一个Controller&#xff0c;其它的controller都继承它 1、新建BaseController 2、在后端配置&#xff0c;此处省略【详情见第12讲四、3、】 3、其它的控制器继承BaseController&#xff0c;这个时候就能够完成全局的跨域 【向后台传cookie和…

【C++】map和set的介绍及使用

前言&#xff1a; map和 set 是 C STL&#xff08;标准模板库&#xff09;中的两种非常重要的容器&#xff0c;它们基于一种叫做平衡二叉搜索树&#xff08;通常是红黑树&#xff09;的数据结构来实现。在 C 中&#xff0c;map 是一个键值对容器&#xff0c;set 只存储唯一的键…

Python的函数(补充浅拷贝和深拷贝)

一、定义 函数的定义&#xff1a;实现【特定功能】的代码块。 形参&#xff1a;函数定义时的参数&#xff0c;没有实际意义 实参&#xff1a;函数调用/使用时的参数&#xff0c;有实际意义 函数的作用&#xff1a; 简化代码提高代码重用性便于维护和修改提高代码的可扩展性…

el-input 正则表达式校验输入框不能输入汉字

<el-form :model"data1" :rules"rules" ref"ruleForm" label-width"210px" class"demo-ruleForm"><el-form-item label"锯路&#xff1a;" prop"sawKref"><el-input class"inptWid…

嵌入式linux系统中I2C控制实现AP3216C传感器方法

大家好,今天主要给大家分享一下,如何使用linux系统里面的I2C进行控制实现。 第一:Linux系统中I2C简介 Linux 内核开发者为了让驱动开发工程师在内核中方便的添加自己的 I2C 设备驱动程序,更容易的在 linux 下驱动自己的 I2C 接口硬件,进而引入了 I2C 总线框架。与 Linux 下…

OceanBase 应用实践:如何处理数据空洞,降低存储空间

问题描述 某保险行业客户的核心系统&#xff0c;从Oracle 迁移到OceanBase之后&#xff0c;发现数据存储空间出现膨胀问题&#xff0c;数据空间 datasize9857715.48M&#xff0c;实际存储占用空间17790702.00M。根据 required_mb - data_mb 值判断&#xff0c;数据空洞较为严重…