]> rtime.felk.cvut.cz Git - frescor/ffmpeg.git/blob - libavformat/frsh.c
90e6f6020d3676e4258fa470b823f1590ca6571b
[frescor/ffmpeg.git] / libavformat / frsh.c
1 /*
2  * FRSH prototype streaming system
3  * Copyright (c) 2000, 2001, 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 /**
23  * @file libavformat/frsh.c
24  * FRSH protocol
25  */
26
27 #include "avformat.h"
28 #include <unistd.h>
29 #include "network.h"
30 #include "os_support.h"
31 #if HAVE_SYS_SELECT_H
32 #include <sys/select.h>
33 #endif
34 #include <sys/time.h>
35
36 #include <frsh.h>
37 #include <frsh_core_types.h>
38
39 long int avformat_frsh_budget, avformat_frsh_period;
40
41 typedef struct {
42     int frsh_fd;
43     int ttl;
44     int buffer_size;
45     int is_multicast;
46     int local_port;
47     int reuse_socket;
48     struct sockaddr_in dest_addr;
49     int dest_addr_len;
50         
51         frsh_send_endpoint_t sepoint;
52         frsh_receive_endpoint_t repoint;
53         frsh_vres_id_t vres;
54         frsh_send_endpoint_protocol_info_t send_pinfo;
55         frsh_contract_t contract;
56         frsh_contract_label_t label;
57         frsh_rel_time_t budget, period;
58 } FRSHContext;
59
60 #define FRSH_TX_BUF_SIZE 32768
61 #define FRSH_MAX_PKT_SIZE 65536
62
63 static int frsh_set_url(struct sockaddr_in *addr, const char *hostname, int port)
64 {
65     /* set the destination address */
66     if (resolve_host(&addr->sin_addr, hostname) < 0)
67         return AVERROR(EIO);
68     addr->sin_family = AF_INET;
69     addr->sin_port = htons(port);
70
71     return sizeof(struct sockaddr_in);
72 }
73
74 static int is_multicast_address(struct sockaddr_in *addr)
75 {
76     return 0;
77 }
78
79 static int 
80 frsh_output_socket_create(FRSHContext *s)
81 {
82         static long int netcont_num = 0;
83         int ret,frsh_fd;
84         char netcont_name[20];
85         struct sockaddr_in addr;
86         int addr_len;
87
88     addr.sin_family = AF_INET;
89     addr.sin_addr.s_addr = htonl (INADDR_ANY);
90     addr.sin_port = htons(s->local_port);
91     addr_len = sizeof(addr);
92
93         /* set params for contract */
94         frsh_network_bytes_to_budget(FRSH_NETPF_FWP, avformat_frsh_budget, &s->budget);
95         s->period = fosa_msec_to_rel_time(avformat_frsh_period);
96         s->send_pinfo.body = NULL;              
97         
98         frsh_fd = frsh_send_endpoint_create(FRSH_NETPF_FWP, 
99                                         s->dest_addr.sin_addr.s_addr,
100                                         ntohs(s->dest_addr.sin_port), s->send_pinfo, 
101                                         &s->sepoint);
102         if (frsh_fd < 0) {
103                         
104                         return -1;
105         }
106         
107         /* Contract negotiation */
108         ret = frsh_contract_init(&s->contract);
109         //if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
110         if (ret) return -1;
111                 
112         ret = frsh_contract_set_basic_params(&s->contract,
113                                              &s->budget,
114                                              &s->period,
115                                              FRSH_WT_BOUNDED,
116                                              FRSH_CT_REGULAR);
117         //if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
118         if (ret) return -1;
119         snprintf(netcont_name, sizeof(netcont_name), "ffmpeg%02ld", ++netcont_num);
120         ret = frsh_contract_set_resource_and_label(&s->contract,FRSH_RT_NETWORK,
121                                                    FRSH_NETPF_FWP,
122                                                    s->label[0] ? s->label : netcont_name);
123         //if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
124         if (ret) return -1;
125
126         ret = frsh_contract_negotiate(&s->contract, &s->vres);
127         //if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
128         if (ret) return -1;
129         
130         printf("Send endpoint created\n");
131         frsh_send_endpoint_bind(s->vres, s->sepoint);
132         printf("Send endpoint bounded\n");
133         
134         //fwp_endpoint_get_params(s->sepoint->protocol_info.body, &node,
135         //              &port, &attr, &fd);
136
137         return frsh_fd;
138 }
139
140 static int 
141 frsh_input_socket_create(FRSHContext *s)
142 {
143         frsh_receive_endpoint_protocol_info_t pi = { NULL, 0 };
144         frsh_endpoint_queueing_info_t qi = { .queue_size=0, .queue_policy=FRSH_QRP_OLDEST };
145         
146         return frsh_receive_endpoint_create(FRSH_NETPF_FWP, s->local_port, qi, pi,
147                                             &s->repoint);
148 }
149                 
150
151
152 static int frsh_port(struct sockaddr_in *addr, int len)
153 {
154     return ntohs(addr->sin_port);
155 }
156
157 /**
158  * If no filename is given to av_open_input_file because you want to
159  * get the local port first, then you must call this function to set
160  * the remote server address.
161  *
162  * url syntax: frsh://host:port[?option=val...]
163  * option: 'ttl=n'       : set the ttl value (for multicast only)
164  *         'localport=n' : set the local port
165  *         'pkt_size=n'  : set max packet size
166  *         'reuse=1'     : enable reusing the socket
167  *
168  * @param s1 media file context
169  * @param uri of the remote server
170  * @return zero if no error.
171  */
172 int frsh_set_remote_url(URLContext *h, const char *uri)
173 {
174     FRSHContext *s = h->priv_data;
175     char hostname[256];
176     int port;
177
178     url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
179
180     /* set the destination address */
181     s->dest_addr_len = frsh_set_url(&s->dest_addr, hostname, port);
182     if (s->dest_addr_len < 0) {
183         return AVERROR(EIO);
184     }
185     s->is_multicast = is_multicast_address(&s->dest_addr);
186
187     return 0;
188 }
189
190 /**
191  * Return the local port used by the FRSH connexion
192  * @param s1 media file context
193  * @return the local port number
194  */
195 int frsh_get_local_port(URLContext *h)
196 {
197     FRSHContext *s = h->priv_data;
198     return s->local_port;
199 }
200
201 /**
202  * Return the frsh file handle for select() usage to wait for several RTP
203  * streams at the same time.
204  * @param h media file context
205  */
206 #if (LIBAVFORMAT_VERSION_MAJOR >= 53)
207 static
208 #endif
209 int frsh_get_file_handle(URLContext *h)
210 {
211     FRSHContext *s = h->priv_data;
212     return s->frsh_fd;
213 }
214
215 /* put it in FRSH context */
216 /* return non zero if error */
217 static int frsh_open(URLContext *h, const char *uri, int flags)
218 {
219     char hostname[1024];
220     int port, frsh_fd = -1, tmp;
221     FRSHContext *s = NULL;
222     int is_output;
223     const char *p;
224     char buf[256];
225     struct sockaddr_in my_addr;
226     int len;
227
228     h->is_streamed = 1;
229     h->max_packet_size = 1472;
230
231     is_output = (flags & URL_WRONLY);
232
233     if(!ff_network_init())
234         return AVERROR(EIO);
235
236     s = av_mallocz(sizeof(FRSHContext));
237     if (!s)
238         return AVERROR(ENOMEM);
239
240     h->priv_data = s;
241     s->ttl = 16;
242     s->buffer_size = is_output ? FRSH_TX_BUF_SIZE : FRSH_MAX_PKT_SIZE;
243
244     p = strchr(uri, '?');
245     if (p) {
246         s->reuse_socket = find_info_tag(buf, sizeof(buf), "reuse", p);
247         if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
248             s->ttl = strtol(buf, NULL, 10);
249         }
250         if (find_info_tag(buf, sizeof(buf), "localport", p)) {
251             s->local_port = strtol(buf, NULL, 10);
252         }
253         if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
254             h->max_packet_size = strtol(buf, NULL, 10);
255         }
256         if (find_info_tag(buf, sizeof(buf), "buffer_size", p)) {
257             s->buffer_size = strtol(buf, NULL, 10);
258         }
259         if (!find_info_tag(s->label, sizeof(s->label),
260                           "contract_label", p)) {
261                 s->label[0]=0;
262         }
263     }
264
265     /* fill the dest addr */
266     url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
267
268     /* XXX: fix url_split */
269     if (hostname[0] == '\0' || hostname[0] == '?') {
270         /* only accepts null hostname if input */
271         if (flags & URL_WRONLY)
272             goto fail;
273     } else {
274         frsh_set_remote_url(h, uri);
275     }
276
277     if (is_output)
278             frsh_fd = frsh_output_socket_create(s);
279     else
280             frsh_fd = frsh_input_socket_create(s);
281     if (frsh_fd < 0)
282         goto fail;
283 #if 0
284     if (s->reuse_socket)
285         if (setsockopt (frsh_fd, SOL_SOCKET, SO_REUSEADDR, &(s->reuse_socket), sizeof(s->reuse_socket)) != 0)
286             goto fail;
287
288     /* bind to the local address if not multicast or if the multicast
289      * bind failed */
290     /*if (bind_ret < 0 && bind(frsh_fd,(struct sockaddr *)&my_addr, len) < 0)
291         goto fail;*/
292 #endif
293
294     len = sizeof(my_addr);
295     getsockname(frsh_fd, (struct sockaddr *)&my_addr, &len);
296     s->local_port = frsh_port(&my_addr, len);
297
298     if (is_output) {
299         /* limit the tx buf size to limit latency */
300         tmp = s->buffer_size;
301         if (setsockopt(frsh_fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) {
302             av_log(NULL, AV_LOG_ERROR, "setsockopt(SO_SNDBUF): %s\n", strerror(errno));
303             goto fail;
304         }
305     } else {
306         /* set frsh recv buffer size to the largest possible frsh packet size to
307          * avoid losing data on OSes that set this too low by default. */
308         tmp = s->buffer_size;
309         if (setsockopt(frsh_fd, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(tmp)) < 0) {
310             av_log(NULL, AV_LOG_WARNING, "setsockopt(SO_RECVBUF): %s\n", strerror(errno));
311         }
312         /* make the socket non-blocking */
313         ff_socket_nonblock(frsh_fd, 1);
314     }
315     s->frsh_fd = frsh_fd;
316     return 0;
317  
318  fail:
319     if (frsh_fd >= 0)
320         closesocket(frsh_fd);
321     av_free(s);
322     return AVERROR(EIO);
323 }
324
325 static int frsh_read(URLContext *h, uint8_t *buf, int size)
326 {
327     FRSHContext *s = h->priv_data;
328     unsigned int from;
329         int len;
330
331     return frsh_receive_sync(s->repoint, buf, size, &len, &from);
332 }
333
334 static int frsh_write(URLContext *h, uint8_t *buf, int size)
335 {
336     FRSHContext *s = h->priv_data;
337     int ret;
338         
339         ret = frsh_send_async(s->sepoint, buf, size);
340         return ret;
341 }
342
343 static int frsh_close(URLContext *h)
344 {
345     FRSHContext *s = h->priv_data;
346     int ret;
347         
348         frsh_send_endpoint_unbind(s->sepoint);
349         ret = frsh_contract_cancel(s->vres);
350         return ret;
351 }
352
353 URLProtocol frsh_protocol = {
354     "frsh",
355     frsh_open,
356     frsh_read,
357     frsh_write,
358     NULL, /* seek */
359     frsh_close,
360     .url_get_file_handle = frsh_get_file_handle,
361 };