/src/vlc/modules/demux/oggseek.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * oggseek.c : ogg seeking functions for ogg demuxer vlc |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2008 - 2010 Gabriel Finch <salsaman@gmail.com> |
5 | | * |
6 | | * Authors: Gabriel Finch <salsaman@gmail.com> |
7 | | * adapted from: http://lives.svn.sourceforge.net/viewvc/lives/trunk/lives-plugins |
8 | | * /plugins/decoders/ogg_theora_decoder.c |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2.1 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with this program; if not, write to the Free Software Foundation, |
22 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | /***************************************************************************** |
26 | | * Preamble |
27 | | *****************************************************************************/ |
28 | | |
29 | | #ifdef HAVE_CONFIG_H |
30 | | # include "config.h" |
31 | | #endif |
32 | | |
33 | | #ifdef HAVE_LIBVORBIS |
34 | | #include <vorbis/codec.h> |
35 | | #endif |
36 | | |
37 | | #include <vlc_common.h> |
38 | | #include <vlc_demux.h> |
39 | | |
40 | | #include <ogg/ogg.h> |
41 | | #include <limits.h> |
42 | | #include <math.h> |
43 | | #include <assert.h> |
44 | | |
45 | | #include "ogg.h" |
46 | | #include "oggseek.h" |
47 | | #include "ogg_granule.h" |
48 | | |
49 | 0 | #define SEGMENT_NOT_FOUND -1 |
50 | | |
51 | 12.4k | #define MAX_PAGE_SIZE 65307 |
52 | | #define MIN_PAGE_SIZE 27 |
53 | | typedef struct packetStartCoordinates |
54 | | { |
55 | | int64_t i_pos; |
56 | | int64_t i_pageno; |
57 | | int64_t i_skip; |
58 | | } packetStartCoordinates; |
59 | | |
60 | | /************************************************************ |
61 | | * index entries |
62 | | *************************************************************/ |
63 | | |
64 | | /* free all entries in index list */ |
65 | | |
66 | | void oggseek_index_entries_free ( demux_index_entry_t *idx ) |
67 | 0 | { |
68 | 0 | demux_index_entry_t *idx_next; |
69 | |
|
70 | 0 | while ( idx != NULL ) |
71 | 0 | { |
72 | 0 | idx_next = idx->p_next; |
73 | 0 | free( idx ); |
74 | 0 | idx = idx_next; |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | |
79 | | /* internal function to create a new list member */ |
80 | | |
81 | | static demux_index_entry_t *index_entry_new( vlc_tick_t i_timestamp, int64_t i_pagepos ) |
82 | 0 | { |
83 | 0 | if ( i_timestamp == VLC_TICK_INVALID || i_pagepos < 1 ) |
84 | 0 | return NULL; |
85 | | |
86 | 0 | demux_index_entry_t *idx = malloc( sizeof(*idx) ); |
87 | 0 | if ( idx ) |
88 | 0 | { |
89 | 0 | idx->i_value = i_timestamp; |
90 | 0 | idx->i_pagepos = i_pagepos; |
91 | 0 | idx->p_next = NULL; |
92 | 0 | } |
93 | 0 | return idx; |
94 | 0 | } |
95 | | |
96 | | /* We insert into index, sorting by pagepos (as a page can match multiple |
97 | | time stamps) */ |
98 | | const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream, |
99 | | vlc_tick_t i_timestamp, |
100 | | int64_t i_pagepos ) |
101 | 0 | { |
102 | 0 | demux_index_entry_t **pp_next = &p_stream->idx; |
103 | 0 | for( ; *pp_next; ) |
104 | 0 | { |
105 | 0 | if( (*pp_next)->i_pagepos >= i_pagepos ) |
106 | 0 | { |
107 | 0 | if( (*pp_next)->i_pagepos == i_pagepos ) |
108 | 0 | return NULL; |
109 | 0 | break; |
110 | 0 | } |
111 | 0 | pp_next = &((*pp_next)->p_next); |
112 | 0 | } |
113 | | |
114 | 0 | demux_index_entry_t *ie = index_entry_new( i_timestamp, i_pagepos ); |
115 | 0 | if ( ie ) |
116 | 0 | { |
117 | 0 | ie->p_next = *pp_next; |
118 | 0 | *pp_next = ie; |
119 | 0 | } |
120 | |
|
121 | 0 | return ie; |
122 | 0 | } |
123 | | |
124 | | static bool OggSeekIndexFind ( logical_stream_t *p_stream, vlc_tick_t i_timestamp, |
125 | | int64_t *pi_pos_lower, int64_t *pi_pos_upper, |
126 | | vlc_tick_t *pi_lower_timestamp ) |
127 | 0 | { |
128 | 0 | demux_index_entry_t *idx = p_stream->idx; |
129 | |
|
130 | 0 | while ( idx != NULL ) |
131 | 0 | { |
132 | 0 | if ( idx->i_value <= i_timestamp ) |
133 | 0 | { |
134 | 0 | if ( !idx->p_next ) /* found on last index */ |
135 | 0 | { |
136 | 0 | *pi_pos_lower = idx->i_pagepos; |
137 | 0 | *pi_lower_timestamp = idx->i_value; |
138 | 0 | return true; |
139 | 0 | } |
140 | 0 | if ( idx->p_next->i_value > i_timestamp ) |
141 | 0 | { |
142 | 0 | *pi_pos_lower = idx->i_pagepos; |
143 | 0 | *pi_lower_timestamp = idx->i_value; |
144 | 0 | *pi_pos_upper = idx->p_next->i_pagepos; |
145 | 0 | return true; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | idx = idx->p_next; |
149 | 0 | } |
150 | | |
151 | 0 | return false; |
152 | 0 | } |
153 | | |
154 | | /********************************************************************* |
155 | | * private functions |
156 | | **********************************************************************/ |
157 | | |
158 | | /* seek in ogg file to offset i_pos and update the sync */ |
159 | | |
160 | | static void seek_byte( demux_t *p_demux, int64_t i_pos ) |
161 | 0 | { |
162 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
163 | |
|
164 | 0 | if ( ! vlc_stream_Seek( p_demux->s, i_pos ) ) |
165 | 0 | { |
166 | 0 | ogg_sync_reset( &p_sys->oy ); |
167 | |
|
168 | 0 | p_sys->i_input_position = i_pos; |
169 | 0 | p_sys->b_page_waiting = false; |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | | |
174 | | |
175 | | /* read bytes from the ogg file to try to find a page start */ |
176 | | |
177 | | static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read ) |
178 | 0 | { |
179 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
180 | |
|
181 | 0 | char *buf; |
182 | 0 | int64_t i_result; |
183 | |
|
184 | 0 | if ( p_sys->i_total_bytes > 0 ) |
185 | 0 | { |
186 | 0 | if ( p_sys->i_input_position + i_bytes_to_read > p_sys->i_total_bytes ) |
187 | 0 | { |
188 | 0 | i_bytes_to_read = p_sys->i_total_bytes - p_sys->i_input_position; |
189 | 0 | if ( i_bytes_to_read <= 0 ) { |
190 | 0 | return 0; |
191 | 0 | } |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | 0 | i_bytes_to_read = __MIN( i_bytes_to_read, INT_MAX ); |
196 | |
|
197 | 0 | seek_byte ( p_demux, p_sys->i_input_position ); |
198 | |
|
199 | 0 | buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read ); |
200 | 0 | if( !buf ) |
201 | 0 | return 0; |
202 | | |
203 | 0 | i_result = vlc_stream_Read( p_demux->s, buf, i_bytes_to_read ); |
204 | |
|
205 | 0 | ogg_sync_wrote( &p_sys->oy, i_result ); |
206 | 0 | return i_result; |
207 | 0 | } |
208 | | |
209 | | |
210 | | void Oggseek_ProbeEnd( demux_t *p_demux ) |
211 | 4.90k | { |
212 | | /* Temporary state */ |
213 | 4.90k | ogg_stream_state os; |
214 | 4.90k | ogg_sync_state oy; |
215 | 4.90k | ogg_page page; |
216 | 4.90k | demux_sys_t *p_sys = p_demux->p_sys; |
217 | 4.90k | int64_t i_pos, i_startpos, i_result, i_granule, i_lowerbound; |
218 | 4.90k | int64_t i_length = 0; |
219 | 4.90k | int64_t i_backup_pos = vlc_stream_Tell( p_demux->s ); |
220 | 4.90k | int64_t i_upperbound = stream_Size( p_demux->s ); |
221 | 4.90k | unsigned int i_backoffset = OGGSEEK_BYTES_TO_READ; |
222 | 4.90k | assert( OGGSEEK_BYTES_TO_READ < UINT_MAX ); |
223 | | |
224 | 4.90k | const char *buffer; |
225 | | |
226 | 4.90k | ogg_stream_init( &os, -1 ); |
227 | 4.90k | ogg_sync_init( &oy ); |
228 | | |
229 | | /* Try to lookup last granule from each logical stream */ |
230 | 4.90k | i_lowerbound = stream_Size( p_demux->s ) - p_sys->i_streams * MAX_PAGE_SIZE * 2; |
231 | 4.90k | i_lowerbound = __MAX( 0, i_lowerbound ); |
232 | | |
233 | 4.90k | i_pos = i_startpos = __MAX( i_lowerbound, i_upperbound - i_backoffset ); |
234 | | |
235 | 4.90k | if ( vlc_stream_Seek( p_demux->s, i_pos ) ) |
236 | 0 | { |
237 | 0 | ogg_sync_clear( &oy ); |
238 | 0 | ogg_stream_clear( &os ); |
239 | 0 | return; |
240 | 0 | } |
241 | | |
242 | 11.0k | while( i_pos >= i_lowerbound ) |
243 | 11.0k | { |
244 | | |
245 | 32.1k | while( i_pos < i_upperbound ) |
246 | 21.1k | { |
247 | 21.1k | if ( oy.unsynced ) |
248 | 17.3k | i_result = ogg_sync_pageseek( &oy, &page ); |
249 | | |
250 | 21.1k | buffer = ogg_sync_buffer( &oy, OGGSEEK_BYTES_TO_READ ); |
251 | 21.1k | if ( buffer == NULL ) goto clean; |
252 | 21.1k | i_result = vlc_stream_Read( p_demux->s, (void*) buffer, OGGSEEK_BYTES_TO_READ ); |
253 | 21.1k | if ( i_result < 1 ) goto clean; |
254 | 21.1k | i_pos += i_result; |
255 | 21.1k | ogg_sync_wrote( &oy, i_result ); |
256 | | |
257 | 75.1k | while( ogg_sync_pageout( &oy, &page ) == 1 ) |
258 | 54.0k | { |
259 | 54.0k | i_granule = ogg_page_granulepos( &page ); |
260 | 54.0k | if ( i_granule == -1 ) continue; |
261 | | |
262 | 166k | for ( int i=0; i< p_sys->i_streams; i++ ) |
263 | 135k | { |
264 | 135k | if ( p_sys->pp_stream[i]->i_serial_no != ogg_page_serialno( &page ) ) |
265 | 113k | continue; |
266 | | |
267 | 21.9k | i_length = Ogg_GranuleToTime( p_sys->pp_stream[i], i_granule, |
268 | 21.9k | !p_sys->pp_stream[i]->b_contiguous, false ); |
269 | 21.9k | if( i_length != VLC_TICK_INVALID ) |
270 | 6.06k | { |
271 | | /* We found at least a page with valid granule */ |
272 | 6.06k | p_sys->i_length = __MAX( p_sys->i_length, i_length - VLC_TICK_0 ); |
273 | 6.06k | } |
274 | 21.9k | break; |
275 | 135k | } |
276 | 53.2k | } |
277 | 21.1k | } |
278 | | |
279 | 11.0k | if( i_startpos == i_lowerbound || |
280 | 7.41k | p_sys->i_length != VLC_TICK_INVALID ) |
281 | 4.90k | goto clean; |
282 | | |
283 | 6.10k | int64_t i_next_upperbound = __MIN(i_startpos + MIN_PAGE_SIZE, i_upperbound); |
284 | | |
285 | | /* Otherwise increase read size, starting earlier */ |
286 | 6.10k | if ( i_backoffset <= MAX_PAGE_SIZE ) |
287 | 4.68k | { |
288 | 4.68k | i_backoffset <<= 1; |
289 | 4.68k | i_startpos = i_upperbound - i_backoffset; |
290 | 4.68k | } |
291 | 1.42k | else |
292 | 1.42k | { |
293 | 1.42k | i_startpos = i_upperbound - MAX_PAGE_SIZE; |
294 | 1.42k | } |
295 | | |
296 | 6.10k | i_upperbound = i_next_upperbound; |
297 | | |
298 | 6.10k | i_startpos = __MAX( i_startpos, i_lowerbound ); |
299 | 6.10k | i_pos = i_startpos; |
300 | | |
301 | 6.10k | if ( vlc_stream_Seek( p_demux->s, i_pos ) ) |
302 | 0 | break; |
303 | 6.10k | } |
304 | | |
305 | 4.90k | clean: |
306 | 4.90k | vlc_stream_Seek( p_demux->s, i_backup_pos ); |
307 | | |
308 | 4.90k | ogg_sync_clear( &oy ); |
309 | 4.90k | ogg_stream_clear( &os ); |
310 | 4.90k | } |
311 | | |
312 | | static void update_page_type( logical_stream_t *p_stream, int64_t i_granule ) |
313 | 0 | { |
314 | 0 | if( i_granule == 0 ) |
315 | 0 | p_stream->page_type = OGGPAGE_HEADER; |
316 | 0 | else if( p_stream->page_type == OGGPAGE_HEADER ) |
317 | 0 | p_stream->page_type = OGGPAGE_FIRST; |
318 | 0 | else |
319 | 0 | p_stream->page_type = OGGPAGE_OTHER; |
320 | 0 | } |
321 | | |
322 | | static int64_t find_first_page_granule( demux_t *p_demux, |
323 | | int64_t i_pos1, int64_t i_pos2, |
324 | | logical_stream_t *p_stream, |
325 | | int64_t *i_granulepos ) |
326 | 0 | { |
327 | 0 | int64_t i_result; |
328 | 0 | *i_granulepos = -1; |
329 | 0 | int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1; |
330 | 0 | int64_t i_bytes_read; |
331 | 0 | int64_t i_packets_checked; |
332 | |
|
333 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
334 | |
|
335 | 0 | ogg_packet op; |
336 | |
|
337 | 0 | seek_byte( p_demux, i_pos1 ); |
338 | |
|
339 | 0 | if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ; |
340 | |
|
341 | 0 | while ( 1 ) |
342 | 0 | { |
343 | |
|
344 | 0 | if ( p_sys->i_input_position >= i_pos2 ) |
345 | 0 | { |
346 | | /* we reached the end and found no pages */ |
347 | 0 | return -1; |
348 | 0 | } |
349 | | |
350 | | /* read next chunk */ |
351 | 0 | if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) ) |
352 | 0 | { |
353 | | /* EOF */ |
354 | 0 | return -1; |
355 | 0 | } |
356 | | |
357 | 0 | i_bytes_to_read = OGGSEEK_BYTES_TO_READ; |
358 | |
|
359 | 0 | i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page ); |
360 | |
|
361 | 0 | if ( i_result < 0 ) |
362 | 0 | { |
363 | | /* found a page, sync to page start */ |
364 | 0 | p_sys->i_input_position -= i_result; |
365 | 0 | i_pos1 = p_sys->i_input_position; |
366 | 0 | continue; |
367 | 0 | } |
368 | | |
369 | 0 | if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 && |
370 | 0 | ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) ) |
371 | 0 | { |
372 | 0 | i_pos1 = p_sys->i_input_position; |
373 | 0 | break; |
374 | 0 | } |
375 | | |
376 | 0 | p_sys->i_input_position += i_bytes_read; |
377 | |
|
378 | 0 | }; |
379 | |
|
380 | 0 | seek_byte( p_demux, p_sys->i_input_position ); |
381 | 0 | ogg_stream_reset( &p_stream->os ); |
382 | | |
383 | | /* prevent reading the whole file if stream is gone */ |
384 | 0 | if( i_pos2 > p_sys->i_input_position + OGGSEEK_SERIALNO_MAX_LOOKUP_BYTES ) |
385 | 0 | i_pos2 = p_sys->i_input_position + OGGSEEK_SERIALNO_MAX_LOOKUP_BYTES; |
386 | |
|
387 | 0 | while( 1 ) |
388 | 0 | { |
389 | |
|
390 | 0 | if ( p_sys->i_input_position >= i_pos2 ) |
391 | 0 | { |
392 | | /* reached the end of the search region and nothing was found */ |
393 | 0 | return p_sys->i_input_position; |
394 | 0 | } |
395 | | |
396 | 0 | p_sys->b_page_waiting = false; |
397 | |
|
398 | 0 | if ( ! ( i_result = oggseek_read_page( p_demux ) ) ) |
399 | 0 | { |
400 | | /* EOF */ |
401 | 0 | return p_sys->i_input_position; |
402 | 0 | } |
403 | | |
404 | | // found a page |
405 | 0 | if ( ogg_stream_pagein( &p_stream->os, &p_sys->current_page ) != 0 ) |
406 | 0 | { |
407 | | /* page is not for this stream or incomplete */ |
408 | 0 | p_sys->i_input_position += i_result; |
409 | 0 | continue; |
410 | 0 | } |
411 | | |
412 | 0 | if ( ogg_page_granulepos( &p_sys->current_page ) <= 0 ) |
413 | 0 | { |
414 | 0 | update_page_type( p_stream, ogg_page_granulepos(( &p_sys->current_page )) ); |
415 | | /* A negative granulepos means that the packet continues on the |
416 | | * next page => read the next page */ |
417 | 0 | p_sys->i_input_position += i_result; |
418 | 0 | continue; |
419 | 0 | } |
420 | | |
421 | 0 | i_packets_checked = 0; |
422 | |
|
423 | 0 | while ( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) |
424 | 0 | { |
425 | 0 | i_packets_checked++; |
426 | 0 | } |
427 | |
|
428 | 0 | if ( i_packets_checked ) |
429 | 0 | { |
430 | 0 | *i_granulepos = ogg_page_granulepos( &p_sys->current_page ); |
431 | 0 | return p_sys->i_input_position; |
432 | 0 | } |
433 | | |
434 | | /* -> start of next page */ |
435 | 0 | p_sys->i_input_position += i_result; |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | | static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream, |
440 | | int64_t i_granulepos, packetStartCoordinates *p_lastpacketcoords, |
441 | | bool b_exact ) |
442 | 0 | { |
443 | 0 | ogg_packet op; |
444 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
445 | 0 | if ( ogg_stream_pagein( &p_stream->os, &p_sys->current_page ) != 0 ) |
446 | 0 | return false; |
447 | 0 | p_sys->b_page_waiting = true; |
448 | 0 | int i=0; |
449 | |
|
450 | 0 | update_page_type( p_stream, ogg_page_granulepos( &p_sys->current_page ) ); |
451 | |
|
452 | 0 | int64_t itarget_frame = Ogg_GetKeyframeGranule( p_stream, i_granulepos ); |
453 | 0 | int64_t iframe = Ogg_GetKeyframeGranule( p_stream, ogg_page_granulepos( &p_sys->current_page ) ); |
454 | |
|
455 | 0 | if ( ! ogg_page_continued( &p_sys->current_page ) ) |
456 | 0 | { |
457 | | /* Start of frame, not continued page, but no packet. */ |
458 | 0 | p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page ); |
459 | 0 | p_lastpacketcoords->i_pos = p_sys->i_input_position; |
460 | 0 | p_lastpacketcoords->i_skip = 0; |
461 | 0 | } |
462 | |
|
463 | 0 | if ( b_exact && iframe > itarget_frame ) |
464 | 0 | { |
465 | 0 | while( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) {}; |
466 | 0 | p_sys->b_page_waiting = false; |
467 | 0 | return false; |
468 | 0 | } |
469 | | |
470 | 0 | while( ogg_stream_packetpeek( &p_stream->os, &op ) > 0 ) |
471 | 0 | { |
472 | 0 | if ( ( !b_exact || itarget_frame == iframe ) && Ogg_IsKeyFrame( p_stream, &op ) ) |
473 | 0 | { |
474 | 0 | OggDebug( |
475 | 0 | msg_Dbg(p_demux, "** KEYFRAME **" ); |
476 | 0 | msg_Dbg(p_demux, "** KEYFRAME PACKET START pageno %"PRId64" OFFSET %"PRId64" skip %"PRId64" **", p_lastpacketcoords->i_pageno, p_lastpacketcoords->i_pos, p_lastpacketcoords->i_skip ); |
477 | 0 | msg_Dbg(p_demux, "KEYFRAME PACKET IS at pageno %"PRId64" OFFSET %"PRId64" with skip %d packet (%d / %d) ", |
478 | 0 | ogg_page_pageno( &p_sys->current_page ), p_sys->i_input_position, i, i+1, ogg_page_packets( &p_sys->current_page ) ); |
479 | 0 | ); |
480 | |
|
481 | 0 | if ( i != 0 ) /* Not continued packet */ |
482 | 0 | { |
483 | | /* We need to handle the case when the packet spans onto N |
484 | | previous page(s). packetout() will be valid only when |
485 | | all segments are assembled. |
486 | | Keyframe flag is only available after assembling last part |
487 | | (when packetout() becomes valid). We have no way to guess |
488 | | keyframe at earlier time. |
489 | | */ |
490 | 0 | p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page ); |
491 | 0 | p_lastpacketcoords->i_pos = p_sys->i_input_position; |
492 | 0 | p_lastpacketcoords->i_skip = i; |
493 | 0 | } |
494 | 0 | return true; |
495 | 0 | } |
496 | | |
497 | 0 | p_lastpacketcoords->i_pageno = ogg_page_pageno( &p_sys->current_page ); |
498 | 0 | p_lastpacketcoords->i_pos = p_sys->i_input_position; |
499 | 0 | p_lastpacketcoords->i_skip = i + 1; |
500 | 0 | i++; |
501 | | /* remove that packet and go sync to next */ |
502 | 0 | ogg_stream_packetout( &p_stream->os, &op ); |
503 | 0 | } |
504 | | |
505 | 0 | return false; |
506 | 0 | } |
507 | | |
508 | | static int64_t OggForwardSeekToFrame( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2, |
509 | | logical_stream_t *p_stream, int64_t i_granulepos, bool b_fastseek ) |
510 | 0 | { |
511 | 0 | int64_t i_result; |
512 | 0 | int64_t i_bytes_to_read; |
513 | 0 | int64_t i_bytes_read; |
514 | |
|
515 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
516 | |
|
517 | 0 | i_bytes_to_read = i_pos2 - i_pos1 + 1; |
518 | 0 | seek_byte( p_demux, i_pos1 ); |
519 | 0 | if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ; |
520 | |
|
521 | 0 | OggDebug( |
522 | 0 | msg_Dbg( p_demux, "Probing Fwd %"PRId64" %"PRId64" for granule %"PRId64, |
523 | 0 | i_pos1, i_pos2, i_granulepos ); |
524 | 0 | ); |
525 | |
|
526 | 0 | while ( 1 ) |
527 | 0 | { |
528 | |
|
529 | 0 | if ( p_sys->i_input_position >= i_pos2 ) |
530 | 0 | return SEGMENT_NOT_FOUND; |
531 | | |
532 | | /* read next chunk */ |
533 | 0 | if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) ) |
534 | 0 | return SEGMENT_NOT_FOUND; |
535 | | |
536 | 0 | i_bytes_to_read = OGGSEEK_BYTES_TO_READ; |
537 | |
|
538 | 0 | i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page ); |
539 | |
|
540 | 0 | if ( i_result < 0 ) |
541 | 0 | { |
542 | | /* found a page, sync to page start */ |
543 | 0 | p_sys->i_input_position -= i_result; |
544 | 0 | i_pos1 = p_sys->i_input_position; |
545 | 0 | continue; |
546 | 0 | } |
547 | | |
548 | 0 | if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 && |
549 | 0 | ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) ) |
550 | 0 | { |
551 | 0 | i_pos1 = p_sys->i_input_position; |
552 | 0 | break; |
553 | 0 | } |
554 | | |
555 | 0 | p_sys->i_input_position += i_bytes_read; |
556 | 0 | }; |
557 | |
|
558 | 0 | seek_byte( p_demux, p_sys->i_input_position ); |
559 | 0 | ogg_stream_reset( &p_stream->os ); |
560 | |
|
561 | 0 | ogg_packet op; |
562 | 0 | while( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) {}; |
563 | |
|
564 | 0 | packetStartCoordinates lastpacket = { -1, -1, -1 }; |
565 | |
|
566 | 0 | while( 1 ) |
567 | 0 | { |
568 | |
|
569 | 0 | if ( p_sys->i_input_position >= i_pos2 ) |
570 | 0 | { |
571 | | /* reached the end of the search region and nothing was found */ |
572 | 0 | break; |
573 | 0 | } |
574 | | |
575 | 0 | p_sys->b_page_waiting = false; |
576 | |
|
577 | 0 | if ( ! ( i_result = oggseek_read_page( p_demux ) ) ) |
578 | 0 | { |
579 | | /* EOF */ |
580 | 0 | break; |
581 | 0 | } |
582 | | |
583 | | // found a page |
584 | 0 | if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) ) |
585 | 0 | { |
586 | | /* page is not for this stream */ |
587 | 0 | p_sys->i_input_position += i_result; |
588 | 0 | continue; |
589 | 0 | } |
590 | | |
591 | 0 | if ( OggSeekToPacket( p_demux, p_stream, i_granulepos, &lastpacket, b_fastseek ) ) |
592 | 0 | { |
593 | 0 | p_sys->i_input_position = lastpacket.i_pos; |
594 | 0 | p_stream->i_skip_frames = 0; |
595 | 0 | return p_sys->i_input_position; |
596 | 0 | } |
597 | | |
598 | | /* -> start of next page */ |
599 | 0 | p_sys->i_input_position += i_result; |
600 | 0 | } |
601 | | |
602 | 0 | return SEGMENT_NOT_FOUND; |
603 | 0 | } |
604 | | |
605 | | static int64_t OggBackwardSeekToFrame( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2, |
606 | | logical_stream_t *p_stream, int64_t i_granulepos ) |
607 | 0 | { |
608 | 0 | int64_t i_result; |
609 | 0 | int64_t i_offset = __MAX( 1 + ( (i_pos2 - i_pos1) >> 1 ), OGGSEEK_BYTES_TO_READ ); |
610 | |
|
611 | 0 | restart: |
612 | |
|
613 | 0 | OggDebug( |
614 | 0 | msg_Dbg( p_demux, "Probing Back %"PRId64" %"PRId64" for granule %"PRId64, |
615 | 0 | i_pos1, i_pos2, i_granulepos ); |
616 | 0 | ); |
617 | |
|
618 | 0 | i_result = OggForwardSeekToFrame( p_demux, i_pos1, i_pos2, p_stream, |
619 | 0 | i_granulepos, true ); |
620 | |
|
621 | 0 | if ( i_result == SEGMENT_NOT_FOUND && i_pos1 > p_stream->i_data_start ) |
622 | 0 | { |
623 | 0 | i_pos1 = __MAX( p_stream->i_data_start, i_pos1 - i_offset ); |
624 | 0 | goto restart; |
625 | 0 | } |
626 | | |
627 | 0 | return i_result; |
628 | 0 | } |
629 | | |
630 | | /* returns pos */ |
631 | | static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stream, |
632 | | vlc_tick_t i_targettime, int64_t i_pos_lower, int64_t i_pos_upper, int64_t *pi_seek_time) |
633 | 0 | { |
634 | 0 | int64_t i_start_pos; |
635 | 0 | int64_t i_end_pos; |
636 | 0 | int64_t i_segsize; |
637 | |
|
638 | 0 | struct |
639 | 0 | { |
640 | 0 | int64_t i_pos; |
641 | 0 | vlc_tick_t i_timestamp; |
642 | 0 | int64_t i_granule; |
643 | 0 | } bestlower = { p_stream->i_data_start, VLC_TICK_INVALID, -1 }, |
644 | 0 | current = { -1, VLC_TICK_INVALID, -1 }, |
645 | 0 | lowestupper = { -1, VLC_TICK_INVALID, -1 }; |
646 | |
|
647 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
648 | |
|
649 | 0 | i_pos_lower = __MAX( i_pos_lower, p_stream->i_data_start ); |
650 | 0 | i_pos_upper = __MIN( i_pos_upper, p_sys->i_total_bytes ); |
651 | 0 | if ( i_pos_upper < 0 ) i_pos_upper = p_sys->i_total_bytes; |
652 | |
|
653 | 0 | i_start_pos = i_pos_lower; |
654 | 0 | i_end_pos = i_pos_upper; |
655 | |
|
656 | 0 | i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1; |
657 | 0 | i_start_pos += i_segsize; |
658 | |
|
659 | 0 | OggDebug( msg_Dbg(p_demux, "Bisecting for time=%"PRId64" between %"PRId64" and %"PRId64, |
660 | 0 | i_targettime, i_pos_lower, i_pos_upper ) ); |
661 | | |
662 | | /* Check lowest possible bound that will never be checked in bisection */ |
663 | 0 | current.i_pos = find_first_page_granule( p_demux, |
664 | 0 | i_pos_lower, |
665 | 0 | __MIN(i_start_pos + PAGE_HEADER_BYTES, i_end_pos), |
666 | 0 | p_stream, |
667 | 0 | ¤t.i_granule ); |
668 | 0 | if( current.i_granule != -1 ) |
669 | 0 | { |
670 | 0 | current.i_timestamp = Ogg_GranuleToTime( p_stream, current.i_granule, |
671 | 0 | !p_stream->b_contiguous, false ); |
672 | 0 | if( current.i_timestamp <= i_targettime ) |
673 | 0 | bestlower = current; |
674 | 0 | else |
675 | 0 | lowestupper = current; |
676 | 0 | } |
677 | |
|
678 | 0 | do |
679 | 0 | { |
680 | | /* see if the frame lies in current segment */ |
681 | 0 | i_start_pos = __MAX( i_start_pos, i_pos_lower ); |
682 | 0 | i_end_pos = __MIN( i_end_pos, i_pos_upper ); |
683 | |
|
684 | 0 | if ( i_start_pos >= i_end_pos ) |
685 | 0 | { |
686 | 0 | if ( i_start_pos == i_pos_lower) |
687 | 0 | { |
688 | 0 | return i_start_pos; |
689 | 0 | } |
690 | 0 | return -1; |
691 | 0 | } |
692 | | |
693 | | |
694 | 0 | current.i_pos = find_first_page_granule( p_demux, |
695 | 0 | i_start_pos, i_end_pos, |
696 | 0 | p_stream, |
697 | 0 | ¤t.i_granule ); |
698 | |
|
699 | 0 | current.i_timestamp = Ogg_GranuleToTime( p_stream, current.i_granule, |
700 | 0 | !p_stream->b_contiguous, false ); |
701 | |
|
702 | 0 | if ( current.i_timestamp == VLC_TICK_INVALID && current.i_granule > 0 ) |
703 | 0 | { |
704 | 0 | msg_Err( p_demux, "Unmatched granule. New codec ?" ); |
705 | 0 | return -1; |
706 | 0 | } |
707 | 0 | else if ( current.i_timestamp < 0 ) /* due to preskip with some codecs */ |
708 | 0 | { |
709 | 0 | current.i_timestamp = 0; |
710 | 0 | } |
711 | | |
712 | 0 | if ( current.i_pos != -1 && current.i_granule != -1 ) |
713 | 0 | { |
714 | | /* found a page */ |
715 | |
|
716 | 0 | if ( current.i_timestamp <= i_targettime ) |
717 | 0 | { |
718 | | /* set our lower bound */ |
719 | 0 | if ( current.i_timestamp > bestlower.i_timestamp ) |
720 | 0 | bestlower = current; |
721 | 0 | i_start_pos = current.i_pos; |
722 | 0 | } |
723 | 0 | else |
724 | 0 | { |
725 | 0 | if ( lowestupper.i_timestamp == VLC_TICK_INVALID || |
726 | 0 | current.i_timestamp < lowestupper.i_timestamp ) |
727 | 0 | lowestupper = current; |
728 | | /* check lower half of segment */ |
729 | 0 | i_start_pos -= i_segsize; |
730 | 0 | i_end_pos -= i_segsize; |
731 | 0 | } |
732 | 0 | } |
733 | 0 | else |
734 | 0 | { |
735 | | /* no keyframe found, check lower segment */ |
736 | 0 | i_end_pos -= i_segsize; |
737 | 0 | i_start_pos -= i_segsize; |
738 | 0 | } |
739 | |
|
740 | 0 | OggDebug( msg_Dbg(p_demux, "Bisect restart i_segsize=%"PRId64" between %"PRId64 |
741 | 0 | " and %"PRId64 " bl %"PRId64" lu %"PRId64, |
742 | 0 | i_segsize, i_start_pos, i_end_pos, bestlower.i_granule, lowestupper.i_granule ) ); |
743 | |
|
744 | 0 | i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1; |
745 | 0 | i_start_pos += i_segsize; |
746 | |
|
747 | 0 | } while ( i_segsize > PAGE_HEADER_BYTES ); |
748 | | |
749 | 0 | if ( bestlower.i_granule == -1 ) |
750 | 0 | { |
751 | 0 | if ( lowestupper.i_granule == -1 ) |
752 | 0 | return -1; |
753 | 0 | else |
754 | 0 | bestlower = lowestupper; |
755 | 0 | } |
756 | | |
757 | 0 | if ( p_stream->b_oggds ) |
758 | 0 | { |
759 | 0 | int64_t a = OggBackwardSeekToFrame( p_demux, |
760 | 0 | __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ), |
761 | 0 | bestlower.i_pos, |
762 | 0 | p_stream, bestlower.i_granule /* unused */ ); |
763 | 0 | *pi_seek_time = bestlower.i_timestamp; |
764 | 0 | return a; |
765 | 0 | } |
766 | | /* If not each packet is usable as keyframe, query the codec for keyframe */ |
767 | 0 | else if ( Ogg_GetKeyframeGranule( p_stream, bestlower.i_granule ) != bestlower.i_granule ) |
768 | 0 | { |
769 | 0 | int64_t i_keyframegranule = Ogg_GetKeyframeGranule( p_stream, bestlower.i_granule ); |
770 | |
|
771 | 0 | OggDebug( msg_Dbg( p_demux, "Need to reseek to keyframe (%"PRId64") granule (%"PRId64"!=%"PRId64") to t=%"PRId64, |
772 | 0 | i_keyframegranule >> p_stream->i_granule_shift, |
773 | 0 | bestlower.i_granule, |
774 | 0 | i_pos_upper, |
775 | 0 | Ogg_GranuleToTime( p_stream, i_keyframegranule, !p_stream->b_contiguous, false ) ) ); |
776 | |
|
777 | 0 | OggDebug( msg_Dbg( p_demux, "Seeking back to %"PRId64, __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ) ) ); |
778 | |
|
779 | 0 | int64_t a = OggBackwardSeekToFrame( p_demux, |
780 | 0 | __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ), |
781 | 0 | stream_Size( p_demux->s ), p_stream, i_keyframegranule ); |
782 | 0 | *pi_seek_time = Ogg_GranuleToTime( p_stream, i_keyframegranule, |
783 | 0 | !p_stream->b_contiguous, false ); |
784 | 0 | return a; |
785 | 0 | } |
786 | | |
787 | 0 | *pi_seek_time = bestlower.i_timestamp; |
788 | 0 | return bestlower.i_pos; |
789 | 0 | } |
790 | | |
791 | | |
792 | | /************************************************************************ |
793 | | * public functions |
794 | | *************************************************************************/ |
795 | | |
796 | | int Oggseek_BlindSeektoAbsoluteTime( demux_t *p_demux, logical_stream_t *p_stream, |
797 | | vlc_tick_t i_time, bool b_fastseek ) |
798 | 0 | { |
799 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
800 | 0 | int64_t i_lowerpos = -1; |
801 | 0 | int64_t i_upperpos = -1; |
802 | 0 | bool b_found = false; |
803 | | |
804 | | /* Search in skeleton */ |
805 | 0 | Ogg_GetBoundsUsingSkeletonIndex( p_stream, i_time, &i_lowerpos, &i_upperpos ); |
806 | 0 | if ( i_lowerpos != -1 ) b_found = true; |
807 | | |
808 | | /* And also search in our own index */ |
809 | 0 | vlc_tick_t foo; |
810 | 0 | if ( !b_found && OggSeekIndexFind( p_stream, i_time, &i_lowerpos, &i_upperpos, &foo ) ) |
811 | 0 | { |
812 | 0 | b_found = true; |
813 | 0 | } |
814 | | |
815 | | /* FIXME: add function to get preload time by codec, ex: opus */ |
816 | | |
817 | | /* or search */ |
818 | 0 | if ( !b_found && b_fastseek ) |
819 | 0 | { |
820 | 0 | int64_t i_sync_time; |
821 | 0 | i_lowerpos = OggBisectSearchByTime( p_demux, p_stream, i_time, |
822 | 0 | p_stream->i_data_start, p_sys->i_total_bytes, |
823 | 0 | &i_sync_time ); |
824 | 0 | b_found = ( i_lowerpos != -1 ); |
825 | 0 | } |
826 | |
|
827 | 0 | if ( !b_found ) return -1; |
828 | | |
829 | 0 | if ( i_lowerpos < p_stream->i_data_start || i_upperpos > p_sys->i_total_bytes ) |
830 | 0 | return -1; |
831 | | |
832 | | /* And really do seek */ |
833 | 0 | p_sys->i_input_position = i_lowerpos; |
834 | 0 | seek_byte( p_demux, p_sys->i_input_position ); |
835 | 0 | ogg_stream_reset( &p_stream->os ); |
836 | |
|
837 | 0 | return i_lowerpos; |
838 | 0 | } |
839 | | |
840 | | int Oggseek_BlindSeektoPosition( demux_t *p_demux, logical_stream_t *p_stream, |
841 | | double f, bool b_canfastseek ) |
842 | 0 | { |
843 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
844 | 0 | OggDebug( msg_Dbg( p_demux, "=================== Seeking To Blind Pos" ) ); |
845 | 0 | int64_t i_size = stream_Size( p_demux->s ); |
846 | 0 | uint64_t i_startpos = vlc_stream_Tell( p_demux->s ); |
847 | 0 | int64_t i_granule; |
848 | 0 | int64_t i_pagepos; |
849 | |
|
850 | 0 | i_size = find_first_page_granule( p_demux, |
851 | 0 | i_size * f, i_size, |
852 | 0 | p_stream, |
853 | 0 | &i_granule ); |
854 | 0 | if( i_granule == -1 ) |
855 | 0 | { |
856 | 0 | if( vlc_stream_Seek( p_demux->s, i_startpos ) != VLC_SUCCESS ) |
857 | 0 | msg_Err( p_demux, "Seek back failed. Not seekable ?" ); |
858 | 0 | return VLC_EGENERIC; |
859 | 0 | } |
860 | | |
861 | 0 | OggDebug( msg_Dbg( p_demux, "Seek start pos is %"PRId64" granule %"PRId64, i_size, i_granule ) ); |
862 | |
|
863 | 0 | i_granule = Ogg_GetKeyframeGranule( p_stream, i_granule ); |
864 | |
|
865 | 0 | if ( b_canfastseek ) |
866 | 0 | { |
867 | | /* Peek back until we meet a keyframe to start our decoding up to our |
868 | | * final seek time */ |
869 | 0 | i_pagepos = OggBackwardSeekToFrame( p_demux, |
870 | 0 | __MAX ( i_size - MAX_PAGE_SIZE, p_stream->i_data_start ), |
871 | 0 | __MIN ( i_size + MAX_PAGE_SIZE, p_sys->i_total_bytes ), |
872 | 0 | p_stream, i_granule ); |
873 | 0 | } |
874 | 0 | else |
875 | 0 | { |
876 | | /* Otherwise, we just sync to the next keyframe we meet */ |
877 | 0 | i_pagepos = OggForwardSeekToFrame( p_demux, |
878 | 0 | __MAX ( i_size - MIN_PAGE_SIZE, p_stream->i_data_start ), |
879 | 0 | stream_Size( p_demux->s ), |
880 | 0 | p_stream, i_granule, false ); |
881 | 0 | } |
882 | |
|
883 | 0 | OggDebug( msg_Dbg( p_demux, "=================== Seeked To %"PRId64" granule %"PRId64, i_pagepos, i_granule ) ); |
884 | 0 | (void) i_pagepos; |
885 | 0 | return VLC_SUCCESS; |
886 | 0 | } |
887 | | |
888 | | int Oggseek_SeektoAbsolutetime( demux_t *p_demux, logical_stream_t *p_stream, |
889 | | vlc_tick_t i_time ) |
890 | 0 | { |
891 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
892 | |
|
893 | 0 | OggDebug( msg_Dbg( p_demux, "=================== Seeking To Absolute Time %"PRId64, i_time ) ); |
894 | 0 | int64_t i_offset_lower = -1; |
895 | 0 | int64_t i_offset_upper = -1; |
896 | |
|
897 | 0 | if ( Ogg_GetBoundsUsingSkeletonIndex( p_stream, i_time, &i_offset_lower, &i_offset_upper ) ) |
898 | 0 | { |
899 | | /* Exact match */ |
900 | 0 | OggDebug( msg_Dbg( p_demux, "Found keyframe at %"PRId64" using skeleton index", i_offset_lower ) ); |
901 | 0 | if ( i_offset_lower == -1 ) i_offset_lower = p_stream->i_data_start; |
902 | 0 | p_sys->i_input_position = i_offset_lower; |
903 | 0 | seek_byte( p_demux, p_sys->i_input_position ); |
904 | 0 | ogg_stream_reset( &p_stream->os ); |
905 | 0 | return i_offset_lower; |
906 | 0 | } |
907 | 0 | OggDebug( msg_Dbg( p_demux, "Search bounds set to %"PRId64" %"PRId64" using skeleton index", i_offset_lower, i_offset_upper ) ); |
908 | | |
909 | |
|
910 | 0 | vlc_tick_t i_lower_index; |
911 | 0 | if(!OggSeekIndexFind( p_stream, i_time, &i_offset_lower, &i_offset_upper, &i_lower_index )) |
912 | 0 | i_lower_index = 0; |
913 | |
|
914 | 0 | i_offset_lower = __MAX( i_offset_lower, p_stream->i_data_start ); |
915 | 0 | i_offset_upper = __MIN( i_offset_upper, p_sys->i_total_bytes ); |
916 | |
|
917 | 0 | int64_t i_sync_time; |
918 | 0 | int64_t i_pagepos = OggBisectSearchByTime( p_demux, p_stream, i_time, |
919 | 0 | i_offset_lower, i_offset_upper, &i_sync_time ); |
920 | 0 | if ( i_pagepos >= 0 ) |
921 | 0 | { |
922 | | /* be sure to clear any state or read+pagein() will fail on same # */ |
923 | 0 | ogg_stream_reset( &p_stream->os ); |
924 | 0 | p_sys->i_input_position = i_pagepos; |
925 | 0 | seek_byte( p_demux, p_sys->i_input_position ); |
926 | 0 | } |
927 | | |
928 | | /* Insert keyframe position into index */ |
929 | 0 | vlc_tick_t index_interval = p_sys->i_length |
930 | 0 | ? vlc_tick_from_sec( ceil( sqrt( SEC_FROM_VLC_TICK( p_sys->i_length ) ) / 2 ) ) |
931 | 0 | : vlc_tick_from_sec( 5 ); |
932 | 0 | if ( i_pagepos >= p_stream->i_data_start && ( i_sync_time - i_lower_index >= index_interval ) ) |
933 | 0 | OggSeek_IndexAdd( p_stream, i_sync_time, i_pagepos ); |
934 | |
|
935 | 0 | OggDebug( msg_Dbg( p_demux, "=================== Seeked To %"PRId64" time %"PRId64, i_pagepos, i_time ) ); |
936 | 0 | return i_pagepos; |
937 | 0 | } |
938 | | |
939 | | /**************************************************************************** |
940 | | * oggseek_read_page: Read a full Ogg page from the physical bitstream. |
941 | | **************************************************************************** |
942 | | * Returns number of bytes read. This should always be > 0 |
943 | | * unless we are at the end of stream. |
944 | | * |
945 | | ****************************************************************************/ |
946 | | |
947 | | int64_t oggseek_read_page( demux_t *p_demux ) |
948 | 0 | { |
949 | 0 | demux_sys_t *p_ogg = p_demux->p_sys ; |
950 | 0 | uint8_t header[PAGE_HEADER_BYTES+255]; |
951 | 0 | int i_nsegs; |
952 | 0 | int i; |
953 | 0 | int64_t i_in_pos; |
954 | 0 | int64_t i_result; |
955 | 0 | int i_page_size; |
956 | 0 | char *buf; |
957 | |
|
958 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
959 | | |
960 | | /* store position of this page */ |
961 | 0 | i_in_pos = p_ogg->i_input_position = vlc_stream_Tell( p_demux->s ); |
962 | |
|
963 | 0 | if ( p_sys->b_page_waiting) { |
964 | 0 | msg_Warn( p_demux, "Ogg page already loaded" ); |
965 | 0 | return 0; |
966 | 0 | } |
967 | | |
968 | 0 | if ( vlc_stream_Read ( p_demux->s, header, PAGE_HEADER_BYTES ) < PAGE_HEADER_BYTES ) |
969 | 0 | { |
970 | 0 | vlc_stream_Seek( p_demux->s, i_in_pos ); |
971 | 0 | msg_Dbg ( p_demux, "Reached clean EOF in ogg file" ); |
972 | 0 | return 0; |
973 | 0 | } |
974 | | |
975 | 0 | i_nsegs = header[ PAGE_HEADER_BYTES - 1 ]; |
976 | |
|
977 | 0 | if ( vlc_stream_Read ( p_demux->s, header+PAGE_HEADER_BYTES, i_nsegs ) < i_nsegs ) |
978 | 0 | { |
979 | 0 | vlc_stream_Seek( p_demux->s, i_in_pos ); |
980 | 0 | msg_Warn ( p_demux, "Reached broken EOF in ogg file" ); |
981 | 0 | return 0; |
982 | 0 | } |
983 | | |
984 | 0 | i_page_size = PAGE_HEADER_BYTES + i_nsegs; |
985 | |
|
986 | 0 | for ( i = 0; i < i_nsegs; i++ ) |
987 | 0 | { |
988 | 0 | i_page_size += header[ PAGE_HEADER_BYTES + i ]; |
989 | 0 | } |
990 | |
|
991 | 0 | ogg_sync_reset( &p_ogg->oy ); |
992 | |
|
993 | 0 | buf = ogg_sync_buffer( &p_ogg->oy, i_page_size ); |
994 | 0 | if( !buf ) |
995 | 0 | return 0; |
996 | | |
997 | 0 | memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs ); |
998 | |
|
999 | 0 | i_result = vlc_stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs, |
1000 | 0 | i_page_size - PAGE_HEADER_BYTES - i_nsegs ); |
1001 | |
|
1002 | 0 | ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs ); |
1003 | | |
1004 | | |
1005 | | |
1006 | |
|
1007 | 0 | if ( ogg_sync_pageout( &p_ogg->oy, &p_ogg->current_page ) != 1 ) |
1008 | 0 | { |
1009 | 0 | msg_Err( p_demux , "Got invalid packet, read %"PRId64" of %i: %s %"PRId64, |
1010 | 0 | i_result, i_page_size, buf, i_in_pos ); |
1011 | 0 | return 0; |
1012 | 0 | } |
1013 | | |
1014 | 0 | return i_result + PAGE_HEADER_BYTES + i_nsegs; |
1015 | 0 | } |
1016 | | |