在SkeyeExPlayer的开发过程中,经测试发现ffmpeg的读取网络流以及网络数据的接口都有较大概率呈现阻塞的问题,ffmpeg也提供了设置阻塞回调或者设置超时等形式来跳出阻塞而不会导致接口永恒卡住;而在某些时候,比方,网络断开工夫过长的时候,这个时候阻塞回调将不在有用而且阻塞的接口也不再返回数据,呈现"永久性"假死的状况,针对这些问题,本文将对其解决形式进行一一解说。
1.播放器完结时接口导致线程卡住
针对该问题,咱们通常能够在ffmpeg的阻塞回调函数中设置退出标记来解决,如下代码所示:
//播放器退出状态标记,解除阻塞 if(pPlayer->player_status & PS_CLOSE) { return AVERROR_EOF; }
2.播放器因为接口卡住而呈现断线
这个问题也就是咱们通常状况下所说的断线重连的解决,断线重来你分两步走,第一步,判断呈现断线的机会;第二布,断线进行重连的解决;
第一步,通常认定读取的网络流数据失落肯定的工夫为断线,阻塞回调函数解决如下:
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进行重连,重连过程如下代码所示:
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接口有概率呈现永久性阻塞,即回调函数进行工作,该函数永久性不在返回的问题,解决办法就是线程调用(当然失常状况下也个别都是线程调用),而后在播放器进行或者已知为卡住的状况下强制完结线程,须要留神的是强制完结线程可能导致内存等资源拜访抵触的问题,须要灵活处理。