mirror of
				https://github.com/nyanmisaka/ffmpeg-rockchip.git
				synced 2025-11-01 04:53:04 +08:00 
			
		
		
		
	Add Haivision SRT protocol
The protocol requires libsrt (https://github.com/Haivision/srt) to be installed Signed-off-by: Sven Dueking <sven.dueking@nablet.com> Signed-off-by: Luca Barbato <lu_zero@gentoo.org>
This commit is contained in:
		 Sven Dueking
					Sven Dueking
				
			
				
					committed by
					
						 Luca Barbato
						Luca Barbato
					
				
			
			
				
	
			
			
			 Luca Barbato
						Luca Barbato
					
				
			
						parent
						
							2124a97a49
						
					
				
				
					commit
					a2fc8dbae8
				
			| @@ -21,6 +21,7 @@ version <next>: | |||||||
| - NVIDIA CUVID-accelerated H.264 and HEVC decoding | - NVIDIA CUVID-accelerated H.264 and HEVC decoding | ||||||
| - Intel QSV-accelerated overlay filter | - Intel QSV-accelerated overlay filter | ||||||
| - AV1 Support through libaom | - AV1 Support through libaom | ||||||
|  | - Haivision SRT protocol via libsrt | ||||||
|  |  | ||||||
|  |  | ||||||
| version 12: | version 12: | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								configure
									
									
									
									
										vendored
									
									
								
							| @@ -213,6 +213,7 @@ External library support: | |||||||
|   --enable-libschroedinger   Dirac video encoding/decoding |   --enable-libschroedinger   Dirac video encoding/decoding | ||||||
|   --enable-libsnappy         snappy compression |   --enable-libsnappy         snappy compression | ||||||
|   --enable-libspeex          Speex audio encoding/decoding |   --enable-libspeex          Speex audio encoding/decoding | ||||||
|  |   --enable-libsrt            Haivision SRT protocol | ||||||
|   --enable-libtheora         Theora video encoding/decoding |   --enable-libtheora         Theora video encoding/decoding | ||||||
|   --enable-libtwolame        MP2 audio encoding |   --enable-libtwolame        MP2 audio encoding | ||||||
|   --enable-libvo-aacenc      AAC audio encoding |   --enable-libvo-aacenc      AAC audio encoding | ||||||
| @@ -1374,6 +1375,7 @@ EXTERNAL_LIBRARY_LIST=" | |||||||
|     libschroedinger |     libschroedinger | ||||||
|     libsnappy |     libsnappy | ||||||
|     libspeex |     libspeex | ||||||
|  |     libsrt | ||||||
|     libtheora |     libtheora | ||||||
|     libtwolame |     libtwolame | ||||||
|     libvorbis |     libvorbis | ||||||
| @@ -2525,6 +2527,8 @@ librtmpt_protocol_deps="librtmp" | |||||||
| librtmpte_protocol_deps="librtmp" | librtmpte_protocol_deps="librtmp" | ||||||
| mmsh_protocol_select="http_protocol" | mmsh_protocol_select="http_protocol" | ||||||
| mmst_protocol_select="network" | mmst_protocol_select="network" | ||||||
|  | libsrt_protocol_deps="libsrt" | ||||||
|  | libsrt_protocol_select="network" | ||||||
| rtmp_protocol_conflict="librtmp_protocol" | rtmp_protocol_conflict="librtmp_protocol" | ||||||
| rtmp_protocol_select="tcp_protocol" | rtmp_protocol_select="tcp_protocol" | ||||||
| rtmp_protocol_suggest="zlib" | rtmp_protocol_suggest="zlib" | ||||||
| @@ -4674,6 +4678,7 @@ enabled librtmp           && require_pkg_config librtmp librtmp librtmp/rtmp.h R | |||||||
| enabled libschroedinger   && require_pkg_config libschroedinger schroedinger-1.0 schroedinger/schro.h schro_init | enabled libschroedinger   && require_pkg_config libschroedinger schroedinger-1.0 schroedinger/schro.h schro_init | ||||||
| enabled libsnappy         && require libsnappy snappy-c.h snappy_compress -lsnappy | enabled libsnappy         && require libsnappy snappy-c.h snappy_compress -lsnappy | ||||||
| enabled libspeex          && require_pkg_config libspeex speex speex/speex.h speex_decoder_init | enabled libspeex          && require_pkg_config libspeex speex speex/speex.h speex_decoder_init | ||||||
|  | enabled libsrt            && require_pkg_config libsrt "srt >= 1.2.0" srt/srt.h srt_socket | ||||||
| enabled libtheora         && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg | enabled libtheora         && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg | ||||||
| enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame | enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame | ||||||
| enabled libvo_aacenc      && require libvo_aacenc vo-aacenc/voAAC.h voGetAACEncAPI -lvo-aacenc | enabled libvo_aacenc      && require libvo_aacenc vo-aacenc/voAAC.h voGetAACEncAPI -lvo-aacenc | ||||||
|   | |||||||
| @@ -655,6 +655,146 @@ To play back the first stream announced on one the default IPv6 SAP multicast ad | |||||||
| avplay sap://[ff0e::2:7ffe] | avplay sap://[ff0e::2:7ffe] | ||||||
| @end example | @end example | ||||||
|  |  | ||||||
|  | @section srt | ||||||
|  |  | ||||||
|  | Haivision Secure Reliable Transport Protocol via libsrt. | ||||||
|  |  | ||||||
|  | The supported syntax for a SRT URL is: | ||||||
|  | @example | ||||||
|  | srt://@var{hostname}:@var{port}[?@var{options}] | ||||||
|  | @end example | ||||||
|  |  | ||||||
|  | @var{options} contains a list of &-separated options of the form | ||||||
|  | @var{key}=@var{val}. | ||||||
|  |  | ||||||
|  | or | ||||||
|  |  | ||||||
|  | @example | ||||||
|  | @var{options} srt://@var{hostname}:@var{port} | ||||||
|  | @end example | ||||||
|  |  | ||||||
|  | @var{options} contains a list of '-@var{key} @var{val}' | ||||||
|  | options. | ||||||
|  |  | ||||||
|  | This protocol accepts the following options. | ||||||
|  |  | ||||||
|  | @table @option | ||||||
|  | @item connect_timeout | ||||||
|  | Connection timeout; SRT cannot connect for RTT > 1500 msec | ||||||
|  | (2 handshake exchanges) with the default connect timeout of | ||||||
|  | 3 seconds. This option applies to the caller and rendezvous | ||||||
|  | connection modes. The connect timeout is 10 times the value | ||||||
|  | set for the rendezvous mode (which can be used as a | ||||||
|  | workaround for this connection problem with earlier versions). | ||||||
|  |  | ||||||
|  | @item ffs=@var{bytes} | ||||||
|  | Flight Flag Size (Window Size), in bytes. FFS is actually an | ||||||
|  | internal parameter and you should set it to not less than | ||||||
|  | @option{recv_buffer_size} and @option{mss}. The default value | ||||||
|  | is relatively large, therefore unless you set a very large receiver buffer, | ||||||
|  | you do not need to change this option. Default value is 25600. | ||||||
|  |  | ||||||
|  | @item inputbw=@var{bytes/seconds} | ||||||
|  | Sender nominal input rate, in bytes per seconds. Used along with | ||||||
|  | @option{oheadbw}, when @option{maxbw} is set to relative (0), to | ||||||
|  | calculate maximum sending rate when recovery packets are sent | ||||||
|  | along with the main media stream: | ||||||
|  | @option{inputbw} * (100 + @option{oheadbw}) / 100 | ||||||
|  | if @option{inputbw} is not set while @option{maxbw} is set to | ||||||
|  | relative (0), the actual input rate is evaluated inside | ||||||
|  | the library. Default value is 0. | ||||||
|  |  | ||||||
|  | @item iptos=@var{tos} | ||||||
|  | IP Type of Service. Applies to sender only. Default value is 0xB8. | ||||||
|  |  | ||||||
|  | @item ipttl=@var{ttl} | ||||||
|  | IP Time To Live. Applies to sender only. Default value is 64. | ||||||
|  |  | ||||||
|  | @item listen_timeout | ||||||
|  | Set socket listen timeout. | ||||||
|  |  | ||||||
|  | @item maxbw=@var{bytes/seconds} | ||||||
|  | Maximum sending bandwidth, in bytes per seconds. | ||||||
|  | -1 infinite (CSRTCC limit is 30mbps) | ||||||
|  | 0 relative to input rate (see @option{inputbw}) | ||||||
|  | >0 absolute limit value | ||||||
|  | Default value is 0 (relative) | ||||||
|  |  | ||||||
|  | @item mode=@var{caller|listener|rendezvous} | ||||||
|  | Connection mode. | ||||||
|  | @option{caller} opens client connection. | ||||||
|  | @option{listener} starts server to listen for incoming connections. | ||||||
|  | @option{rendezvous} use Rendez-Vous connection mode. | ||||||
|  | Default value is caller. | ||||||
|  |  | ||||||
|  | @item mss=@var{bytes} | ||||||
|  | Maximum Segment Size, in bytes. Used for buffer allocation | ||||||
|  | and rate calculation using a packet counter assuming fully | ||||||
|  | filled packets. The smallest MSS between the peers is | ||||||
|  | used. This is 1500 by default in the overall internet. | ||||||
|  | This is the maximum size of the UDP packet and can be | ||||||
|  | only decreased, unless you have some unusual dedicated | ||||||
|  | network settings. Default value is 1500. | ||||||
|  |  | ||||||
|  | @item nakreport=@var{1|0} | ||||||
|  | If set to 1, Receiver will send `UMSG_LOSSREPORT` messages | ||||||
|  | periodically until a lost packet is retransmitted or | ||||||
|  | intentionally dropped. Default value is 1. | ||||||
|  |  | ||||||
|  | @item oheadbw=@var{percents} | ||||||
|  | Recovery bandwidth overhead above input rate, in percents. | ||||||
|  | See @option{inputbw}. Default value is 25%. | ||||||
|  |  | ||||||
|  | @item passphrase=@var{string} | ||||||
|  | HaiCrypt Encryption/Decryption Passphrase string, length | ||||||
|  | from 10 to 79 characters. The passphrase is the shared | ||||||
|  | secret between the sender and the receiver. It is used | ||||||
|  | to generate the Key Encrypting Key using PBKDF2 | ||||||
|  | (Password-Based Key Derivation Function). It is used | ||||||
|  | only if @option{pbkeylen} is non-zero. It is used on | ||||||
|  | the receiver only if the received data is encrypted. | ||||||
|  | The configured passphrase cannot be recovered (write-only). | ||||||
|  |  | ||||||
|  | @item pbkeylen=@var{bytes} | ||||||
|  | Sender encryption key length, in bytes. | ||||||
|  | Only can be set to 0, 16, 24 and 32. | ||||||
|  | Enable sender encryption if not 0. | ||||||
|  | Not required on receiver (set to 0), | ||||||
|  | key size obtained from sender in HaiCrypt handshake. | ||||||
|  | Default value is 0. | ||||||
|  |  | ||||||
|  | @item recv_buffer_size=@var{bytes} | ||||||
|  | Set receive buffer size, expressed in bytes. | ||||||
|  |  | ||||||
|  | @item send_buffer_size=@var{bytes} | ||||||
|  | Set send buffer size, expressed in bytes. | ||||||
|  |  | ||||||
|  | @item rw_timeout | ||||||
|  | Set raise error timeout for read/write optations. | ||||||
|  |  | ||||||
|  | This option is only relevant in read mode: | ||||||
|  | if no data arrived in more than this time | ||||||
|  | interval, raise error. | ||||||
|  |  | ||||||
|  | @item tlpktdrop=@var{1|0} | ||||||
|  | Too-late Packet Drop. When enabled on receiver, it skips | ||||||
|  | missing packets that have not been delivered in time and | ||||||
|  | delivers the following packets to the application when | ||||||
|  | their time-to-play has come. It also sends a fake ACK to | ||||||
|  | the sender. When enabled on sender and enabled on the | ||||||
|  | receiving peer, the sender drops the older packets that | ||||||
|  | have no chance of being delivered in time. It was | ||||||
|  | automatically enabled in the sender if the receiver | ||||||
|  | supports it. | ||||||
|  |  | ||||||
|  | @item tsbpddelay | ||||||
|  | Timestamp-based Packet Delivery Delay. | ||||||
|  | Used to absorb burst of missed packet retransmission. | ||||||
|  |  | ||||||
|  | @end table | ||||||
|  |  | ||||||
|  | For more information see: @url{https://github.com/Haivision/srt}. | ||||||
|  |  | ||||||
| @section tcp | @section tcp | ||||||
|  |  | ||||||
| Transmission Control Protocol. | Transmission Control Protocol. | ||||||
|   | |||||||
| @@ -414,6 +414,9 @@ OBJS-$(CONFIG_TLS_PROTOCOL)              += tls.o $(TLS-OBJS-yes) | |||||||
| OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o | OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o | ||||||
| OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o | OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o | ||||||
|  |  | ||||||
|  | # external libraries | ||||||
|  | OBJS-$(CONFIG_LIBSRT_PROTOCOL)           += libsrt.o | ||||||
|  |  | ||||||
| SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h | SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h | ||||||
| SKIPHEADERS-$(CONFIG_NETWORK)            += network.h rtsp.h | SKIPHEADERS-$(CONFIG_NETWORK)            += network.h rtsp.h | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										546
									
								
								libavformat/libsrt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										546
									
								
								libavformat/libsrt.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,546 @@ | |||||||
|  | /* | ||||||
|  |  * This file is part of Libav. | ||||||
|  |  * | ||||||
|  |  * Libav is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  |  * License as published by the Free Software Foundation; either | ||||||
|  |  * version 2.1 of the License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * Libav is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * Lesser General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Lesser General Public | ||||||
|  |  * License along with Libav; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @file | ||||||
|  |  * Haivision Open SRT (Secure Reliable Transport) protocol | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <srt/srt.h> | ||||||
|  |  | ||||||
|  | #include "libavutil/avassert.h" | ||||||
|  | #include "libavutil/opt.h" | ||||||
|  | #include "libavutil/parseutils.h" | ||||||
|  | #include "libavutil/time.h" | ||||||
|  |  | ||||||
|  | #include "avformat.h" | ||||||
|  | #include "internal.h" | ||||||
|  | #include "network.h" | ||||||
|  | #include "os_support.h" | ||||||
|  | #include "url.h" | ||||||
|  |  | ||||||
|  | enum SRTMode { | ||||||
|  |     SRT_MODE_CALLER = 0, | ||||||
|  |     SRT_MODE_LISTENER = 1, | ||||||
|  |     SRT_MODE_RENDEZVOUS = 2 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef struct SRTContext { | ||||||
|  |     const AVClass *class; | ||||||
|  |     int fd; | ||||||
|  |     int eid; | ||||||
|  |     int64_t rw_timeout; | ||||||
|  |     int64_t listen_timeout; | ||||||
|  |     int recv_buffer_size; | ||||||
|  |     int send_buffer_size; | ||||||
|  |  | ||||||
|  |     int64_t maxbw; | ||||||
|  |     int pbkeylen; | ||||||
|  |     char *passphrase; | ||||||
|  |     int mss; | ||||||
|  |     int ffs; | ||||||
|  |     int ipttl; | ||||||
|  |     int iptos; | ||||||
|  |     int64_t inputbw; | ||||||
|  |     int oheadbw; | ||||||
|  |     int64_t tsbpddelay; | ||||||
|  |     int tlpktdrop; | ||||||
|  |     int nakreport; | ||||||
|  |     int64_t connect_timeout; | ||||||
|  |     enum SRTMode mode; | ||||||
|  | } SRTContext; | ||||||
|  |  | ||||||
|  | #define D AV_OPT_FLAG_DECODING_PARAM | ||||||
|  | #define E AV_OPT_FLAG_ENCODING_PARAM | ||||||
|  | #define OFFSET(x) offsetof(SRTContext, x) | ||||||
|  | static const AVOption libsrt_options[] = { | ||||||
|  |     { "rw_timeout",     "Timeout of socket I/O operations",                                     OFFSET(rw_timeout),       AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, | ||||||
|  |     { "listen_timeout", "Connection awaiting timeout",                                          OFFSET(listen_timeout),   AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, | ||||||
|  |     { "send_buffer_size", "Socket send buffer size (in bytes)",                                 OFFSET(send_buffer_size), AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, INT_MAX,   .flags = D|E }, | ||||||
|  |     { "recv_buffer_size", "Socket receive buffer size (in bytes)",                              OFFSET(recv_buffer_size), AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, INT_MAX,   .flags = D|E }, | ||||||
|  |     { "maxbw",          "Maximum bandwidth (bytes per second) that the connection can use",     OFFSET(maxbw),            AV_OPT_TYPE_INT64,    { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, | ||||||
|  |     { "pbkeylen",       "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)",             OFFSET(pbkeylen),         AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 32,        .flags = D|E }, | ||||||
|  |     { "passphrase",     "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto",             OFFSET(passphrase),       AV_OPT_TYPE_STRING,   { .str = NULL },              .flags = D|E }, | ||||||
|  |     { "mss",            "The Maximum Segment Size",                                             OFFSET(mss),              AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1500,      .flags = D|E }, | ||||||
|  |     { "ffs",            "Flight flag size (window size) (in bytes)",                            OFFSET(ffs),              AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, INT_MAX,   .flags = D|E }, | ||||||
|  |     { "ipttl",          "IP Time To Live",                                                      OFFSET(ipttl),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E }, | ||||||
|  |     { "iptos",          "IP Type of Service",                                                   OFFSET(iptos),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E }, | ||||||
|  |     { "inputbw",        "Estimated input stream rate",                                          OFFSET(inputbw),          AV_OPT_TYPE_INT64,    { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, | ||||||
|  |     { "oheadbw",        "MaxBW ceiling based on % over input stream rate",                      OFFSET(oheadbw),          AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 100,       .flags = D|E }, | ||||||
|  |     { "tsbpddelay",     "TsbPd receiver delay to absorb burst of missed packet retransmission", OFFSET(tsbpddelay),       AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, | ||||||
|  |     { "tlpktdrop",      "Enable receiver pkt drop",                                             OFFSET(tlpktdrop),        AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1,         .flags = D|E }, | ||||||
|  |     { "nakreport",      "Enable receiver to send periodic NAK reports",                         OFFSET(nakreport),        AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1,         .flags = D|E }, | ||||||
|  |     { "connect_timeout", "Connect timeout. Caller default: 3000, rendezvous (x 10)",            OFFSET(connect_timeout),  AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, | ||||||
|  |     { "mode",           "Connection mode (caller, listener, rendezvous)",                       OFFSET(mode),             AV_OPT_TYPE_INT,      { .i64 = SRT_MODE_CALLER }, SRT_MODE_CALLER, SRT_MODE_RENDEZVOUS, .flags = D|E, "mode" }, | ||||||
|  |     { "caller",         NULL, 0, AV_OPT_TYPE_CONST,  { .i64 = SRT_MODE_CALLER },     INT_MIN, INT_MAX, .flags = D|E, "mode" }, | ||||||
|  |     { "listener",       NULL, 0, AV_OPT_TYPE_CONST,  { .i64 = SRT_MODE_LISTENER },   INT_MIN, INT_MAX, .flags = D|E, "mode" }, | ||||||
|  |     { "rendezvous",     NULL, 0, AV_OPT_TYPE_CONST,  { .i64 = SRT_MODE_RENDEZVOUS }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, | ||||||
|  |     { NULL } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int libsrt_neterrno(URLContext *h) | ||||||
|  | { | ||||||
|  |     int err = srt_getlasterror(NULL); | ||||||
|  |     av_log(h, AV_LOG_ERROR, "%s\n", srt_getlasterror_str()); | ||||||
|  |     if (err == SRT_EASYNCRCV) | ||||||
|  |         return AVERROR(EAGAIN); | ||||||
|  |     return AVERROR_UNKNOWN; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_socket_nonblock(int socket, int enable) | ||||||
|  | { | ||||||
|  |     int ret = srt_setsockopt(socket, 0, SRTO_SNDSYN, &enable, sizeof(enable)); | ||||||
|  |     if (ret < 0) | ||||||
|  |         return ret; | ||||||
|  |     return srt_setsockopt(socket, 0, SRTO_RCVSYN, &enable, sizeof(enable)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_network_wait_fd(URLContext *h, int eid, int fd, int write) | ||||||
|  | { | ||||||
|  |     int ret, len = 1; | ||||||
|  |     int modes = write ? SRT_EPOLL_OUT : SRT_EPOLL_IN; | ||||||
|  |     SRTSOCKET ready[1]; | ||||||
|  |  | ||||||
|  |     if (srt_epoll_add_usock(eid, fd, &modes) < 0) | ||||||
|  |         return libsrt_neterrno(h); | ||||||
|  |     if (write) { | ||||||
|  |         ret = srt_epoll_wait(eid, 0, 0, ready, &len, POLLING_TIME, 0, 0, 0, 0); | ||||||
|  |     } else { | ||||||
|  |         ret = srt_epoll_wait(eid, ready, &len, 0, 0, POLLING_TIME, 0, 0, 0, 0); | ||||||
|  |     } | ||||||
|  |     if (ret < 0) { | ||||||
|  |         if (srt_getlasterror(NULL) == SRT_ETIMEOUT) | ||||||
|  |             ret = AVERROR(EAGAIN); | ||||||
|  |         else | ||||||
|  |             ret = libsrt_neterrno(h); | ||||||
|  |     } else { | ||||||
|  |         ret = 0; | ||||||
|  |     } | ||||||
|  |     if (srt_epoll_remove_usock(eid, fd) < 0) | ||||||
|  |         return libsrt_neterrno(h); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* TODO de-duplicate code from ff_network_wait_fd_timeout() */ | ||||||
|  |  | ||||||
|  | static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) | ||||||
|  | { | ||||||
|  |     int ret; | ||||||
|  |     int64_t wait_start = 0; | ||||||
|  |  | ||||||
|  |     while (1) { | ||||||
|  |         if (ff_check_interrupt(int_cb)) | ||||||
|  |             return AVERROR_EXIT; | ||||||
|  |         ret = libsrt_network_wait_fd(h, eid, fd, write); | ||||||
|  |         if (ret != AVERROR(EAGAIN)) | ||||||
|  |             return ret; | ||||||
|  |         if (timeout > 0) { | ||||||
|  |             if (!wait_start) | ||||||
|  |                 wait_start = av_gettime_relative(); | ||||||
|  |             else if (av_gettime_relative() - wait_start > timeout) | ||||||
|  |                 return AVERROR(ETIMEDOUT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h, int timeout) | ||||||
|  | { | ||||||
|  |     int ret; | ||||||
|  |     int reuse = 1; | ||||||
|  |     if (srt_setsockopt(fd, SOL_SOCKET, SRTO_REUSEADDR, &reuse, sizeof(reuse))) { | ||||||
|  |         av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_REUSEADDR) failed\n"); | ||||||
|  |     } | ||||||
|  |     ret = srt_bind(fd, addr, addrlen); | ||||||
|  |     if (ret) | ||||||
|  |         return libsrt_neterrno(h); | ||||||
|  |  | ||||||
|  |     ret = srt_listen(fd, 1); | ||||||
|  |     if (ret) | ||||||
|  |         return libsrt_neterrno(h); | ||||||
|  |  | ||||||
|  |     while ((ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback))) { | ||||||
|  |         switch (ret) { | ||||||
|  |         case AVERROR(ETIMEDOUT): | ||||||
|  |             continue; | ||||||
|  |         default: | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = srt_accept(fd, NULL, NULL); | ||||||
|  |     if (ret < 0) | ||||||
|  |         return libsrt_neterrno(h); | ||||||
|  |     if (libsrt_socket_nonblock(ret, 1) < 0) | ||||||
|  |         av_log(h, AV_LOG_DEBUG, "libsrt_socket_nonblock failed\n"); | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_listen_connect(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h, int will_try_next) | ||||||
|  | { | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     if (libsrt_socket_nonblock(fd, 1) < 0) | ||||||
|  |         av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); | ||||||
|  |  | ||||||
|  |     while ((ret = srt_connect(fd, addr, addrlen))) { | ||||||
|  |         ret = libsrt_neterrno(h); | ||||||
|  |         switch (ret) { | ||||||
|  |         case AVERROR(EINTR): | ||||||
|  |             if (ff_check_interrupt(&h->interrupt_callback)) | ||||||
|  |                 return AVERROR_EXIT; | ||||||
|  |             continue; | ||||||
|  |         case AVERROR(EINPROGRESS): | ||||||
|  |         case AVERROR(EAGAIN): | ||||||
|  |             ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback); | ||||||
|  |             if (ret < 0) | ||||||
|  |                 return ret; | ||||||
|  |             ret = srt_getlasterror(NULL); | ||||||
|  |             srt_clearlasterror(); | ||||||
|  |             if (ret != 0) { | ||||||
|  |                 char buf[128]; | ||||||
|  |                 ret = AVERROR(ret); | ||||||
|  |                 av_strerror(ret, buf, sizeof(buf)); | ||||||
|  |                 if (will_try_next) | ||||||
|  |                     av_log(h, AV_LOG_WARNING, | ||||||
|  |                            "Connection to %s failed (%s), trying next address\n", | ||||||
|  |                            h->filename, buf); | ||||||
|  |                 else | ||||||
|  |                     av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", | ||||||
|  |                            h->filename, buf); | ||||||
|  |             } | ||||||
|  |         default: | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_setsockopt(URLContext *h, int fd, SRT_SOCKOPT optname, const char * optnamestr, const void * optval, int optlen) | ||||||
|  | { | ||||||
|  |     if (srt_setsockopt(fd, 0, optname, optval, optlen) < 0) { | ||||||
|  |         av_log(h, AV_LOG_ERROR, "failed to set option %s on socket: %s\n", optnamestr, srt_getlasterror_str()); | ||||||
|  |         return AVERROR(EIO); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* - The "POST" options can be altered any time on a connected socket. | ||||||
|  |      They MAY have also some meaning when set prior to connecting; such | ||||||
|  |      option is SRTO_RCVSYN, which makes connect/accept call asynchronous. | ||||||
|  |      Because of that this option is treated special way in this app. */ | ||||||
|  | static int libsrt_set_options_post(URLContext *h, int fd) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |  | ||||||
|  |     if ((s->inputbw >= 0 && libsrt_setsockopt(h, fd, SRTO_INPUTBW, "SRTO_INPUTBW", &s->inputbw, sizeof(s->inputbw)) < 0) || | ||||||
|  |         (s->oheadbw >= 0 && libsrt_setsockopt(h, fd, SRTO_OHEADBW, "SRTO_OHEADBW", &s->oheadbw, sizeof(s->oheadbw)) < 0)) { | ||||||
|  |         return AVERROR(EIO); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* - The "PRE" options must be set prior to connecting and can't be altered | ||||||
|  |      on a connected socket, however if set on a listening socket, they are | ||||||
|  |      derived by accept-ed socket. */ | ||||||
|  | static int libsrt_set_options_pre(URLContext *h, int fd) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |     int yes = 1; | ||||||
|  |     int tsbpddelay = s->tsbpddelay / 1000; | ||||||
|  |     int connect_timeout = s->connect_timeout; | ||||||
|  |  | ||||||
|  |     if ((s->mode == SRT_MODE_RENDEZVOUS && libsrt_setsockopt(h, fd, SRTO_RENDEZVOUS, "SRTO_RENDEZVOUS", &yes, sizeof(yes)) < 0) || | ||||||
|  |         (s->maxbw >= 0 && libsrt_setsockopt(h, fd, SRTO_MAXBW, "SRTO_MAXBW", &s->maxbw, sizeof(s->maxbw)) < 0) || | ||||||
|  |         (s->pbkeylen >= 0 && libsrt_setsockopt(h, fd, SRTO_PBKEYLEN, "SRTO_PBKEYLEN", &s->pbkeylen, sizeof(s->pbkeylen)) < 0) || | ||||||
|  |         (s->passphrase && libsrt_setsockopt(h, fd, SRTO_PASSPHRASE, "SRTO_PASSPHRASE", &s->passphrase, sizeof(s->passphrase)) < 0) || | ||||||
|  |         (s->mss >= 0 && libsrt_setsockopt(h, fd, SRTO_MSS, "SRTO_MMS", &s->mss, sizeof(s->mss)) < 0) || | ||||||
|  |         (s->ffs >= 0 && libsrt_setsockopt(h, fd, SRTO_FC, "SRTO_FC", &s->ffs, sizeof(s->ffs)) < 0) || | ||||||
|  |         (s->ipttl >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTTL, "SRTO_UPTTL", &s->ipttl, sizeof(s->ipttl)) < 0) || | ||||||
|  |         (s->iptos >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTOS, "SRTO_IPTOS", &s->iptos, sizeof(s->iptos)) < 0) || | ||||||
|  |         (tsbpddelay >= 0 && libsrt_setsockopt(h, fd, SRTO_TSBPDDELAY, "SRTO_TSBPDELAY", &tsbpddelay, sizeof(tsbpddelay)) < 0) || | ||||||
|  |         (s->tlpktdrop >= 0 && libsrt_setsockopt(h, fd, SRTO_TLPKTDROP, "SRTO_TLPKDROP", &s->tlpktdrop, sizeof(s->tlpktdrop)) < 0) || | ||||||
|  |         (s->nakreport >= 0 && libsrt_setsockopt(h, fd, SRTO_NAKREPORT, "SRTO_NAKREPORT", &s->nakreport, sizeof(s->nakreport)) < 0) || | ||||||
|  |         (connect_timeout >= 0 && libsrt_setsockopt(h, fd, SRTO_CONNTIMEO, "SRTO_CONNTIMEO", &connect_timeout, sizeof(connect_timeout)) <0 )) { | ||||||
|  |         return AVERROR(EIO); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int libsrt_setup(URLContext *h, const char *uri, int flags) | ||||||
|  | { | ||||||
|  |     struct addrinfo hints = { 0 }, *ai, *cur_ai; | ||||||
|  |     int port, fd = -1; | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |     const char *p; | ||||||
|  |     char buf[256]; | ||||||
|  |     int ret; | ||||||
|  |     char hostname[1024],proto[1024],path[1024]; | ||||||
|  |     char portstr[10]; | ||||||
|  |     int open_timeout = 5000000; | ||||||
|  |     int eid; | ||||||
|  |  | ||||||
|  |     eid = srt_epoll_create(); | ||||||
|  |     if (eid < 0) | ||||||
|  |         return libsrt_neterrno(h); | ||||||
|  |     s->eid = eid; | ||||||
|  |  | ||||||
|  |     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), | ||||||
|  |         &port, path, sizeof(path), uri); | ||||||
|  |     if (strcmp(proto, "srt")) | ||||||
|  |         return AVERROR(EINVAL); | ||||||
|  |     if (port <= 0 || port >= 65536) { | ||||||
|  |         av_log(h, AV_LOG_ERROR, "Port missing in uri\n"); | ||||||
|  |         return AVERROR(EINVAL); | ||||||
|  |     } | ||||||
|  |     p = strchr(uri, '?'); | ||||||
|  |     if (p) { | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { | ||||||
|  |             s->rw_timeout = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { | ||||||
|  |             s->listen_timeout = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (s->rw_timeout >= 0) { | ||||||
|  |         open_timeout = h->rw_timeout = s->rw_timeout; | ||||||
|  |     } | ||||||
|  |     hints.ai_family = AF_UNSPEC; | ||||||
|  |     hints.ai_socktype = SOCK_DGRAM; | ||||||
|  |     snprintf(portstr, sizeof(portstr), "%d", port); | ||||||
|  |     if (s->mode == SRT_MODE_LISTENER) | ||||||
|  |         hints.ai_flags |= AI_PASSIVE; | ||||||
|  |     ret = getaddrinfo(hostname[0] ? hostname : NULL, portstr, &hints, &ai); | ||||||
|  |     if (ret) { | ||||||
|  |         av_log(h, AV_LOG_ERROR, | ||||||
|  |                "Failed to resolve hostname %s: %s\n", | ||||||
|  |                hostname, gai_strerror(ret)); | ||||||
|  |         return AVERROR(EIO); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cur_ai = ai; | ||||||
|  |  | ||||||
|  |  restart: | ||||||
|  |  | ||||||
|  |     fd = srt_socket(cur_ai->ai_family, cur_ai->ai_socktype, 0); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         ret = libsrt_neterrno(h); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((ret = libsrt_set_options_pre(h, fd)) < 0) { | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Set the socket's send or receive buffer sizes, if specified. | ||||||
|  |        If unspecified or setting fails, system default is used. */ | ||||||
|  |     if (s->recv_buffer_size > 0) { | ||||||
|  |         srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size)); | ||||||
|  |     } | ||||||
|  |     if (s->send_buffer_size > 0) { | ||||||
|  |         srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size)); | ||||||
|  |     } | ||||||
|  |     if (s->mode == SRT_MODE_LISTENER) { | ||||||
|  |         // multi-client | ||||||
|  |         if ((ret = libsrt_listen(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, open_timeout / 1000)) < 0) | ||||||
|  |             goto fail1; | ||||||
|  |         fd = ret; | ||||||
|  |     } else { | ||||||
|  |         if (s->mode == SRT_MODE_RENDEZVOUS) { | ||||||
|  |             ret = srt_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); | ||||||
|  |             if (ret) | ||||||
|  |                 goto fail1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ((ret = libsrt_listen_connect(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, | ||||||
|  |                                           open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) { | ||||||
|  |             if (ret == AVERROR_EXIT) | ||||||
|  |                 goto fail1; | ||||||
|  |             else | ||||||
|  |                 goto fail; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if ((ret = libsrt_set_options_post(h, fd)) < 0) { | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     h->is_streamed = 1; | ||||||
|  |     s->fd = fd; | ||||||
|  |  | ||||||
|  |     freeaddrinfo(ai); | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  |  fail: | ||||||
|  |     if (cur_ai->ai_next) { | ||||||
|  |         /* Retry with the next sockaddr */ | ||||||
|  |         cur_ai = cur_ai->ai_next; | ||||||
|  |         if (fd >= 0) | ||||||
|  |             srt_close(fd); | ||||||
|  |         ret = 0; | ||||||
|  |         goto restart; | ||||||
|  |     } | ||||||
|  |  fail1: | ||||||
|  |     if (fd >= 0) | ||||||
|  |         srt_close(fd); | ||||||
|  |     freeaddrinfo(ai); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_open(URLContext *h, const char *uri, int flags) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |     const char * p; | ||||||
|  |     char buf[256]; | ||||||
|  |  | ||||||
|  |     if (srt_startup() < 0) { | ||||||
|  |         return AVERROR_UNKNOWN; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* SRT options (srt/srt.h) */ | ||||||
|  |     p = strchr(uri, '?'); | ||||||
|  |     if (p) { | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "maxbw", p)) { | ||||||
|  |             s->maxbw = strtoll(buf, NULL, 0); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "pbkeylen", p)) { | ||||||
|  |             s->pbkeylen = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) { | ||||||
|  |             s->passphrase = av_strndup(buf, strlen(buf)); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "mss", p)) { | ||||||
|  |             s->mss = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "ffs", p)) { | ||||||
|  |             s->ffs = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "ipttl", p)) { | ||||||
|  |             s->ipttl = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "iptos", p)) { | ||||||
|  |             s->iptos = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "inputbw", p)) { | ||||||
|  |             s->inputbw = strtoll(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "oheadbw", p)) { | ||||||
|  |             s->oheadbw = strtoll(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "tsbpddelay", p)) { | ||||||
|  |             s->tsbpddelay = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "tlpktdrop", p)) { | ||||||
|  |             s->tlpktdrop = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "nakreport", p)) { | ||||||
|  |             s->nakreport = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "connect_timeout", p)) { | ||||||
|  |             s->connect_timeout = strtol(buf, NULL, 10); | ||||||
|  |         } | ||||||
|  |         if (av_find_info_tag(buf, sizeof(buf), "mode", p)) { | ||||||
|  |             if (!strcmp(buf, "caller")) { | ||||||
|  |                 s->mode = SRT_MODE_CALLER; | ||||||
|  |             } else if (!strcmp(buf, "listener")) { | ||||||
|  |                 s->mode = SRT_MODE_LISTENER; | ||||||
|  |             } else if (!strcmp(buf, "rendezvous")) { | ||||||
|  |                 s->mode = SRT_MODE_RENDEZVOUS; | ||||||
|  |             } else { | ||||||
|  |                 return AVERROR(EIO); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return libsrt_setup(h, uri, flags); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_read(URLContext *h, uint8_t *buf, int size) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     if (!(h->flags & AVIO_FLAG_NONBLOCK)) { | ||||||
|  |         ret = libsrt_network_wait_fd_timeout(h, s->eid, s->fd, 0, h->rw_timeout, &h->interrupt_callback); | ||||||
|  |         if (ret) | ||||||
|  |             return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = srt_recvmsg(s->fd, buf, size); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         ret = libsrt_neterrno(h); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_write(URLContext *h, const uint8_t *buf, int size) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     if (!(h->flags & AVIO_FLAG_NONBLOCK)) { | ||||||
|  |         ret = libsrt_network_wait_fd_timeout(h, s->eid, s->fd, 1, h->rw_timeout, &h->interrupt_callback); | ||||||
|  |         if (ret) | ||||||
|  |             return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = srt_sendmsg(s->fd, buf, size, -1, 0); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         ret = libsrt_neterrno(h); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_close(URLContext *h) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |  | ||||||
|  |     srt_close(s->fd); | ||||||
|  |  | ||||||
|  |     srt_epoll_release(s->eid); | ||||||
|  |  | ||||||
|  |     srt_cleanup(); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int libsrt_get_file_handle(URLContext *h) | ||||||
|  | { | ||||||
|  |     SRTContext *s = h->priv_data; | ||||||
|  |     return s->fd; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const AVClass libsrt_class = { | ||||||
|  |     .class_name = "libsrt", | ||||||
|  |     .item_name  = av_default_item_name, | ||||||
|  |     .option     = libsrt_options, | ||||||
|  |     .version    = LIBAVUTIL_VERSION_INT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const URLProtocol ff_libsrt_protocol = { | ||||||
|  |     .name                = "srt", | ||||||
|  |     .url_open            = libsrt_open, | ||||||
|  |     .url_read            = libsrt_read, | ||||||
|  |     .url_write           = libsrt_write, | ||||||
|  |     .url_close           = libsrt_close, | ||||||
|  |     .url_get_file_handle = libsrt_get_file_handle, | ||||||
|  |     .priv_data_size      = sizeof(SRTContext), | ||||||
|  |     .flags               = URL_PROTOCOL_FLAG_NETWORK, | ||||||
|  |     .priv_data_class     = &libsrt_class, | ||||||
|  | }; | ||||||
| @@ -56,6 +56,7 @@ extern const URLProtocol ff_librtmpe_protocol; | |||||||
| extern const URLProtocol ff_librtmps_protocol; | extern const URLProtocol ff_librtmps_protocol; | ||||||
| extern const URLProtocol ff_librtmpt_protocol; | extern const URLProtocol ff_librtmpt_protocol; | ||||||
| extern const URLProtocol ff_librtmpte_protocol; | extern const URLProtocol ff_librtmpte_protocol; | ||||||
|  | extern const URLProtocol ff_libsrt_protocol; | ||||||
|  |  | ||||||
| #include "libavformat/protocol_list.c" | #include "libavformat/protocol_list.c" | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user