Mercurial > libdvdread4.hg
comparison searching.c @ 0:427b7da5cbdb src
first split of dvdread; it's just a copy of dvdnav still to be cleaned
author | nicodvb |
---|---|
date | Sun, 01 Jun 2008 08:39:07 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:427b7da5cbdb |
---|---|
1 /* | |
2 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net> | |
3 * | |
4 * This file is part of libdvdnav, a DVD navigation library. | |
5 * | |
6 * libdvdnav is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * libdvdnav is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | |
19 * | |
20 * $Id$ | |
21 * | |
22 */ | |
23 | |
24 #ifdef HAVE_CONFIG_H | |
25 #include "config.h" | |
26 #endif | |
27 | |
28 #include <assert.h> | |
29 #include <inttypes.h> | |
30 #include <limits.h> | |
31 #include <stdio.h> | |
32 #include <string.h> | |
33 #include <stdlib.h> | |
34 #include <sys/time.h> | |
35 #include "dvd_types.h" | |
36 #include "nav_types.h" | |
37 #include "ifo_types.h" | |
38 #include "remap.h" | |
39 #include "vm/decoder.h" | |
40 #include "vm/vm.h" | |
41 #include "dvdnav.h" | |
42 #include "dvdnav_internal.h" | |
43 | |
44 /* | |
45 #define LOG_DEBUG | |
46 */ | |
47 | |
48 /* Searching API calls */ | |
49 | |
50 /* Scan the ADMAP for a particular block number. */ | |
51 /* Return placed in vobu. */ | |
52 /* Returns error status */ | |
53 /* FIXME: Maybe need to handle seeking outside current cell. */ | |
54 static dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, uint32_t seekto_block, uint32_t *vobu) { | |
55 vobu_admap_t *admap = NULL; | |
56 | |
57 #ifdef LOG_DEBUG | |
58 fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", seekto_block); | |
59 #endif | |
60 *vobu = -1; | |
61 | |
62 /* Search through the VOBU_ADMAP for the nearest VOBU | |
63 * to the target block */ | |
64 switch(domain) { | |
65 case FP_DOMAIN: | |
66 case VMGM_DOMAIN: | |
67 admap = this->vm->vmgi->menu_vobu_admap; | |
68 break; | |
69 case VTSM_DOMAIN: | |
70 admap = this->vm->vtsi->menu_vobu_admap; | |
71 break; | |
72 case VTS_DOMAIN: | |
73 admap = this->vm->vtsi->vts_vobu_admap; | |
74 break; | |
75 default: | |
76 fprintf(MSG_OUT, "libdvdnav: Error: Unknown domain for seeking.\n"); | |
77 } | |
78 if(admap) { | |
79 uint32_t address = 0; | |
80 uint32_t vobu_start, next_vobu; | |
81 int admap_entries = (admap->last_byte + 1 - VOBU_ADMAP_SIZE)/VOBU_ADMAP_SIZE; | |
82 | |
83 /* Search through ADMAP for best sector */ | |
84 vobu_start = SRI_END_OF_CELL; | |
85 /* FIXME: Implement a faster search algorithm */ | |
86 while(address < admap_entries) { | |
87 next_vobu = admap->vobu_start_sectors[address]; | |
88 | |
89 /* fprintf(MSG_OUT, "libdvdnav: Found block %u\n", next_vobu); */ | |
90 | |
91 if(vobu_start <= seekto_block && next_vobu > seekto_block) | |
92 break; | |
93 vobu_start = next_vobu; | |
94 address++; | |
95 } | |
96 *vobu = vobu_start; | |
97 return DVDNAV_STATUS_OK; | |
98 } | |
99 fprintf(MSG_OUT, "libdvdnav: admap not located\n"); | |
100 return DVDNAV_STATUS_ERR; | |
101 } | |
102 | |
103 /* FIXME: right now, this function does not use the time tables but interpolates | |
104 only the cell times */ | |
105 dvdnav_status_t dvdnav_time_search(dvdnav_t *this, | |
106 uint64_t time) { | |
107 | |
108 uint64_t target = time; | |
109 uint64_t length = 0; | |
110 uint32_t first_cell_nr, last_cell_nr, cell_nr; | |
111 int32_t found; | |
112 cell_playback_t *cell; | |
113 dvd_state_t *state; | |
114 | |
115 if(this->position_current.still != 0) { | |
116 printerr("Cannot seek in a still frame."); | |
117 return DVDNAV_STATUS_ERR; | |
118 } | |
119 | |
120 pthread_mutex_lock(&this->vm_lock); | |
121 state = &(this->vm->state); | |
122 if(!state->pgc) { | |
123 printerr("No current PGC."); | |
124 pthread_mutex_unlock(&this->vm_lock); | |
125 return DVDNAV_STATUS_ERR; | |
126 } | |
127 | |
128 | |
129 this->cur_cell_time = 0; | |
130 if (this->pgc_based) { | |
131 first_cell_nr = 1; | |
132 last_cell_nr = state->pgc->nr_of_cells; | |
133 } else { | |
134 /* Find start cell of program. */ | |
135 first_cell_nr = state->pgc->program_map[state->pgN-1]; | |
136 /* Find end cell of program */ | |
137 if(state->pgN < state->pgc->nr_of_programs) | |
138 last_cell_nr = state->pgc->program_map[state->pgN] - 1; | |
139 else | |
140 last_cell_nr = state->pgc->nr_of_cells; | |
141 } | |
142 | |
143 found = 0; | |
144 for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) { | |
145 cell = &(state->pgc->cell_playback[cell_nr-1]); | |
146 if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL) | |
147 continue; | |
148 length = dvdnav_convert_time(&cell->playback_time); | |
149 if (target >= length) { | |
150 target -= length; | |
151 } else { | |
152 /* FIXME: there must be a better way than interpolation */ | |
153 target = target * (cell->last_sector - cell->first_sector + 1) / length; | |
154 target += cell->first_sector; | |
155 | |
156 found = 1; | |
157 break; | |
158 } | |
159 } | |
160 | |
161 if(found) { | |
162 uint32_t vobu; | |
163 #ifdef LOG_DEBUG | |
164 fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n", | |
165 cell_nr, first_cell_nr, last_cell_nr); | |
166 #endif | |
167 if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) { | |
168 uint32_t start = state->pgc->cell_playback[cell_nr-1].first_sector; | |
169 | |
170 if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) { | |
171 #ifdef LOG_DEBUG | |
172 fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" , | |
173 state->cellN, state->blockN, target, vobu, start); | |
174 #endif | |
175 this->vm->hop_channel += HOP_SEEK; | |
176 pthread_mutex_unlock(&this->vm_lock); | |
177 return DVDNAV_STATUS_OK; | |
178 } | |
179 } | |
180 } | |
181 | |
182 fprintf(MSG_OUT, "libdvdnav: Error when seeking\n"); | |
183 printerr("Error when seeking."); | |
184 pthread_mutex_unlock(&this->vm_lock); | |
185 return DVDNAV_STATUS_ERR; | |
186 } | |
187 | |
188 dvdnav_status_t dvdnav_sector_search(dvdnav_t *this, | |
189 uint64_t offset, int32_t origin) { | |
190 uint32_t target = 0; | |
191 uint32_t length = 0; | |
192 uint32_t first_cell_nr, last_cell_nr, cell_nr; | |
193 int32_t found; | |
194 cell_playback_t *cell; | |
195 dvd_state_t *state; | |
196 dvdnav_status_t result; | |
197 | |
198 if(this->position_current.still != 0) { | |
199 printerr("Cannot seek in a still frame."); | |
200 return DVDNAV_STATUS_ERR; | |
201 } | |
202 | |
203 result = dvdnav_get_position(this, &target, &length); | |
204 if(!result) { | |
205 return DVDNAV_STATUS_ERR; | |
206 } | |
207 | |
208 pthread_mutex_lock(&this->vm_lock); | |
209 state = &(this->vm->state); | |
210 if(!state->pgc) { | |
211 printerr("No current PGC."); | |
212 pthread_mutex_unlock(&this->vm_lock); | |
213 return DVDNAV_STATUS_ERR; | |
214 } | |
215 #ifdef LOG_DEBUG | |
216 fprintf(MSG_OUT, "libdvdnav: seeking to offset=%lu pos=%u length=%u\n", offset, target, length); | |
217 fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN); | |
218 #endif | |
219 | |
220 switch(origin) { | |
221 case SEEK_SET: | |
222 if(offset >= length) { | |
223 printerr("Request to seek behind end."); | |
224 pthread_mutex_unlock(&this->vm_lock); | |
225 return DVDNAV_STATUS_ERR; | |
226 } | |
227 target = offset; | |
228 break; | |
229 case SEEK_CUR: | |
230 if(target + offset >= length) { | |
231 printerr("Request to seek behind end."); | |
232 pthread_mutex_unlock(&this->vm_lock); | |
233 return DVDNAV_STATUS_ERR; | |
234 } | |
235 target += offset; | |
236 break; | |
237 case SEEK_END: | |
238 if(length < offset) { | |
239 printerr("Request to seek before start."); | |
240 pthread_mutex_unlock(&this->vm_lock); | |
241 return DVDNAV_STATUS_ERR; | |
242 } | |
243 target = length - offset; | |
244 break; | |
245 default: | |
246 /* Error occured */ | |
247 printerr("Illegal seek mode."); | |
248 pthread_mutex_unlock(&this->vm_lock); | |
249 return DVDNAV_STATUS_ERR; | |
250 } | |
251 | |
252 this->cur_cell_time = 0; | |
253 if (this->pgc_based) { | |
254 first_cell_nr = 1; | |
255 last_cell_nr = state->pgc->nr_of_cells; | |
256 } else { | |
257 /* Find start cell of program. */ | |
258 first_cell_nr = state->pgc->program_map[state->pgN-1]; | |
259 /* Find end cell of program */ | |
260 if(state->pgN < state->pgc->nr_of_programs) | |
261 last_cell_nr = state->pgc->program_map[state->pgN] - 1; | |
262 else | |
263 last_cell_nr = state->pgc->nr_of_cells; | |
264 } | |
265 | |
266 found = 0; | |
267 for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) { | |
268 cell = &(state->pgc->cell_playback[cell_nr-1]); | |
269 if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL) | |
270 continue; | |
271 length = cell->last_sector - cell->first_sector + 1; | |
272 if (target >= length) { | |
273 target -= length; | |
274 } else { | |
275 /* convert the target sector from Cell-relative to absolute physical sector */ | |
276 target += cell->first_sector; | |
277 found = 1; | |
278 break; | |
279 } | |
280 } | |
281 | |
282 if(found) { | |
283 int32_t vobu; | |
284 #ifdef LOG_DEBUG | |
285 fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n", | |
286 cell_nr, first_cell_nr, last_cell_nr); | |
287 #endif | |
288 if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) { | |
289 int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector; | |
290 | |
291 if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) { | |
292 #ifdef LOG_DEBUG | |
293 fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" , | |
294 state->cellN, state->blockN, target, vobu, start); | |
295 #endif | |
296 this->vm->hop_channel += HOP_SEEK; | |
297 pthread_mutex_unlock(&this->vm_lock); | |
298 return DVDNAV_STATUS_OK; | |
299 } | |
300 } | |
301 } | |
302 | |
303 fprintf(MSG_OUT, "libdvdnav: Error when seeking\n"); | |
304 fprintf(MSG_OUT, "libdvdnav: FIXME: Implement seeking to location %u\n", target); | |
305 printerr("Error when seeking."); | |
306 pthread_mutex_unlock(&this->vm_lock); | |
307 return DVDNAV_STATUS_ERR; | |
308 } | |
309 | |
310 dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int32_t part) { | |
311 int32_t title, old_part; | |
312 | |
313 if (dvdnav_current_title_info(this, &title, &old_part) == DVDNAV_STATUS_OK) | |
314 return dvdnav_part_play(this, title, part); | |
315 return DVDNAV_STATUS_ERR; | |
316 } | |
317 | |
318 dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) { | |
319 pthread_mutex_lock(&this->vm_lock); | |
320 if(!this->vm->state.pgc) { | |
321 printerr("No current PGC."); | |
322 pthread_mutex_unlock(&this->vm_lock); | |
323 return DVDNAV_STATUS_ERR; | |
324 } | |
325 | |
326 #ifdef LOG_DEBUG | |
327 fprintf(MSG_OUT, "libdvdnav: previous chapter\n"); | |
328 #endif | |
329 if (!vm_jump_prev_pg(this->vm)) { | |
330 fprintf(MSG_OUT, "libdvdnav: previous chapter failed.\n"); | |
331 printerr("Skip to previous chapter failed."); | |
332 pthread_mutex_unlock(&this->vm_lock); | |
333 return DVDNAV_STATUS_ERR; | |
334 } | |
335 this->cur_cell_time = 0; | |
336 this->position_current.still = 0; | |
337 this->vm->hop_channel++; | |
338 #ifdef LOG_DEBUG | |
339 fprintf(MSG_OUT, "libdvdnav: previous chapter done\n"); | |
340 #endif | |
341 pthread_mutex_unlock(&this->vm_lock); | |
342 | |
343 return DVDNAV_STATUS_OK; | |
344 } | |
345 | |
346 dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) { | |
347 pthread_mutex_lock(&this->vm_lock); | |
348 if(!this->vm->state.pgc) { | |
349 printerr("No current PGC."); | |
350 pthread_mutex_unlock(&this->vm_lock); | |
351 return DVDNAV_STATUS_ERR; | |
352 } | |
353 | |
354 #ifdef LOG_DEBUG | |
355 fprintf(MSG_OUT, "libdvdnav: top chapter\n"); | |
356 #endif | |
357 if (!vm_jump_top_pg(this->vm)) { | |
358 fprintf(MSG_OUT, "libdvdnav: top chapter failed.\n"); | |
359 printerr("Skip to top chapter failed."); | |
360 pthread_mutex_unlock(&this->vm_lock); | |
361 return DVDNAV_STATUS_ERR; | |
362 } | |
363 this->cur_cell_time = 0; | |
364 this->position_current.still = 0; | |
365 this->vm->hop_channel++; | |
366 #ifdef LOG_DEBUG | |
367 fprintf(MSG_OUT, "libdvdnav: top chapter done\n"); | |
368 #endif | |
369 pthread_mutex_unlock(&this->vm_lock); | |
370 | |
371 return DVDNAV_STATUS_OK; | |
372 } | |
373 | |
374 dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) { | |
375 vm_t *try_vm; | |
376 | |
377 pthread_mutex_lock(&this->vm_lock); | |
378 if(!this->vm->state.pgc) { | |
379 printerr("No current PGC."); | |
380 pthread_mutex_unlock(&this->vm_lock); | |
381 return DVDNAV_STATUS_ERR; | |
382 } | |
383 | |
384 #ifdef LOG_DEBUG | |
385 fprintf(MSG_OUT, "libdvdnav: next chapter\n"); | |
386 #endif | |
387 /* make a copy of current VM and try to navigate the copy to the next PG */ | |
388 try_vm = vm_new_copy(this->vm); | |
389 if (!vm_jump_next_pg(try_vm) || try_vm->stopped) { | |
390 vm_free_copy(try_vm); | |
391 /* next_pg failed, try to jump at least to the next cell */ | |
392 try_vm = vm_new_copy(this->vm); | |
393 vm_get_next_cell(try_vm); | |
394 if (try_vm->stopped) { | |
395 vm_free_copy(try_vm); | |
396 fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n"); | |
397 printerr("Skip to next chapter failed."); | |
398 pthread_mutex_unlock(&this->vm_lock); | |
399 return DVDNAV_STATUS_ERR; | |
400 } | |
401 } | |
402 this->cur_cell_time = 0; | |
403 /* merge changes on success */ | |
404 vm_merge(this->vm, try_vm); | |
405 vm_free_copy(try_vm); | |
406 this->position_current.still = 0; | |
407 this->vm->hop_channel++; | |
408 #ifdef LOG_DEBUG | |
409 fprintf(MSG_OUT, "libdvdnav: next chapter done\n"); | |
410 #endif | |
411 pthread_mutex_unlock(&this->vm_lock); | |
412 | |
413 return DVDNAV_STATUS_OK; | |
414 } | |
415 | |
416 dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) { | |
417 vm_t *try_vm; | |
418 | |
419 pthread_mutex_lock(&this->vm_lock); | |
420 if(!this->vm->state.pgc) { | |
421 printerr("No current PGC."); | |
422 pthread_mutex_unlock(&this->vm_lock); | |
423 return DVDNAV_STATUS_ERR; | |
424 } | |
425 | |
426 this->cur_cell_time = 0; | |
427 /* make a copy of current VM and try to navigate the copy to the menu */ | |
428 try_vm = vm_new_copy(this->vm); | |
429 if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != VTS_DOMAIN)) { | |
430 /* Try resume */ | |
431 if (vm_jump_resume(try_vm) && !try_vm->stopped) { | |
432 /* merge changes on success */ | |
433 vm_merge(this->vm, try_vm); | |
434 vm_free_copy(try_vm); | |
435 this->position_current.still = 0; | |
436 this->vm->hop_channel++; | |
437 pthread_mutex_unlock(&this->vm_lock); | |
438 return DVDNAV_STATUS_OK; | |
439 } | |
440 } | |
441 if (menu == DVD_MENU_Escape) menu = DVD_MENU_Root; | |
442 | |
443 if (vm_jump_menu(try_vm, menu) && !try_vm->stopped) { | |
444 /* merge changes on success */ | |
445 vm_merge(this->vm, try_vm); | |
446 vm_free_copy(try_vm); | |
447 this->position_current.still = 0; | |
448 this->vm->hop_channel++; | |
449 pthread_mutex_unlock(&this->vm_lock); | |
450 return DVDNAV_STATUS_OK; | |
451 } else { | |
452 vm_free_copy(try_vm); | |
453 printerr("No such menu or menu not reachable."); | |
454 pthread_mutex_unlock(&this->vm_lock); | |
455 return DVDNAV_STATUS_ERR; | |
456 } | |
457 } | |
458 | |
459 dvdnav_status_t dvdnav_get_position(dvdnav_t *this, uint32_t *pos, | |
460 uint32_t *len) { | |
461 uint32_t cur_sector; | |
462 int32_t cell_nr, first_cell_nr, last_cell_nr; | |
463 cell_playback_t *cell; | |
464 dvd_state_t *state; | |
465 | |
466 if(!this->started) { | |
467 printerr("Virtual DVD machine not started."); | |
468 return DVDNAV_STATUS_ERR; | |
469 } | |
470 | |
471 pthread_mutex_lock(&this->vm_lock); | |
472 state = &(this->vm->state); | |
473 if(!state->pgc || this->vm->stopped) { | |
474 printerr("No current PGC."); | |
475 pthread_mutex_unlock(&this->vm_lock); | |
476 return DVDNAV_STATUS_ERR; | |
477 } | |
478 if (this->position_current.hop_channel != this->vm->hop_channel || | |
479 this->position_current.domain != state->domain || | |
480 this->position_current.vts != state->vtsN || | |
481 this->position_current.cell_restart != state->cell_restart) { | |
482 printerr("New position not yet determined."); | |
483 pthread_mutex_unlock(&this->vm_lock); | |
484 return DVDNAV_STATUS_ERR; | |
485 } | |
486 | |
487 /* Get current sector */ | |
488 cur_sector = this->vobu.vobu_start + this->vobu.blockN; | |
489 | |
490 if (this->pgc_based) { | |
491 first_cell_nr = 1; | |
492 last_cell_nr = state->pgc->nr_of_cells; | |
493 } else { | |
494 /* Find start cell of program. */ | |
495 first_cell_nr = state->pgc->program_map[state->pgN-1]; | |
496 /* Find end cell of program */ | |
497 if(state->pgN < state->pgc->nr_of_programs) | |
498 last_cell_nr = state->pgc->program_map[state->pgN] - 1; | |
499 else | |
500 last_cell_nr = state->pgc->nr_of_cells; | |
501 } | |
502 | |
503 *pos = -1; | |
504 *len = 0; | |
505 for (cell_nr = first_cell_nr; cell_nr <= last_cell_nr; cell_nr++) { | |
506 cell = &(state->pgc->cell_playback[cell_nr-1]); | |
507 if (cell_nr == state->cellN) { | |
508 /* the current sector is in this cell, | |
509 * pos is length of PG up to here + sector's offset in this cell */ | |
510 *pos = *len + cur_sector - cell->first_sector; | |
511 } | |
512 *len += cell->last_sector - cell->first_sector + 1; | |
513 } | |
514 | |
515 assert((signed)*pos != -1); | |
516 | |
517 pthread_mutex_unlock(&this->vm_lock); | |
518 | |
519 return DVDNAV_STATUS_OK; | |
520 } | |
521 | |
522 dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this, | |
523 uint32_t *pos, | |
524 uint32_t *len) { | |
525 uint32_t cur_sector; | |
526 uint32_t first_cell_nr; | |
527 uint32_t last_cell_nr; | |
528 cell_playback_t *first_cell; | |
529 cell_playback_t *last_cell; | |
530 dvd_state_t *state; | |
531 | |
532 state = &(this->vm->state); | |
533 if(!state->pgc) { | |
534 printerr("No current PGC."); | |
535 return DVDNAV_STATUS_ERR; | |
536 } | |
537 | |
538 /* Get current sector */ | |
539 cur_sector = this->vobu.vobu_start + this->vobu.blockN; | |
540 | |
541 /* Now find first and last cells in title. */ | |
542 first_cell_nr = state->pgc->program_map[0]; | |
543 first_cell = &(state->pgc->cell_playback[first_cell_nr-1]); | |
544 last_cell_nr = state->pgc->nr_of_cells; | |
545 last_cell = &(state->pgc->cell_playback[last_cell_nr-1]); | |
546 | |
547 *pos = cur_sector - first_cell->first_sector; | |
548 *len = last_cell->last_sector - first_cell->first_sector; | |
549 | |
550 return DVDNAV_STATUS_OK; | |
551 } | |
552 | |
553 uint32_t dvdnav_describe_title_chapters(dvdnav_t *this, int32_t title, uint64_t **times, uint64_t *duration) { | |
554 int32_t retval=0; | |
555 uint16_t parts, i; | |
556 title_info_t *ptitle = NULL; | |
557 ptt_info_t *ptt = NULL; | |
558 ifo_handle_t *ifo; | |
559 pgc_t *pgc; | |
560 cell_playback_t *cell; | |
561 uint64_t length, *tmp=NULL; | |
562 | |
563 *times = NULL; | |
564 *duration = 0; | |
565 pthread_mutex_lock(&this->vm_lock); | |
566 if(!this->vm->vmgi) { | |
567 printerr("Bad VM state or missing VTSI."); | |
568 goto fail; | |
569 } | |
570 if(!this->started) { | |
571 /* don't report an error but be nice */ | |
572 vm_start(this->vm); | |
573 this->started = 1; | |
574 } | |
575 ifo = vm_get_title_ifo(this->vm, title); | |
576 if(!ifo || !ifo->vts_pgcit) { | |
577 printerr("Couldn't open IFO for chosen title, exit."); | |
578 goto fail; | |
579 } | |
580 | |
581 ptitle = &this->vm->vmgi->tt_srpt->title[title-1]; | |
582 parts = ptitle->nr_of_ptts; | |
583 ptt = ifo->vts_ptt_srpt->title[ptitle->vts_ttn-1].ptt; | |
584 | |
585 tmp = calloc(1, sizeof(uint64_t)*parts); | |
586 if(!tmp) | |
587 goto fail; | |
588 | |
589 length = 0; | |
590 for(i=0; i<parts; i++) { | |
591 uint32_t cellnr, endcellnr; | |
592 pgc = ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc; | |
593 if(ptt[i].pgn > pgc->nr_of_programs) { | |
594 printerr("WRONG part number."); | |
595 goto fail; | |
596 } | |
597 | |
598 cellnr = pgc->program_map[ptt[i].pgn-1]; | |
599 if(ptt[i].pgn < pgc->nr_of_programs) | |
600 endcellnr = pgc->program_map[ptt[i].pgn]; | |
601 else | |
602 endcellnr = 0; | |
603 | |
604 do { | |
605 cell = &pgc->cell_playback[cellnr-1]; | |
606 if(!(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && | |
607 cell->block_mode != BLOCK_MODE_FIRST_CELL | |
608 )) | |
609 { | |
610 tmp[i] = length + dvdnav_convert_time(&cell->playback_time); | |
611 length = tmp[i]; | |
612 } | |
613 cellnr++; | |
614 } while(cellnr < endcellnr); | |
615 } | |
616 *duration = length; | |
617 vm_ifo_close(ifo); | |
618 retval = parts; | |
619 *times = tmp; | |
620 | |
621 fail: | |
622 pthread_mutex_unlock(&this->vm_lock); | |
623 if(!retval && tmp) | |
624 free(tmp); | |
625 return retval; | |
626 } |