Statistics
| Branch: | Revision:

ffmpeg / libavformat / tcp.c @ 814c5641

History | View | Annotate | Download (5.5 KB)

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 <unistd.h>
23
#include "network.h"
24
#include "os_support.h"
25
#if HAVE_SYS_SELECT_H
26
#include <sys/select.h>
27
#endif
28
#include <sys/time.h>
29

    
30
typedef struct TCPContext {
31
    int fd;
32
} TCPContext;
33

    
34
/* return non zero if error */
35
static int tcp_open(URLContext *h, const char *uri, int flags)
36
{
37
    struct addrinfo hints, *ai, *cur_ai;
38
    int port, fd = -1;
39
    TCPContext *s = NULL;
40
    fd_set wfds;
41
    int fd_max, ret;
42
    struct timeval tv;
43
    socklen_t optlen;
44
    char hostname[1024],proto[1024],path[1024];
45
    char portstr[10];
46

    
47
    url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
48
        &port, path, sizeof(path), uri);
49
    if (strcmp(proto,"tcp") || port <= 0 || port >= 65536)
50
        return AVERROR(EINVAL);
51

    
52
    memset(&hints, 0, sizeof(hints));
53
    hints.ai_family = AF_UNSPEC;
54
    hints.ai_socktype = SOCK_STREAM;
55
    snprintf(portstr, sizeof(portstr), "%d", port);
56
    if (getaddrinfo(hostname, portstr, &hints, &ai))
57
        return AVERROR(EIO);
58

    
59
    cur_ai = ai;
60

    
61
 restart:
62
    fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
63
    if (fd < 0)
64
        goto fail;
65
    ff_socket_nonblock(fd, 1);
66

    
67
 redo:
68
    ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
69
    if (ret < 0) {
70
        if (ff_neterrno() == FF_NETERROR(EINTR))
71
            goto redo;
72
        if (ff_neterrno() != FF_NETERROR(EINPROGRESS) &&
73
            ff_neterrno() != FF_NETERROR(EAGAIN))
74
            goto fail;
75

    
76
        /* wait until we are connected or until abort */
77
        for(;;) {
78
            if (url_interrupt_cb()) {
79
                ret = AVERROR(EINTR);
80
                goto fail1;
81
            }
82
            fd_max = fd;
83
            FD_ZERO(&wfds);
84
            FD_SET(fd, &wfds);
85
            tv.tv_sec = 0;
86
            tv.tv_usec = 100 * 1000;
87
            ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
88
            if (ret > 0 && FD_ISSET(fd, &wfds))
89
                break;
90
        }
91

    
92
        /* test error */
93
        optlen = sizeof(ret);
94
        getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
95
        if (ret != 0)
96
            goto fail;
97
    }
98
    s = av_malloc(sizeof(TCPContext));
99
    if (!s) {
100
        freeaddrinfo(ai);
101
        return AVERROR(ENOMEM);
102
    }
103
    h->priv_data = s;
104
    h->is_streamed = 1;
105
    s->fd = fd;
106
    freeaddrinfo(ai);
107
    return 0;
108

    
109
 fail:
110
    if (cur_ai->ai_next) {
111
        /* Retry with the next sockaddr */
112
        cur_ai = cur_ai->ai_next;
113
        if (fd >= 0)
114
            closesocket(fd);
115
        goto restart;
116
    }
117
    ret = AVERROR(EIO);
118
 fail1:
119
    if (fd >= 0)
120
        closesocket(fd);
121
    freeaddrinfo(ai);
122
    return ret;
123
}
124

    
125
static int tcp_read(URLContext *h, uint8_t *buf, int size)
126
{
127
    TCPContext *s = h->priv_data;
128
    int len, fd_max, ret;
129
    fd_set rfds;
130
    struct timeval tv;
131

    
132
    for (;;) {
133
        if (url_interrupt_cb())
134
            return AVERROR(EINTR);
135
        fd_max = s->fd;
136
        FD_ZERO(&rfds);
137
        FD_SET(s->fd, &rfds);
138
        tv.tv_sec = 0;
139
        tv.tv_usec = 100 * 1000;
140
        ret = select(fd_max + 1, &rfds, NULL, NULL, &tv);
141
        if (ret > 0 && FD_ISSET(s->fd, &rfds)) {
142
            len = recv(s->fd, buf, size, 0);
143
            if (len < 0) {
144
                if (ff_neterrno() != FF_NETERROR(EINTR) &&
145
                    ff_neterrno() != FF_NETERROR(EAGAIN))
146
                    return AVERROR(ff_neterrno());
147
            } else return len;
148
        } else if (ret < 0) {
149
            return -1;
150
        }
151
    }
152
}
153

    
154
static int tcp_write(URLContext *h, uint8_t *buf, int size)
155
{
156
    TCPContext *s = h->priv_data;
157
    int ret, size1, fd_max, len;
158
    fd_set wfds;
159
    struct timeval tv;
160

    
161
    size1 = size;
162
    while (size > 0) {
163
        if (url_interrupt_cb())
164
            return AVERROR(EINTR);
165
        fd_max = s->fd;
166
        FD_ZERO(&wfds);
167
        FD_SET(s->fd, &wfds);
168
        tv.tv_sec = 0;
169
        tv.tv_usec = 100 * 1000;
170
        ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
171
        if (ret > 0 && FD_ISSET(s->fd, &wfds)) {
172
            len = send(s->fd, buf, size, 0);
173
            if (len < 0) {
174
                if (ff_neterrno() != FF_NETERROR(EINTR) &&
175
                    ff_neterrno() != FF_NETERROR(EAGAIN))
176
                    return AVERROR(ff_neterrno());
177
                continue;
178
            }
179
            size -= len;
180
            buf += len;
181
        } else if (ret < 0) {
182
            return -1;
183
        }
184
    }
185
    return size1 - size;
186
}
187

    
188
static int tcp_close(URLContext *h)
189
{
190
    TCPContext *s = h->priv_data;
191
    closesocket(s->fd);
192
    av_free(s);
193
    return 0;
194
}
195

    
196
static int tcp_get_file_handle(URLContext *h)
197
{
198
    TCPContext *s = h->priv_data;
199
    return s->fd;
200
}
201

    
202
URLProtocol tcp_protocol = {
203
    "tcp",
204
    tcp_open,
205
    tcp_read,
206
    tcp_write,
207
    NULL, /* seek */
208
    tcp_close,
209
    .url_get_file_handle = tcp_get_file_handle,
210
};