Opened 7 years ago

Last modified 3 weeks ago

#6471 new defect

RTMPS stream does not work

Reported by: Ruben Sanchez Castellano Owned by:
Priority: normal Component: avformat
Version: git-master Keywords: rtmps
Cc: adionc@gmail.com Blocked By:
Blocking: Reproduced by developer: yes
Analyzed by developer: no

Description

Using FFmpeg v3.3.1 and trying to open a stream to an RTMPS URL (Facebook with SSL actually) results in a few RTMP packets written to the output stream but then libavformat tries to read something in return. The thing is Facebook does not returns anything resulting in a connection hanged after 5-6 packets.
I solved this issue with this:

rtmpproto.c:3220 from:

    ret = ffurl_read(rt->stream, &c, 1);

to:

    //ret = ffurl_read(rt->stream, &c, 1);
    ret = AVERROR(EAGAIN));

This modification will force to not read anything from the server so the connection does not hang. With this fix I can see the RTMPS stream on Facebook Live preview and does not affect other RTMP streams.

Change History (7)

comment:1 by Adion, 5 years ago

I encountered the same problem, it appears to still exist in FFmpeg v4.1.3 and I think I found the underlying reason for this failure.

The full code is:

    /* set stream into nonblocking mode */
    rt->stream->flags |= AVIO_FLAG_NONBLOCK;

    /* try to read one byte from the stream */
    ret = ffurl_read(rt->stream, &c, 1);

    /* switch the stream back into blocking mode */
    rt->stream->flags &= ~AVIO_FLAG_NONBLOCK;

The problem is that the non-blocking stream flag is not passed down to underlying protocols.
In the case of rtmp, this is not a problem since the protocol is tcp directly.
In the case of rtmps, the protocol is one of the tls implementations, and (at least for securetransport on mac and schannel on windows) they both use a secondary URLProtocol to make the call to tcp.
On this secondary URLProtocol, the flag is not updated however.
Instead of commenting out the lines, I've fixed it in tls_read instead, but to fix it anywhere might require deeper changes (a function to update URLProtocol flags so that protocols know they have to change the flags in the protocols they use perhaps?)

Anyway, here are the changes I've made to temporarily fix this:
In tls_schannel.c tls_read function, change the ffurl_read function to:

int set_flag_nonblock = 0;
if (h->flags&AVIO_FLAG_NONBLOCK && !(s->tcp->flags&AVIO_FLAG_NONBLOCK)) {
  s->tcp->flags |= AVIO_FLAG_NONBLOCK;
  set_flag_nonblock = 1;
}
ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset,
                 c->enc_buf_size - c->enc_buf_offset);
if (set_flag_nonblock)
  s->tcp->flags &= ~AVIO_FLAG_NONBLOCK;

In tls_securetransport.c I did something similar for tls_read_cb.

I don't use openssl, but at first sight it seems to use a similar way so probably is affected as well.

Last edited 5 years ago by Adion (previous) (diff)

comment:2 by Carl Eugen Hoyos, 5 years ago

Keywords: facebook removed
Priority: importantnormal

Please send your patch - made with git format-patch - to the FFmpeg development mailing list.

comment:3 by Adion, 5 years ago

Cc: adionc@gmail.com added

Is it actually useful to send the patch in the way I did it?
The fix I did and tried is limited to schannel, and a similar one for securetransport.
There seem to be 4 other ssl implementations that are likely affected, and possibly more multi-layered protocols that might be affected too that I'm not aware of.

comment:4 by lebossejames, 4 years ago

Is it works now with ffmpeg?

comment:5 by Gyan, 2 years ago

Adion's workaround does the trick with SecureTransport backend.

If testers are available, I can submit that and similar patches for other TLS libs.

comment:6 by skliffmueller, 18 months ago

Reproduced by developer: set

I have had to use the fix described above for one of my projects, and can confirm both the existence of the problem, and the solution to be a working solution. Although I made my own patch in vcpkg. I will describe the process encase others find it of value.

I use vcpkg ffmpeg:x64-windows build in Visual Studio 2022. To make a vcpkg patch, you must first create a patch file <vcpkg_install_dir>/ports/ffmpeg/nonblock-tls.patch with the following diff contents:

diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c
index d4959f7..4c10dad 100644
--- a/libavformat/tls_schannel.c
+++ b/libavformat/tls_schannel.c
@@ -415,8 +415,16 @@ static int tls_read(URLContext *h, uint8_t *buf, int len)
             }
         }
 
-        ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset,
-                         c->enc_buf_size - c->enc_buf_offset);
+		int set_flag_nonblock = 0;
+		if (h->flags&AVIO_FLAG_NONBLOCK && !(s->tcp->flags&AVIO_FLAG_NONBLOCK)) {
+		  s->tcp->flags |= AVIO_FLAG_NONBLOCK;
+		  set_flag_nonblock = 1;
+		}
+		ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset,
+						 c->enc_buf_size - c->enc_buf_offset);
+		if (set_flag_nonblock)
+		  s->tcp->flags &= ~AVIO_FLAG_NONBLOCK;				 
+						 			 
         if (ret == AVERROR_EOF) {
             c->connection_closed = 1;
             ret = 0;
diff --git a/libavformat/tls_securetransport.c b/libavformat/tls_securetransport.c
index f6a1a5e..518100c 100644
--- a/libavformat/tls_securetransport.c
+++ b/libavformat/tls_securetransport.c
@@ -198,7 +198,16 @@ static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dat
     URLContext *h = (URLContext*)connection;
     TLSContext *c = h->priv_data;
     size_t requested = *dataLength;
+
+	int set_flag_nonblock = 0;
+	if (h->flags&AVIO_FLAG_NONBLOCK && !(c->tls_shared.tcp->flags&AVIO_FLAG_NONBLOCK)) {
+		c->tls_shared.tcp->flags |= AVIO_FLAG_NONBLOCK;
+		set_flag_nonblock = 1;
+	}
     int read = ffurl_read(c->tls_shared.tcp, data, requested);
+	if (set_flag_nonblock)
+		c->tls_shared.tcp->flags &= ~AVIO_FLAG_NONBLOCK;	
+	
     if (read <= 0) {
         *dataLength = 0;
         switch(AVUNERROR(read)) {

Then you must update the portfile.cmake file in the same directory <vcpkg_install_dir>/ports/ffmpeg/portfile.cmake There will be a list of .patch files under a PATCHES directive, add the nonblock-tls.patch file to that list for example:

if(VCPKG_TARGET_IS_WINDOWS)
    set(PATCHES 0017-Patch-for-ticket-9019-CUDA-Compile-Broken-Using-MSVC.patch)  # https://trac.ffmpeg.org/ticket/9019
endif()
vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO ffmpeg/ffmpeg
    REF n4.4.1
    SHA512 a53e617937f9892c5cfddb00896be9ad8a3e398dc7cf3b6c893b52ff38aff6ff0cbc61a44cd5f93d9a28f775e71ae82996a5e2b699a769c1de8f882aab34c797
    HEAD_REF master
    PATCHES
        0001-create-lib-libraries.patch
        0003-fix-windowsinclude.patch
        0004-fix-debug-build.patch
        0006-fix-StaticFeatures.patch
        0007-fix-lib-naming.patch
        0009-Fix-fdk-detection.patch
        0010-Fix-x264-detection.patch
        0011-Fix-x265-detection.patch
        0012-Fix-ssl-110-detection.patch
        0013-define-WINVER.patch
        0014-avfilter-dependency-fix.patch  # https://ffmpeg.org/pipermail/ffmpeg-devel/2021-February/275819.html
        0015-Fix-xml2-detection.patch
        0016-configure-dnn-needs-avformat.patch  # https://ffmpeg.org/pipermail/ffmpeg-devel/2021-May/279926.html
        ${PATCHES}
        0018-libaom-Dont-use-aom_codec_av1_dx_algo.patch
        0019-libx264-Do-not-explicitly-set-X264_API_IMPORTS.patch
        0020-fix-aarch64-libswscale.patch
        0021-fix-sdl2-version-check.patch
        nonblock-tls.patch
)

Now you must remove and then reinstall ffmpeg using vcpkg.

comment:7 by Dennis E. Mungai, 3 weeks ago

Yep, this bug is still present on ffmpeg 6.x series.

Note: See TracTickets for help on using tickets.