You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

616 lines
21KB

  1. /*
  2. * RTP network protocol
  3. * Copyright (c) 2002 Fabrice Bellard
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * FFmpeg is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with FFmpeg; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. /**
  22. * @file
  23. * RTP protocol
  24. */
  25. #include "libavutil/parseutils.h"
  26. #include "libavutil/avstring.h"
  27. #include "libavutil/opt.h"
  28. #include "avformat.h"
  29. #include "avio_internal.h"
  30. #include "rtp.h"
  31. #include "rtpproto.h"
  32. #include "url.h"
  33. #include <stdarg.h>
  34. #include "internal.h"
  35. #include "network.h"
  36. #include "os_support.h"
  37. #include <fcntl.h>
  38. #if HAVE_POLL_H
  39. #include <sys/poll.h>
  40. #endif
  41. typedef struct RTPContext {
  42. const AVClass *class;
  43. URLContext *rtp_hd, *rtcp_hd;
  44. int rtp_fd, rtcp_fd, nb_ssm_include_addrs, nb_ssm_exclude_addrs;
  45. struct sockaddr_storage **ssm_include_addrs, **ssm_exclude_addrs;
  46. int write_to_source;
  47. struct sockaddr_storage last_rtp_source, last_rtcp_source;
  48. socklen_t last_rtp_source_len, last_rtcp_source_len;
  49. int ttl;
  50. int buffer_size;
  51. int rtcp_port, local_rtpport, local_rtcpport;
  52. int connect;
  53. int pkt_size;
  54. int dscp;
  55. char *sources;
  56. char *block;
  57. } RTPContext;
  58. #define OFFSET(x) offsetof(RTPContext, x)
  59. #define D AV_OPT_FLAG_DECODING_PARAM
  60. #define E AV_OPT_FLAG_ENCODING_PARAM
  61. static const AVOption options[] = {
  62. { "ttl", "Time to live (in milliseconds, multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  63. { "buffer_size", "Send/Receive buffer size (in bytes)", OFFSET(buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  64. { "rtcp_port", "Custom rtcp port", OFFSET(rtcp_port), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  65. { "local_rtpport", "Local rtp port", OFFSET(local_rtpport), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  66. { "local_rtcpport", "Local rtcp port", OFFSET(local_rtcpport), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  67. { "connect", "Connect socket", OFFSET(connect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
  68. { "write_to_source", "Send packets to the source address of the latest received packet", OFFSET(write_to_source), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
  69. { "pkt_size", "Maximum packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  70. { "dscp", "DSCP class", OFFSET(dscp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
  71. { "sources", "Source list", OFFSET(sources), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E },
  72. { "block", "Block list", OFFSET(block), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E },
  73. { NULL }
  74. };
  75. static const AVClass rtp_class = {
  76. .class_name = "rtp",
  77. .item_name = av_default_item_name,
  78. .option = options,
  79. .version = LIBAVUTIL_VERSION_INT,
  80. };
  81. /**
  82. * If no filename is given to av_open_input_file because you want to
  83. * get the local port first, then you must call this function to set
  84. * the remote server address.
  85. *
  86. * @param h media file context
  87. * @param uri of the remote server
  88. * @return zero if no error.
  89. */
  90. int ff_rtp_set_remote_url(URLContext *h, const char *uri)
  91. {
  92. RTPContext *s = h->priv_data;
  93. char hostname[256];
  94. int port, rtcp_port;
  95. const char *p;
  96. char buf[1024];
  97. char path[1024];
  98. av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
  99. path, sizeof(path), uri);
  100. rtcp_port = port + 1;
  101. p = strchr(uri, '?');
  102. if (p) {
  103. if (av_find_info_tag(buf, sizeof(buf), "rtcpport", p)) {
  104. rtcp_port = strtol(buf, NULL, 10);
  105. }
  106. }
  107. ff_url_join(buf, sizeof(buf), "udp", NULL, hostname, port, "%s", path);
  108. ff_udp_set_remote_url(s->rtp_hd, buf);
  109. ff_url_join(buf, sizeof(buf), "udp", NULL, hostname, rtcp_port, "%s", path);
  110. ff_udp_set_remote_url(s->rtcp_hd, buf);
  111. return 0;
  112. }
  113. static struct addrinfo* rtp_resolve_host(const char *hostname, int port,
  114. int type, int family, int flags)
  115. {
  116. struct addrinfo hints = { 0 }, *res = 0;
  117. int error;
  118. char service[16];
  119. snprintf(service, sizeof(service), "%d", port);
  120. hints.ai_socktype = type;
  121. hints.ai_family = family;
  122. hints.ai_flags = flags;
  123. if ((error = getaddrinfo(hostname, service, &hints, &res))) {
  124. res = NULL;
  125. av_log(NULL, AV_LOG_ERROR, "rtp_resolve_host: %s\n", gai_strerror(error));
  126. }
  127. return res;
  128. }
  129. static int compare_addr(const struct sockaddr_storage *a,
  130. const struct sockaddr_storage *b)
  131. {
  132. if (a->ss_family != b->ss_family)
  133. return 1;
  134. if (a->ss_family == AF_INET) {
  135. return (((const struct sockaddr_in *)a)->sin_addr.s_addr !=
  136. ((const struct sockaddr_in *)b)->sin_addr.s_addr);
  137. }
  138. #if HAVE_STRUCT_SOCKADDR_IN6
  139. if (a->ss_family == AF_INET6) {
  140. const uint8_t *s6_addr_a = ((const struct sockaddr_in6 *)a)->sin6_addr.s6_addr;
  141. const uint8_t *s6_addr_b = ((const struct sockaddr_in6 *)b)->sin6_addr.s6_addr;
  142. return memcmp(s6_addr_a, s6_addr_b, 16);
  143. }
  144. #endif
  145. return 1;
  146. }
  147. static int get_port(const struct sockaddr_storage *ss)
  148. {
  149. if (ss->ss_family == AF_INET)
  150. return ntohs(((const struct sockaddr_in *)ss)->sin_port);
  151. #if HAVE_STRUCT_SOCKADDR_IN6
  152. if (ss->ss_family == AF_INET6)
  153. return ntohs(((const struct sockaddr_in6 *)ss)->sin6_port);
  154. #endif
  155. return 0;
  156. }
  157. static void set_port(struct sockaddr_storage *ss, int port)
  158. {
  159. if (ss->ss_family == AF_INET)
  160. ((struct sockaddr_in *)ss)->sin_port = htons(port);
  161. #if HAVE_STRUCT_SOCKADDR_IN6
  162. else if (ss->ss_family == AF_INET6)
  163. ((struct sockaddr_in6 *)ss)->sin6_port = htons(port);
  164. #endif
  165. }
  166. static int rtp_check_source_lists(RTPContext *s, struct sockaddr_storage *source_addr_ptr)
  167. {
  168. int i;
  169. if (s->nb_ssm_exclude_addrs) {
  170. for (i = 0; i < s->nb_ssm_exclude_addrs; i++) {
  171. if (!compare_addr(source_addr_ptr, s->ssm_exclude_addrs[i]))
  172. return 1;
  173. }
  174. }
  175. if (s->nb_ssm_include_addrs) {
  176. for (i = 0; i < s->nb_ssm_include_addrs; i++) {
  177. if (!compare_addr(source_addr_ptr, s->ssm_include_addrs[i]))
  178. return 0;
  179. }
  180. return 1;
  181. }
  182. return 0;
  183. }
  184. /**
  185. * add option to url of the form:
  186. * "http://host:port/path?option1=val1&option2=val2...
  187. */
  188. static av_printf_format(3, 4) void url_add_option(char *buf, int buf_size, const char *fmt, ...)
  189. {
  190. char buf1[1024];
  191. va_list ap;
  192. va_start(ap, fmt);
  193. if (strchr(buf, '?'))
  194. av_strlcat(buf, "&", buf_size);
  195. else
  196. av_strlcat(buf, "?", buf_size);
  197. vsnprintf(buf1, sizeof(buf1), fmt, ap);
  198. av_strlcat(buf, buf1, buf_size);
  199. va_end(ap);
  200. }
  201. static void build_udp_url(RTPContext *s,
  202. char *buf, int buf_size,
  203. const char *hostname,
  204. int port, int local_port,
  205. const char *include_sources,
  206. const char *exclude_sources)
  207. {
  208. ff_url_join(buf, buf_size, "udp", NULL, hostname, port, NULL);
  209. if (local_port >= 0)
  210. url_add_option(buf, buf_size, "localport=%d", local_port);
  211. if (s->ttl >= 0)
  212. url_add_option(buf, buf_size, "ttl=%d", s->ttl);
  213. if (s->buffer_size >= 0)
  214. url_add_option(buf, buf_size, "buffer_size=%d", s->buffer_size);
  215. if (s->pkt_size >= 0)
  216. url_add_option(buf, buf_size, "pkt_size=%d", s->pkt_size);
  217. if (s->connect)
  218. url_add_option(buf, buf_size, "connect=1");
  219. if (s->dscp >= 0)
  220. url_add_option(buf, buf_size, "dscp=%d", s->dscp);
  221. url_add_option(buf, buf_size, "fifo_size=0");
  222. if (include_sources && include_sources[0])
  223. url_add_option(buf, buf_size, "sources=%s", include_sources);
  224. if (exclude_sources && exclude_sources[0])
  225. url_add_option(buf, buf_size, "block=%s", exclude_sources);
  226. }
  227. static void rtp_parse_addr_list(URLContext *h, char *buf,
  228. struct sockaddr_storage ***address_list_ptr,
  229. int *address_list_size_ptr)
  230. {
  231. struct addrinfo *ai = NULL;
  232. struct sockaddr_storage *source_addr;
  233. char tmp = '\0', *p = buf, *next;
  234. /* Resolve all of the IPs */
  235. while (p && p[0]) {
  236. next = strchr(p, ',');
  237. if (next) {
  238. tmp = *next;
  239. *next = '\0';
  240. }
  241. ai = rtp_resolve_host(p, 0, SOCK_DGRAM, AF_UNSPEC, 0);
  242. if (ai) {
  243. source_addr = av_mallocz(sizeof(struct sockaddr_storage));
  244. if (!source_addr) {
  245. freeaddrinfo(ai);
  246. break;
  247. }
  248. memcpy(source_addr, ai->ai_addr, ai->ai_addrlen);
  249. freeaddrinfo(ai);
  250. dynarray_add(address_list_ptr, address_list_size_ptr, source_addr);
  251. } else {
  252. av_log(h, AV_LOG_WARNING, "Unable to resolve %s\n", p);
  253. }
  254. if (next) {
  255. *next = tmp;
  256. p = next + 1;
  257. } else {
  258. p = NULL;
  259. }
  260. }
  261. }
  262. /**
  263. * url syntax: rtp://host:port[?option=val...]
  264. * option: 'ttl=n' : set the ttl value (for multicast only)
  265. * 'rtcpport=n' : set the remote rtcp port to n
  266. * 'localrtpport=n' : set the local rtp port to n
  267. * 'localrtcpport=n' : set the local rtcp port to n
  268. * 'pkt_size=n' : set max packet size
  269. * 'connect=0/1' : do a connect() on the UDP socket
  270. * 'sources=ip[,ip]' : list allowed source IP addresses
  271. * 'block=ip[,ip]' : list disallowed source IP addresses
  272. * 'write_to_source=0/1' : send packets to the source address of the latest received packet
  273. * 'dscp=n' : set DSCP value to n (QoS)
  274. * deprecated option:
  275. * 'localport=n' : set the local port to n
  276. *
  277. * if rtcpport isn't set the rtcp port will be the rtp port + 1
  278. * if local rtp port isn't set any available port will be used for the local
  279. * rtp and rtcp ports
  280. * if the local rtcp port is not set it will be the local rtp port + 1
  281. */
  282. static int rtp_open(URLContext *h, const char *uri, int flags)
  283. {
  284. RTPContext *s = h->priv_data;
  285. int rtp_port;
  286. char hostname[256], include_sources[1024] = "", exclude_sources[1024] = "";
  287. char *sources = include_sources, *block = exclude_sources;
  288. char buf[1024];
  289. char path[1024];
  290. const char *p;
  291. int i, max_retry_count = 3;
  292. av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
  293. path, sizeof(path), uri);
  294. /* extract parameters */
  295. if (s->rtcp_port < 0)
  296. s->rtcp_port = rtp_port + 1;
  297. p = strchr(uri, '?');
  298. if (p) {
  299. if (av_find_info_tag(buf, sizeof(buf), "ttl", p)) {
  300. s->ttl = strtol(buf, NULL, 10);
  301. }
  302. if (av_find_info_tag(buf, sizeof(buf), "rtcpport", p)) {
  303. s->rtcp_port = strtol(buf, NULL, 10);
  304. }
  305. if (av_find_info_tag(buf, sizeof(buf), "localport", p)) {
  306. s->local_rtpport = strtol(buf, NULL, 10);
  307. }
  308. if (av_find_info_tag(buf, sizeof(buf), "localrtpport", p)) {
  309. s->local_rtpport = strtol(buf, NULL, 10);
  310. }
  311. if (av_find_info_tag(buf, sizeof(buf), "localrtcpport", p)) {
  312. s->local_rtcpport = strtol(buf, NULL, 10);
  313. }
  314. if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
  315. s->pkt_size = strtol(buf, NULL, 10);
  316. }
  317. if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
  318. s->connect = strtol(buf, NULL, 10);
  319. }
  320. if (av_find_info_tag(buf, sizeof(buf), "write_to_source", p)) {
  321. s->write_to_source = strtol(buf, NULL, 10);
  322. }
  323. if (av_find_info_tag(buf, sizeof(buf), "dscp", p)) {
  324. s->dscp = strtol(buf, NULL, 10);
  325. }
  326. if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
  327. av_strlcpy(include_sources, buf, sizeof(include_sources));
  328. rtp_parse_addr_list(h, buf, &s->ssm_include_addrs, &s->nb_ssm_include_addrs);
  329. } else {
  330. rtp_parse_addr_list(h, s->sources, &s->ssm_include_addrs, &s->nb_ssm_include_addrs);
  331. sources = s->sources;
  332. }
  333. if (av_find_info_tag(buf, sizeof(buf), "block", p)) {
  334. av_strlcpy(exclude_sources, buf, sizeof(exclude_sources));
  335. rtp_parse_addr_list(h, buf, &s->ssm_exclude_addrs, &s->nb_ssm_exclude_addrs);
  336. } else {
  337. rtp_parse_addr_list(h, s->block, &s->ssm_exclude_addrs, &s->nb_ssm_exclude_addrs);
  338. block = s->block;
  339. }
  340. }
  341. for (i = 0; i < max_retry_count; i++) {
  342. build_udp_url(s, buf, sizeof(buf),
  343. hostname, rtp_port, s->local_rtpport,
  344. sources, block);
  345. if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
  346. goto fail;
  347. s->local_rtpport = ff_udp_get_local_port(s->rtp_hd);
  348. if(s->local_rtpport == 65535) {
  349. s->local_rtpport = -1;
  350. continue;
  351. }
  352. if (s->local_rtcpport < 0) {
  353. s->local_rtcpport = s->local_rtpport + 1;
  354. build_udp_url(s, buf, sizeof(buf),
  355. hostname, s->rtcp_port, s->local_rtcpport,
  356. sources, block);
  357. if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) {
  358. s->local_rtpport = s->local_rtcpport = -1;
  359. continue;
  360. }
  361. break;
  362. }
  363. build_udp_url(s, buf, sizeof(buf),
  364. hostname, s->rtcp_port, s->local_rtcpport,
  365. sources, block);
  366. if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
  367. goto fail;
  368. break;
  369. }
  370. /* just to ease handle access. XXX: need to suppress direct handle
  371. access */
  372. s->rtp_fd = ffurl_get_file_handle(s->rtp_hd);
  373. s->rtcp_fd = ffurl_get_file_handle(s->rtcp_hd);
  374. h->max_packet_size = s->rtp_hd->max_packet_size;
  375. h->is_streamed = 1;
  376. return 0;
  377. fail:
  378. if (s->rtp_hd)
  379. ffurl_close(s->rtp_hd);
  380. if (s->rtcp_hd)
  381. ffurl_close(s->rtcp_hd);
  382. return AVERROR(EIO);
  383. }
  384. static int rtp_read(URLContext *h, uint8_t *buf, int size)
  385. {
  386. RTPContext *s = h->priv_data;
  387. int len, n, i;
  388. struct pollfd p[2] = {{s->rtp_fd, POLLIN, 0}, {s->rtcp_fd, POLLIN, 0}};
  389. int poll_delay = h->flags & AVIO_FLAG_NONBLOCK ? 0 : 100;
  390. struct sockaddr_storage *addrs[2] = { &s->last_rtp_source, &s->last_rtcp_source };
  391. socklen_t *addr_lens[2] = { &s->last_rtp_source_len, &s->last_rtcp_source_len };
  392. for(;;) {
  393. if (ff_check_interrupt(&h->interrupt_callback))
  394. return AVERROR_EXIT;
  395. n = poll(p, 2, poll_delay);
  396. if (n > 0) {
  397. /* first try RTCP, then RTP */
  398. for (i = 1; i >= 0; i--) {
  399. if (!(p[i].revents & POLLIN))
  400. continue;
  401. *addr_lens[i] = sizeof(*addrs[i]);
  402. len = recvfrom(p[i].fd, buf, size, 0,
  403. (struct sockaddr *)addrs[i], addr_lens[i]);
  404. if (len < 0) {
  405. if (ff_neterrno() == AVERROR(EAGAIN) ||
  406. ff_neterrno() == AVERROR(EINTR))
  407. continue;
  408. return AVERROR(EIO);
  409. }
  410. if (rtp_check_source_lists(s, addrs[i]))
  411. continue;
  412. return len;
  413. }
  414. } else if (n < 0) {
  415. if (ff_neterrno() == AVERROR(EINTR))
  416. continue;
  417. return AVERROR(EIO);
  418. }
  419. if (h->flags & AVIO_FLAG_NONBLOCK)
  420. return AVERROR(EAGAIN);
  421. }
  422. return len;
  423. }
  424. static int rtp_write(URLContext *h, const uint8_t *buf, int size)
  425. {
  426. RTPContext *s = h->priv_data;
  427. int ret;
  428. URLContext *hd;
  429. if (size < 2)
  430. return AVERROR(EINVAL);
  431. if ((buf[0] & 0xc0) != (RTP_VERSION << 6))
  432. av_log(h, AV_LOG_WARNING, "Data doesn't look like RTP packets, "
  433. "make sure the RTP muxer is used\n");
  434. if (s->write_to_source) {
  435. int fd;
  436. struct sockaddr_storage *source, temp_source;
  437. socklen_t *source_len, temp_len;
  438. if (!s->last_rtp_source.ss_family && !s->last_rtcp_source.ss_family) {
  439. av_log(h, AV_LOG_ERROR,
  440. "Unable to send packet to source, no packets received yet\n");
  441. // Intentionally not returning an error here
  442. return size;
  443. }
  444. if (RTP_PT_IS_RTCP(buf[1])) {
  445. fd = s->rtcp_fd;
  446. source = &s->last_rtcp_source;
  447. source_len = &s->last_rtcp_source_len;
  448. } else {
  449. fd = s->rtp_fd;
  450. source = &s->last_rtp_source;
  451. source_len = &s->last_rtp_source_len;
  452. }
  453. if (!source->ss_family) {
  454. source = &temp_source;
  455. source_len = &temp_len;
  456. if (RTP_PT_IS_RTCP(buf[1])) {
  457. temp_source = s->last_rtp_source;
  458. temp_len = s->last_rtp_source_len;
  459. set_port(source, get_port(source) + 1);
  460. av_log(h, AV_LOG_INFO,
  461. "Not received any RTCP packets yet, inferring peer port "
  462. "from the RTP port\n");
  463. } else {
  464. temp_source = s->last_rtcp_source;
  465. temp_len = s->last_rtcp_source_len;
  466. set_port(source, get_port(source) - 1);
  467. av_log(h, AV_LOG_INFO,
  468. "Not received any RTP packets yet, inferring peer port "
  469. "from the RTCP port\n");
  470. }
  471. }
  472. if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
  473. ret = ff_network_wait_fd(fd, 1);
  474. if (ret < 0)
  475. return ret;
  476. }
  477. ret = sendto(fd, buf, size, 0, (struct sockaddr *) source,
  478. *source_len);
  479. return ret < 0 ? ff_neterrno() : ret;
  480. }
  481. if (RTP_PT_IS_RTCP(buf[1])) {
  482. /* RTCP payload type */
  483. hd = s->rtcp_hd;
  484. } else {
  485. /* RTP payload type */
  486. hd = s->rtp_hd;
  487. }
  488. ret = ffurl_write(hd, buf, size);
  489. return ret;
  490. }
  491. static int rtp_close(URLContext *h)
  492. {
  493. RTPContext *s = h->priv_data;
  494. int i;
  495. for (i = 0; i < s->nb_ssm_include_addrs; i++)
  496. av_freep(&s->ssm_include_addrs[i]);
  497. av_freep(&s->ssm_include_addrs);
  498. for (i = 0; i < s->nb_ssm_exclude_addrs; i++)
  499. av_freep(&s->ssm_exclude_addrs[i]);
  500. av_freep(&s->ssm_exclude_addrs);
  501. ffurl_close(s->rtp_hd);
  502. ffurl_close(s->rtcp_hd);
  503. return 0;
  504. }
  505. /**
  506. * Return the local rtp port used by the RTP connection
  507. * @param h media file context
  508. * @return the local port number
  509. */
  510. int ff_rtp_get_local_rtp_port(URLContext *h)
  511. {
  512. RTPContext *s = h->priv_data;
  513. return ff_udp_get_local_port(s->rtp_hd);
  514. }
  515. /**
  516. * Return the local rtcp port used by the RTP connection
  517. * @param h media file context
  518. * @return the local port number
  519. */
  520. int ff_rtp_get_local_rtcp_port(URLContext *h)
  521. {
  522. RTPContext *s = h->priv_data;
  523. return ff_udp_get_local_port(s->rtcp_hd);
  524. }
  525. static int rtp_get_file_handle(URLContext *h)
  526. {
  527. RTPContext *s = h->priv_data;
  528. return s->rtp_fd;
  529. }
  530. static int rtp_get_multi_file_handle(URLContext *h, int **handles,
  531. int *numhandles)
  532. {
  533. RTPContext *s = h->priv_data;
  534. int *hs = *handles = av_malloc(sizeof(**handles) * 2);
  535. if (!hs)
  536. return AVERROR(ENOMEM);
  537. hs[0] = s->rtp_fd;
  538. hs[1] = s->rtcp_fd;
  539. *numhandles = 2;
  540. return 0;
  541. }
  542. URLProtocol ff_rtp_protocol = {
  543. .name = "rtp",
  544. .url_open = rtp_open,
  545. .url_read = rtp_read,
  546. .url_write = rtp_write,
  547. .url_close = rtp_close,
  548. .url_get_file_handle = rtp_get_file_handle,
  549. .url_get_multi_file_handle = rtp_get_multi_file_handle,
  550. .priv_data_size = sizeof(RTPContext),
  551. .flags = URL_PROTOCOL_FLAG_NETWORK,
  552. .priv_data_class = &rtp_class,
  553. };