Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * TCP 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. #include "avformat.h"
  22. #include "libavutil/parseutils.h"
  23. #include "libavutil/opt.h"
  24. #include "libavutil/time.h"
  25. #include "internal.h"
  26. #include "network.h"
  27. #include "os_support.h"
  28. #include "url.h"
  29. #if HAVE_POLL_H
  30. #include <poll.h>
  31. #endif
  32.  
  33. typedef struct TCPContext {
  34.     const AVClass *class;
  35.     int fd;
  36.     int listen;
  37.     int open_timeout;
  38.     int rw_timeout;
  39.     int listen_timeout;
  40. } TCPContext;
  41.  
  42. #define OFFSET(x) offsetof(TCPContext, x)
  43. #define D AV_OPT_FLAG_DECODING_PARAM
  44. #define E AV_OPT_FLAG_ENCODING_PARAM
  45. static const AVOption options[] = {
  46. {"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
  47. {"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
  48. {"listen_timeout", "connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
  49. {NULL}
  50. };
  51.  
  52. static const AVClass tcp_context_class = {
  53.     .class_name = "tcp",
  54.     .item_name  = av_default_item_name,
  55.     .option     = options,
  56.     .version    = LIBAVUTIL_VERSION_INT,
  57. };
  58.  
  59. /* return non zero if error */
  60. static int tcp_open(URLContext *h, const char *uri, int flags)
  61. {
  62.     struct addrinfo hints = { 0 }, *ai, *cur_ai;
  63.     int port, fd = -1;
  64.     TCPContext *s = h->priv_data;
  65.     const char *p;
  66.     char buf[256];
  67.     int ret;
  68.     char hostname[1024],proto[1024],path[1024];
  69.     char portstr[10];
  70.     s->open_timeout = 5000000;
  71.  
  72.     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
  73.         &port, path, sizeof(path), uri);
  74.     if (strcmp(proto, "tcp"))
  75.         return AVERROR(EINVAL);
  76.     if (port <= 0 || port >= 65536) {
  77.         av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
  78.         return AVERROR(EINVAL);
  79.     }
  80.     p = strchr(uri, '?');
  81.     if (p) {
  82.         if (av_find_info_tag(buf, sizeof(buf), "listen", p))
  83.             s->listen = 1;
  84.         if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
  85.             s->rw_timeout = strtol(buf, NULL, 10);
  86.         }
  87.         if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
  88.             s->listen_timeout = strtol(buf, NULL, 10);
  89.         }
  90.     }
  91.     if (s->rw_timeout >= 0) {
  92.         s->open_timeout =
  93.         h->rw_timeout   = s->rw_timeout;
  94.     }
  95.     hints.ai_family = AF_UNSPEC;
  96.     hints.ai_socktype = SOCK_STREAM;
  97.     snprintf(portstr, sizeof(portstr), "%d", port);
  98.     if (s->listen)
  99.         hints.ai_flags |= AI_PASSIVE;
  100.     if (!hostname[0])
  101.         ret = getaddrinfo(NULL, portstr, &hints, &ai);
  102.     else
  103.         ret = getaddrinfo(hostname, portstr, &hints, &ai);
  104.     if (ret) {
  105.         av_log(h, AV_LOG_ERROR,
  106.                "Failed to resolve hostname %s: %s\n",
  107.                hostname, gai_strerror(ret));
  108.         return AVERROR(EIO);
  109.     }
  110.  
  111.     cur_ai = ai;
  112.  
  113.  restart:
  114.     fd = ff_socket(cur_ai->ai_family,
  115.                    cur_ai->ai_socktype,
  116.                    cur_ai->ai_protocol);
  117.     if (fd < 0) {
  118.         ret = ff_neterrno();
  119.         goto fail;
  120.     }
  121.  
  122.     if (s->listen) {
  123.         if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
  124.                                  s->listen_timeout, h)) < 0) {
  125.             ret = fd;
  126.             goto fail1;
  127.         }
  128.     } else {
  129.         if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
  130.                                      s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
  131.  
  132.             if (ret == AVERROR_EXIT)
  133.                 goto fail1;
  134.             else
  135.                 goto fail;
  136.         }
  137.     }
  138.  
  139.     h->is_streamed = 1;
  140.     s->fd = fd;
  141.     freeaddrinfo(ai);
  142.     return 0;
  143.  
  144.  fail:
  145.     if (cur_ai->ai_next) {
  146.         /* Retry with the next sockaddr */
  147.         cur_ai = cur_ai->ai_next;
  148.         if (fd >= 0)
  149.             closesocket(fd);
  150.         ret = 0;
  151.         goto restart;
  152.     }
  153.  fail1:
  154.     if (fd >= 0)
  155.         closesocket(fd);
  156.     freeaddrinfo(ai);
  157.     return ret;
  158. }
  159.  
  160. static int tcp_read(URLContext *h, uint8_t *buf, int size)
  161. {
  162.     TCPContext *s = h->priv_data;
  163.     int ret;
  164.  
  165.     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
  166.         ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
  167.         if (ret)
  168.             return ret;
  169.     }
  170.     ret = recv(s->fd, buf, size, 0);
  171.     return ret < 0 ? ff_neterrno() : ret;
  172. }
  173.  
  174. static int tcp_write(URLContext *h, const uint8_t *buf, int size)
  175. {
  176.     TCPContext *s = h->priv_data;
  177.     int ret;
  178.  
  179.     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
  180.         ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
  181.         if (ret)
  182.             return ret;
  183.     }
  184.     ret = send(s->fd, buf, size, 0);
  185.     return ret < 0 ? ff_neterrno() : ret;
  186. }
  187.  
  188. static int tcp_shutdown(URLContext *h, int flags)
  189. {
  190.     TCPContext *s = h->priv_data;
  191.     int how;
  192.  
  193.     if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
  194.         how = SHUT_RDWR;
  195.     } else if (flags & AVIO_FLAG_WRITE) {
  196.         how = SHUT_WR;
  197.     } else {
  198.         how = SHUT_RD;
  199.     }
  200.  
  201.     return shutdown(s->fd, how);
  202. }
  203.  
  204. static int tcp_close(URLContext *h)
  205. {
  206.     TCPContext *s = h->priv_data;
  207.     closesocket(s->fd);
  208.     return 0;
  209. }
  210.  
  211. static int tcp_get_file_handle(URLContext *h)
  212. {
  213.     TCPContext *s = h->priv_data;
  214.     return s->fd;
  215. }
  216.  
  217. URLProtocol ff_tcp_protocol = {
  218.     .name                = "tcp",
  219.     .url_open            = tcp_open,
  220.     .url_read            = tcp_read,
  221.     .url_write           = tcp_write,
  222.     .url_close           = tcp_close,
  223.     .url_get_file_handle = tcp_get_file_handle,
  224.     .url_shutdown        = tcp_shutdown,
  225.     .priv_data_size      = sizeof(TCPContext),
  226.     .priv_data_class     = &tcp_context_class,
  227.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  228. };
  229.