Fix vf_tcdump's compilation
[mplayer/kovensky.git] / libdvdnav / searching.c
blobaead0c33991a1a1992cd8ef65771a46dab241428
1 /*
2 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
4 * This file is part of libdvdnav, a DVD navigation library.
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.
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.
16 * You should have received a copy of the GNU General Public License along
17 * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <assert.h>
26 #include <inttypes.h>
27 #include <limits.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <sys/time.h>
32 #include "dvdnav/dvdnav.h"
33 #include <dvdread/nav_types.h>
34 #include <dvdread/ifo_types.h>
35 #include "remap.h"
36 #include "vm/decoder.h"
37 #include "vm/vm.h"
38 #include "dvdnav_internal.h"
41 #define LOG_DEBUG
44 /* Searching API calls */
46 /* Scan the ADMAP for a particular block number. */
47 /* Return placed in vobu. */
48 /* Returns error status */
49 /* FIXME: Maybe need to handle seeking outside current cell. */
50 static dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, uint32_t seekto_block, uint32_t *vobu) {
51 vobu_admap_t *admap = NULL;
53 #ifdef LOG_DEBUG
54 fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", seekto_block);
55 #endif
56 *vobu = -1;
58 /* Search through the VOBU_ADMAP for the nearest VOBU
59 * to the target block */
60 switch(domain) {
61 case FP_DOMAIN:
62 case VMGM_DOMAIN:
63 admap = this->vm->vmgi->menu_vobu_admap;
64 break;
65 case VTSM_DOMAIN:
66 admap = this->vm->vtsi->menu_vobu_admap;
67 break;
68 case VTS_DOMAIN:
69 admap = this->vm->vtsi->vts_vobu_admap;
70 break;
71 default:
72 fprintf(MSG_OUT, "libdvdnav: Error: Unknown domain for seeking.\n");
74 if(admap) {
75 uint32_t address = 0;
76 uint32_t vobu_start, next_vobu;
77 int admap_entries = (admap->last_byte + 1 - VOBU_ADMAP_SIZE)/VOBU_ADMAP_SIZE;
79 /* Search through ADMAP for best sector */
80 vobu_start = SRI_END_OF_CELL;
81 /* FIXME: Implement a faster search algorithm */
82 while(address < admap_entries) {
83 next_vobu = admap->vobu_start_sectors[address];
85 /* fprintf(MSG_OUT, "libdvdnav: Found block %u\n", next_vobu); */
87 if(vobu_start <= seekto_block && next_vobu > seekto_block)
88 break;
89 vobu_start = next_vobu;
90 address++;
92 *vobu = vobu_start;
93 return DVDNAV_STATUS_OK;
95 fprintf(MSG_OUT, "libdvdnav: admap not located\n");
96 return DVDNAV_STATUS_ERR;
99 /* FIXME: right now, this function does not use the time tables but interpolates
100 only the cell times */
101 dvdnav_status_t dvdnav_time_search(dvdnav_t *this,
102 uint64_t time) {
104 uint64_t target = time;
105 uint64_t length = 0;
106 uint32_t first_cell_nr, last_cell_nr, cell_nr;
107 int32_t found;
108 cell_playback_t *cell;
109 dvd_state_t *state;
111 if(this->position_current.still != 0) {
112 printerr("Cannot seek in a still frame.");
113 return DVDNAV_STATUS_ERR;
116 pthread_mutex_lock(&this->vm_lock);
117 state = &(this->vm->state);
118 if(!state->pgc) {
119 printerr("No current PGC.");
120 pthread_mutex_unlock(&this->vm_lock);
121 return DVDNAV_STATUS_ERR;
125 this->cur_cell_time = 0;
126 if (this->pgc_based) {
127 first_cell_nr = 1;
128 last_cell_nr = state->pgc->nr_of_cells;
129 } else {
130 /* Find start cell of program. */
131 first_cell_nr = state->pgc->program_map[state->pgN-1];
132 /* Find end cell of program */
133 if(state->pgN < state->pgc->nr_of_programs)
134 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
135 else
136 last_cell_nr = state->pgc->nr_of_cells;
139 found = 0;
140 for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
141 cell = &(state->pgc->cell_playback[cell_nr-1]);
142 if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
143 continue;
144 length = dvdnav_convert_time(&cell->playback_time);
145 if (target >= length) {
146 target -= length;
147 } else {
148 /* FIXME: there must be a better way than interpolation */
149 target = target * (cell->last_sector - cell->first_sector + 1) / length;
150 target += cell->first_sector;
152 found = 1;
153 break;
157 if(found) {
158 uint32_t vobu;
159 #ifdef LOG_DEBUG
160 fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
161 cell_nr, first_cell_nr, last_cell_nr);
162 #endif
163 if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) {
164 uint32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
166 if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
167 #ifdef LOG_DEBUG
168 fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
169 state->cellN, state->blockN, target, vobu, start);
170 #endif
171 this->vm->hop_channel += HOP_SEEK;
172 pthread_mutex_unlock(&this->vm_lock);
173 return DVDNAV_STATUS_OK;
178 fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
179 printerr("Error when seeking.");
180 pthread_mutex_unlock(&this->vm_lock);
181 return DVDNAV_STATUS_ERR;
184 dvdnav_status_t dvdnav_sector_search(dvdnav_t *this,
185 uint64_t offset, int32_t origin) {
186 uint32_t target = 0;
187 uint32_t length = 0;
188 uint32_t first_cell_nr, last_cell_nr, cell_nr;
189 int32_t found;
190 cell_playback_t *cell;
191 dvd_state_t *state;
192 dvdnav_status_t result;
194 if(this->position_current.still != 0) {
195 printerr("Cannot seek in a still frame.");
196 return DVDNAV_STATUS_ERR;
199 result = dvdnav_get_position(this, &target, &length);
200 if(!result) {
201 return DVDNAV_STATUS_ERR;
204 pthread_mutex_lock(&this->vm_lock);
205 state = &(this->vm->state);
206 if(!state->pgc) {
207 printerr("No current PGC.");
208 pthread_mutex_unlock(&this->vm_lock);
209 return DVDNAV_STATUS_ERR;
211 #ifdef LOG_DEBUG
212 fprintf(MSG_OUT, "libdvdnav: seeking to offset=%lu pos=%u length=%u\n", offset, target, length);
213 fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN);
214 #endif
216 switch(origin) {
217 case SEEK_SET:
218 if(offset >= length) {
219 printerr("Request to seek behind end.");
220 pthread_mutex_unlock(&this->vm_lock);
221 return DVDNAV_STATUS_ERR;
223 target = offset;
224 break;
225 case SEEK_CUR:
226 if(target + offset >= length) {
227 printerr("Request to seek behind end.");
228 pthread_mutex_unlock(&this->vm_lock);
229 return DVDNAV_STATUS_ERR;
231 target += offset;
232 break;
233 case SEEK_END:
234 if(length < offset) {
235 printerr("Request to seek before start.");
236 pthread_mutex_unlock(&this->vm_lock);
237 return DVDNAV_STATUS_ERR;
239 target = length - offset;
240 break;
241 default:
242 /* Error occured */
243 printerr("Illegal seek mode.");
244 pthread_mutex_unlock(&this->vm_lock);
245 return DVDNAV_STATUS_ERR;
248 this->cur_cell_time = 0;
249 if (this->pgc_based) {
250 first_cell_nr = 1;
251 last_cell_nr = state->pgc->nr_of_cells;
252 } else {
253 /* Find start cell of program. */
254 first_cell_nr = state->pgc->program_map[state->pgN-1];
255 /* Find end cell of program */
256 if(state->pgN < state->pgc->nr_of_programs)
257 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
258 else
259 last_cell_nr = state->pgc->nr_of_cells;
262 found = 0;
263 for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
264 cell = &(state->pgc->cell_playback[cell_nr-1]);
265 if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
266 continue;
267 length = cell->last_sector - cell->first_sector + 1;
268 if (target >= length) {
269 target -= length;
270 } else {
271 /* convert the target sector from Cell-relative to absolute physical sector */
272 target += cell->first_sector;
273 found = 1;
274 break;
278 if(found) {
279 uint32_t vobu;
280 #ifdef LOG_DEBUG
281 fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
282 cell_nr, first_cell_nr, last_cell_nr);
283 #endif
284 if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) {
285 int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
287 if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
288 #ifdef LOG_DEBUG
289 fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
290 state->cellN, state->blockN, target, vobu, start);
291 #endif
292 this->vm->hop_channel += HOP_SEEK;
293 pthread_mutex_unlock(&this->vm_lock);
294 return DVDNAV_STATUS_OK;
299 fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
300 fprintf(MSG_OUT, "libdvdnav: FIXME: Implement seeking to location %u\n", target);
301 printerr("Error when seeking.");
302 pthread_mutex_unlock(&this->vm_lock);
303 return DVDNAV_STATUS_ERR;
306 dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int32_t part) {
307 int32_t title, old_part;
309 if (dvdnav_current_title_info(this, &title, &old_part) == DVDNAV_STATUS_OK)
310 return dvdnav_part_play(this, title, part);
311 return DVDNAV_STATUS_ERR;
314 dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
315 pthread_mutex_lock(&this->vm_lock);
316 if(!this->vm->state.pgc) {
317 printerr("No current PGC.");
318 pthread_mutex_unlock(&this->vm_lock);
319 return DVDNAV_STATUS_ERR;
322 #ifdef LOG_DEBUG
323 fprintf(MSG_OUT, "libdvdnav: previous chapter\n");
324 #endif
325 if (!vm_jump_prev_pg(this->vm)) {
326 fprintf(MSG_OUT, "libdvdnav: previous chapter failed.\n");
327 printerr("Skip to previous chapter failed.");
328 pthread_mutex_unlock(&this->vm_lock);
329 return DVDNAV_STATUS_ERR;
331 this->cur_cell_time = 0;
332 this->position_current.still = 0;
333 this->vm->hop_channel++;
334 #ifdef LOG_DEBUG
335 fprintf(MSG_OUT, "libdvdnav: previous chapter done\n");
336 #endif
337 pthread_mutex_unlock(&this->vm_lock);
339 return DVDNAV_STATUS_OK;
342 dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
343 pthread_mutex_lock(&this->vm_lock);
344 if(!this->vm->state.pgc) {
345 printerr("No current PGC.");
346 pthread_mutex_unlock(&this->vm_lock);
347 return DVDNAV_STATUS_ERR;
350 #ifdef LOG_DEBUG
351 fprintf(MSG_OUT, "libdvdnav: top chapter\n");
352 #endif
353 if (!vm_jump_top_pg(this->vm)) {
354 fprintf(MSG_OUT, "libdvdnav: top chapter failed.\n");
355 printerr("Skip to top chapter failed.");
356 pthread_mutex_unlock(&this->vm_lock);
357 return DVDNAV_STATUS_ERR;
359 this->cur_cell_time = 0;
360 this->position_current.still = 0;
361 this->vm->hop_channel++;
362 #ifdef LOG_DEBUG
363 fprintf(MSG_OUT, "libdvdnav: top chapter done\n");
364 #endif
365 pthread_mutex_unlock(&this->vm_lock);
367 return DVDNAV_STATUS_OK;
370 dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
371 vm_t *try_vm;
373 pthread_mutex_lock(&this->vm_lock);
374 if(!this->vm->state.pgc) {
375 printerr("No current PGC.");
376 pthread_mutex_unlock(&this->vm_lock);
377 return DVDNAV_STATUS_ERR;
380 #ifdef LOG_DEBUG
381 fprintf(MSG_OUT, "libdvdnav: next chapter\n");
382 #endif
383 /* make a copy of current VM and try to navigate the copy to the next PG */
384 try_vm = vm_new_copy(this->vm);
385 if (!vm_jump_next_pg(try_vm) || try_vm->stopped) {
386 vm_free_copy(try_vm);
387 /* next_pg failed, try to jump at least to the next cell */
388 try_vm = vm_new_copy(this->vm);
389 vm_get_next_cell(try_vm);
390 if (try_vm->stopped) {
391 vm_free_copy(try_vm);
392 fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n");
393 printerr("Skip to next chapter failed.");
394 pthread_mutex_unlock(&this->vm_lock);
395 return DVDNAV_STATUS_ERR;
398 this->cur_cell_time = 0;
399 /* merge changes on success */
400 vm_merge(this->vm, try_vm);
401 vm_free_copy(try_vm);
402 this->position_current.still = 0;
403 this->vm->hop_channel++;
404 #ifdef LOG_DEBUG
405 fprintf(MSG_OUT, "libdvdnav: next chapter done\n");
406 #endif
407 pthread_mutex_unlock(&this->vm_lock);
409 return DVDNAV_STATUS_OK;
412 dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
413 vm_t *try_vm;
415 pthread_mutex_lock(&this->vm_lock);
416 if(!this->vm->state.pgc) {
417 printerr("No current PGC.");
418 pthread_mutex_unlock(&this->vm_lock);
419 return DVDNAV_STATUS_ERR;
422 this->cur_cell_time = 0;
423 /* make a copy of current VM and try to navigate the copy to the menu */
424 try_vm = vm_new_copy(this->vm);
425 if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != VTS_DOMAIN)) {
426 /* Try resume */
427 if (vm_jump_resume(try_vm) && !try_vm->stopped) {
428 /* merge changes on success */
429 vm_merge(this->vm, try_vm);
430 vm_free_copy(try_vm);
431 this->position_current.still = 0;
432 this->vm->hop_channel++;
433 pthread_mutex_unlock(&this->vm_lock);
434 return DVDNAV_STATUS_OK;
437 if (menu == DVD_MENU_Escape) menu = DVD_MENU_Root;
439 if (vm_jump_menu(try_vm, menu) && !try_vm->stopped) {
440 /* merge changes on success */
441 vm_merge(this->vm, try_vm);
442 vm_free_copy(try_vm);
443 this->position_current.still = 0;
444 this->vm->hop_channel++;
445 pthread_mutex_unlock(&this->vm_lock);
446 return DVDNAV_STATUS_OK;
447 } else {
448 vm_free_copy(try_vm);
449 printerr("No such menu or menu not reachable.");
450 pthread_mutex_unlock(&this->vm_lock);
451 return DVDNAV_STATUS_ERR;
455 dvdnav_status_t dvdnav_get_position(dvdnav_t *this, uint32_t *pos,
456 uint32_t *len) {
457 uint32_t cur_sector;
458 int32_t cell_nr, first_cell_nr, last_cell_nr;
459 cell_playback_t *cell;
460 dvd_state_t *state;
462 if(!this->started) {
463 printerr("Virtual DVD machine not started.");
464 return DVDNAV_STATUS_ERR;
467 pthread_mutex_lock(&this->vm_lock);
468 state = &(this->vm->state);
469 if(!state->pgc || this->vm->stopped) {
470 printerr("No current PGC.");
471 pthread_mutex_unlock(&this->vm_lock);
472 return DVDNAV_STATUS_ERR;
474 if (this->position_current.hop_channel != this->vm->hop_channel ||
475 this->position_current.domain != state->domain ||
476 this->position_current.vts != state->vtsN ||
477 this->position_current.cell_restart != state->cell_restart) {
478 printerr("New position not yet determined.");
479 pthread_mutex_unlock(&this->vm_lock);
480 return DVDNAV_STATUS_ERR;
483 /* Get current sector */
484 cur_sector = this->vobu.vobu_start + this->vobu.blockN;
486 if (this->pgc_based) {
487 first_cell_nr = 1;
488 last_cell_nr = state->pgc->nr_of_cells;
489 } else {
490 /* Find start cell of program. */
491 first_cell_nr = state->pgc->program_map[state->pgN-1];
492 /* Find end cell of program */
493 if(state->pgN < state->pgc->nr_of_programs)
494 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
495 else
496 last_cell_nr = state->pgc->nr_of_cells;
499 *pos = -1;
500 *len = 0;
501 for (cell_nr = first_cell_nr; cell_nr <= last_cell_nr; cell_nr++) {
502 cell = &(state->pgc->cell_playback[cell_nr-1]);
503 if (cell_nr == state->cellN) {
504 /* the current sector is in this cell,
505 * pos is length of PG up to here + sector's offset in this cell */
506 *pos = *len + cur_sector - cell->first_sector;
508 *len += cell->last_sector - cell->first_sector + 1;
511 assert((signed)*pos != -1);
513 pthread_mutex_unlock(&this->vm_lock);
515 return DVDNAV_STATUS_OK;
518 dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
519 uint32_t *pos,
520 uint32_t *len) {
521 uint32_t cur_sector;
522 uint32_t first_cell_nr;
523 uint32_t last_cell_nr;
524 cell_playback_t *first_cell;
525 cell_playback_t *last_cell;
526 dvd_state_t *state;
528 state = &(this->vm->state);
529 if(!state->pgc) {
530 printerr("No current PGC.");
531 return DVDNAV_STATUS_ERR;
534 /* Get current sector */
535 cur_sector = this->vobu.vobu_start + this->vobu.blockN;
537 /* Now find first and last cells in title. */
538 first_cell_nr = state->pgc->program_map[0];
539 first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
540 last_cell_nr = state->pgc->nr_of_cells;
541 last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
543 *pos = cur_sector - first_cell->first_sector;
544 *len = last_cell->last_sector - first_cell->first_sector;
546 return DVDNAV_STATUS_OK;
549 uint32_t dvdnav_describe_title_chapters(dvdnav_t *this, int32_t title, uint64_t **times, uint64_t *duration) {
550 int32_t retval=0;
551 uint16_t parts, i;
552 title_info_t *ptitle = NULL;
553 ptt_info_t *ptt = NULL;
554 ifo_handle_t *ifo = NULL;
555 pgc_t *pgc;
556 cell_playback_t *cell;
557 uint64_t length, *tmp=NULL;
559 *times = NULL;
560 *duration = 0;
561 pthread_mutex_lock(&this->vm_lock);
562 if(!this->vm->vmgi) {
563 printerr("Bad VM state or missing VTSI.");
564 goto fail;
566 if(!this->started) {
567 /* don't report an error but be nice */
568 vm_start(this->vm);
569 this->started = 1;
571 ifo = vm_get_title_ifo(this->vm, title);
572 if(!ifo || !ifo->vts_pgcit) {
573 printerr("Couldn't open IFO for chosen title, exit.");
574 retval = 0;
575 goto fail;
578 ptitle = &this->vm->vmgi->tt_srpt->title[title-1];
579 parts = ptitle->nr_of_ptts;
580 ptt = ifo->vts_ptt_srpt->title[ptitle->vts_ttn-1].ptt;
582 tmp = calloc(1, sizeof(uint64_t)*parts);
583 if(!tmp)
584 goto fail;
586 length = 0;
587 for(i=0; i<parts; i++) {
588 uint32_t cellnr, endcellnr;
589 pgc = ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc;
590 if(ptt[i].pgn > pgc->nr_of_programs) {
591 printerr("WRONG part number.");
592 goto fail;
595 cellnr = pgc->program_map[ptt[i].pgn-1];
596 if(ptt[i].pgn < pgc->nr_of_programs)
597 endcellnr = pgc->program_map[ptt[i].pgn];
598 else
599 endcellnr = 0;
601 do {
602 cell = &pgc->cell_playback[cellnr-1];
603 if(!(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK &&
604 cell->block_mode != BLOCK_MODE_FIRST_CELL
607 tmp[i] = length + dvdnav_convert_time(&cell->playback_time);
608 length = tmp[i];
610 cellnr++;
611 } while(cellnr < endcellnr);
613 *duration = length;
614 vm_ifo_close(ifo);
615 ifo = NULL;
616 retval = parts;
617 *times = tmp;
619 fail:
620 pthread_mutex_unlock(&this->vm_lock);
621 if(!retval && ifo)
622 vm_ifo_close(ifo);
623 if(!retval && tmp)
624 free(tmp);
625 return retval;