When a client close a rtsp connexion, it is supposed to send the TEARDOWN packet according to https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol to tell to the server to stop the streaming. ffmpeg doesn't (controlled by wireshark, works with some other soft), while i guess it worked at a given time while the code to do it is inside ffmpeg.
In short, because my tv provider allows only 1 channel at a time, i've to wait 30 seconds between each channel change. However, even with 2 channels at a time, the traffic is higer for 30 secondes and there are troubles until the end of the server streaming of the 1st channel. Below, you'll find more detail, and a not perfect at all but working in my case patch to show the problem more in detail. How to reproduce: % ffplay 'rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=ld' The attached file is not really a patch while it's not the good way to fix. However, it works in my case. I found that ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); doesn't send the teardown packet, but the same command for DESCRIBE works, so i tried to call this function before (i supposed that something is closed too soon, or not flushed) : The thing is that rtsp::read_close function containing ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); is called after is->abort_request = 1; SDL_WaitThread(is->read_tid, NULL); and at the end, the packet is never sent. To quickly and badly fix, i've added a preclose function called before the sdl_waitthread. I'have troubleshooted this bug for 1 week, and only yesterday i finally found it was related to ffmpeg, so i'm new to the ffmpeg code, so for the moment, i don't the architecture and the best place to put the TEARDOWN packet. (at the end of the thread ? or at the same place but fixing something else). You guess i didn't found immediatly it was a missing teardown packet in the rtsp protocol (i'm new to that protocol too...).
diff --git a/ffplay.c b/ffplay.c index 4a084b4..c47cc95 100644 --- a/ffplay.c +++ b/ffplay.c @@ -1192,6 +1192,8 @@ static void stream_component_close(VideoState *is, int stream_index) static void stream_close(VideoState *is) { + avformat_preclose_input(&is->ic); + /* XXX: use a special url_shutdown call to abort parse cleanly */ is->abort_request = 1; SDL_WaitThread(is->read_tid, NULL); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index f3ffcfb..76d9402 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -693,6 +693,12 @@ typedef struct AVInputFormat { int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); /** + * The stream is going to be closed. The AVFormatContext and AVStreams are not + * freed by this function + */ + int (*read_preclose)(struct AVFormatContext *); + + /** * Close the stream. The AVFormatContext and AVStreams are not * freed by this function */ @@ -2231,6 +2237,14 @@ int av_read_play(AVFormatContext *s); int av_read_pause(AVFormatContext *s); /** + * Call before closing input AVFormatContext. + */ +void avformat_preclose_input(AVFormatContext **s); +/** + * @} + */ + +/** * Close an opened input AVFormatContext. Free it and all its contents * and set *s to NULL. */ diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index 3c0010e..17afd47 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -53,13 +53,20 @@ static const struct RTSPStatusMessage { { 0, "NULL" } }; -static int rtsp_read_close(AVFormatContext *s) +static int rtsp_read_preclose(AVFormatContext *s) { RTSPState *rt = s->priv_data; - if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) - ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); + if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) { + ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); + } + return 0; +} +static int rtsp_read_close(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + ff_rtsp_close_streams(s); ff_rtsp_close_connections(s); ff_network_close(); @@ -551,7 +558,7 @@ static int rtsp_read_play(AVFormatContext *s) ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK) { return ff_rtsp_averror(reply->status_code, -1); - } + } if (rt->transport == RTSP_TRANSPORT_RTP && reply->range_start != AV_NOPTS_VALUE) { for (i = 0; i < rt->nb_rtsp_streams; i++) { @@ -616,7 +623,7 @@ int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) } if (!content) return AVERROR_INVALIDDATA; - + av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); /* now we got the SDP description, we parse it */ ret = ff_sdp_parse(s, (const char *)content); @@ -966,6 +973,7 @@ AVInputFormat ff_rtsp_demuxer = { .read_probe = rtsp_probe, .read_header = rtsp_read_header, .read_packet = rtsp_read_packet, + .read_preclose = rtsp_read_preclose, .read_close = rtsp_read_close, .read_seek = rtsp_read_seek, .flags = AVFMT_NOFILE, diff --git a/libavformat/utils.c b/libavformat/utils.c index 689473e..853fb59 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -3707,6 +3707,20 @@ void avformat_free_context(AVFormatContext *s) av_free(s); } +void avformat_preclose_input(AVFormatContext **ps) +{ + AVFormatContext *s; + + if (!ps || !*ps) + return; + + s = *ps; + + if (s->iformat) + if (s->iformat->read_preclose) + s->iformat->read_preclose(s); +} + void avformat_close_input(AVFormatContext **ps) { AVFormatContext *s;
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel