00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #define _BSD_SOURCE
00028
00029 #include "avformat.h"
00030 #include "avio_internal.h"
00031 #include "libavutil/parseutils.h"
00032 #include "libavutil/fifo.h"
00033 #include "libavutil/intreadwrite.h"
00034 #include "libavutil/avstring.h"
00035 #include <unistd.h>
00036 #include "internal.h"
00037 #include "network.h"
00038 #include "os_support.h"
00039 #include "url.h"
00040
00041 #if HAVE_PTHREAD_CANCEL
00042 #include <pthread.h>
00043 #endif
00044
00045 #include <sys/time.h>
00046
00047 #ifndef IPV6_ADD_MEMBERSHIP
00048 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
00049 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
00050 #endif
00051
00052 #define UDP_TX_BUF_SIZE 32768
00053 #define UDP_MAX_PKT_SIZE 65536
00054
00055 typedef struct {
00056 int udp_fd;
00057 int ttl;
00058 int buffer_size;
00059 int is_multicast;
00060 int local_port;
00061 int reuse_socket;
00062 int overrun_nonfatal;
00063 struct sockaddr_storage dest_addr;
00064 int dest_addr_len;
00065 int is_connected;
00066
00067
00068 int circular_buffer_size;
00069 AVFifoBuffer *fifo;
00070 int circular_buffer_error;
00071 #if HAVE_PTHREAD_CANCEL
00072 pthread_t circular_buffer_thread;
00073 pthread_mutex_t mutex;
00074 pthread_cond_t cond;
00075 int thread_started;
00076 #endif
00077 uint8_t tmp[UDP_MAX_PKT_SIZE+4];
00078 int remaining_in_dg;
00079 } UDPContext;
00080
00081 static int udp_set_multicast_ttl(int sockfd, int mcastTTL,
00082 struct sockaddr *addr)
00083 {
00084 #ifdef IP_MULTICAST_TTL
00085 if (addr->sa_family == AF_INET) {
00086 if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &mcastTTL, sizeof(mcastTTL)) < 0) {
00087 av_log(NULL, AV_LOG_ERROR, "setsockopt(IP_MULTICAST_TTL): %s\n", strerror(errno));
00088 return -1;
00089 }
00090 }
00091 #endif
00092 #if defined(IPPROTO_IPV6) && defined(IPV6_MULTICAST_HOPS)
00093 if (addr->sa_family == AF_INET6) {
00094 if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastTTL, sizeof(mcastTTL)) < 0) {
00095 av_log(NULL, AV_LOG_ERROR, "setsockopt(IPV6_MULTICAST_HOPS): %s\n", strerror(errno));
00096 return -1;
00097 }
00098 }
00099 #endif
00100 return 0;
00101 }
00102
00103 static int udp_join_multicast_group(int sockfd, struct sockaddr *addr)
00104 {
00105 #ifdef IP_ADD_MEMBERSHIP
00106 if (addr->sa_family == AF_INET) {
00107 struct ip_mreq mreq;
00108
00109 mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
00110 mreq.imr_interface.s_addr= INADDR_ANY;
00111 if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
00112 av_log(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
00113 return -1;
00114 }
00115 }
00116 #endif
00117 #if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6)
00118 if (addr->sa_family == AF_INET6) {
00119 struct ipv6_mreq mreq6;
00120
00121 memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
00122 mreq6.ipv6mr_interface= 0;
00123 if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
00124 av_log(NULL, AV_LOG_ERROR, "setsockopt(IPV6_ADD_MEMBERSHIP): %s\n", strerror(errno));
00125 return -1;
00126 }
00127 }
00128 #endif
00129 return 0;
00130 }
00131
00132 static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr)
00133 {
00134 #ifdef IP_DROP_MEMBERSHIP
00135 if (addr->sa_family == AF_INET) {
00136 struct ip_mreq mreq;
00137
00138 mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
00139 mreq.imr_interface.s_addr= INADDR_ANY;
00140 if (setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
00141 av_log(NULL, AV_LOG_ERROR, "setsockopt(IP_DROP_MEMBERSHIP): %s\n", strerror(errno));
00142 return -1;
00143 }
00144 }
00145 #endif
00146 #if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6)
00147 if (addr->sa_family == AF_INET6) {
00148 struct ipv6_mreq mreq6;
00149
00150 memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
00151 mreq6.ipv6mr_interface= 0;
00152 if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
00153 av_log(NULL, AV_LOG_ERROR, "setsockopt(IPV6_DROP_MEMBERSHIP): %s\n", strerror(errno));
00154 return -1;
00155 }
00156 }
00157 #endif
00158 return 0;
00159 }
00160
00161 static struct addrinfo* udp_resolve_host(const char *hostname, int port,
00162 int type, int family, int flags)
00163 {
00164 struct addrinfo hints = { 0 }, *res = 0;
00165 int error;
00166 char sport[16];
00167 const char *node = 0, *service = "0";
00168
00169 if (port > 0) {
00170 snprintf(sport, sizeof(sport), "%d", port);
00171 service = sport;
00172 }
00173 if ((hostname) && (hostname[0] != '\0') && (hostname[0] != '?')) {
00174 node = hostname;
00175 }
00176 hints.ai_socktype = type;
00177 hints.ai_family = family;
00178 hints.ai_flags = flags;
00179 if ((error = getaddrinfo(node, service, &hints, &res))) {
00180 res = NULL;
00181 av_log(NULL, AV_LOG_ERROR, "udp_resolve_host: %s\n", gai_strerror(error));
00182 }
00183
00184 return res;
00185 }
00186
00187 static int udp_set_url(struct sockaddr_storage *addr,
00188 const char *hostname, int port)
00189 {
00190 struct addrinfo *res0;
00191 int addr_len;
00192
00193 res0 = udp_resolve_host(hostname, port, SOCK_DGRAM, AF_UNSPEC, 0);
00194 if (res0 == 0) return AVERROR(EIO);
00195 memcpy(addr, res0->ai_addr, res0->ai_addrlen);
00196 addr_len = res0->ai_addrlen;
00197 freeaddrinfo(res0);
00198
00199 return addr_len;
00200 }
00201
00202 static int udp_socket_create(UDPContext *s, struct sockaddr_storage *addr,
00203 int *addr_len, const char *localaddr)
00204 {
00205 int udp_fd = -1;
00206 struct addrinfo *res0 = NULL, *res = NULL;
00207 int family = AF_UNSPEC;
00208
00209 if (((struct sockaddr *) &s->dest_addr)->sa_family)
00210 family = ((struct sockaddr *) &s->dest_addr)->sa_family;
00211 res0 = udp_resolve_host(localaddr[0] ? localaddr : NULL, s->local_port,
00212 SOCK_DGRAM, family, AI_PASSIVE);
00213 if (res0 == 0)
00214 goto fail;
00215 for (res = res0; res; res=res->ai_next) {
00216 udp_fd = socket(res->ai_family, SOCK_DGRAM, 0);
00217 if (udp_fd > 0) break;
00218 av_log(NULL, AV_LOG_ERROR, "socket: %s\n", strerror(errno));
00219 }
00220
00221 if (udp_fd < 0)
00222 goto fail;
00223
00224 memcpy(addr, res->ai_addr, res->ai_addrlen);
00225 *addr_len = res->ai_addrlen;
00226
00227 freeaddrinfo(res0);
00228
00229 return udp_fd;
00230
00231 fail:
00232 if (udp_fd >= 0)
00233 closesocket(udp_fd);
00234 if(res0)
00235 freeaddrinfo(res0);
00236 return -1;
00237 }
00238
00239 static int udp_port(struct sockaddr_storage *addr, int addr_len)
00240 {
00241 char sbuf[sizeof(int)*3+1];
00242
00243 if (getnameinfo((struct sockaddr *)addr, addr_len, NULL, 0, sbuf, sizeof(sbuf), NI_NUMERICSERV) != 0) {
00244 av_log(NULL, AV_LOG_ERROR, "getnameinfo: %s\n", strerror(errno));
00245 return -1;
00246 }
00247
00248 return strtol(sbuf, NULL, 10);
00249 }
00250
00251
00268 int ff_udp_set_remote_url(URLContext *h, const char *uri)
00269 {
00270 UDPContext *s = h->priv_data;
00271 char hostname[256], buf[10];
00272 int port;
00273 const char *p;
00274
00275 av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
00276
00277
00278 s->dest_addr_len = udp_set_url(&s->dest_addr, hostname, port);
00279 if (s->dest_addr_len < 0) {
00280 return AVERROR(EIO);
00281 }
00282 s->is_multicast = ff_is_multicast_address((struct sockaddr*) &s->dest_addr);
00283 p = strchr(uri, '?');
00284 if (p) {
00285 if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
00286 int was_connected = s->is_connected;
00287 s->is_connected = strtol(buf, NULL, 10);
00288 if (s->is_connected && !was_connected) {
00289 if (connect(s->udp_fd, (struct sockaddr *) &s->dest_addr,
00290 s->dest_addr_len)) {
00291 s->is_connected = 0;
00292 av_log(h, AV_LOG_ERROR, "connect: %s\n", strerror(errno));
00293 return AVERROR(EIO);
00294 }
00295 }
00296 }
00297 }
00298
00299 return 0;
00300 }
00301
00307 int ff_udp_get_local_port(URLContext *h)
00308 {
00309 UDPContext *s = h->priv_data;
00310 return s->local_port;
00311 }
00312
00318 static int udp_get_file_handle(URLContext *h)
00319 {
00320 UDPContext *s = h->priv_data;
00321 return s->udp_fd;
00322 }
00323
00324 #if HAVE_PTHREAD_CANCEL
00325 static void *circular_buffer_task( void *_URLContext)
00326 {
00327 URLContext *h = _URLContext;
00328 UDPContext *s = h->priv_data;
00329 int old_cancelstate;
00330
00331 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
00332 ff_socket_nonblock(s->udp_fd, 0);
00333 pthread_mutex_lock(&s->mutex);
00334 while(1) {
00335 int len;
00336
00337 pthread_mutex_unlock(&s->mutex);
00338
00339
00340
00341 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
00342 len = recv(s->udp_fd, s->tmp+4, sizeof(s->tmp)-4, 0);
00343 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
00344 pthread_mutex_lock(&s->mutex);
00345 if (len < 0) {
00346 if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) {
00347 s->circular_buffer_error = ff_neterrno();
00348 goto end;
00349 }
00350 continue;
00351 }
00352 AV_WL32(s->tmp, len);
00353
00354 if(av_fifo_space(s->fifo) < len + 4) {
00355
00356 if (s->overrun_nonfatal) {
00357 av_log(h, AV_LOG_WARNING, "Circular buffer overrun. "
00358 "Surviving due to overrun_nonfatal option\n");
00359 continue;
00360 } else {
00361 av_log(h, AV_LOG_ERROR, "Circular buffer overrun. "
00362 "To avoid, increase fifo_size URL option. "
00363 "To survive in such case, use overrun_nonfatal option\n");
00364 s->circular_buffer_error = AVERROR(EIO);
00365 goto end;
00366 }
00367 }
00368 av_fifo_generic_write(s->fifo, s->tmp, len+4, NULL);
00369 pthread_cond_signal(&s->cond);
00370 }
00371
00372 end:
00373 pthread_cond_signal(&s->cond);
00374 pthread_mutex_unlock(&s->mutex);
00375 return NULL;
00376 }
00377 #endif
00378
00379
00380
00381 static int udp_open(URLContext *h, const char *uri, int flags)
00382 {
00383 char hostname[1024], localaddr[1024] = "";
00384 int port, udp_fd = -1, tmp, bind_ret = -1;
00385 UDPContext *s = h->priv_data;
00386 int is_output;
00387 const char *p;
00388 char buf[256];
00389 struct sockaddr_storage my_addr;
00390 int len;
00391 int reuse_specified = 0;
00392
00393 h->is_streamed = 1;
00394 h->max_packet_size = 1472;
00395
00396 is_output = !(flags & AVIO_FLAG_READ);
00397
00398 s->ttl = 16;
00399 s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE;
00400
00401 s->circular_buffer_size = 7*188*4096;
00402
00403 p = strchr(uri, '?');
00404 if (p) {
00405 if (av_find_info_tag(buf, sizeof(buf), "reuse", p)) {
00406 char *endptr = NULL;
00407 s->reuse_socket = strtol(buf, &endptr, 10);
00408
00409 if (buf == endptr)
00410 s->reuse_socket = 1;
00411 reuse_specified = 1;
00412 }
00413 if (av_find_info_tag(buf, sizeof(buf), "overrun_nonfatal", p)) {
00414 char *endptr = NULL;
00415 s->overrun_nonfatal = strtol(buf, &endptr, 10);
00416
00417 if (buf == endptr)
00418 s->overrun_nonfatal = 1;
00419 }
00420 if (av_find_info_tag(buf, sizeof(buf), "ttl", p)) {
00421 s->ttl = strtol(buf, NULL, 10);
00422 }
00423 if (av_find_info_tag(buf, sizeof(buf), "localport", p)) {
00424 s->local_port = strtol(buf, NULL, 10);
00425 }
00426 if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
00427 h->max_packet_size = strtol(buf, NULL, 10);
00428 }
00429 if (av_find_info_tag(buf, sizeof(buf), "buffer_size", p)) {
00430 s->buffer_size = strtol(buf, NULL, 10);
00431 }
00432 if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
00433 s->is_connected = strtol(buf, NULL, 10);
00434 }
00435 if (av_find_info_tag(buf, sizeof(buf), "fifo_size", p)) {
00436 s->circular_buffer_size = strtol(buf, NULL, 10)*188;
00437 }
00438 if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
00439 av_strlcpy(localaddr, buf, sizeof(localaddr));
00440 }
00441 }
00442
00443
00444 av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
00445
00446
00447 if (hostname[0] == '\0' || hostname[0] == '?') {
00448
00449 if (!(flags & AVIO_FLAG_READ))
00450 goto fail;
00451 } else {
00452 if (ff_udp_set_remote_url(h, uri) < 0)
00453 goto fail;
00454 }
00455
00456 if ((s->is_multicast || !s->local_port) && (h->flags & AVIO_FLAG_READ))
00457 s->local_port = port;
00458 udp_fd = udp_socket_create(s, &my_addr, &len, localaddr);
00459 if (udp_fd < 0)
00460 goto fail;
00461
00462
00463
00464
00465 if (s->reuse_socket || (s->is_multicast && !reuse_specified)) {
00466 s->reuse_socket = 1;
00467 if (setsockopt (udp_fd, SOL_SOCKET, SO_REUSEADDR, &(s->reuse_socket), sizeof(s->reuse_socket)) != 0)
00468 goto fail;
00469 }
00470
00471
00472
00473
00474
00475 if (s->is_multicast && !(h->flags & AVIO_FLAG_WRITE)) {
00476 bind_ret = bind(udp_fd,(struct sockaddr *)&s->dest_addr, len);
00477 }
00478
00479
00480
00481 if (bind_ret < 0 && bind(udp_fd,(struct sockaddr *)&my_addr, len) < 0) {
00482 av_log(h, AV_LOG_ERROR, "bind failed: %s\n", strerror(errno));
00483 goto fail;
00484 }
00485
00486 len = sizeof(my_addr);
00487 getsockname(udp_fd, (struct sockaddr *)&my_addr, &len);
00488 s->local_port = udp_port(&my_addr, len);
00489
00490 if (s->is_multicast) {
00491 if (h->flags & AVIO_FLAG_WRITE) {
00492
00493 if (udp_set_multicast_ttl(udp_fd, s->ttl, (struct sockaddr *)&s->dest_addr) < 0)
00494 goto fail;
00495 }
00496 if (h->flags & AVIO_FLAG_READ) {
00497
00498 if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0)
00499 goto fail;
00500 }
00501 }
00502
00503 if (is_output) {
00504
00505 tmp = s->buffer_size;
00506 if (setsockopt(udp_fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) {
00507 av_log(h, AV_LOG_ERROR, "setsockopt(SO_SNDBUF): %s\n", strerror(errno));
00508 goto fail;
00509 }
00510 } else {
00511
00512
00513 tmp = s->buffer_size;
00514 if (setsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(tmp)) < 0) {
00515 av_log(h, AV_LOG_WARNING, "setsockopt(SO_RECVBUF): %s\n", strerror(errno));
00516 }
00517
00518 ff_socket_nonblock(udp_fd, 1);
00519 }
00520 if (s->is_connected) {
00521 if (connect(udp_fd, (struct sockaddr *) &s->dest_addr, s->dest_addr_len)) {
00522 av_log(h, AV_LOG_ERROR, "connect: %s\n", strerror(errno));
00523 goto fail;
00524 }
00525 }
00526
00527 s->udp_fd = udp_fd;
00528
00529 #if HAVE_PTHREAD_CANCEL
00530 if (!is_output && s->circular_buffer_size) {
00531 int ret;
00532
00533
00534 s->fifo = av_fifo_alloc(s->circular_buffer_size);
00535 ret = pthread_mutex_init(&s->mutex, NULL);
00536 if (ret != 0) {
00537 av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", strerror(ret));
00538 goto fail;
00539 }
00540 ret = pthread_cond_init(&s->cond, NULL);
00541 if (ret != 0) {
00542 av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", strerror(ret));
00543 goto cond_fail;
00544 }
00545 ret = pthread_create(&s->circular_buffer_thread, NULL, circular_buffer_task, h);
00546 if (ret != 0) {
00547 av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", strerror(ret));
00548 goto thread_fail;
00549 }
00550 s->thread_started = 1;
00551 }
00552 #endif
00553
00554 return 0;
00555 #if HAVE_PTHREAD_CANCEL
00556 thread_fail:
00557 pthread_cond_destroy(&s->cond);
00558 cond_fail:
00559 pthread_mutex_destroy(&s->mutex);
00560 #endif
00561 fail:
00562 if (udp_fd >= 0)
00563 closesocket(udp_fd);
00564 av_fifo_free(s->fifo);
00565 return AVERROR(EIO);
00566 }
00567
00568 static int udp_read(URLContext *h, uint8_t *buf, int size)
00569 {
00570 UDPContext *s = h->priv_data;
00571 int ret;
00572 int avail, nonblock = h->flags & AVIO_FLAG_NONBLOCK;
00573
00574 #if HAVE_PTHREAD_CANCEL
00575 if (s->fifo) {
00576 pthread_mutex_lock(&s->mutex);
00577 do {
00578 avail = av_fifo_size(s->fifo);
00579 if (avail) {
00580 uint8_t tmp[4];
00581
00582 av_fifo_generic_read(s->fifo, tmp, 4, NULL);
00583 avail= AV_RL32(tmp);
00584 if(avail > size){
00585 av_log(h, AV_LOG_WARNING, "Part of datagram lost due to insufficient buffer size\n");
00586 avail= size;
00587 }
00588
00589 av_fifo_generic_read(s->fifo, buf, avail, NULL);
00590 av_fifo_drain(s->fifo, AV_RL32(tmp) - avail);
00591 pthread_mutex_unlock(&s->mutex);
00592 return avail;
00593 } else if(s->circular_buffer_error){
00594 int err = s->circular_buffer_error;
00595 pthread_mutex_unlock(&s->mutex);
00596 return err;
00597 } else if(nonblock) {
00598 pthread_mutex_unlock(&s->mutex);
00599 return AVERROR(EAGAIN);
00600 }
00601 else {
00602
00603
00604 int64_t t = av_gettime() + 100000;
00605 struct timespec tv = { .tv_sec = t / 1000000,
00606 .tv_nsec = (t % 1000000) * 1000 };
00607 if (pthread_cond_timedwait(&s->cond, &s->mutex, &tv) < 0)
00608 return AVERROR(errno == ETIMEDOUT ? EAGAIN : errno);
00609 nonblock = 1;
00610 }
00611 } while( 1);
00612 }
00613 #endif
00614
00615 if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
00616 ret = ff_network_wait_fd(s->udp_fd, 0);
00617 if (ret < 0)
00618 return ret;
00619 }
00620 ret = recv(s->udp_fd, buf, size, 0);
00621
00622 return ret < 0 ? ff_neterrno() : ret;
00623 }
00624
00625 static int udp_write(URLContext *h, const uint8_t *buf, int size)
00626 {
00627 UDPContext *s = h->priv_data;
00628 int ret;
00629
00630 if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
00631 ret = ff_network_wait_fd(s->udp_fd, 1);
00632 if (ret < 0)
00633 return ret;
00634 }
00635
00636 if (!s->is_connected) {
00637 ret = sendto (s->udp_fd, buf, size, 0,
00638 (struct sockaddr *) &s->dest_addr,
00639 s->dest_addr_len);
00640 } else
00641 ret = send(s->udp_fd, buf, size, 0);
00642
00643 return ret < 0 ? ff_neterrno() : ret;
00644 }
00645
00646 static int udp_close(URLContext *h)
00647 {
00648 UDPContext *s = h->priv_data;
00649 int ret;
00650
00651 if (s->is_multicast && (h->flags & AVIO_FLAG_READ))
00652 udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr);
00653 closesocket(s->udp_fd);
00654 av_fifo_free(s->fifo);
00655 #if HAVE_PTHREAD_CANCEL
00656 if (s->thread_started) {
00657 pthread_cancel(s->circular_buffer_thread);
00658 ret = pthread_join(s->circular_buffer_thread, NULL);
00659 if (ret != 0)
00660 av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret));
00661 }
00662
00663 pthread_mutex_destroy(&s->mutex);
00664 pthread_cond_destroy(&s->cond);
00665 #endif
00666 return 0;
00667 }
00668
00669 URLProtocol ff_udp_protocol = {
00670 .name = "udp",
00671 .url_open = udp_open,
00672 .url_read = udp_read,
00673 .url_write = udp_write,
00674 .url_close = udp_close,
00675 .url_get_file_handle = udp_get_file_handle,
00676 .priv_data_size = sizeof(UDPContext),
00677 .flags = URL_PROTOCOL_FLAG_NETWORK,
00678 };