关于c++:SkeyeExPlayerWindows开发系列之解决ffmpeg接口调用卡住的问题

3次阅读

共计 4033 个字符,预计需要花费 11 分钟才能阅读完成。

在 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 接口有概率呈现永久性阻塞,即回调函数进行工作,该函数永久性不在返回的问题,解决办法就是线程调用(当然失常状况下也个别都是线程调用),而后在播放器进行或者已知为卡住的状况下强制完结线程,须要留神的是强制完结线程可能导致内存等资源拜访抵触的问题,须要灵活处理。

正文完
 0