SkeyeExPlayer(Windows) development series to solve the problem of stuck ffmpeg interface call

In the development process of SkeyeExPlayer, it is found that the interface of reading network flow and network data of ffmpeg has a high probability of blocking. Ffmpeg also provides methods such as setting blocking callback or setting timeout to jump out of blocking without permanently blocking the interface; In some cases, for example, when the network is disconnected for a long time, the blocking callback will no longer be useful, and the blocked interface will no longer return data, resulting in "permanent" fake death. For these problems, this paper will explain their processing methods one by one.

1. When the player ends, the interface causes the thread to get stuck
To solve this problem, we can usually set the exit flag in the blocking callback function of ffmpeg, as shown in the following code:

	//Player exit status flag, unblock
	if(pPlayer->player_status & PS_CLOSE)
	{
		return AVERROR_EOF;  
	}

2. The player is disconnected because the interface is stuck
This problem is what we usually call the treatment of disconnection and reconnection. You can go in two steps. The first step is to judge the timing of disconnection; The second line breaking treatment;
In the first step, it is generally recognized that the read network flow data is lost for a certain time as disconnection, and the blocking callback function is processed as follows:

	int64_t curTime = av_gettime();
	//5s timeout exit  
	if ((curTime - pPlayer->cur_read_time) > pPlayer->reconnect_time * 1000 * 1000)//5 second reconnection
	{
		pPlayer->error_flag = 1;
		char sErrorInfo[100] = { 0, };
		sprintf(sErrorInfo, "interrupt_cb() enter´╝îThe stream has been disconnected, attempting to reconnect......curtime=%lld\n", curTime);
		OutputDebugStringA(sErrorInfo);
		return AVERROR_STREAM_NOT_FOUND;//AVERROR_EOF;
	}

cur_read_time removes a current timestamp when initially reading the network stream, and AV_ read_ The timestamp of each frame is updated during frame. If the value is not updated after a certain time, we will determine that the network has been disconnected and set error_flag =1 for reconnection. The reconnection process is shown in the following code:

	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 and AV_ read_ Handling of permanent blocking of frame interface

After testing, the avformat provided by ffmpeg_ open_ Input and AV_ read_ There is a probability that the frame interface will be permanently blocked, that is, the callback function stops working and the function will not return permanently. The solution is to call the thread (of course, it is generally called by the thread under normal circumstances), and then forcibly terminate the thread when the player stops or is known to be stuck. It should be noted that forcibly terminating the thread may lead to access conflicts of resources such as memory, which needs to be handled flexibly.

Tags: network ffmpeg

Posted by bond00 on Wed, 18 May 2022 03:58:47 +0300