static int Control(access_t *access, int query, va_list args) { stream_t *s = access->p_sys->s; if (!s) return VLC_EGENERIC; switch (query) { case ACCESS_CAN_SEEK: { bool *b = va_arg(args, bool *); return stream_Control(s, STREAM_CAN_SEEK, b); } case ACCESS_CAN_FASTSEEK: { bool *b = va_arg(args, bool *); return stream_Control(s, STREAM_CAN_FASTSEEK, b); } /* FIXME the following request should ask the underlying access object */ case ACCESS_CAN_PAUSE: case ACCESS_CAN_CONTROL_PACE: { bool *b = va_arg(args, bool *); *b = true; return VLC_SUCCESS; } case ACCESS_GET_PTS_DELAY: { int64_t *delay = va_arg(args, int64_t *); *delay = DEFAULT_PTS_DELAY; return VLC_SUCCESS; } case ACCESS_SET_PAUSE_STATE: return VLC_SUCCESS; default: return VLC_EGENERIC; } }
int main(void) { ssize_t val; char buf[16]; bool b; test_init(); vlc = libvlc_new(0, NULL); assert(vlc != NULL); parent = VLC_OBJECT(vlc->p_libvlc_int); s = vlc_stream_fifo_New(parent); assert(s != NULL); val = stream_Control(s, STREAM_CAN_SEEK, &b); assert(val == VLC_SUCCESS && !b); val = stream_GetSize(s, &(uint64_t){ 0 }); assert(val < 0); val = stream_Control(s, STREAM_GET_PTS_DELAY, &(int64_t){ 0 }); assert(val == VLC_SUCCESS); stream_Delete(s); vlc_stream_fifo_Close(s); s = vlc_stream_fifo_New(parent); assert(s != NULL); val = vlc_stream_fifo_Write(s, "123", 3); vlc_stream_fifo_Close(s); val = stream_Read(s, buf, sizeof (buf)); assert(val == 3); assert(memcmp(buf, "123", 3) == 0); val = stream_Read(s, buf, sizeof (buf)); assert(val == 0); stream_Delete(s); s = vlc_stream_fifo_New(parent); assert(s != NULL); val = vlc_stream_fifo_Write(s, "Hello ", 6); assert(val == 6); val = vlc_stream_fifo_Write(s, "world!\n", 7); assert(val == 7); val = vlc_stream_fifo_Write(s, "blahblah", 8); assert(val == 8); val = stream_Read(s, buf, 13); assert(val == 13); assert(memcmp(buf, "Hello world!\n", 13) == 0); stream_Delete(s); val = vlc_stream_fifo_Write(s, "cough cough", 11); assert(val == -1 && errno == EPIPE); vlc_stream_fifo_Close(s); libvlc_release(vlc); return 0; }
/***************************************************************************** * ParseTags: check if ID3/APE tags at common locations. ****************************************************************************/ static int ParseTags( vlc_object_t *p_this ) { demux_meta_t *p_demux_meta = (demux_meta_t *)p_this; demux_t *p_demux = (demux_t *)p_demux_meta->p_demux; bool b_seekable; int64_t i_init; msg_Dbg( p_demux_meta, "checking for ID3v1/2 and APEv1/2 tags" ); stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable ); if( !b_seekable ) return VLC_EGENERIC; i_init = stream_Tell( p_demux->s ); TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments ); p_demux_meta->p_meta = NULL; /* */ CheckFooter( p_demux_meta ); /* */ CheckHeader( p_demux_meta ); /* Restore position * Demuxer will not see tags at the start as src/input/demux.c skips it * for them */ stream_Seek( p_demux->s, i_init ); if( !p_demux_meta->p_meta && p_demux_meta->i_attachments <= 0 ) return VLC_EGENERIC; return VLC_SUCCESS; }
uint64 vlc_stream_io_callback::toRead( void ) { uint64_t i_size; if( s == NULL) return 0; stream_Control( s, STREAM_GET_SIZE, &i_size ); if( i_size == 0 ) return UINT64_MAX; return (uint64) i_size - stream_Tell( s ); }
int AVI_ChunkReadRoot( stream_t *s, avi_chunk_t *p_root ) { avi_chunk_list_t *p_list = (avi_chunk_list_t*)p_root; avi_chunk_t *p_chk; bool b_seekable; stream_Control( s, STREAM_CAN_FASTSEEK, &b_seekable ); p_list->i_chunk_pos = 0; p_list->i_chunk_size = stream_Size( s ); p_list->i_chunk_fourcc = AVIFOURCC_LIST; p_list->p_father = NULL; p_list->p_next = NULL; p_list->p_first = NULL; p_list->p_last = NULL; p_list->i_type = VLC_FOURCC( 'r', 'o', 'o', 't' ); for( ; ; ) { p_chk = malloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( !p_root->common.p_first ) { p_root->common.p_first = p_chk; } else { p_root->common.p_last->common.p_next = p_chk; } p_root->common.p_last = p_chk; if( AVI_ChunkRead( s, p_chk, p_root ) || ( stream_Tell( s ) >= (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found first RIFF-AVI */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF && p_chk->list.i_type == AVIFOURCC_AVI && !b_seekable ) { break; } } AVI_ChunkDumpDebug_level( (vlc_object_t*)s, p_root, 0 ); return VLC_SUCCESS; }
/* Allow to append indexes after starting playback */ int AVI_ChunkFetchIndexes( stream_t *s, avi_chunk_t *p_riff ) { avi_chunk_t *p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0 ); if ( !p_movi ) return VLC_EGENERIC; avi_chunk_t *p_chk; uint64_t i_indexpos = 8 + p_movi->common.i_chunk_pos + p_movi->common.i_chunk_size; bool b_seekable = false; int i_ret = VLC_SUCCESS; stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); if ( !b_seekable || stream_Seek( s, i_indexpos ) ) return VLC_EGENERIC; for( ; ; ) { p_chk = xmalloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if (unlikely( !p_riff->common.p_first )) p_riff->common.p_first = p_chk; else p_riff->common.p_last->common.p_next = p_chk; p_riff->common.p_last = p_chk; i_ret = AVI_ChunkRead( s, p_chk, p_riff ); if( i_ret ) break; if( p_chk->common.p_father->common.i_chunk_size > 0 && ( stream_Tell( s ) > (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found any index */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_indx || p_chk->common.i_chunk_fourcc == AVIFOURCC_idx1 ) { break; } } return i_ret; }
static int OpenCallback(struct archive *p_archive, void *p_object) { VLC_UNUSED(p_archive); callback_data_t *p_data = (callback_data_t *) p_object; access_sys_t *p_sys = p_data->p_access->p_sys; p_sys->p_stream = stream_UrlNew( p_data->p_access, p_data->psz_uri ); if(!p_sys->p_stream) return ARCHIVE_FATAL; /* Seek callback must only be set if calls are guaranteed to succeed */ stream_Control(p_sys->p_stream, STREAM_CAN_SEEK, &p_sys->b_source_canseek); if(p_sys->b_source_canseek) archive_read_set_seek_callback(p_sys->p_archive, SeekCallback); return ARCHIVE_OK; }
static bool IsTarga(stream_t *s) { /* The header is not enough to ensure proper detection, we need * to have a look at the footer. But doing so can be slow. So * try to avoid it when possible */ const uint8_t *header; if (stream_Peek(s, &header, 18) < 18) /* Targa fixed header */ return false; if (header[1] > 1) /* Color Map Type */ return false; if ((header[1] != 0 || header[3 + 4] != 0) && header[3 + 4] != 8 && header[3 + 4] != 15 && header[3 + 4] != 16 && header[3 + 4] != 24 && header[3 + 4] != 32) return false; if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */ return false; if (GetWLE(&header[8 + 4]) <= 0 || /* Width */ GetWLE(&header[8 + 6]) <= 0) /* Height */ return false; if (header[8 + 8] != 8 && header[8 + 8] != 15 && header[8 + 8] != 16 && header[8 + 8] != 24 && header[8 + 8] != 32) return false; if (header[8 + 9] & 0xc0) /* Reserved bits */ return false; const int64_t size = stream_Size(s); if (size <= 18 + 26) return false; bool can_seek; if (stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek) return false; const int64_t position = stream_Tell(s); if (stream_Seek(s, size - 26)) return false; const uint8_t *footer; bool is_targa = stream_Peek(s, &footer, 26) >= 26 && !memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18); stream_Seek(s, position); return is_targa; }
static picture_t *ImageReadUrl( image_handler_t *p_image, const char *psz_url, video_format_t *p_fmt_in, video_format_t *p_fmt_out ) { block_t *p_block; picture_t *p_pic; stream_t *p_stream = NULL; int i_size; p_stream = stream_UrlNew( p_image->p_parent, psz_url ); if( !p_stream ) { msg_Dbg( p_image->p_parent, "could not open %s for reading", psz_url ); return NULL; } i_size = stream_Size( p_stream ); p_block = block_New( p_image->p_parent, i_size ); stream_Read( p_stream, p_block->p_buffer, i_size ); if( !p_fmt_in->i_chroma ) { char *psz_mime = NULL; stream_Control( p_stream, STREAM_GET_CONTENT_TYPE, &psz_mime ); if( psz_mime ) p_fmt_in->i_chroma = image_Mime2Fourcc( psz_mime ); free( psz_mime ); } stream_Delete( p_stream ); if( !p_fmt_in->i_chroma ) { /* Try to guess format from file name */ p_fmt_in->i_chroma = image_Ext2Fourcc( psz_url ); } p_pic = ImageRead( p_image, p_block, p_fmt_in, p_fmt_out ); return p_pic; }
static int Control (stream_t *stream, int query, va_list args) { stream_sys_t *p_sys = stream->p_sys; switch (query) { case STREAM_CAN_SEEK: case STREAM_CAN_FASTSEEK: *(va_arg (args, bool *)) = false; break; case STREAM_CAN_PAUSE: *(va_arg (args, bool *)) = p_sys->can_pause; break; case STREAM_CAN_CONTROL_PACE: *(va_arg (args, bool *)) = p_sys->can_pace; break; case STREAM_GET_POSITION: *(va_arg (args, uint64_t *)) = p_sys->offset; break; case STREAM_GET_SIZE: *(va_arg (args, uint64_t *)) = 0; break; case STREAM_GET_PTS_DELAY: *va_arg (args, int64_t *) = p_sys->pts_delay; break; case STREAM_SET_PAUSE_STATE: { bool paused = va_arg (args, unsigned); vlc_mutex_lock (&p_sys->lock); stream_Control (stream->p_source, STREAM_SET_PAUSE_STATE, paused); p_sys->paused = paused; vlc_cond_signal (&p_sys->wait); vlc_mutex_unlock (&p_sys->lock); break; } default: return VLC_EGENERIC; } return VLC_SUCCESS; }
static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr, const char *volume_mrl) { const uint8_t *peek; int min_size = 7+21; if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) min_size += 8; if (hdr->size < (unsigned)min_size) return VLC_EGENERIC; if (stream_Peek(s, &peek, min_size) < min_size) return VLC_EGENERIC; /* */ uint32_t file_size_low = GetDWLE(&peek[7+4]); uint8_t method = peek[7+18]; uint16_t name_size = GetWLE(&peek[7+19]); uint32_t file_size_high = 0; if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) file_size_high = GetDWLE(&peek[7+25]); const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low; char *name = calloc(1, name_size + 1); if (!name) return VLC_EGENERIC; const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25); if (name_offset + name_size <= hdr->size) { const int max_size = name_offset + name_size; if (stream_Peek(s, &peek, max_size) < max_size) { free(name); return VLC_EGENERIC; } memcpy(name, &peek[name_offset], name_size); } rar_file_t *current = NULL; if (method != 0x30) { msg_Warn(s, "Ignoring compressed file %s (method=0x%2.2x)", name, method); goto exit; } /* */ if( *count > 0 ) current = (*file)[*count - 1]; if (current && (current->is_complete || strcmp(current->name, name) || (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0)) current = NULL; if (!current) { if (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) goto exit; current = malloc(sizeof(*current)); if (!current) goto exit; TAB_APPEND(*count, *file, current); current->name = name; current->size = file_size; current->is_complete = false; current->real_size = 0; TAB_INIT(current->chunk_count, current->chunk); name = NULL; } /* Append chunks */ rar_file_chunk_t *chunk = malloc(sizeof(*chunk)); if (chunk) { chunk->mrl = strdup(volume_mrl); chunk->offset = stream_Tell(s) + hdr->size; chunk->size = hdr->add_size; chunk->cummulated_size = 0; if (current->chunk_count > 0) { rar_file_chunk_t *previous = current->chunk[current->chunk_count-1]; chunk->cummulated_size += previous->cummulated_size + previous->size; } TAB_APPEND(current->chunk_count, current->chunk, chunk); current->real_size += hdr->add_size; } if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0) current->is_complete = true; exit: /* */ free(name); /* We stop on the first non empty file if we cannot seek */ if (current) { bool can_seek = false; stream_Control(s, STREAM_CAN_SEEK, &can_seek); if (!can_seek && current->size > 0) return VLC_EGENERIC; } if (SkipBlock(s, hdr)) return VLC_EGENERIC; return VLC_SUCCESS; }
/** * Pipe data through an external executable. * @param stream the stream filter object. * @param path path to the executable. */ static int Open (stream_t *stream, const char *path) { stream_sys_t *p_sys = stream->p_sys = malloc (sizeof (*p_sys)); if (p_sys == NULL) return VLC_ENOMEM; stream->pf_read = Read; stream->pf_peek = Peek; stream->pf_control = Control; vlc_cond_init (&p_sys->wait); vlc_mutex_init (&p_sys->lock); p_sys->paused = false; p_sys->pid = -1; p_sys->offset = 0; p_sys->peeked = NULL; stream_Control (stream->p_source, STREAM_CAN_PAUSE, &p_sys->can_pause); stream_Control (stream->p_source, STREAM_CAN_CONTROL_PACE, &p_sys->can_pace); stream_Control (stream->p_source, STREAM_GET_PTS_DELAY, &p_sys->pts_delay); /* I am not a big fan of the pyramid style, but I cannot think of anything * better here. There are too many failure cases. */ int ret = VLC_EGENERIC; int comp[2]; /* We use two pipes rather than one stream socket pair, so that we can * use vmsplice() on Linux. */ if (vlc_pipe (comp) == 0) { p_sys->write_fd = comp[1]; int uncomp[2]; if (vlc_pipe (uncomp) == 0) { p_sys->read_fd = uncomp[0]; #if (_POSIX_SPAWN >= 0) posix_spawn_file_actions_t actions; if (posix_spawn_file_actions_init (&actions) == 0) { char *const argv[] = { (char *)path, NULL }; if (!posix_spawn_file_actions_adddup2 (&actions, comp[0], 0) && !posix_spawn_file_actions_adddup2 (&actions, uncomp[1], 1) && !posix_spawnp (&p_sys->pid, path, &actions, NULL, argv, environ)) { if (vlc_clone (&p_sys->thread, Thread, stream, VLC_THREAD_PRIORITY_INPUT) == 0) ret = VLC_SUCCESS; } else { msg_Err (stream, "cannot execute %s", path); p_sys->pid = -1; } posix_spawn_file_actions_destroy (&actions); } #else /* _POSIX_SPAWN */ switch (p_sys->pid = fork ()) { case -1: msg_Err (stream, "cannot fork: %s", vlc_strerror_c(errno)); break; case 0: dup2 (comp[0], 0); dup2 (uncomp[1], 1); execlp (path, path, (char *)NULL); exit (1); /* if we get, execlp() failed! */ default: if (vlc_clone (&p_sys->thread, Thread, stream, VLC_THREAD_PRIORITY_INPUT) == 0) ret = VLC_SUCCESS; } #endif /* _POSIX_SPAWN < 0 */ close (uncomp[1]); if (ret != VLC_SUCCESS) close (uncomp[0]); } close (comp[0]); if (ret != VLC_SUCCESS) close (comp[1]); } if (ret == VLC_SUCCESS) return VLC_SUCCESS; if (p_sys->pid != -1) while (waitpid (p_sys->pid, &(int){ 0 }, 0) == -1);
/**************************************************************************** * * Functions to read chunks * ****************************************************************************/ static int AVI_ChunkRead_list( stream_t *s, avi_chunk_t *p_container ) { avi_chunk_t *p_chk; const uint8_t *p_peek; bool b_seekable; if( p_container->common.i_chunk_size > 0 && p_container->common.i_chunk_size < 8 ) { /* empty box */ msg_Warn( (vlc_object_t*)s, "empty list chunk" ); return VLC_EGENERIC; } if( stream_Peek( s, &p_peek, 12 ) < 12 ) { msg_Warn( (vlc_object_t*)s, "cannot peek while reading list chunk" ); return VLC_EGENERIC; } stream_Control( s, STREAM_CAN_FASTSEEK, &b_seekable ); p_container->list.i_type = GetFOURCC( p_peek + 8 ); /* XXX fixed for on2 hack */ if( p_container->common.i_chunk_fourcc == AVIFOURCC_ON2 && p_container->list.i_type == AVIFOURCC_ON2f ) { p_container->common.i_chunk_fourcc = AVIFOURCC_RIFF; p_container->list.i_type = AVIFOURCC_AVI; } if( p_container->common.i_chunk_fourcc == AVIFOURCC_LIST && p_container->list.i_type == AVIFOURCC_movi ) { msg_Dbg( (vlc_object_t*)s, "skipping movi chunk" ); if( b_seekable ) { return AVI_NextChunk( s, p_container ); } return VLC_SUCCESS; /* point at begining of LIST-movi */ } if( stream_Read( s, NULL, 12 ) != 12 ) { msg_Warn( (vlc_object_t*)s, "cannot enter chunk" ); return VLC_EGENERIC; } #ifdef AVI_DEBUG msg_Dbg( (vlc_object_t*)s, "found LIST chunk: \'%4.4s\'", (char*)&p_container->list.i_type ); #endif msg_Dbg( (vlc_object_t*)s, "<list \'%4.4s\'>", (char*)&p_container->list.i_type ); for( ; ; ) { p_chk = malloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( !p_container->common.p_first ) { p_container->common.p_first = p_chk; } else { p_container->common.p_last->common.p_next = p_chk; } p_container->common.p_last = p_chk; if( AVI_ChunkRead( s, p_chk, p_container ) ) { break; } if( p_chk->common.p_father->common.i_chunk_size > 0 && ( stream_Tell( s ) > (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found LIST-movi */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST && p_chk->list.i_type == AVIFOURCC_movi && ( !b_seekable || p_chk->common.i_chunk_size == 0 ) ) { break; } } msg_Dbg( (vlc_object_t*)s, "</list \'%4.4s\'>", (char*)&p_container->list.i_type ); return VLC_SUCCESS; }
static int ControlSetTime( demux_t *p_demux, int64_t i_time ) { demux_sys_t *p_sys = p_demux->p_sys; int64_t i_delta_time; bool b_seekable; int i; /* */ stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable ); if( !b_seekable ) return VLC_EGENERIC; /* */ assert( p_sys->i_seekpoint > 0 ); /* ReadMeta ensure at least (0,0) */ for( i = p_sys->i_seekpoint-1; i >= 0; i-- ) { if( p_sys->seekpoint[i]->i_time_offset <= i_time ) break; } i_delta_time = i_time - p_sys->seekpoint[i]->i_time_offset; /* XXX We do exact seek if it's not too far away(45s) */ if( i_delta_time < 45*INT64_C(1000000) ) { if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos ) ) return VLC_EGENERIC; p_sys->i_time_offset = p_sys->seekpoint[i]->i_time_offset - p_sys->i_pts; p_sys->i_pts_start = p_sys->i_pts+i_delta_time; es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, p_sys->i_pts_start + p_sys->i_time_offset ); } else { int64_t i_delta_offset; int64_t i_next_time; int64_t i_next_offset; if( i+1 < p_sys->i_seekpoint ) { i_next_time = p_sys->seekpoint[i+1]->i_time_offset; i_next_offset = p_sys->seekpoint[i+1]->i_byte_offset; } else { i_next_time = p_sys->i_length; i_next_offset = stream_Size(p_demux->s)-p_sys->i_data_pos; } i_delta_offset = 0; if( i_next_time-p_sys->seekpoint[i]->i_time_offset > 0 ) i_delta_offset = (i_next_offset - p_sys->seekpoint[i]->i_byte_offset) * i_delta_time / (i_next_time-p_sys->seekpoint[i]->i_time_offset); if( stream_Seek( p_demux->s, p_sys->seekpoint[i]->i_byte_offset+p_sys->i_data_pos + i_delta_offset ) ) return VLC_EGENERIC; p_sys->i_pts_start = p_sys->i_pts; p_sys->i_time_offset = (p_sys->seekpoint[i]->i_time_offset+i_delta_time) - p_sys->i_pts; } return VLC_SUCCESS; }
static int AStreamSeekStream(stream_t *s, uint64_t i_pos) { stream_sys_t *sys = s->p_sys; stream_track_t *p_current = &sys->tk[sys->i_tk]; if (p_current->i_start >= p_current->i_end && i_pos >= p_current->i_end) return 0; /* EOF */ #ifdef STREAM_DEBUG msg_Dbg(s, "AStreamSeekStream: to %"PRId64" pos=%"PRId64 " tk=%d start=%"PRId64" offset=%d end=%"PRId64, i_pos, sys->i_pos, sys->i_tk, p_current->i_start, sys->i_offset, p_current->i_end); #endif bool b_aseek; stream_Control(s->p_source, STREAM_CAN_SEEK, &b_aseek); if (!b_aseek && i_pos < p_current->i_start) { msg_Warn(s, "AStreamSeekStream: can't seek"); return VLC_EGENERIC; } bool b_afastseek; stream_Control(s->p_source, STREAM_CAN_FASTSEEK, &b_afastseek); /* FIXME compute seek cost (instead of static 'stupid' value) */ uint64_t i_skip_threshold; if (b_aseek) i_skip_threshold = b_afastseek ? 128 : 3 * sys->i_read_size; else i_skip_threshold = INT64_MAX; /* Date the current track */ p_current->date = mdate(); /* Search a new track slot */ stream_track_t *tk = NULL; int i_tk_idx = -1; /* Prefer the current track */ if (p_current->i_start <= i_pos && i_pos <= p_current->i_end + i_skip_threshold) { tk = p_current; i_tk_idx = sys->i_tk; } if (!tk) { /* Try to maximize already read data */ for (int i = 0; i < STREAM_CACHE_TRACK; i++) { stream_track_t *t = &sys->tk[i]; if (t->i_start > i_pos || i_pos > t->i_end) continue; if (!tk || tk->i_end < t->i_end) { tk = t; i_tk_idx = i; } } } if (!tk) { /* Use the oldest unused */ for (int i = 0; i < STREAM_CACHE_TRACK; i++) { stream_track_t *t = &sys->tk[i]; if (!tk || tk->date > t->date) { tk = t; i_tk_idx = i; } } } assert(i_tk_idx >= 0 && i_tk_idx < STREAM_CACHE_TRACK); if (tk != p_current) i_skip_threshold = 0; if (tk->i_start <= i_pos && i_pos <= tk->i_end + i_skip_threshold) { #ifdef STREAM_DEBUG msg_Err(s, "AStreamSeekStream: reusing %d start=%"PRId64 " end=%"PRId64"(%s)", i_tk_idx, tk->i_start, tk->i_end, tk != p_current ? "seek" : i_pos > tk->i_end ? "skip" : "noseek"); #endif if (tk != p_current) { assert(b_aseek); /* Seek at the end of the buffer * TODO it is stupid to seek now, it would be better to delay it */ if (stream_Seek(s->p_source, tk->i_end)) { msg_Err(s, "AStreamSeekStream: hard seek failed"); return VLC_EGENERIC; } } else if (i_pos > tk->i_end) { uint64_t i_skip = i_pos - tk->i_end; while (i_skip > 0) { const int i_read_max = __MIN(10 * STREAM_READ_ATONCE, i_skip); int i_read = 0; if ((i_read = AStreamReadNoSeekStream(s, NULL, i_read_max)) < 0) { msg_Err(s, "AStreamSeekStream: skip failed"); return VLC_EGENERIC; } else if (i_read == 0) return VLC_SUCCESS; /* EOF */ i_skip -= i_read_max; } } } else { #ifdef STREAM_DEBUG msg_Err(s, "AStreamSeekStream: hard seek"); #endif /* Nothing good, seek and choose oldest segment */ if (stream_Seek(s->p_source, i_pos)) { msg_Err(s, "AStreamSeekStream: hard seek failed"); return VLC_EGENERIC; } tk->i_start = i_pos; tk->i_end = i_pos; } sys->i_offset = i_pos - tk->i_start; sys->i_tk = i_tk_idx; sys->i_pos = i_pos; /* If there is not enough data left in the track, refill */ /* TODO How to get a correct value for * - refilling threshold * - how much to refill */ if (tk->i_end < tk->i_start + sys->i_offset + sys->i_read_size) { if (sys->i_used < STREAM_READ_ATONCE / 2) sys->i_used = STREAM_READ_ATONCE / 2; if (AStreamRefillStream(s)) return VLC_EGENERIC; } return VLC_SUCCESS; }