int stream_read_internal(stream_t *s, void *buf, int len) { int orig_len = len; // we will retry even if we already reached EOF previously. switch(s->type){ case STREAMTYPE_STREAM: #ifdef CONFIG_NETWORKING if( s->streaming_ctrl!=NULL && s->streaming_ctrl->streaming_read ) { len=s->streaming_ctrl->streaming_read(s->fd, buf, len, s->streaming_ctrl); if (s->streaming_ctrl->status == streaming_stopped_e) s->eof = 1; } else #endif if (s->fill_buffer) len = s->fill_buffer(s, buf, len); else len = read(s->fd, buf, len); break; case STREAMTYPE_DS: len = demux_read_data((demux_stream_t*)s->priv, buf, len); break; default: len= s->fill_buffer ? s->fill_buffer(s, buf, len) : 0; } if(len<=0){ off_t pos = s->pos; // do not retry if this looks like proper eof if (s->eof || (s->end_pos && pos == s->end_pos)) goto eof_out; // dvdnav has some horrible hacks to "suspend" reads, // we need to skip this code or seeks will hang. if (s->type == STREAMTYPE_DVDNAV) goto eof_out; // just in case this is an error e.g. due to network // timeout reset and retry // Seeking is used as a hack to make network streams // reopen the connection, ideally they would implement // e.g. a STREAM_CTRL_RECONNECT to do this s->eof=1; stream_reset(s); if (stream_seek_internal(s, pos) >= 0 || s->pos != pos) // seek failed goto eof_out; // make sure EOF is set to ensure no endless loops s->eof=1; return stream_read_internal(s, buf, orig_len); eof_out: s->eof=1; return 0; } // When reading succeeded we are obviously not at eof. // This e.g. avoids issues with eof getting stuck when lavf seeks in MPEG-TS s->eof=0; s->pos+=len; return len; }
int stream_seek_long(stream_t *s,off_t pos){ int res; off_t newpos=0; // if( mp_msg_test(MSGT_STREAM,MSGL_DBG3) ) printf("seek_long to 0x%X\n",(unsigned int)pos); s->buf_pos=s->buf_len=0; if(s->mode == STREAM_WRITE) { if(!s->seek || !s->seek(s,pos)) return 0; return 1; } if(s->sector_size) newpos = (pos/s->sector_size)*s->sector_size; else newpos = pos&(~((off_t)STREAM_BUFFER_SIZE-1)); if( mp_msg_test(MSGT_STREAM,MSGL_DBG3) ){ mp_msg(MSGT_STREAM,MSGL_DBG3, "s->pos=%"PRIX64" newpos=%"PRIX64" new_bufpos=%"PRIX64" buflen=%X \n", (int64_t)s->pos,(int64_t)newpos,(int64_t)pos,s->buf_len); } pos-=newpos; res = stream_seek_internal(s, newpos); if (res >= 0) return res; while(s->pos<newpos){ if(stream_fill_buffer(s)<=0) break; // EOF } s->eof = 0; // EOF reset when seek succeeds. while (stream_fill_buffer(s) > 0) { if(pos<=s->buf_len){ s->buf_pos=pos; // byte position in sector return 1; } pos -= s->buf_len; } // Fill failed, but seek still is a success. s->pos += pos; s->buf_pos = 0; s->buf_len = 0; mp_msg(MSGT_STREAM,MSGL_V, "stream_seek: Seek to/past EOF: no buffer preloaded.\n"); return 1; }
int stream_seek_long(stream_t *s, int64_t pos){ int res; int64_t newpos=0; // if( mp_msg_test(MSGT_STREAM,MSGL_DBG3) ) printf("seek_long to 0x%X\n",(unsigned int)pos); s->buf_pos=s->buf_len=0; if(s->mode == STREAM_WRITE) { if(!s->seek || !s->seek(s,pos)) return 0; return 1; } if(s->sector_size) newpos = (pos/s->sector_size)*s->sector_size; else newpos = pos&(~((int64_t)STREAM_BUFFER_SIZE-1)); if( mp_msg_test(MSGT_STREAM,MSGL_DBG3) ){ mp_msg(MSGT_STREAM,MSGL_DBG3, "s->pos=%"PRIX64" newpos=%"PRIX64" new_bufpos=%"PRIX64" buflen=%X \n", (int64_t)s->pos,(int64_t)newpos,(int64_t)pos,s->buf_len); } pos-=newpos; res = stream_seek_internal(s, newpos); if (res >= 0) return res; while(s->pos<newpos){ if(stream_fill_buffer(s)<=0) break; // EOF } while(stream_fill_buffer(s) > 0 && pos >= 0) { if(pos<=s->buf_len){ s->buf_pos=pos; // byte position in sector return 1; } pos -= s->buf_len; } // if(pos==s->buf_len) printf("XXX Seek to last byte of file -> EOF\n"); mp_msg(MSGT_STREAM,MSGL_V,"stream_seek: WARNING! Can't seek to 0x%"PRIX64" !\n",(int64_t)(pos+newpos)); return 0; }
static int stream_reconnect(stream_t *s) { #define MAX_RECONNECT_RETRIES 5 #define RECONNECT_SLEEP_MS 1000 int retry = 0; int64_t pos = s->pos; // Seeking is used as a hack to make network streams // reopen the connection, ideally they would implement // e.g. a STREAM_CTRL_RECONNECT to do this do { if (retry >= MAX_RECONNECT_RETRIES) return 0; if (retry) usec_sleep(RECONNECT_SLEEP_MS * 1000); retry++; s->eof=1; stream_reset(s); } while (stream_seek_internal(s, pos) >= 0 || s->pos != pos); // seek failed return 1; }
static int cache_fill(cache_vars_t *s) { int back,back2,newb,space,len,pos; off_t read=s->read_filepos; int read_chunk; int wraparound_copy = 0; if(read<s->min_filepos || read>s->max_filepos){ // seek... mp_msg(MSGT_CACHE,MSGL_DBG2,"Out of boundaries... seeking to 0x%"PRIX64" \n",(int64_t)read); // drop cache contents only if seeking backward or too much fwd. // This is also done for on-disk files, since it loses the backseek cache. // That in turn can cause major bandwidth increase and performance // issues with e.g. mov or badly interleaved files if(read<s->min_filepos || read>=s->max_filepos+s->seek_limit) { s->offset= // FIXME!? s->min_filepos=s->max_filepos=read; // drop cache content :( if(s->stream->eof) stream_reset(s->stream); stream_seek_internal(s->stream,read); mp_msg(MSGT_CACHE,MSGL_DBG2,"Seek done. new pos: 0x%"PRIX64" \n",(int64_t)stream_tell(s->stream)); } } // calc number of back-bytes: back=read - s->min_filepos; if(back<0) back=0; // strange... if(back>s->back_size) back=s->back_size; // calc number of new bytes: newb=s->max_filepos - read; if(newb<0) newb=0; // strange... // calc free buffer space: space=s->buffer_size - (newb+back); // calc bufferpos: pos=s->max_filepos - s->offset; if(pos>=s->buffer_size) pos-=s->buffer_size; // wrap-around if(space<s->fill_limit){ // printf("Buffer is full (%d bytes free, limit: %d)\n",space,s->fill_limit); return 0; // no fill... } // printf("### read=0x%X back=%d newb=%d space=%d pos=%d\n",read,back,newb,space,pos); // try to avoid wrap-around. If not possible due to sector size // do an extra copy. if(space>s->buffer_size-pos) { if (s->buffer_size-pos >= s->sector_size) { space=s->buffer_size-pos; } else { space = s->sector_size; wraparound_copy = 1; } } // limit one-time block size read_chunk = s->stream->read_chunk; if (!read_chunk) read_chunk = 4*s->sector_size; space = FFMIN(space, read_chunk); #if 1 // back+newb+space <= buffer_size back2=s->buffer_size-(space+newb); // max back size if(s->min_filepos<(read-back2)) s->min_filepos=read-back2; #else s->min_filepos=read-back; // avoid seeking-back to temp area... #endif if (wraparound_copy) { int to_copy; len = stream_read_internal(s->stream, s->stream->buffer, space); to_copy = FFMIN(len, s->buffer_size-pos); memcpy(s->buffer + pos, s->stream->buffer, to_copy); memcpy(s->buffer, s->stream->buffer + to_copy, len - to_copy); } else len = stream_read_internal(s->stream, &s->buffer[pos], space); s->eof= !len; s->max_filepos+=len; if(pos+len>=s->buffer_size){ // wrap... s->offset+=s->buffer_size; } return len; }