MythTV  0.26-pre
searching.c
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends