static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min, int (*transfer_func)(URLContext *h, unsigned char *buf, int size)) { int ret, len; int64_t timeouttime = av_gettime()+10*(1000*1000);/*10*1S.*/ int retry=0; len = 0; while (len < size_min) { ret = transfer_func(h, buf+len, size-len);/*low level retry 1S*/ if (url_interrupt_cb()) return AVERROR_EXIT; if (ret == AVERROR(EINTR)) continue; if (h->flags & AVIO_FLAG_NONBLOCK) return ret; if (ret == AVERROR(EAGAIN)) { if(av_gettime()>=timeouttime) return AVERROR(EAGAIN); av_log(NULL,AV_LOG_INFO,"retry_transfer_wrapper,retry=%d\n",retry++); } else if (ret < 1){ return ret < 0 ? ret : len; }else{ len+=ret; } if (url_interrupt_cb()) /*at least try several times for some teardown cmd finished.*/ return AVERROR_EXIT; } return len; }
static int applehttp_read(URLContext *h, uint8_t *buf, int size) { AppleHTTPContext *s = h->priv_data; const char *url; int ret; start: if (s->seg_hd) { ret = ffurl_read(s->seg_hd, buf, size); if (ret > 0) return ret; } if (s->seg_hd) { ffurl_close(s->seg_hd); s->seg_hd = NULL; s->cur_seq_no++; } retry: if (!s->finished) { int64_t now = av_gettime(); if (now - s->last_load_time >= s->target_duration*1000000) if ((ret = parse_playlist(h, s->playlisturl)) < 0) return ret; } if (s->cur_seq_no < s->start_seq_no) { av_log(h, AV_LOG_WARNING, "skipping %d segments ahead, expired from playlist\n", s->start_seq_no - s->cur_seq_no); s->cur_seq_no = s->start_seq_no; } if (s->cur_seq_no - s->start_seq_no >= s->n_segments) { if (s->finished) return AVERROR_EOF; while (av_gettime() - s->last_load_time < s->target_duration*1000000) { if (url_interrupt_cb()) return AVERROR_EXIT; usleep(100*1000); } goto retry; } url = s->segments[s->cur_seq_no - s->start_seq_no]->url, av_log(h, AV_LOG_DEBUG, "opening %s\n", url); ret = ffurl_open(&s->seg_hd, url, AVIO_FLAG_READ); if (ret < 0) { if (url_interrupt_cb()) return AVERROR_EXIT; av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url); s->cur_seq_no++; goto retry; } goto start; }
int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) { #define MAX_RETRY (6*60) //10S*6*60=60MIN; int maxretry=MAX_RETRY; int toread=size; int readedlen=0; if (!(h->flags & AVIO_FLAG_READ)) return AVERROR(EIO); while(toread>0 && maxretry-->0){ int ret; ret=retry_transfer_wrapper(h, buf+readedlen, toread, toread, h->prot->url_read); if(ret>0){ toread-=ret; readedlen+=ret; }else if(ret!=AVERROR(EAGAIN)){ return ret;/*error of EOF*/ } if(url_interrupt_cb()) return AVERROR_EXIT; } if((size-toread)!=0) return (size-toread); else return AVERROR(ETIMEDOUT);/*retry too long time,//time out...*/ }
static int tcp_write(URLContext *h, const uint8_t *buf, int size) { TCPContext *s = h->priv_data; int ret, size1, len; struct pollfd p = {s->fd, POLLOUT, 0}; size1 = size; while (size > 0) { if (url_interrupt_cb()) return AVERROR(EINTR); ret = poll(&p, 1, 100); if (ret == 1 && p.revents & POLLOUT) { len = send(s->fd, buf, size, 0); if (len < 0) { if (ff_neterrno() != FF_NETERROR(EINTR) && ff_neterrno() != FF_NETERROR(EAGAIN)) return ff_neterrno(); continue; } size -= len; buf += len; } else if (ret < 0) { if (ff_neterrno() == FF_NETERROR(EINTR)) continue; return -1; } } return size1 - size; }
static int list_read(URLContext *h, unsigned char *buf, int size) { struct list_mgt *mgt = h->priv_data; int len = AVERROR(EIO); int counts = 200; do { if (url_interrupt_cb()) { //av_log(NULL, AV_LOG_ERROR, " url_interrupt_cb\n"); len = AVERROR(EINTR); break; } len = CacheHttp_Read(mgt->cache_http_handle, buf, size); /* if (len == AVERROR(EAGAIN)) { usleep(1000 * 10); break; }*/ if (len <0) { usleep(1000 * 10); } if (len >= 0) { break; } } while (counts-- > 0); return len; }
static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min, int (*transfer_func)(URLContext *h, unsigned char *buf, int size)) { int ret, len; int fast_retries = 5; len = 0; while (len < size_min) { ret = transfer_func(h, buf+len, size-len); if (ret == AVERROR(EINTR)) continue; if (h->flags & AVIO_FLAG_NONBLOCK) return ret; if (ret == AVERROR(EAGAIN)) { ret = 0; if (fast_retries) fast_retries--; else usleep(1000); } else if (ret < 1) return ret < 0 ? ret : len; if (ret) fast_retries = FFMAX(fast_retries, 2); len += ret; if (url_interrupt_cb()) return AVERROR_EXIT; } return len; }
static int tcp_read(URLContext *h, uint8_t *buf, int size) { TCPContext *s = h->priv_data; int len, fd_max, ret; fd_set rfds; struct timeval tv; for (;;) { if (url_interrupt_cb()) return AVERROR(EINTR); fd_max = s->fd; FD_ZERO(&rfds); FD_SET(s->fd, &rfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(fd_max + 1, &rfds, NULL, NULL, &tv); if (ret > 0 && FD_ISSET(s->fd, &rfds)) { len = recv(s->fd, buf, size, 0); if (len < 0) { if (ff_neterrno() != FF_NETERROR(EINTR) && ff_neterrno() != FF_NETERROR(EAGAIN)) return AVERROR(errno); } else return len; } else if (ret < 0) { return -1; } } }
static int ffmpeg_hls_read(URLContext *h, unsigned char *buf, int size){ if(h == NULL||h->priv_data == NULL||size<=0){ RLOG("failed to read data\n"); return -1; } FFMPEG_HLS_CONTEXT* ctx = (FFMPEG_HLS_CONTEXT*)h->priv_data; void * hSession = ctx->hls_ctx; int len = AVERROR(EIO); int counts = 10; do { if (url_interrupt_cb()>0) { RLOG("url_interrupt_cb\n"); len = 0; break; } len = m3u_session_read_data(hSession, buf,size); if (len <0) { if(len == AVERROR(EAGAIN)){ usleep(1000*100); }else{ break; } } if (len >= 0) { break; } } while (counts-- > 0); if(ctx->debug_level>5){ RLOG("%s,want:%d,got:%d\n",__FUNCTION__,size,len); } return len; }
static int rtmp_read(URLContext *s, uint8_t *buf, int size) { RTMP *r = s->priv_data; int nRead = 0; int retries = 20; do { if (url_interrupt_cb()>0) { nRead = 0; break; } nRead = RTMP_Read(r, buf, size); if (nRead > 0) { break; } else { if (r->m_read.status == RTMP_READ_EOF) { nRead = 0; break; } if(nRead == 0){ nRead = AVERROR(EAGAIN); usleep(1000*100); }else{ break; } } } while (retries-- > 0); return nRead; }
static int udp_read(URLContext *h, uint8_t *buf, int size) { UDPContext *s = h->priv_data; int len; fd_set rfds; int ret; struct timeval tv; for(;;) { if (url_interrupt_cb()) return AVERROR(EINTR); FD_ZERO(&rfds); FD_SET(s->udp_fd, &rfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(s->udp_fd + 1, &rfds, NULL, NULL, &tv); if (ret < 0) return AVERROR(EIO); if (!(ret > 0 && FD_ISSET(s->udp_fd, &rfds))) continue; len = recv(s->udp_fd, buf, size, 0); if (len < 0) { if (ff_neterrno() != FF_NETERROR(EAGAIN) && ff_neterrno() != FF_NETERROR(EINTR)) return AVERROR(EIO); } else { break; } } return len; }
static int tcp_read(URLContext *h, uint8_t *buf, int size) { TCPContext *s = h->priv_data; int len, fd_max, ret; fd_set rfds; struct timeval tv; for (;;) { if (url_interrupt_cb()) return -EINTR; fd_max = s->fd; FD_ZERO(&rfds); FD_SET(s->fd, &rfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(fd_max + 1, &rfds, NULL, NULL, &tv); if (ret > 0 && FD_ISSET(s->fd, &rfds)) { #ifdef __BEOS__ len = recv(s->fd, buf, size, 0); #else len = read(s->fd, buf, size); #endif if (len < 0) { if (errno != EINTR && errno != EAGAIN) #ifdef __BEOS__ return errno; #else return -errno; #endif } else return len; } else if (ret < 0) { return -1; } } }
static int file_read(URLContext *h, unsigned char *buf, int size) { int fd = (intptr_t) h->priv_data; int rv; do { rv = read(fd, buf, size); if (rv <= 0 && ((h->flags & URL_ACTIVEFILE) == URL_ACTIVEFILE)) { #ifdef __MINGW32__ usleep(20000); #else struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 20000000; nanosleep(&ts, NULL); #endif if (url_interrupt_cb()) return -EINTR; } else break; } while (1); return rv; }
static int udp_read(URLContext *h, uint8_t *buf, int size) { UDPContext *s = h->priv_data; struct pollfd p = {s->udp_fd, POLLIN, 0}; int len; int ret; for(;;) { if (url_interrupt_cb()) return AVERROR(EINTR); ret = poll(&p, 1, 100); if (ret < 0) { if (ff_neterrno() == FF_NETERROR(EINTR)) continue; return AVERROR(EIO); } if (!(ret == 1 && p.revents & POLLIN)) continue; len = recv(s->udp_fd, buf, size, 0); if (len < 0) { if (ff_neterrno() != FF_NETERROR(EAGAIN) && ff_neterrno() != FF_NETERROR(EINTR)) return AVERROR(EIO); } else { break; } } return len; }
static int tcp_write(URLContext *h, uint8_t *buf, int size) { TCPContext *s = h->priv_data; int ret, size1, fd_max, len; fd_set wfds; struct timeval tv; size1 = size; while (size > 0) { if (url_interrupt_cb()) return AVERROR(EINTR); fd_max = s->fd; FD_ZERO(&wfds); FD_SET(s->fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); if (ret > 0 && FD_ISSET(s->fd, &wfds)) { len = send(s->fd, buf, size, 0); if (len < 0) { if (ff_neterrno() != FF_NETERROR(EINTR) && ff_neterrno() != FF_NETERROR(EAGAIN)) return AVERROR(errno); continue; } size -= len; buf += len; } else if (ret < 0) { return -1; } } return size1 - size; }
static void *circular_buffer_task( void *_URLContext) { URLContext *h = _URLContext; UDPContext *s = h->priv_data; fd_set rfds; struct timeval tv; for(;;) { int left; int ret; int len; if (url_interrupt_cb()) { s->circular_buffer_error = EINTR; return NULL; } FD_ZERO(&rfds); FD_SET(s->udp_fd, &rfds); tv.tv_sec = 1; tv.tv_usec = 0; ret = select(s->udp_fd + 1, &rfds, NULL, NULL, &tv); if (ret < 0) { if (ff_neterrno() == AVERROR(EINTR)) continue; s->circular_buffer_error = EIO; return NULL; } if (!(ret > 0 && FD_ISSET(s->udp_fd, &rfds))) continue; /* How much do we have left to the end of the buffer */ /* Whats the minimum we can read so that we dont comletely fill the buffer */ left = av_fifo_space(s->fifo); left = FFMIN(left, s->fifo->end - s->fifo->wptr); /* No Space left, error, what do we do now */ if( !left) { av_log(h, AV_LOG_ERROR, "circular_buffer: OVERRUN\n"); s->circular_buffer_error = EIO; return NULL; } len = recv(s->udp_fd, s->fifo->wptr, left, 0); if (len < 0) { if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) { s->circular_buffer_error = EIO; return NULL; } } s->fifo->wptr += len; if (s->fifo->wptr >= s->fifo->end) s->fifo->wptr = s->fifo->buffer; s->fifo->wndx += len; } return NULL; }
static int interrupt_call_cb(void){ if (url_interrupt_cb()) { RLOG("[%s]:url_interrupt_cb\n",__FUNCTION__); return 1; } return 0; }
static int rtp_read(URLContext *h, uint8_t *buf, int size) { RTPContext *s = h->priv_data; struct sockaddr_storage from; socklen_t from_len; int len, n; struct pollfd p[2] = {{s->rtp_fd, POLLIN, 0}, {s->rtcp_fd, POLLIN, 0}}; for(;;) { if (url_interrupt_cb()) return AVERROR_EXIT; /* build fdset to listen to RTP and RTCP packets */ n = poll(p, 2, 100); if (n > 0) { /* first try RTCP */ if (p[1].revents & POLLIN) { from_len = sizeof(from); len = recvfrom (s->rtcp_fd, buf, size, 0, (struct sockaddr *)&from, &from_len); if (len < 0) { if (ff_neterrno() == AVERROR(EAGAIN) || ff_neterrno() == AVERROR(EINTR)) continue; return AVERROR(EIO); } break; } /* then RTP */ if (p[0].revents & POLLIN) { from_len = sizeof(from); len = recvfrom (s->rtp_fd, buf, size, 0, (struct sockaddr *)&from, &from_len); if (len < 0) { if (ff_neterrno() == AVERROR(EAGAIN) || ff_neterrno() == AVERROR(EINTR)) continue; return AVERROR(EIO); } break; } } else if (n < 0) { if (ff_neterrno() == AVERROR(EINTR)) continue; return AVERROR(EIO); } } return len; }
int CacheHttp_Read(void * handle, uint8_t * cache, int size) { if(!handle) return AVERROR(EIO); CacheHttpContext * s = (CacheHttpContext *)handle; pthread_mutex_lock(&s->read_mutex); if (s->fifo) { int avail; avail = av_fifo_size(s->fifo); //av_log(NULL, AV_LOG_INFO, "----------- http_read avail=%d, size=%d ",avail,size); if(s->is_first_read>0) { float value = 0.0; int ret = -1; ret = am_getconfig_float("libplayer.hls.initial_buffered", &value); if(ret>=0) { if(avail/1024<value) { //av_log(NULL, AV_LOG_INFO, "buffer data avail=%d, initial buffer buffered data size=%f ",avail,value*1024); pthread_mutex_unlock(&s->read_mutex); return AVERROR(EAGAIN); } } s->is_first_read = 0; } if(url_interrupt_cb()) { pthread_mutex_unlock(&s->read_mutex); return 0; } else if(avail) { // Maximum amount available size = FFMIN( avail, size); av_fifo_generic_read(s->fifo, cache, size, NULL); pthread_mutex_unlock(&s->read_mutex); return size; } else if(s->EXITED) { pthread_mutex_unlock(&s->read_mutex); return 0; } else if(!s->finish_flag) { pthread_mutex_unlock(&s->read_mutex); //read just need retry return AVERROR(EAGAIN); } } pthread_mutex_unlock(&s->read_mutex); return 0; }
static void fresh_item_list(struct list_mgt *mgt){ int retries = 5; int reload_interval = mgt->target_duration * 500000; do{ if((switchto_next_item(mgt))!=NULL){ av_log(NULL, AV_LOG_INFO, "refresh the Playlist,item num:%d,filename:%s\n",mgt->item_num,mgt->filename); break; } else{ if (url_interrupt_cb()){ av_log(NULL, AV_LOG_ERROR," url_interrupt_cb\n"); break; } usleep(reload_interval); //50ms av_log(NULL, AV_LOG_INFO, "no new item,wait next refresh,current item num:%d\n",mgt->item_num); } }while(retries-->0);//50ms*5 }
static int tcp_write(URLContext *h, uint8_t *buf, int size) { TCPContext *s = h->priv_data; int ret, size1, fd_max, len; fd_set wfds; struct timeval tv; size1 = size; while (size > 0) { if (url_interrupt_cb()) return -EINTR; fd_max = s->fd; FD_ZERO(&wfds); FD_SET(s->fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); if (ret > 0 && FD_ISSET(s->fd, &wfds)) { #ifdef __BEOS__ len = send(s->fd, buf, size, 0); #else len = write(s->fd, buf, size); #endif if (len < 0) { if (errno != EINTR && errno != EAGAIN) { #ifdef __BEOS__ return errno; #else return -errno; #endif } continue; } size -= len; buf += len; } else if (ret < 0) { return -1; } } return size1 - size; }
static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, uint8_t *buf, int buf_size) { RTSPState *rt = s->priv_data; RTSPStream *rtsp_st; fd_set rfds; int fd1, fd2, fd_max, n, i, ret; struct timeval tv; for(;;) { if (url_interrupt_cb()) return -1; FD_ZERO(&rfds); fd_max = -1; for(i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; /* currently, we cannot probe RTCP handle because of blocking restrictions */ rtp_get_file_handles(rtsp_st->rtp_handle, &fd1, &fd2); if (fd1 > fd_max) fd_max = fd1; FD_SET(fd1, &rfds); } tv.tv_sec = 0; tv.tv_usec = 100 * 1000; n = select(fd_max + 1, &rfds, NULL, NULL, &tv); if (n > 0) { for(i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; rtp_get_file_handles(rtsp_st->rtp_handle, &fd1, &fd2); if (FD_ISSET(fd1, &rfds)) { ret = url_read(rtsp_st->rtp_handle, buf, buf_size); if (ret > 0) { *prtsp_st = rtsp_st; return ret; } } } } } }
static int tcp_read(URLContext *h, uint8_t *buf, int size) { TCPContext *s = h->priv_data; int len, fd_max, ret; fd_set rfds; struct timeval tv; int tout_cnt=100; //tout_cnt*time_out=10s for (;;) { if (url_interrupt_cb()) return AVERROR(EINTR); fd_max = s->fd; FD_ZERO(&rfds); FD_SET(s->fd, &rfds); tv.tv_sec = 0; tv.tv_usec = 300 * 1000; ret = select(fd_max + 1, &rfds, NULL, NULL, &tv); if (ret > 0 && FD_ISSET(s->fd, &rfds)) { len = recv(s->fd, buf, size, 0); if (len < 0) { av_log(NULL,AV_LOG_INFO,"tcp_read recv failed: len= %d\n",len); if (ff_neterrno() != FF_NETERROR(EINTR) && ff_neterrno() != FF_NETERROR(EAGAIN)) return AVERROR(ff_neterrno()); } else return len; } else if (ret < 0) { av_log(NULL,AV_LOG_INFO,"tcp_read select failed: ret= %d\n",ret); return -1; } else if(ret == 0){ tout_cnt --; av_log(NULL,AV_LOG_INFO, "tcp_read(%d): read timeout,try again\n",tout_cnt); if(tout_cnt == 0) { return AVERROR(EAGAIN); } } } }
/* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { struct sockaddr_in dest_addr; char hostname[1024], *q; int port, fd = -1; TCPContext *s; const char *p; fd_set wfds; int fd_max, ret; struct timeval tv; socklen_t optlen; char proto[1024],path[1024],tmp[1024]; // PETR: protocol and path strings url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); // PETR: use url_split if (strcmp(proto,"tcp")) goto fail; // PETR: check protocol if ((q = strchr(hostname,'@'))) { strcpy(tmp,q+1); strcpy(hostname,tmp); } // PETR: take only the part after '@' for tcp protocol s = av_malloc(sizeof(TCPContext)); if (!s) return -ENOMEM; h->priv_data = s; if (port <= 0 || port >= 65536) goto fail; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(port); if (resolve_host(&dest_addr.sin_addr, hostname) < 0) goto fail; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) goto fail; fcntl(fd, F_SETFL, O_NONBLOCK); redo: ret = connect(fd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (ret < 0) { if (errno == EINTR) goto redo; if (errno != EINPROGRESS) goto fail; /* wait until we are connected or until abort */ for(;;) { if (url_interrupt_cb()) { ret = -EINTR; goto fail1; } fd_max = fd; FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); if (ret > 0 && FD_ISSET(fd, &wfds)) break; } /* test error */ optlen = sizeof(ret); getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); if (ret != 0) goto fail; } s->fd = fd; return 0; fail: ret = AVERROR_IO; fail1: if (fd >= 0) close(fd); av_free(s); return ret; }
/* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { struct addrinfo hints, *ai, *cur_ai; int port, fd = -1; TCPContext *s = NULL; int listen_socket = 0; const char *p; char buf[256]; int ret; socklen_t optlen; int timeout = 500, listen_timeout = -1; char hostname[1024],proto[1024],path[1024]; char portstr[10]; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); if (strcmp(proto, "tcp")) return AVERROR(EINVAL); if (port <= 0 || port >= 65536) { av_log(h, AV_LOG_ERROR, "Port missing in uri\n"); return AVERROR(EINVAL); } p = strchr(uri, '?'); if (p) { if (av_find_info_tag(buf, sizeof(buf), "listen", p)) listen_socket = 1; if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { timeout = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { listen_timeout = strtol(buf, NULL, 10); } } memset(&hints, 0, sizeof(hints)); if(am_getconfig_bool_def("media.libplayer.ipv4only",1)) hints.ai_family = AF_INET; else hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf(portstr, sizeof(portstr), "%d", port); av_log(h, AV_LOG_INFO,"tcp will get address from dns!\n"); if (listen_socket) hints.ai_flags |= AI_PASSIVE; if (!hostname[0]) ret = getaddrinfo(NULL, portstr, &hints, &ai); else ret = getaddrinfo(hostname, portstr, &hints, &ai); if (ret) { av_log(h, AV_LOG_ERROR, "Failed to resolve hostname %s: %s\n", hostname, gai_strerror(ret)); return AVERROR(EIO); } av_log(h, AV_LOG_INFO,"resolved %s's ipaddress \n",hostname); cur_ai = ai; restart: ret = AVERROR(EIO); fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); if (fd < 0) goto fail; if (listen_socket) { int fd1; int reuse = 1; struct pollfd lp = { fd, POLLIN, 0 }; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); if (ret) { ret = ff_neterrno(); goto fail1; } ret = listen(fd, 1); if (ret) { ret = ff_neterrno(); goto fail1; } ret = poll(&lp, 1, listen_timeout >= 0 ? listen_timeout : -1); if (ret <= 0) { ret = AVERROR(ETIMEDOUT); goto fail1; } fd1 = accept(fd, NULL, NULL); if (fd1 < 0) { ret = ff_neterrno(); goto fail1; } closesocket(fd); fd = fd1; ff_socket_nonblock(fd, 1); } else { redo: ff_socket_nonblock(fd, 1); ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); } if (ret < 0) { struct pollfd p = {fd, POLLOUT, 0}; ret = ff_neterrno(); if (ret == AVERROR(EINTR)) { if (url_interrupt_cb()) { ret = AVERROR_EXIT; goto fail1; } goto redo; } if (ret != AVERROR(EINPROGRESS) && ret != AVERROR(EAGAIN)) goto fail; /* wait until we are connected or until abort */ while(timeout--) { if (url_interrupt_cb()) { ret = AVERROR_EXIT; goto fail1; } ret = poll(&p, 1, 100); if (ret > 0) break; } if (ret <= 0) { av_log(h, AV_LOG_ERROR, "TCP connection to %s:%d timeout failed!\n", hostname, port); ret = AVERROR(ETIMEDOUT); goto fail; } /* test error */ optlen = sizeof(ret); if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen)) ret = AVUNERROR(ff_neterrno()); if (ret != 0) { char errbuf[100]; ret = AVERROR(ret); av_strerror(ret, errbuf, sizeof(errbuf)); av_log(h, AV_LOG_ERROR, "TCP connection to %s:%d failed: %s, ret = %d\n", hostname, port, errbuf, ret); ret = AVERROR(ret); if(ret>0) ret = AVERROR(EIO); goto fail; } } av_log(h, AV_LOG_INFO,"tcp connect %s ok!\n",hostname); s = av_malloc(sizeof(TCPContext)); if (!s) { freeaddrinfo(ai); return AVERROR(ENOMEM); } h->priv_data = s; h->is_streamed = 1; s->fd = fd; freeaddrinfo(ai); return 0; fail: if (cur_ai->ai_next) { /* Retry with the next sockaddr */ cur_ai = cur_ai->ai_next; if (fd >= 0) closesocket(fd); goto restart; } fail1: if (fd >= 0) closesocket(fd); freeaddrinfo(ai); return ret; }
static void *circular_buffer_task( void *_handle) { CacheHttpContext * s = (CacheHttpContext *)_handle; URLContext *h = NULL; float config_value = 0.0; void * fp = NULL; int config_ret = 0; while(!s->EXIT) { av_log(h, AV_LOG_ERROR, "----------circular_buffer_task item "); s->reset_flag = 1; if (url_interrupt_cb()) { s->circular_buffer_error = EINTR; goto FAIL; } if(h) { CacheHttp_ffurl_close(h); config_ret = am_getconfig_float("libplayer.hls.dump",&config_value); if(config_ret >= 0 && (int)config_value == 2) CacheHttp_dump_close(fp); h = NULL; } list_item_t * item = getCurrentSegment(NULL); if(!item||(!item->file&&!item->flags&ENDLIST_FLAG)) { usleep(WAIT_TIME); continue; } s->reset_flag = 0; s->item_starttime = item->start_time; s->item_duration = item->duration; s->have_list_end = item->have_list_end; s->ktype = item->ktype; if(item->key_ctx!=NULL&& s->ktype==KEY_AES_128) { ff_data_to_hex(s->iv, item->key_ctx->iv, sizeof(item->key_ctx->iv), 0); ff_data_to_hex(s->key, item->key_ctx->key, sizeof(item->key_ctx->key), 0); s->iv[32] = s->key[32] = '\0'; } if(item&&item->flags&ENDLIST_FLAG) { s->finish_flag =1; } else { s->finish_flag =0; } if(s->finish_flag) { av_log(NULL, AV_LOG_INFO, "ENDLIST_FLAG, return 0\n"); //break; usleep(500*1000); continue; } int err, http_code; char* filename = NULL; if(s->ktype == KEY_NONE) { filename = av_strdup(item->file); } else { char url[MAX_URL_SIZE]; if (strstr(item->file, "://")) snprintf(url, sizeof(url), "crypto+%s", item->file); else snprintf(url, sizeof(url), "crypto:%s", item->file); filename = av_strdup(url); } int retry_num = 0; OPEN_RETRY: if(s->RESET) goto SKIP; err = CacheHttp_advanced_ffurl_open_h(&h, filename,AVIO_FLAG_READ|AVIO_FLAG_NONBLOCK, s->headers, &http_code,s); if (err) { if(url_interrupt_cb()) { if(filename) { av_free(filename); filename = NULL; } break; } if(1 == http_code && !s->have_list_end) { av_log(h, AV_LOG_ERROR, "----------CacheHttpContext : ffurl_open_h 404\n"); if(retry_num++ < LIVE_HTTP_RETRY_TIMES) { usleep(WAIT_TIME); goto OPEN_RETRY; } else { goto SKIP; } } else if(s->have_list_end || ((2 == http_code || 3 == http_code)&& !s->have_list_end)) { usleep(1000*20); goto OPEN_RETRY; } else if(!s->have_list_end&&err ==AVERROR(EIO)) { if(retry_num++ < LIVE_HTTP_RETRY_TIMES) {//if live streaming,just keep on 2s. usleep(WAIT_TIME); goto OPEN_RETRY; } else { av_log(h, AV_LOG_ERROR, "----------CacheHttpContext : ffurl_open_h failed ,%d\n",err); if(filename) { av_free(filename); filename = NULL; } break; } } else { av_log(h, AV_LOG_ERROR, "----------CacheHttpContext : ffurl_open_h failed ,%d\n",err); if(filename) { av_free(filename); filename = NULL; } break; } } if(h && s->seek_flag) { int64_t cur_pos = CacheHttp_ffurl_seek(h, 0, SEEK_CUR); int64_t pos_ret = CacheHttp_ffurl_seek(h, s->seek_pos-cur_pos, SEEK_CUR); av_log(NULL,AV_LOG_INFO,"--------------> cachehttp_seek seek_pos=%lld, pos_ret=%lld", s->seek_pos, pos_ret); s->seek_flag = 0; } s->hd = h; s->item_pos = 0; s->item_size = CacheHttp_ffurl_seek(s->hd, 0, AVSEEK_SIZE); item->item_size = s->item_size; char tmpbuf[TMP_BUFFER_SIZE]; int left = 0; int tmpdatasize = 0; config_ret = am_getconfig_float("libplayer.hls.dump",&config_value); if(config_ret >= 0 && config_value > 0) CacheHttp_dump_open(&fp, filename, (int)config_value); while(!s->EXIT) { if(s->RESET) break; if (url_interrupt_cb()) { s->circular_buffer_error = EINTR; break; } if(!s->hd) break; if(s->hd && tmpdatasize <= 0) { bandwidth_measure_start_read(s->bandwidth_measure); tmpdatasize = CacheHttp_ffurl_read(s->hd, tmpbuf, TMP_BUFFER_SIZE); bandwidth_measure_finish_read(s->bandwidth_measure,tmpdatasize); } //if(tmpdatasize > 0) { pthread_mutex_lock(&s->read_mutex); left = av_fifo_space(s->fifo); left = FFMIN(left, s->fifo->end - s->fifo->wptr); if( !left) { pthread_mutex_unlock(&s->read_mutex); usleep(WAIT_TIME); continue; } left = FFMIN(left, tmpdatasize); if(left >0) { memcpy(s->fifo->wptr, tmpbuf , left); tmpdatasize-=left; } if(tmpdatasize>0) { memmove(tmpbuf, tmpbuf+left , tmpdatasize); } if (left > 0) { config_ret = am_getconfig_float("libplayer.hls.dump",&config_value); if(config_ret >= 0 && config_value > 0) CacheHttp_dump_write(fp, s->fifo->wptr, left); s->fifo->wptr += left; if (s->fifo->wptr >= s->fifo->end) s->fifo->wptr = s->fifo->buffer; s->fifo->wndx += left; s->item_pos += left; } else if(left == AVERROR(EAGAIN) || (left < 0 && s->have_list_end&& left != AVERROR_EOF)) { pthread_mutex_unlock(&s->read_mutex); continue; } else { pthread_mutex_unlock(&s->read_mutex); av_log(h, AV_LOG_ERROR, "---------- circular_buffer_task read left = %d\n", left); break; } pthread_mutex_unlock(&s->read_mutex); //} //usleep(WAIT_TIME); } SKIP: if(filename) { av_free(filename); filename = NULL; } if(!s->RESET) switchNextSegment(NULL); } FAIL: if(h) CacheHttp_ffurl_close(h); s->hd = NULL; s->EXITED = 1; config_ret = am_getconfig_float("libplayer.hls.dump",&config_value); if(config_ret >= 0 && config_value > 0) CacheHttp_dump_close(fp); av_log(NULL, AV_LOG_ERROR, "---------> CacheHttp thread quit !"); return NULL; }
static int list_read(URLContext *h, unsigned char *buf, int size) { struct list_mgt *mgt = h->priv_data; int len=AVERROR(EIO); struct list_item *item=mgt->current_item; retry: if (url_interrupt_cb()) return AVERROR(EINTR); av_log(NULL, AV_LOG_INFO, "list_read start buf=%x,size=%d\n",buf,size); if(!mgt->cur_uio ) { if(item && item->file) { ByteIOContext *bio; av_log(NULL, AV_LOG_INFO, "list_read switch to new file=%s\n",item->file); len=url_fopen(&bio,item->file,O_RDONLY | URL_MINI_BUFFER | URL_NO_LP_BUFFER); if(len!=0) { return len; } if(url_is_file_list(bio,item->file)) { /*have 32 bytes space at the end..*/ memmove(item->file+5,item->file,strlen(item->file)+1); memcpy(item->file,"list:",5); url_fclose(bio); len=url_fopen(&bio,item->file,mgt->flags | URL_MINI_BUFFER | URL_NO_LP_BUFFER); if(len!=0) { return len; } } mgt->cur_uio=bio; } } if(mgt->cur_uio) len=get_buffer(mgt->cur_uio,buf,size); if(len==AVERROR(EAGAIN)) return AVERROR(EAGAIN);/*not end,bug need to*/ else if((len<=0)&& mgt->current_item!=NULL) {/*end of the file*/ av_log(NULL, AV_LOG_INFO, "try switchto_next_item buf=%x,size=%d,len=%d\n",buf,size,len); if(item && (item->flags & ENDLIST_FLAG)) return 0; item=switchto_next_item(mgt); if(!item){ return AVERROR(EAGAIN);/*not end,but need to refresh the list later*/ } if(mgt->cur_uio) url_fclose(mgt->cur_uio); mgt->cur_uio=NULL; mgt->current_item=item; if(item->flags & ENDLIST_FLAG){ av_log(NULL, AV_LOG_INFO, "reach list end now!,item=%x\n",item); return 0;/*endof file*/ } else if(item->flags & DISCONTINUE_FLAG){ av_log(NULL, AV_LOG_INFO, "Discontiue item \n"); //1 TODO:need to notify uper level stream is changed goto retry; } else{ goto retry; } } av_log(NULL, AV_LOG_INFO, "list_read end buf=%x,size=%d\n",buf,size); return len; }
static int list_read(URLContext *h, unsigned char *buf, int size) { struct list_mgt *mgt = h->priv_data; int len=AVERROR(EIO); struct list_item *item=mgt->current_item; int retries = 10; retry: if (url_interrupt_cb()){ av_log(NULL, AV_LOG_ERROR," url_interrupt_cb\n"); return AVERROR(EINTR); } //av_log(NULL, AV_LOG_INFO, "list_read start buf=%x,size=%d\n",buf,size); if(!mgt->cur_uio ) { if(item && item->file) { ByteIOContext *bio; av_log(NULL, AV_LOG_INFO, "list_read switch to new file=%s\n",item->file); len=url_fopen(&bio,item->file,AVIO_FLAG_READ | URL_MINI_BUFFER | URL_NO_LP_BUFFER); if(len!=0) { av_log(NULL, AV_LOG_ERROR, "list url_fopen failed =%d\n",len); return len; } if(url_is_file_list(bio,item->file)) { mgt->have_sub_list = 1; /*have 32 bytes space at the end..*/ memmove(item->file+5,item->file,strlen(item->file)+1); memcpy(item->file,"list:",5); url_fclose(bio); len=url_fopen(&bio,item->file,mgt->flags | URL_MINI_BUFFER | URL_NO_LP_BUFFER); if(len!=0) { av_log(NULL, AV_LOG_INFO, "file list url_fopen failed =%d\n",len); return len; } }else{ if(item->ktype == KEY_AES_128){ if(item->key_ctx&&!item->crypto){ item->crypto = av_mallocz(sizeof(struct AESCryptoContext)); if(!item->crypto){ len = AVERROR(ENOMEM); return len; } item->crypto->aes = av_mallocz(av_aes_size); if(!item->crypto->aes){ len = AVERROR(ENOMEM); return len; } av_aes_init(item->crypto->aes,item->key_ctx->key, 128, 1); item->crypto->have_init = 1; } bio->is_encrypted_media =1; } } mgt->cur_uio=bio; } } if(mgt->cur_uio){ if(size>0&&mgt->cur_uio->is_encrypted_media>0&&mgt->current_item->key_ctx&&mgt->current_item->crypto){//codes from crypto.c int blocks; struct AESCryptoContext* c = mgt->current_item->crypto; readagain: len = 0; if (c->outdata > 0) { size = FFMIN(size, c->outdata); memcpy(buf, c->outptr, size); c->outptr += size; c->outdata -= size; len =size; } // We avoid using the last block until we've found EOF, // since we'll remove PKCS7 padding at the end. So make // sure we've got at least 2 blocks, so we can decrypt // at least one. while (c->indata - c->indata_used < 2*BLOCKSIZE) { int n = get_buffer(mgt->cur_uio,c->inbuffer + c->indata, sizeof(c->inbuffer) - c->indata); if (n <= 0) { c->eof = 1; break; } c->indata += n; } blocks = (c->indata - c->indata_used) / BLOCKSIZE; if (blocks!=0){ if (!c->eof) blocks--; av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks,mgt->current_item->key_ctx->iv, 1); c->outdata = BLOCKSIZE * blocks; c->outptr = c->outbuffer; c->indata_used += BLOCKSIZE * blocks; if (c->indata_used >= sizeof(c->inbuffer)/2) { memmove(c->inbuffer, c->inbuffer + c->indata_used, c->indata - c->indata_used); c->indata -= c->indata_used; c->indata_used = 0; } if (c->eof) { // Remove PKCS7 padding at the end int padding = c->outbuffer[c->outdata - 1]; c->outdata -= padding; } if(len==0){ goto readagain; } }else{ len =0; } }else{ len=get_buffer(mgt->cur_uio,buf,size); } //av_log(NULL, AV_LOG_INFO, "list_read get_buffer=%d\n",len); } if(len==AVERROR(EAGAIN)) return AVERROR(EAGAIN);/*not end,bug need to*/ else if((len<=0)&& mgt->current_item!=NULL) {/*end of the file*/ av_log(NULL, AV_LOG_INFO, "try switchto_next_item buf=%x,size=%d,len=%d\n",buf,size,len); if(item && (item->flags & ENDLIST_FLAG)){ if(mgt->cur_uio) url_fclose(mgt->cur_uio); av_log(NULL, AV_LOG_INFO, "ENDLIST_FLAG, return 0\n"); return 0; } item=switchto_next_item(mgt); if(!item){ if(mgt->flags&REAL_STREAMING_FLAG){ fresh_item_list(mgt); if(retries>0){ retries --; goto retry; } } av_log(NULL, AV_LOG_INFO, "Need more retry by player logic\n"); return AVERROR(EAGAIN);/*not end,but need to refresh the list later*/ } if(mgt->cur_uio) url_fclose(mgt->cur_uio); mgt->cur_uio=NULL; mgt->current_item=item; if(item->flags & ENDLIST_FLAG){ av_log(NULL, AV_LOG_INFO, "reach list end now!,item=%x\n",item); return 0;/*endof file*/ } else if(item->flags & DISCONTINUE_FLAG){ av_log(NULL, AV_LOG_INFO, "Discontiue item \n"); //1 TODO:need to notify uper level stream is changed goto retry; } else{ av_log(NULL, AV_LOG_INFO, "[%s]item->flags=%x \n", __FUNCTION__, item->flags); goto retry; } } #if 0 if(mgt->flags&REAL_STREAMING_FLAG&&mgt->item_num<4){ //force refresh items fresh_item_list(mgt); } #endif //av_log(NULL, AV_LOG_INFO, "list_read end buf=%x,size=%d return len=%x\n",buf,size,len); return len; }
/* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { struct addrinfo hints, *ai, *cur_ai; int port, fd = -1; TCPContext *s = NULL; int listen_socket = 0; const char *p; char buf[256]; int ret; socklen_t optlen; char hostname[1024],proto[1024],path[1024]; char portstr[10]; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) return AVERROR(EINVAL); p = strchr(uri, '?'); if (p) { if (av_find_info_tag(buf, sizeof(buf), "listen", p)) listen_socket = 1; } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf(portstr, sizeof(portstr), "%d", port); ret = getaddrinfo(hostname, portstr, &hints, &ai); if (ret) { av_log(NULL, AV_LOG_ERROR, "Failed to resolve hostname %s: %s\n", hostname, gai_strerror(ret)); return AVERROR(EIO); } cur_ai = ai; restart: fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); if (fd < 0) goto fail; if (listen_socket) { int fd1; ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); listen(fd, 1); fd1 = accept(fd, NULL, NULL); closesocket(fd); fd = fd1; } else { redo: ff_socket_nonblock(fd, 1); //av_log (NULL, AV_LOG_INFO, "connecting....\n"); ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); //av_log (NULL, AV_LOG_INFO, "connecting done.\n"); } if (ret < 0) { int timeout = 10; //1s timeout struct pollfd p = {fd, POLLOUT, 0}; if (ff_neterrno() == AVERROR(EINTR)) { if (url_interrupt_cb()) { ret = AVERROR_EXIT; goto fail1; } goto redo; } if (ff_neterrno() != AVERROR(EINPROGRESS) && ff_neterrno() != AVERROR(EAGAIN)) goto fail; /* wait until we are connected or until abort */ for(;;) { if (url_interrupt_cb()) { ret = AVERROR_EXIT; goto fail1; } ret = poll(&p, 1, 100); if (ret > 0) break; if(!--timeout){ av_log(NULL, AV_LOG_ERROR, "TCP open %s:%d timeout\n", hostname, port); goto fail; } } /* test error */ optlen = sizeof(ret); getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); if (ret != 0) { av_log(NULL, AV_LOG_ERROR, "TCP connection to %s:%d failed: %s\n", hostname, port, strerror(ret)); goto fail; } } s = av_malloc(sizeof(TCPContext)); if (!s) { ret = AVERROR(ENOMEM); goto fail1; } h->priv_data = s; h->is_streamed = 1; s->fd = fd; freeaddrinfo(ai); return 0; fail: if (cur_ai->ai_next) { /* Retry with the next sockaddr */ cur_ai = cur_ai->ai_next; if (fd >= 0) closesocket(fd); goto restart; } ret = AVERROR(EIO); fail1: if (fd >= 0) closesocket(fd); freeaddrinfo(ai); return ret; }
/* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { struct sockaddr_in dest_addr; int port, fd = -1; TCPContext *s = NULL; fd_set wfds; int fd_max, ret; struct timeval tv; socklen_t optlen; char hostname[1024],proto[1024],path[1024]; if(!ff_network_init()) return AVERROR(EIO); url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) return AVERROR(EINVAL); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(port); if (resolve_host(&dest_addr.sin_addr, hostname) < 0) return AVERROR(EIO); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) return AVERROR(EIO); ff_socket_nonblock(fd, 1); redo: ret = connect(fd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (ret < 0) { if (ff_neterrno() == FF_NETERROR(EINTR)) goto redo; if (ff_neterrno() != FF_NETERROR(EINPROGRESS) && ff_neterrno() != FF_NETERROR(EAGAIN)) goto fail; /* wait until we are connected or until abort */ for(;;) { if (url_interrupt_cb()) { ret = AVERROR(EINTR); goto fail1; } fd_max = fd; FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); if (ret > 0 && FD_ISSET(fd, &wfds)) break; } /* test error */ optlen = sizeof(ret); getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); if (ret != 0) goto fail; } s = av_malloc(sizeof(TCPContext)); if (!s) return AVERROR(ENOMEM); h->priv_data = s; h->is_streamed = 1; s->fd = fd; return 0; fail: ret = AVERROR(EIO); fail1: if (fd >= 0) closesocket(fd); return ret; }
static int _ffmpeg_cmf_getopt(URLContext *h, uint32_t cmd, uint32_t flag, int64_t* info){ FFMPEG_HLS_CONTEXT* ctx = (FFMPEG_HLS_CONTEXT*)h->priv_data; int ret=-1; CmfPrivContext_t* cmf_ctx = ctx->cmf_ctx; RLOG("[%s]:cmd:%d,flag:%d\n",__FUNCTION__,cmd,flag); if(ctx == NULL||cmf_ctx == NULL){ RLOG("Failed call :%s\n",__FUNCTION__); return -1; } if(cmd == AVCMD_TOTAL_NUM){ *info = cmf_ctx->totol_clip_num; RLOG("Get total clip num:%d\n",cmf_ctx->totol_clip_num); }else if(cmd == AVCMD_TOTAL_DURATION){ *info = ctx->durationUs/1000; RLOG("Get duration:%lld\n",*info); }else if(cmd == AVCMD_SLICE_START_OFFSET){ *info = -1; RLOG("Get clip start offset\n"); }else if(cmd == AVCMD_SLICE_INDEX){ *info = cmf_ctx->cur_clip_index; RLOG("Get clip index:%d\n",cmf_ctx->cur_clip_index); }else if(cmd == AVCMD_SLICE_SIZE){ if(cmf_ctx->cur_clip_len>0){ *info = cmf_ctx->cur_clip_len; }else{//ugly codes int64_t ret = hls_cmf_get_fsize(ctx->hls_ctx,cmf_ctx,1); if(ret<=0){//10s,maybe chunk stream,can't get size from server int counts=100; while(counts-->0&&ret<=0){ if (url_interrupt_cb()) { RLOG("url_interrupt_cb,%d\n",__LINE__); ret = 0; break; } ret = hls_cmf_get_fsize(ctx->hls_ctx,cmf_ctx,2); usleep(100*1000);//100ms } if(ret<=0&&url_interrupt_cb()==0){ ret = hls_cmf_get_fsize(ctx->hls_ctx,cmf_ctx,3); } } *info = ret; } RLOG("Get clip length:%lld\n",*info); }else if(cmd == AVCMD_SLICE_STARTTIME){ if(ctx->durationUs<=0){ *info = -1; }else{ *info = cmf_ctx->cur_clip_st/1000; } RLOG("Get clip start time:%lld\n",*info); }else if(cmd == AVCMD_SLICE_ENDTIME){ if(ctx->durationUs<=0){ *info = -1; }else{ *info = cmf_ctx->cur_clip_end/1000; } RLOG("Get clip end time:%lld\n",*info); }else{ RLOG("Can't excute this case\n"); } return 0; }