|
MythTV
0.26-pre
|
00001 /* 00002 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net> 00003 * 00004 * This file is part of libdvdnav, a DVD navigation library. 00005 * 00006 * libdvdnav is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * libdvdnav is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with libdvdnav; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 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 #define LOG_DEBUG 00042 */ 00043 00044 /* Searching API calls */ 00045 00046 /* Scan the ADMAP for a particular block number. */ 00047 /* Return placed in vobu. */ 00048 /* Returns error status */ 00049 /* FIXME: Maybe need to handle seeking outside current cell. */ 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 /* Search through the VOBU_ADMAP for the nearest VOBU 00059 * to the target block */ 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 /* Search through ADMAP for best sector */ 00080 vobu_start = SRI_END_OF_CELL; 00081 /* use binary search algorithm to improve efficiency */ 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 /* Find start cell of program. */ 00168 first_cell_nr = state->pgc->program_map[state->pgN-1]; 00169 /* Find end cell of program */ 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 /* Error occured */ 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 /* Find start cell of program. */ 00293 first_cell_nr = state->pgc->program_map[state->pgN-1]; 00294 /* Find end cell of program */ 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 /* convert the target sector from Cell-relative to absolute physical sector */ 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 /* make a copy of current VM and try to navigate the copy to the next PG */ 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 /* next_pg failed, try to jump at least to the next cell */ 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 /* merge changes on success */ 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 /* make a copy of current VM and try to navigate the copy to the menu */ 00485 try_vm = vm_new_copy(this->vm); 00486 if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != VTS_DOMAIN)) { 00487 /* Try resume */ 00488 if (vm_jump_resume(try_vm) && !try_vm->stopped) { 00489 /* merge changes on success */ 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 /* merge changes on success */ 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 /* Get current sector */ 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 /* Find start cell of program. */ 00556 first_cell_nr = state->pgc->program_map[state->pgN-1]; 00557 /* Find end cell of program */ 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 /* the current sector is in this cell, 00570 * pos is length of PG up to here + sector's offset in this cell */ 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 /* Get current sector */ 00605 cur_sector = this->vobu.vobu_start + this->vobu.blockN; 00606 00607 /* Now find first and last cells in title. */ 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 /* don't report an error but be nice */ 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 }
1.7.6.1