IJK播放rtsp流结束的时候未发送teardown标志,问题分析与解决方案

ijkplayer在播放rtsp流结束后,和后台的心跳连接一直在,没有被断开。通过抓包分析发现是因为没有发送teardown结束标志。

问题分析

我们先研究一下ffmpeg关闭rtsp流的过程。我们可以大致了解方法avformat_close_input(),可参考下面文章:

FFmpeg源代码分析:avformat_close_input()


通过跟踪代码我们发现具体过程如下图所示:




按照正常的流程,ffmpeg关闭rtsp流的时候,走了ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL)生成rtsp的完整TEARDOWN命令,然后通过tcp的send发送,通过抓包验证确实是发送了的

抓到的TEARDOWN信息:

TEARDOWN rtsp://192.168.2.82:8554/SubStream1 RTSP/1.0
CSeq: 7
User-Agent: Lavf57.56.100
Session: 162572178

Reply: RTSP/1.0 200 OK

那么为什么ijkplayer在播放rtsp流结束后没有发送呢?我们再具体分析一下ijkplayer调用ffmpeg关闭rtsp流的过程:




通过跟踪代码流程,我们发现,在走到retry_transfer_wrapper的时候调用ff_check_interrupt对URLContext的interrupt_callback进行了检测,结果不为NULL,就调用interrupt_callback后返回了,没有执行到tcp_write。而interrupt_callback是在前面的ijkmp_stop中设置的。

解决方案

ffmpeg官方已经对这个问题给出了patch:
然而并没有效果。
方案是增加了预关闭操作,为AVInputFormat添加了read_preclose成员方法,并在ff_rtsp_demuxer中定义了它的实现,就是rtsp_read_preclose,也就是调用ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL),方法调用的位置是在stream_close,而ijkplayer在播放rtsp流结束的时候,这个方法是在调用ijkmp_stop之后,所以仍然会有这个问题。
解决方法就是在调用ijkmp_stop之前通过avformat_preclose_input 调用这个方法:
添加方法:
int ffp_prestop_l(FFPlayer *ffp)
{
    assert(ffp);
    VideoState *is = ffp->is;
    if (is){
        avformat_preclose_input(&is->ic);
    }
    return 0;
}
在关闭player时,调用ijkmp_stop(_mediaPlayer)前增加此方法,也就是在android\ijkplayer_jni.c的IjkMediaPlayer_stop中增加ffp_prestop_l。
static void
IjkMediaPlayer_stop(JNIEnv *env, jobject thiz)
{
    IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
    JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: stop: null mp", LABEL_RETURN);
    ffp_prestop_l(mp->ffplayer);
    ijkmp_stop(mp);

LABEL_RETURN:
    ijkmp_dec_ref_p(&mp);
}



猜你喜欢

转载自blog.csdn.net/xuqiqiang1993/article/details/66973861
今日推荐