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.