00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024
00025 #include <assert.h>
00026 #include <inttypes.h>
00027 #include <limits.h>
00028 #include <stdio.h>
00029 #include <string.h>
00030 #include <stdlib.h>
00031 #include <sys/time.h>
00032 #include "dvdnav/dvdnav.h"
00033 #include <dvdread/nav_types.h>
00034 #include <dvdread/ifo_types.h>
00035 #include "remap.h"
00036 #include "vm/decoder.h"
00037 #include "vm/vm.h"
00038 #include "dvdnav_internal.h"
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050 static dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, uint32_t seekto_block, uint32_t *vobu) {
00051 vobu_admap_t *admap = NULL;
00052
00053 #ifdef LOG_DEBUG
00054 fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", seekto_block);
00055 #endif
00056 *vobu = -1;
00057
00058
00059
00060 switch(domain) {
00061 case FP_DOMAIN:
00062 case VMGM_DOMAIN:
00063 admap = this->vm->vmgi->menu_vobu_admap;
00064 break;
00065 case VTSM_DOMAIN:
00066 admap = this->vm->vtsi->menu_vobu_admap;
00067 break;
00068 case VTS_DOMAIN:
00069 admap = this->vm->vtsi->vts_vobu_admap;
00070 break;
00071 default:
00072 fprintf(MSG_OUT, "libdvdnav: Error: Unknown domain for seeking.\n");
00073 }
00074 if(admap) {
00075 uint32_t address = 0;
00076 uint32_t vobu_start, next_vobu, first_address, last_address;
00077 int32_t found = 0;
00078
00079
00080 vobu_start = SRI_END_OF_CELL;
00081
00082 if (admap->last_byte > 20 &&
00083 admap->vobu_start_sectors[20] >= seekto_block)
00084 {
00085 while((!found) && ((address<<2) < admap->last_byte)) {
00086 next_vobu = admap->vobu_start_sectors[address];
00087
00088 if (next_vobu == seekto_block) {
00089 vobu_start = next_vobu;
00090 found = 1;
00091 } else if (vobu_start < seekto_block && next_vobu > seekto_block) {
00092 found = 1;
00093 } else {
00094 vobu_start = next_vobu;
00095 }
00096 address++;
00097 }
00098 }
00099 else {
00100 found = 0;
00101 first_address = 0;
00102 last_address = admap->last_byte >> 2;
00103 while (first_address <= last_address)
00104 {
00105 address = (first_address + last_address) / 2;
00106 next_vobu = admap->vobu_start_sectors[address];
00107 vobu_start = next_vobu;
00108 if (seekto_block > next_vobu)
00109 first_address = address + 1;
00110 else if (seekto_block < next_vobu)
00111 last_address = address - 1;
00112 else {
00113 break;
00114 }
00115 }
00116 found = 1;
00117 if (next_vobu > seekto_block)
00118 vobu_start = admap->vobu_start_sectors[last_address - 1];
00119 }
00120 if(found) {
00121 *vobu = vobu_start;
00122 return DVDNAV_STATUS_OK;
00123 } else {
00124 fprintf(MSG_OUT, "libdvdnav: Could not locate block\n");
00125 return DVDNAV_STATUS_ERR;
00126 }
00127 }
00128 fprintf(MSG_OUT, "libdvdnav: admap not located\n");
00129 return DVDNAV_STATUS_ERR;
00130 }
00131
00132 dvdnav_status_t dvdnav_absolute_time_search(dvdnav_t *this,
00133 uint64_t time, uint search_to_nearest_cell) {
00134
00135 uint64_t target = time;
00136 uint64_t length = 0;
00137 uint64_t cell_length = 0;
00138 uint64_t prev_length = 0;
00139 uint32_t first_cell_nr, last_cell_nr, cell_nr;
00140 int32_t found;
00141 uint64_t offset = 0;
00142 float diff2 = 1.0;
00143
00144 cell_playback_t *cell;
00145 dvd_state_t *state;
00146 dvdnav_status_t result;
00147
00148 if(this->position_current.still != 0) {
00149 printerr("Cannot seek in a still frame.");
00150 return DVDNAV_STATUS_ERR;
00151 }
00152
00153 pthread_mutex_lock(&this->vm_lock);
00154 state = &(this->vm->state);
00155 if(!state->pgc) {
00156 printerr("No current PGC.");
00157 pthread_mutex_unlock(&this->vm_lock);
00158 return DVDNAV_STATUS_ERR;
00159 }
00160
00161
00162 this->cur_cell_time = 0;
00163 if (this->pgc_based) {
00164 first_cell_nr = 1;
00165 last_cell_nr = state->pgc->nr_of_cells;
00166 } else {
00167
00168 first_cell_nr = state->pgc->program_map[state->pgN-1];
00169
00170 if(state->pgN < state->pgc->nr_of_programs)
00171 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
00172 else
00173 last_cell_nr = state->pgc->nr_of_cells;
00174 }
00175
00176 found = 0;
00177 for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
00178 cell = &(state->pgc->cell_playback[cell_nr-1]);
00179 if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
00180 continue;
00181 cell_length = dvdnav_convert_time(&cell->playback_time);
00182 length += cell_length;
00183 if (target <= length) {
00184 offset = (cell->last_sector - cell->first_sector);
00185 diff2 = ((double)target - (double)prev_length) / (double)cell_length;
00186 offset = (diff2 * offset);
00187 target = cell->first_sector;
00188 if (!search_to_nearest_cell)
00189 target += offset;
00190 found = 1;
00191 break;
00192 }
00193 prev_length = length;
00194 }
00195
00196 if(found) {
00197 uint32_t vobu;
00198 #ifdef LOG_DEBUG
00199 fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
00200 cell_nr, first_cell_nr, last_cell_nr);
00201 #endif
00202 if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) {
00203 uint32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
00204
00205 if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
00206 #ifdef LOG_DEBUG
00207 fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
00208 state->cellN, state->blockN, target, vobu, start);
00209 #endif
00210 this->vm->hop_channel += HOP_SEEK;
00211 pthread_mutex_unlock(&this->vm_lock);
00212 return DVDNAV_STATUS_OK;
00213 }
00214 }
00215 }
00216
00217 fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
00218 printerr("Error when seeking.");
00219 pthread_mutex_unlock(&this->vm_lock);
00220 return DVDNAV_STATUS_ERR;
00221 }
00222
00223 dvdnav_status_t dvdnav_sector_search(dvdnav_t *this,
00224 uint64_t offset, int32_t origin) {
00225 uint32_t target = 0;
00226 uint32_t length = 0;
00227 uint32_t first_cell_nr, last_cell_nr, cell_nr;
00228 int32_t found;
00229 cell_playback_t *cell;
00230 dvd_state_t *state;
00231 dvdnav_status_t result;
00232
00233 if(this->position_current.still != 0) {
00234 printerr("Cannot seek in a still frame.");
00235 return DVDNAV_STATUS_ERR;
00236 }
00237
00238 result = dvdnav_get_position(this, &target, &length);
00239 if(!result) {
00240 return DVDNAV_STATUS_ERR;
00241 }
00242
00243 pthread_mutex_lock(&this->vm_lock);
00244 state = &(this->vm->state);
00245 if(!state->pgc) {
00246 printerr("No current PGC.");
00247 pthread_mutex_unlock(&this->vm_lock);
00248 return DVDNAV_STATUS_ERR;
00249 }
00250 #ifdef LOG_DEBUG
00251 fprintf(MSG_OUT, "libdvdnav: seeking to offset=%lu pos=%u length=%u\n", offset, target, length);
00252 fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN);
00253 #endif
00254
00255 switch(origin) {
00256 case SEEK_SET:
00257 if(offset >= length) {
00258 printerr("Request to seek behind end.");
00259 pthread_mutex_unlock(&this->vm_lock);
00260 return DVDNAV_STATUS_ERR;
00261 }
00262 target = offset;
00263 break;
00264 case SEEK_CUR:
00265 if(target + offset >= length) {
00266 printerr("Request to seek behind end.");
00267 pthread_mutex_unlock(&this->vm_lock);
00268 return DVDNAV_STATUS_ERR;
00269 }
00270 target += offset;
00271 break;
00272 case SEEK_END:
00273 if(length < offset) {
00274 printerr("Request to seek before start.");
00275 pthread_mutex_unlock(&this->vm_lock);
00276 return DVDNAV_STATUS_ERR;
00277 }
00278 target = length - offset;
00279 break;
00280 default:
00281
00282 printerr("Illegal seek mode.");
00283 pthread_mutex_unlock(&this->vm_lock);
00284 return DVDNAV_STATUS_ERR;
00285 }
00286
00287 this->cur_cell_time = 0;
00288 if (this->pgc_based) {
00289 first_cell_nr = 1;
00290 last_cell_nr = state->pgc->nr_of_cells;
00291 } else {
00292
00293 first_cell_nr = state->pgc->program_map[state->pgN-1];
00294
00295 if(state->pgN < state->pgc->nr_of_programs)
00296 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
00297 else
00298 last_cell_nr = state->pgc->nr_of_cells;
00299 }
00300
00301 found = 0;
00302 for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
00303 cell = &(state->pgc->cell_playback[cell_nr-1]);
00304 if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
00305 continue;
00306 length = cell->last_sector - cell->first_sector + 1;
00307 if (target >= length) {
00308 target -= length;
00309 } else {
00310
00311 target += cell->first_sector;
00312 found = 1;
00313 break;
00314 }
00315 }
00316
00317 if(found) {
00318 uint32_t vobu;
00319 #ifdef LOG_DEBUG
00320 fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
00321 cell_nr, first_cell_nr, last_cell_nr);
00322 #endif
00323 if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) {
00324 int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
00325
00326 if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
00327 #ifdef LOG_DEBUG
00328 fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
00329 state->cellN, state->blockN, target, vobu, start);
00330 #endif
00331 this->vm->hop_channel += HOP_SEEK;
00332 pthread_mutex_unlock(&this->vm_lock);
00333 return DVDNAV_STATUS_OK;
00334 }
00335 }
00336 }
00337
00338 fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
00339 fprintf(MSG_OUT, "libdvdnav: FIXME: Implement seeking to location %u\n", target);
00340 printerr("Error when seeking.");
00341 pthread_mutex_unlock(&this->vm_lock);
00342 return DVDNAV_STATUS_ERR;
00343 }
00344
00345 dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int32_t part) {
00346 int32_t title, old_part;
00347
00348 if (dvdnav_current_title_info(this, &title, &old_part) == DVDNAV_STATUS_OK)
00349 return dvdnav_part_play(this, title, part);
00350 return DVDNAV_STATUS_ERR;
00351 }
00352
00353 dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
00354
00355 if(!this) {
00356 printerr("Passed a NULL pointer.");
00357 return DVDNAV_STATUS_ERR;
00358 }
00359
00360 pthread_mutex_lock(&this->vm_lock);
00361 if(!this->vm->state.pgc) {
00362 printerr("No current PGC.");
00363 pthread_mutex_unlock(&this->vm_lock);
00364 return DVDNAV_STATUS_ERR;
00365 }
00366
00367 #ifdef LOG_DEBUG
00368 fprintf(MSG_OUT, "libdvdnav: previous chapter\n");
00369 #endif
00370 if (!vm_jump_prev_pg(this->vm)) {
00371 fprintf(MSG_OUT, "libdvdnav: previous chapter failed.\n");
00372 printerr("Skip to previous chapter failed.");
00373 pthread_mutex_unlock(&this->vm_lock);
00374 return DVDNAV_STATUS_ERR;
00375 }
00376 this->cur_cell_time = 0;
00377 this->position_current.still = 0;
00378 this->vm->hop_channel++;
00379 #ifdef LOG_DEBUG
00380 fprintf(MSG_OUT, "libdvdnav: previous chapter done\n");
00381 #endif
00382 pthread_mutex_unlock(&this->vm_lock);
00383
00384 return DVDNAV_STATUS_OK;
00385 }
00386
00387 dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
00388
00389 if(!this) {
00390 printerr("Passed a NULL pointer.");
00391 return DVDNAV_STATUS_ERR;
00392 }
00393
00394 pthread_mutex_lock(&this->vm_lock);
00395 if(!this->vm->state.pgc) {
00396 printerr("No current PGC.");
00397 pthread_mutex_unlock(&this->vm_lock);
00398 return DVDNAV_STATUS_ERR;
00399 }
00400
00401 #ifdef LOG_DEBUG
00402 fprintf(MSG_OUT, "libdvdnav: top chapter\n");
00403 #endif
00404 if (!vm_jump_top_pg(this->vm)) {
00405 fprintf(MSG_OUT, "libdvdnav: top chapter failed.\n");
00406 printerr("Skip to top chapter failed.");
00407 pthread_mutex_unlock(&this->vm_lock);
00408 return DVDNAV_STATUS_ERR;
00409 }
00410 this->cur_cell_time = 0;
00411 this->position_current.still = 0;
00412 this->vm->hop_channel++;
00413 #ifdef LOG_DEBUG
00414 fprintf(MSG_OUT, "libdvdnav: top chapter done\n");
00415 #endif
00416 pthread_mutex_unlock(&this->vm_lock);
00417
00418 return DVDNAV_STATUS_OK;
00419 }
00420
00421 dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
00422 vm_t *try_vm;
00423
00424 if(!this) {
00425 printerr("Passed a NULL pointer.");
00426 return DVDNAV_STATUS_ERR;
00427 }
00428
00429 pthread_mutex_lock(&this->vm_lock);
00430 if(!this->vm->state.pgc) {
00431 printerr("No current PGC.");
00432 pthread_mutex_unlock(&this->vm_lock);
00433 return DVDNAV_STATUS_ERR;
00434 }
00435
00436 #ifdef LOG_DEBUG
00437 fprintf(MSG_OUT, "libdvdnav: next chapter\n");
00438 #endif
00439
00440 try_vm = vm_new_copy(this->vm);
00441 if (!vm_jump_next_pg(try_vm) || try_vm->stopped) {
00442 vm_free_copy(try_vm);
00443
00444 try_vm = vm_new_copy(this->vm);
00445 vm_get_next_cell(try_vm);
00446 if (try_vm->stopped) {
00447 vm_free_copy(try_vm);
00448 fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n");
00449 printerr("Skip to next chapter failed.");
00450 pthread_mutex_unlock(&this->vm_lock);
00451 return DVDNAV_STATUS_ERR;
00452 }
00453 }
00454 this->cur_cell_time = 0;
00455
00456 vm_merge(this->vm, try_vm);
00457 vm_free_copy(try_vm);
00458 this->position_current.still = 0;
00459 this->vm->hop_channel++;
00460 #ifdef LOG_DEBUG
00461 fprintf(MSG_OUT, "libdvdnav: next chapter done\n");
00462 #endif
00463 pthread_mutex_unlock(&this->vm_lock);
00464
00465 return DVDNAV_STATUS_OK;
00466 }
00467
00468 dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
00469 vm_t *try_vm;
00470
00471 if(!this) {
00472 printerr("Passed a NULL pointer.");
00473 return DVDNAV_STATUS_ERR;
00474 }
00475
00476 pthread_mutex_lock(&this->vm_lock);
00477 if(!this->vm->state.pgc) {
00478 printerr("No current PGC.");
00479 pthread_mutex_unlock(&this->vm_lock);
00480 return DVDNAV_STATUS_ERR;
00481 }
00482
00483 this->cur_cell_time = 0;
00484
00485 try_vm = vm_new_copy(this->vm);
00486 if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != VTS_DOMAIN)) {
00487
00488 if (vm_jump_resume(try_vm) && !try_vm->stopped) {
00489
00490 vm_merge(this->vm, try_vm);
00491 vm_free_copy(try_vm);
00492 this->position_current.still = 0;
00493 this->vm->hop_channel++;
00494 pthread_mutex_unlock(&this->vm_lock);
00495 return DVDNAV_STATUS_OK;
00496 }
00497 }
00498 if (menu == DVD_MENU_Escape) menu = DVD_MENU_Root;
00499
00500 if (vm_jump_menu(try_vm, menu) && !try_vm->stopped) {
00501
00502 vm_merge(this->vm, try_vm);
00503 vm_free_copy(try_vm);
00504 this->position_current.still = 0;
00505 this->vm->hop_channel++;
00506 pthread_mutex_unlock(&this->vm_lock);
00507 return DVDNAV_STATUS_OK;
00508 } else {
00509 vm_free_copy(try_vm);
00510 printerr("No such menu or menu not reachable.");
00511 pthread_mutex_unlock(&this->vm_lock);
00512 return DVDNAV_STATUS_ERR;
00513 }
00514 }
00515
00516 dvdnav_status_t dvdnav_get_position(dvdnav_t *this, uint32_t *pos,
00517 uint32_t *len) {
00518 uint32_t cur_sector;
00519 int32_t cell_nr, first_cell_nr, last_cell_nr;
00520 cell_playback_t *cell;
00521 dvd_state_t *state;
00522
00523 if(!this || !pos || !len) {
00524 printerr("Passed a NULL pointer.");
00525 return DVDNAV_STATUS_ERR;
00526 }
00527 if(!this->started) {
00528 printerr("Virtual DVD machine not started.");
00529 return DVDNAV_STATUS_ERR;
00530 }
00531
00532 pthread_mutex_lock(&this->vm_lock);
00533 state = &(this->vm->state);
00534 if(!state->pgc || this->vm->stopped) {
00535 printerr("No current PGC.");
00536 pthread_mutex_unlock(&this->vm_lock);
00537 return DVDNAV_STATUS_ERR;
00538 }
00539 if (this->position_current.hop_channel != this->vm->hop_channel ||
00540 this->position_current.domain != state->domain ||
00541 this->position_current.vts != state->vtsN ||
00542 this->position_current.cell_restart != state->cell_restart) {
00543 printerr("New position not yet determined.");
00544 pthread_mutex_unlock(&this->vm_lock);
00545 return DVDNAV_STATUS_ERR;
00546 }
00547
00548
00549 cur_sector = this->vobu.vobu_start + this->vobu.blockN;
00550
00551 if (this->pgc_based) {
00552 first_cell_nr = 1;
00553 last_cell_nr = state->pgc->nr_of_cells;
00554 } else {
00555
00556 first_cell_nr = state->pgc->program_map[state->pgN-1];
00557
00558 if(state->pgN < state->pgc->nr_of_programs)
00559 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
00560 else
00561 last_cell_nr = state->pgc->nr_of_cells;
00562 }
00563
00564 *pos = -1;
00565 *len = 0;
00566 for (cell_nr = first_cell_nr; cell_nr <= last_cell_nr; cell_nr++) {
00567 cell = &(state->pgc->cell_playback[cell_nr-1]);
00568 if (cell_nr == state->cellN) {
00569
00570
00571 *pos = *len + cur_sector - cell->first_sector;
00572 }
00573 *len += cell->last_sector - cell->first_sector + 1;
00574 }
00575
00576 assert((signed)*pos != -1);
00577
00578 pthread_mutex_unlock(&this->vm_lock);
00579
00580 return DVDNAV_STATUS_OK;
00581 }
00582
00583 dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
00584 uint32_t *pos,
00585 uint32_t *len) {
00586 uint32_t cur_sector;
00587 uint32_t first_cell_nr;
00588 uint32_t last_cell_nr;
00589 cell_playback_t *first_cell;
00590 cell_playback_t *last_cell;
00591 dvd_state_t *state;
00592
00593 if(!this || !pos || !len) {
00594 printerr("Passed a NULL pointer.");
00595 return DVDNAV_STATUS_ERR;
00596 }
00597
00598 state = &(this->vm->state);
00599 if(!state->pgc) {
00600 printerr("No current PGC.");
00601 return DVDNAV_STATUS_ERR;
00602 }
00603
00604
00605 cur_sector = this->vobu.vobu_start + this->vobu.blockN;
00606
00607
00608 first_cell_nr = state->pgc->program_map[0];
00609 first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
00610 last_cell_nr = state->pgc->nr_of_cells;
00611 last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
00612
00613 *pos = cur_sector - first_cell->first_sector;
00614 *len = last_cell->last_sector - first_cell->first_sector;
00615
00616 return DVDNAV_STATUS_OK;
00617 }
00618
00628 dvdnav_status_t dvdnav_relative_time_search(dvdnav_t *this,
00629 int relative_time)
00630 {
00631 if(!this) {
00632 printerr("Passed a NULL pointer.");
00633 return DVDNAV_STATUS_ERR;
00634 }
00635
00636 uint32_t cur_vobu, new_vobu = 0, start, offset;
00637 uint32_t first_cell_nr, last_cell_nr, cell_nr;
00638 cell_playback_t *cell;
00639 int i, length, scan_admap;
00640
00641 dsi_t * dsi;
00642 dvd_state_t *state;
00643 int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11,
00644 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
00645 pthread_mutex_lock(&this->vm_lock);
00646 length = relative_time;
00647 state = &(this->vm->state);
00648 cell_nr = state->cellN -1;
00649 cell = &(state->pgc->cell_playback[cell_nr]);
00650 cur_vobu = this->vobu.vobu_start;
00651 scan_admap = 0;
00652
00653 if (this->pgc_based) {
00654 first_cell_nr = 0;
00655 last_cell_nr = state->pgc->nr_of_cells - 1;
00656 } else {
00657 printerr("dvdnav_time_relative_time_search: works only if pgc_based is enabled");
00658 pthread_mutex_unlock(&this->vm_lock);
00659 return DVDNAV_STATUS_ERR;
00660 }
00661
00662 if (length != 0)
00663 {
00664 dsi = dvdnav_get_current_nav_dsi(this);
00665 if (length > 0) {
00666 for (i = 0; i < 19; i++) {
00667 if (stime[i]/2.0 <= length/2.0) {
00668 offset = dsi->vobu_sri.fwda[i];
00669 if (offset >> 31) {
00670 new_vobu = cur_vobu + (offset & 0xffff);
00671 } else {
00672 if (cell_nr == last_cell_nr) {
00673 offset = state->pgc->cell_playback[last_cell_nr].last_sector;
00674 scan_admap = 1;
00675 } else {
00676 cell_nr++;
00677 new_vobu = state->pgc->cell_playback[cell_nr].first_sector;
00678 }
00679 }
00680 break;
00681 }
00682 }
00683 } else {
00684 for (i = 0; i < 19; i++) {
00685 if (stime[18 - i]/2.0 >= abs(length)/2.0)
00686 {
00687 offset = dsi->vobu_sri.bwda[i];
00688 if (offset >> 31) {
00689 new_vobu = cur_vobu - (offset & 0xffff);
00690 } else {
00691 if (cell_nr == first_cell_nr) {
00692 new_vobu = 0;
00693 } else {
00694 cell_nr--;
00695 offset = state->pgc->cell_playback[cell_nr].last_sector;
00696 scan_admap = 1;
00697 }
00698 }
00699 break;
00700 }
00701 }
00702 }
00703 }
00704
00705 if (scan_admap)
00706 {
00707 if (dvdnav_scan_admap(this, state->domain, offset, &new_vobu) == DVDNAV_STATUS_ERR) {
00708 pthread_mutex_unlock(&this->vm_lock);
00709 return DVDNAV_STATUS_ERR;
00710 }
00711 }
00712 start = state->pgc->cell_playback[cell_nr].first_sector;
00713 if (vm_jump_cell_block(this->vm, cell_nr+1, new_vobu - start)) {
00714 this->vm->hop_channel += HOP_SEEK;
00715 }
00716 pthread_mutex_unlock(&this->vm_lock);
00717 return DVDNAV_STATUS_OK;
00718 }
00719
00720 uint32_t dvdnav_describe_title_chapters(dvdnav_t *this, int32_t title, uint64_t **times, uint64_t *duration) {
00721 int32_t retval=0;
00722 uint16_t parts, i;
00723 title_info_t *ptitle = NULL;
00724 ptt_info_t *ptt = NULL;
00725 ifo_handle_t *ifo = NULL;
00726 pgc_t *pgc;
00727 cell_playback_t *cell;
00728 uint64_t length, *tmp=NULL;
00729
00730 *times = NULL;
00731 *duration = 0;
00732 pthread_mutex_lock(&this->vm_lock);
00733 if(!this->vm->vmgi) {
00734 printerr("Bad VM state or missing VTSI.");
00735 goto fail;
00736 }
00737 if(!this->started) {
00738
00739 vm_start(this->vm);
00740 this->started = 1;
00741 }
00742 ifo = vm_get_title_ifo(this->vm, title);
00743 if(!ifo || !ifo->vts_pgcit) {
00744 printerr("Couldn't open IFO for chosen title, exit.");
00745 retval = 0;
00746 goto fail;
00747 }
00748
00749 ptitle = &this->vm->vmgi->tt_srpt->title[title-1];
00750 parts = ptitle->nr_of_ptts;
00751 ptt = ifo->vts_ptt_srpt->title[ptitle->vts_ttn-1].ptt;
00752
00753 tmp = calloc(1, sizeof(uint64_t)*parts);
00754 if(!tmp)
00755 goto fail;
00756
00757 length = 0;
00758 for(i=0; i<parts; i++) {
00759 uint32_t cellnr, endcellnr;
00760 pgc = ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc;
00761 if(ptt[i].pgn > pgc->nr_of_programs) {
00762 printerr("WRONG part number.");
00763 goto fail;
00764 }
00765
00766 cellnr = pgc->program_map[ptt[i].pgn-1];
00767 if(ptt[i].pgn < pgc->nr_of_programs)
00768 endcellnr = pgc->program_map[ptt[i].pgn];
00769 else
00770 endcellnr = 0;
00771
00772 do {
00773 cell = &pgc->cell_playback[cellnr-1];
00774 if(!(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK &&
00775 cell->block_mode != BLOCK_MODE_FIRST_CELL
00776 ))
00777 {
00778 tmp[i] = length + dvdnav_convert_time(&cell->playback_time);
00779 length = tmp[i];
00780 }
00781 cellnr++;
00782 } while(cellnr < endcellnr);
00783 }
00784 *duration = length;
00785 vm_ifo_close(ifo);
00786 ifo = NULL;
00787 retval = parts;
00788 *times = tmp;
00789
00790 fail:
00791 pthread_mutex_unlock(&this->vm_lock);
00792 if(ifo)
00793 vm_ifo_close(ifo);
00794 if(!retval && tmp)
00795 free(tmp);
00796 return retval;
00797 }