Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SkeyeExPlayer(Windows)开发系列之解决ffmpeg接口调用卡住的问题

SkeyeExPlayer(Windows)开发系列之解决ffmpeg接口调用卡住的问题

原创
作者头像
Openskeye
发布于 2023-04-14 03:14:35
发布于 2023-04-14 03:14:35
6220
举报

在SkeyeExPlayer的开发过程中,经测试发现ffmpeg的读取网络流以及网络数据的接口都有较大概率出现阻塞的问题,ffmpeg也提供了设置阻塞回调或者设置超时等方式来跳出阻塞而不会导致接口永久卡住;而在某些时候,比如,网络断开时间过长的时候,这个时候阻塞回调将不在有用而且阻塞的接口也不再返回数据,出现"永久性"假死的情况,针对这些问题,本文将对其处理方式进行一一讲解。

1.播放器结束时接口导致线程卡住

针对该问题,我们通常可以在ffmpeg的阻塞回调函数中设置退出标志来解决,如下代码所示:

代码语言:txt
AI代码解释
复制
	//播放器退出状态标志,解除阻塞
	if(pPlayer->player_status & PS_CLOSE)
	{
		return AVERROR_EOF;  
	}

2.播放器因为接口卡住而出现断线

这个问题也就是我们通常情况下所说的断线重连的处理,断线重来你分两步走,第一步,判断出现断线的时机;第二布,断线进行重连的处理;

第一步,通常认定读取的网络流数据丢失一定的时间为断线,阻塞回调函数处理如下:

代码语言:txt
AI代码解释
复制
	int64_t curTime = av_gettime();
	//5s超时退出  
	if ((curTime - pPlayer->cur_read_time) > pPlayer->reconnect_time * 1000 * 1000)//5秒一次的重连
	{
		pPlayer->error_flag = 1;
		char sErrorInfo[100] = { 0, };
		sprintf(sErrorInfo, "interrupt_cb() enter,流已断开,正在尝试重连......curtime=%lld\n", curTime);
		OutputDebugStringA(sErrorInfo);
		return AVERROR_STREAM_NOT_FOUND;//AVERROR_EOF;
	}

cur_read_time在初始读取网络流时去一个当前的时间戳,以及av_read_frame时每一帧进行时间戳的更新,如果过一定的时间仍未更新该值,我们则认定网络已经断开,置error_flag =1进行重连,重连过程如下代码所示:

代码语言:txt
AI代码解释
复制
	while (!(player->player_status & PS_CLOSE))
	{
		usleep(1000*1000);
		if (player->error_flag>0)//must be sth error
		{
			if (player->player_status & PS_CLOSE)
				goto error_handler;

			player->b_ready = 0;
			player_pause(player);
			usleep(500*1000);

			if (player->player_status & PS_CLOSE)
				goto error_handler;

			int64_t media_duration = -1;
			int64_t media_seek_pos = -1;
			if (player->avformat_context)
				media_duration = (player->avformat_context->duration * 1000 / AV_TIME_BASE);	
			render_getparam(player->render, PARAM_MEDIA_POSITION, &media_seek_pos);
			if (media_seek_pos > 0)
				media_seek_pos += 500;

			if (player->acodec_context) avcodec_close(player->acodec_context);
			player->acodec_context = NULL;
			if (player->vcodec_context) avcodec_close(player->vcodec_context);
			player->vcodec_context = NULL;
			if (player->avformat_context)
			{
				avformat_close_input(&player->avformat_context);
				avformat_free_context(player->avformat_context);
			}
			player->avformat_context = NULL;

			//++ for avdevice
			char          *url = player->file_url;
			AVInputFormat *fmt = NULL;
			void		  *win = player->render_hwnd;

			player->avformat_context = avformat_alloc_context();
			player->avformat_context->interrupt_callback.callback = interrupt_cb;
			player->avformat_context->interrupt_callback.opaque = player;

			// open input file
			AVDictionary *options = NULL;
			//++ for find trsp
			if ((strstr(url, "rtsp") == url) || (strstr(url, "RTSP") == url))
			{
				if (player->link_mode == STREAM_LINK_TCP)
					av_dict_set(&options, "rtsp_transport", "tcp", 0);
				else
					av_dict_set(&options, "rtsp_transport", "udp", 0);
			}
			//-- for find trsp
			player->cur_read_time = av_gettime();
			if (avformat_open_input(&player->avformat_context, url, fmt, &options) != 0) 
			{
				continue;
				//goto error_handler;
			}
			if (player->player_status & PS_CLOSE)
				goto error_handler;

			// find stream info
			if (avformat_find_stream_info(player->avformat_context, NULL) < 0) 
			{
				continue;
				//goto error_handler;
			}
			if (player->player_status & PS_CLOSE)
				goto error_handler;

			// set current audio & video stream
			player->astream_index = -1; reinit_stream(player, AVMEDIA_TYPE_AUDIO, 0);
			player->vstream_index = -1; reinit_stream(player, AVMEDIA_TYPE_VIDEO, 0);

			// for audio
			if (player->astream_index != -1)
			{
				arate = player->acodec_context->sample_rate;
				aformat = player->acodec_context->sample_fmt;
				alayout = player->acodec_context->channel_layout;
				//++ fix audio channel layout issue
				if (alayout == 0) {
					alayout = av_get_default_channel_layout(player->acodec_context->channels);
				}
				//-- fix audio channel layout issue
			}

			// for video
			if (player->vstream_index != -1) {
				vrate = player->avformat_context->streams[player->vstream_index]->r_frame_rate;
				if (vrate.num / vrate.den >= 100) {
					vrate.num = 25;
					vrate.den = 1;
				}
				player->vcodec_context->pix_fmt = vformat;
				width = player->vcodec_context->width;
				height = player->vcodec_context->height;
			}

#if 0
			// open render
			player->render = render_open(ADEV_RENDER_TYPE_WAVEOUT, arate, (AVSampleFormat)aformat, alayout,
				player->render_mode/*VDEV_RENDER_TYPE_GDI*//*VDEV_RENDER_TYPE_D3D*/, win, vrate, vformat, width, height, player->speed);
			if (player->vstream_index == -1) {
				int effect = VISUAL_EFFECT_WAVEFORM;
				render_setparam(player->render, PARAM_VISUAL_EFFECT, &effect);
			}
#endif
			if (player->player_status & PS_CLOSE)
				goto error_handler;
			player->b_ready = 1;
		}
	}

3.avformat_open_input以及av_read_frame接口出现永久性阻塞的处理

经测试,ffmpeg提供的avformat_open_input以及av_read_frame接口有概率出现永久性阻塞,即回调函数停止工作,该函数永久性不在返回的问题,解决办法就是线程调用(当然正常情况下也一般都是线程调用),然后在播放器停止或者已知为卡住的情况下强制结束线程,需要注意的是强制结束线程可能导致内存等资源访问冲突的问题,需要灵活处理。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SkeyeExPlayer(Windows)开发之框架讲解
SkeyeExPlayer for Windows是基于ffmpeg进行开发的全功能播放器,开发过程中参考了很多开源的播放器,诸如vlc和ffplay等,其中最强大的莫过于vlc,但是鉴于vlc框架过于庞大而其中仍存在诸多问题而舍弃了,而其他的更倾向于演示demo,只能提供部分借鉴意义;故而,SkeyeExPlayer 一贯秉承Skeye系列小而精,接口简单功能强大的宗旨从新设计了一套框架,该套框架能适应多线程调用以及多个播放实例同时运行,和SkeyePlayer一样Skeye; 当然,在此也郑重的感谢各大开源播放器以及ffmpeg的作者的无私奉献。
Openskeye
2023/04/17
3730
ffmpeg+sdl播放类
前段时间一直捣鼓ffmpeg,觉得还是VLC比较亲切,虽然我现在都不知道VLC怎么用了。 除了雷神的博客,主要参考的还是这个博客:http://blog.yundiantech.com/?log=in
_gongluck
2018/03/08
1.2K0
Ffplay源码之read_thread解析(二)
大家好,我是小涂,本周继续给大家分享ffplay中的read_thread这个线程源码的解读,这算是自己的一个学习记录过程吧。
用户6280468
2022/03/21
6930
Ffplay源码之read_thread解析(二)
​FFmpeg 开发(15):学习如何使用 FFmpeg 打造一个自己的通用播放器?
前面 FFmpeg 系列的文章中,已经实现了 FFmpeg 的编译和集成,基于 FFmpeg 实现音视频的播放、录制,并结合 OpenGL 添加丰富的滤镜等功能,这些 demo 基本上将 FFmpeg 使用涉及到的知识点基本上覆盖了。
字节流动
2022/01/21
1.3K0
​FFmpeg 开发(15):学习如何使用 FFmpeg 打造一个自己的通用播放器?
FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode
https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff
gongluck
2019/06/15
1.2K0
C++ ffmpeg+dxva2实现硬解码「建议收藏」
0.前言 参考博客:ffmpeg实现dxva2硬件加速 下载源码:GitHub:https://github.com/Yacov-lu/ffmpeg-DXVA-decode
全栈程序员站长
2022/11/16
2.3K0
C++ ffmpeg+dxva2实现硬解码「建议收藏」
ffmpeg实战实现音视频解封装!
大家好,很长一段时间没有继续更新ffmpeg的相关技术文章了,最近更多的时间和精力主要集中在给自己不断灌入新的知识,所以接下来只要有时间就会疯狂输出所学习到的技术干货!
用户6280468
2022/03/21
1.2K0
ffmpeg实战实现音视频解封装!
ffmpeg中av_seek_frame使用样例(代码实现)
av_seek_frame使用时需要使用四个参数 av_seek_frame(fmt_ctx, -1 , 20 * AV_TIME_BASE, AVSEEK_FLAG_ANY); 参数一: fmt_ctx为容器内容; 参数二: 流索引, stream_index 参数三: 将要定位处的时间戳 参数四: seek功能flag 容器即AVFormatContext, 其中包含了一些视频标准格式中对应的封装信息,例如stream个数,stream类型,AVCodec,字幕信息等; stream_index,容器中包含了stream,有音频stream,视频stream,3D的电影有多个视频stream,subtitle stream等stream信息。 那么如果需要seek到对应的时间戳处需要考虑几个问题: 1. 得到AVFormatContext信息 2. 需要得到stream_index信息 3. 需要设定将要定位的时间戳信息 4. seek功能需要根据需求设置 结合上述四点,可以实现代码步骤如下: av_register_all(); avformat_open_input avformat_find_stream_info open_codec_context av_seek_frame 以上为简单的seek步骤,当然细节部分,还要有一些操作,例如avpackets相关的操作等 下面测试用例编译命令为
用户3765803
2019/03/05
3.8K0
ffmpeg中av_seek_frame使用样例(代码实现)
SkeyeExPlayer(Windows)开发系列之采用ffmpeg进行录像
这篇和ffmpeg进行截图类似,不过省略掉编码的过程,从网络上或者文件读取的数据为编码后的数据,直接进行写文件即可,本文以写MP4文件为例进行讲解。
Openskeye
2023/04/14
2560
ffmpeg直播项目
一个描述数字音视频传输和播放流程的简单步骤:录制 -> 编码 -> 网络传输 -> 解码 -> 播放
Gnep@97
2023/12/30
3680
ffmpeg直播项目
FFmpeg封装格式处理4-转封装例程
源码修改自 FFmpeg 4.1 自带的例程 remuxing.c。代码非常简短:
叶余
2019/04/02
1.1K0
FFmpeg封装格式处理4-转封装例程
11.QT-ffmpeg+QAudioOutput实现音频播放器
由于QAudioOutput支持的输入数据必须是原始数据,所以播放mp3,WAV,AAC等格式文件,需要解封装后才能支持播放.
诺谦
2020/09/14
2.9K0
【Android 音视频开发:FFmpeg音视频编解码篇】三、Android FFmpeg视频解码播放
本文很长,因为可能有比较多的小伙伴对 JNI C/C++ 不是很熟悉,所以本文比较详细的对 FFmpeg 用到的代码进行讲解,完整的演示了一遍 FFmpeg 的解码和渲染过程,并且对解码过程进行了封装。
开发的猫
2020/04/01
3.4K0
原创:学习如何使用 FFmpeg 打造自己的播放器
前面 FFmpeg 系列的文章中,已经实现了 FFmpeg 的编译和集成,基于 FFmpeg 实现音视频的播放、录制,并结合 OpenGL 添加丰富的滤镜等功能,这些 demo 基本上将 FFmpeg 使用涉及到的知识点基本上覆盖了。
字节流动
2022/02/09
2.4K0
原创:学习如何使用 FFmpeg 打造自己的播放器
AVFormatContext封装层:理论与实战
AVFormatContext 是一个贯穿始终的数据结构,很多函数都用到它作为参数,是输入输出相关信息的一个容器,本文讲解 AVFormatContext 的封装层,主要包括两大数据结构:AVInputFormat,AVOutputFormat。
Gnep@97
2023/12/06
6030
AVFormatContext封装层:理论与实战
基于FFmpeg进行RTMP推流(一)简介
这里的bin、include、lib就是我们刚才在FFmpeg下载的相关文件。 src是我们的项目源码目录。 新建Win32控制台应用程序、选择位置、项目名称。注意:去掉“为结局方案创建目录”的勾选
用户2929716
2018/08/23
14.3K0
基于FFmpeg进行RTMP推流(一)简介
FFMPEG硬件编解码器使用
     在前文《视频编解码硬件方案漫谈》中我们介绍硬件视频编解码的一般方案,本文我们进一步介绍音视频编解码如何在ffmpeg使用显卡硬件进行加速。
用户4148957
2022/06/14
4.2K0
FFMPEG硬件编解码器使用
FFmpeg简单转码程序--视频剪辑
学习了雷神的文章,慕斯人分享精神,感其英年而逝,不胜唏嘘。他有分享一个转码程序《最简单的基于FFMPEG的转码程序》其中使用了filter(参考了ffmpeg.c中的流程),他曾说想再编写一个不需要filter的版本,可惜未有机会。恰好工作中有相关ffmpeg处理内容,故狗尾续貂,撰写本文。
jacker
2018/09/01
8.7K0
FFmpeg简单转码程序--视频剪辑
FFmpeg 播放 RTSP/Webcam 流
本文将介绍 FFmpeg 如何播放 RTSP/Webcam/File 流。流程如下:
GoCoding
2021/09/09
1.7K0
SkeyeExPlayer(Windows)开发系列之采用ffmpeg进行截图
下面我们讲解下SkeyeExPlayer的截图功能,截图原理就是将YUV/RGB原始数据压缩成jpg或者png等格式(当然bmp格式是不需要压缩的),然后存储成文件的过程;我们以jpg格式为例进行讲解;一般情况下可以使用libjpeg库进行jpeg格式压缩,在不使用libjpeg的情况下,可以使用ffmpeg(内部也集成了libjpeg库)提供的接口进行压缩并写文件,这个方式也普遍适用于写MP4或者其他文件,下面我们讲解下ffmpeg进行截图的流程。
Openskeye
2023/04/14
3220
相关推荐
SkeyeExPlayer(Windows)开发之框架讲解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档