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
[email protected]
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel