~ubuntu-branches/ubuntu/quantal/libdvdnav/quantal-proposed

« back to all changes in this revision

Viewing changes to .pc/06-pgcn-bounds.patch/src/searching.c

  • Committer: Package Import Robot
  • Author(s): Benjamin Drung
  • Date: 2013-07-20 00:37:27 UTC
  • mfrom: (1.1.7)
  • Revision ID: package-import@ubuntu.com-20130720003727-4pifxdfniu3l210i
Tags: 4.2.0+20120524-2ubuntu0.1
* debian/patches/03-Make-sure-pgc-is-valid.patch,
  debian/patches/04-Ignore-parts-where-the-pgc-start-byte-is-wrong.patch,
  debian/patches/05-Skip-PGCs-w-a-cell-number-of-0.patch: Validate PGC values
  before accessing them to avoid causing a crash.
* debian/patches/06-pgcn-bounds.patch: Check for out-of-bounds values for
  pgcn. Fixes a crash in dvdnav_describe_title_chapters() with vlc, lsdvd, and
  other video players. This occurs with the "Inside Man" DVD. Thanks to Bryce
  Harrington <bryce@ubuntu.com>. (LP: #1094499)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
 
3
 *
 
4
 * This file is part of libdvdnav, a DVD navigation library.
 
5
 *
 
6
 * libdvdnav is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * libdvdnav is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License along
 
17
 * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
19
 */
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include "config.h"
 
23
#endif
 
24
 
 
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"
 
39
 
 
40
/*
 
41
#define LOG_DEBUG
 
42
*/
 
43
 
 
44
/* Searching API calls */
 
45
 
 
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, int next, uint32_t *vobu) {
 
51
  vobu_admap_t *admap = NULL;
 
52
 
 
53
#ifdef LOG_DEBUG
 
54
  fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", seekto_block);
 
55
#endif
 
56
  *vobu = -1;
 
57
 
 
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");
 
73
  }
 
74
  if(admap) {
 
75
    uint32_t address = 0;
 
76
    uint32_t vobu_start, next_vobu = 0;
 
77
    int admap_entries = (admap->last_byte + 1 - VOBU_ADMAP_SIZE)/VOBU_ADMAP_SIZE;
 
78
 
 
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];
 
84
 
 
85
      /* fprintf(MSG_OUT, "libdvdnav: Found block %u\n", next_vobu); */
 
86
 
 
87
      if(vobu_start <= seekto_block && next_vobu > seekto_block)
 
88
        break;
 
89
      vobu_start = next_vobu;
 
90
      address++;
 
91
    }
 
92
    *vobu = next ? next_vobu : vobu_start;
 
93
    return DVDNAV_STATUS_OK;
 
94
  }
 
95
  fprintf(MSG_OUT, "libdvdnav: admap not located\n");
 
96
  return DVDNAV_STATUS_ERR;
 
97
}
 
98
 
 
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) {
 
103
 
 
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;
 
110
 
 
111
  if(this->position_current.still != 0) {
 
112
    printerr("Cannot seek in a still frame.");
 
113
    return DVDNAV_STATUS_ERR;
 
114
  }
 
115
 
 
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;
 
122
  }
 
123
 
 
124
 
 
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;
 
137
  }
 
138
 
 
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;
 
151
 
 
152
      found = 1;
 
153
      break;
 
154
    }
 
155
  }
 
156
 
 
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, 0, &vobu) == DVDNAV_STATUS_OK) {
 
164
      uint32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
 
165
 
 
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;
 
174
      }
 
175
    }
 
176
  }
 
177
 
 
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;
 
182
}
 
183
 
 
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 current_pos;
 
188
  uint32_t cur_sector;
 
189
  uint32_t cur_cell_nr;
 
190
  uint32_t length = 0;
 
191
  uint32_t first_cell_nr, last_cell_nr, cell_nr;
 
192
  int32_t found;
 
193
  int forward = 0;
 
194
  cell_playback_t *cell;
 
195
  dvd_state_t *state;
 
196
  dvdnav_status_t result;
 
197
 
 
198
  if(this->position_current.still != 0) {
 
199
    printerr("Cannot seek in a still frame.");
 
200
    return DVDNAV_STATUS_ERR;
 
201
  }
 
202
 
 
203
  result = dvdnav_get_position(this, &target, &length);
 
204
  if(!result) {
 
205
    return DVDNAV_STATUS_ERR;
 
206
  }
 
207
 
 
208
  pthread_mutex_lock(&this->vm_lock);
 
209
  state = &(this->vm->state);
 
210
  if(!state->pgc) {
 
211
    printerr("No current PGC.");
 
212
    pthread_mutex_unlock(&this->vm_lock);
 
213
    return DVDNAV_STATUS_ERR;
 
214
  }
 
215
#ifdef LOG_DEBUG
 
216
  fprintf(MSG_OUT, "libdvdnav: seeking to offset=%lu pos=%u length=%u\n", offset, target, length);
 
217
  fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN);
 
218
#endif
 
219
 
 
220
  current_pos = target;
 
221
  cur_sector = this->vobu.vobu_start + this->vobu.blockN;
 
222
  cur_cell_nr = state->cellN;
 
223
 
 
224
  switch(origin) {
 
225
   case SEEK_SET:
 
226
    if(offset >= length) {
 
227
      printerr("Request to seek behind end.");
 
228
      pthread_mutex_unlock(&this->vm_lock);
 
229
      return DVDNAV_STATUS_ERR;
 
230
    }
 
231
    target = offset;
 
232
    break;
 
233
   case SEEK_CUR:
 
234
    if(target + offset >= length) {
 
235
      printerr("Request to seek behind end.");
 
236
      pthread_mutex_unlock(&this->vm_lock);
 
237
      return DVDNAV_STATUS_ERR;
 
238
    }
 
239
    target += offset;
 
240
    break;
 
241
   case SEEK_END:
 
242
    if(length < offset) {
 
243
      printerr("Request to seek before start.");
 
244
      pthread_mutex_unlock(&this->vm_lock);
 
245
      return DVDNAV_STATUS_ERR;
 
246
    }
 
247
    target = length - offset;
 
248
    break;
 
249
   default:
 
250
    /* Error occured */
 
251
    printerr("Illegal seek mode.");
 
252
    pthread_mutex_unlock(&this->vm_lock);
 
253
    return DVDNAV_STATUS_ERR;
 
254
  }
 
255
  forward = target > current_pos;
 
256
 
 
257
  this->cur_cell_time = 0;
 
258
  if (this->pgc_based) {
 
259
    first_cell_nr = 1;
 
260
    last_cell_nr = state->pgc->nr_of_cells;
 
261
  } else {
 
262
    /* Find start cell of program. */
 
263
    first_cell_nr = state->pgc->program_map[state->pgN-1];
 
264
    /* Find end cell of program */
 
265
    if(state->pgN < state->pgc->nr_of_programs)
 
266
      last_cell_nr = state->pgc->program_map[state->pgN] - 1;
 
267
    else
 
268
      last_cell_nr = state->pgc->nr_of_cells;
 
269
  }
 
270
 
 
271
  found = 0;
 
272
  for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
 
273
    cell =  &(state->pgc->cell_playback[cell_nr-1]);
 
274
    if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
 
275
      continue;
 
276
    length = cell->last_sector - cell->first_sector + 1;
 
277
    if (target >= length) {
 
278
      target -= length;
 
279
    } else {
 
280
      /* convert the target sector from Cell-relative to absolute physical sector */
 
281
      target += cell->first_sector;
 
282
      if (forward && (cell_nr == cur_cell_nr)) {
 
283
        uint32_t vobu;
 
284
        /* if we are seeking forward from the current position, make sure
 
285
         * we move to a new position that is after our current position.
 
286
         * simply truncating to the vobu will go backwards */
 
287
        if (dvdnav_scan_admap(this, state->domain, target, 0, &vobu) != DVDNAV_STATUS_OK)
 
288
          break;
 
289
        if (vobu <= cur_sector) {
 
290
          if (dvdnav_scan_admap(this, state->domain, target, 1, &vobu) != DVDNAV_STATUS_OK)
 
291
            break;
 
292
          if (vobu > cell->last_sector) {
 
293
            if (cell_nr == last_cell_nr)
 
294
              break;
 
295
            cell_nr++;
 
296
            cell =  &(state->pgc->cell_playback[cell_nr-1]);
 
297
            target = cell->first_sector;
 
298
          } else {
 
299
            target = vobu;
 
300
          }
 
301
        }
 
302
      }
 
303
      found = 1;
 
304
      break;
 
305
    }
 
306
  }
 
307
 
 
308
  if(found) {
 
309
    uint32_t vobu;
 
310
#ifdef LOG_DEBUG
 
311
    fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
 
312
            cell_nr, first_cell_nr, last_cell_nr);
 
313
#endif
 
314
    if (dvdnav_scan_admap(this, state->domain, target, 0, &vobu) == DVDNAV_STATUS_OK) {
 
315
      int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
 
316
 
 
317
      if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
 
318
#ifdef LOG_DEBUG
 
319
        fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
 
320
          state->cellN, state->blockN, target, vobu, start);
 
321
#endif
 
322
        this->vm->hop_channel += HOP_SEEK;
 
323
        pthread_mutex_unlock(&this->vm_lock);
 
324
        return DVDNAV_STATUS_OK;
 
325
      }
 
326
    }
 
327
  }
 
328
 
 
329
  fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
 
330
  fprintf(MSG_OUT, "libdvdnav: FIXME: Implement seeking to location %u\n", target);
 
331
  printerr("Error when seeking.");
 
332
  pthread_mutex_unlock(&this->vm_lock);
 
333
  return DVDNAV_STATUS_ERR;
 
334
}
 
335
 
 
336
dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int32_t part) {
 
337
  int32_t title, old_part;
 
338
 
 
339
  if (dvdnav_current_title_info(this, &title, &old_part) == DVDNAV_STATUS_OK)
 
340
    return dvdnav_part_play(this, title, part);
 
341
  return DVDNAV_STATUS_ERR;
 
342
}
 
343
 
 
344
dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
 
345
  pthread_mutex_lock(&this->vm_lock);
 
346
  if(!this->vm->state.pgc) {
 
347
    printerr("No current PGC.");
 
348
    pthread_mutex_unlock(&this->vm_lock);
 
349
    return DVDNAV_STATUS_ERR;
 
350
  }
 
351
 
 
352
#ifdef LOG_DEBUG
 
353
  fprintf(MSG_OUT, "libdvdnav: previous chapter\n");
 
354
#endif
 
355
  if (!vm_jump_prev_pg(this->vm)) {
 
356
    fprintf(MSG_OUT, "libdvdnav: previous chapter failed.\n");
 
357
    printerr("Skip to previous chapter failed.");
 
358
    pthread_mutex_unlock(&this->vm_lock);
 
359
    return DVDNAV_STATUS_ERR;
 
360
  }
 
361
  this->cur_cell_time = 0;
 
362
  this->position_current.still = 0;
 
363
  this->vm->hop_channel++;
 
364
#ifdef LOG_DEBUG
 
365
  fprintf(MSG_OUT, "libdvdnav: previous chapter done\n");
 
366
#endif
 
367
  pthread_mutex_unlock(&this->vm_lock);
 
368
 
 
369
  return DVDNAV_STATUS_OK;
 
370
}
 
371
 
 
372
dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
 
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;
 
378
  }
 
379
 
 
380
#ifdef LOG_DEBUG
 
381
  fprintf(MSG_OUT, "libdvdnav: top chapter\n");
 
382
#endif
 
383
  if (!vm_jump_top_pg(this->vm)) {
 
384
    fprintf(MSG_OUT, "libdvdnav: top chapter failed.\n");
 
385
    printerr("Skip to top chapter failed.");
 
386
    pthread_mutex_unlock(&this->vm_lock);
 
387
    return DVDNAV_STATUS_ERR;
 
388
  }
 
389
  this->cur_cell_time = 0;
 
390
  this->position_current.still = 0;
 
391
  this->vm->hop_channel++;
 
392
#ifdef LOG_DEBUG
 
393
  fprintf(MSG_OUT, "libdvdnav: top chapter done\n");
 
394
#endif
 
395
  pthread_mutex_unlock(&this->vm_lock);
 
396
 
 
397
  return DVDNAV_STATUS_OK;
 
398
}
 
399
 
 
400
dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
 
401
  vm_t *try_vm;
 
402
 
 
403
  pthread_mutex_lock(&this->vm_lock);
 
404
  if(!this->vm->state.pgc) {
 
405
    printerr("No current PGC.");
 
406
    goto fail;
 
407
  }
 
408
 
 
409
#ifdef LOG_DEBUG
 
410
  fprintf(MSG_OUT, "libdvdnav: next chapter\n");
 
411
#endif
 
412
  /* make a copy of current VM and try to navigate the copy to the next PG */
 
413
  try_vm = vm_new_copy(this->vm);
 
414
  if (try_vm == NULL) {
 
415
    printerr("Unable to copy the VM.");
 
416
    goto fail;
 
417
  }
 
418
 
 
419
  if (!vm_jump_next_pg(try_vm) || try_vm->stopped) {
 
420
    vm_free_copy(try_vm);
 
421
    /* next_pg failed, try to jump at least to the next cell */
 
422
    try_vm = vm_new_copy(this->vm);
 
423
    if (try_vm == NULL) {
 
424
      printerr("Unable to copy the VM.");
 
425
      goto fail;
 
426
    }
 
427
    vm_get_next_cell(try_vm);
 
428
    if (try_vm->stopped) {
 
429
      vm_free_copy(try_vm);
 
430
      fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n");
 
431
      printerr("Skip to next chapter failed.");
 
432
      goto fail;
 
433
    }
 
434
  }
 
435
  this->cur_cell_time = 0;
 
436
  /* merge changes on success */
 
437
  vm_merge(this->vm, try_vm);
 
438
  vm_free_copy(try_vm);
 
439
  this->position_current.still = 0;
 
440
  this->vm->hop_channel++;
 
441
#ifdef LOG_DEBUG
 
442
  fprintf(MSG_OUT, "libdvdnav: next chapter done\n");
 
443
#endif
 
444
  pthread_mutex_unlock(&this->vm_lock);
 
445
 
 
446
  return DVDNAV_STATUS_OK;
 
447
 
 
448
fail:
 
449
  pthread_mutex_unlock(&this->vm_lock);
 
450
  return DVDNAV_STATUS_ERR;
 
451
}
 
452
 
 
453
dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
 
454
  vm_t *try_vm;
 
455
 
 
456
  pthread_mutex_lock(&this->vm_lock);
 
457
  if(!this->vm->state.pgc) {
 
458
    printerr("No current PGC.");
 
459
    goto fail;
 
460
  }
 
461
 
 
462
  this->cur_cell_time = 0;
 
463
  /* make a copy of current VM and try to navigate the copy to the menu */
 
464
  try_vm = vm_new_copy(this->vm);
 
465
  if (try_vm == NULL) {
 
466
    printerr("Unable to copy VM.");
 
467
    goto fail;
 
468
  }
 
469
 
 
470
  if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != VTS_DOMAIN)) {
 
471
    /* Try resume */
 
472
    if (vm_jump_resume(try_vm) && !try_vm->stopped) {
 
473
        /* merge changes on success */
 
474
        vm_merge(this->vm, try_vm);
 
475
        vm_free_copy(try_vm);
 
476
        this->position_current.still = 0;
 
477
        this->vm->hop_channel++;
 
478
        pthread_mutex_unlock(&this->vm_lock);
 
479
        return DVDNAV_STATUS_OK;
 
480
    }
 
481
  }
 
482
  if (menu == DVD_MENU_Escape) menu = DVD_MENU_Root;
 
483
 
 
484
  if (vm_jump_menu(try_vm, menu) && !try_vm->stopped) {
 
485
    /* merge changes on success */
 
486
    vm_merge(this->vm, try_vm);
 
487
    vm_free_copy(try_vm);
 
488
    this->position_current.still = 0;
 
489
    this->vm->hop_channel++;
 
490
    pthread_mutex_unlock(&this->vm_lock);
 
491
    return DVDNAV_STATUS_OK;
 
492
  } else {
 
493
    vm_free_copy(try_vm);
 
494
    printerr("No such menu or menu not reachable.");
 
495
    goto fail;
 
496
  }
 
497
 
 
498
fail:
 
499
  pthread_mutex_unlock(&this->vm_lock);
 
500
  return DVDNAV_STATUS_ERR;
 
501
}
 
502
 
 
503
dvdnav_status_t dvdnav_get_position(dvdnav_t *this, uint32_t *pos,
 
504
                                    uint32_t *len) {
 
505
  uint32_t cur_sector;
 
506
  int32_t cell_nr, first_cell_nr, last_cell_nr;
 
507
  cell_playback_t *cell;
 
508
  dvd_state_t *state;
 
509
 
 
510
  if(!this->started) {
 
511
    printerr("Virtual DVD machine not started.");
 
512
    return DVDNAV_STATUS_ERR;
 
513
  }
 
514
 
 
515
  pthread_mutex_lock(&this->vm_lock);
 
516
  state = &(this->vm->state);
 
517
  if(!state->pgc || this->vm->stopped) {
 
518
    printerr("No current PGC.");
 
519
    pthread_mutex_unlock(&this->vm_lock);
 
520
    return DVDNAV_STATUS_ERR;
 
521
  }
 
522
  if (this->position_current.hop_channel  != this->vm->hop_channel ||
 
523
      this->position_current.domain       != state->domain         ||
 
524
      this->position_current.vts          != state->vtsN           ||
 
525
      this->position_current.cell_restart != state->cell_restart) {
 
526
    printerr("New position not yet determined.");
 
527
    pthread_mutex_unlock(&this->vm_lock);
 
528
    return DVDNAV_STATUS_ERR;
 
529
  }
 
530
 
 
531
  /* Get current sector */
 
532
  cur_sector = this->vobu.vobu_start + this->vobu.blockN;
 
533
 
 
534
  if (this->pgc_based) {
 
535
    first_cell_nr = 1;
 
536
    last_cell_nr = state->pgc->nr_of_cells;
 
537
  } else {
 
538
    /* Find start cell of program. */
 
539
    first_cell_nr = state->pgc->program_map[state->pgN-1];
 
540
    /* Find end cell of program */
 
541
    if(state->pgN < state->pgc->nr_of_programs)
 
542
      last_cell_nr = state->pgc->program_map[state->pgN] - 1;
 
543
    else
 
544
      last_cell_nr = state->pgc->nr_of_cells;
 
545
  }
 
546
 
 
547
  *pos = -1;
 
548
  *len = 0;
 
549
  for (cell_nr = first_cell_nr; cell_nr <= last_cell_nr; cell_nr++) {
 
550
    cell = &(state->pgc->cell_playback[cell_nr-1]);
 
551
    if (cell_nr == state->cellN) {
 
552
      /* the current sector is in this cell,
 
553
       * pos is length of PG up to here + sector's offset in this cell */
 
554
      *pos = *len + cur_sector - cell->first_sector;
 
555
    }
 
556
    *len += cell->last_sector - cell->first_sector + 1;
 
557
  }
 
558
 
 
559
  assert((signed)*pos != -1);
 
560
 
 
561
  pthread_mutex_unlock(&this->vm_lock);
 
562
 
 
563
  return DVDNAV_STATUS_OK;
 
564
}
 
565
 
 
566
dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
 
567
                                             uint32_t *pos,
 
568
                                             uint32_t *len) {
 
569
  uint32_t cur_sector;
 
570
  uint32_t first_cell_nr;
 
571
  uint32_t last_cell_nr;
 
572
  cell_playback_t *first_cell;
 
573
  cell_playback_t *last_cell;
 
574
  dvd_state_t *state;
 
575
 
 
576
  state = &(this->vm->state);
 
577
  if(!state->pgc) {
 
578
    printerr("No current PGC.");
 
579
    return DVDNAV_STATUS_ERR;
 
580
  }
 
581
 
 
582
  /* Get current sector */
 
583
  cur_sector = this->vobu.vobu_start + this->vobu.blockN;
 
584
 
 
585
  /* Now find first and last cells in title. */
 
586
  first_cell_nr = state->pgc->program_map[0];
 
587
  first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
 
588
  last_cell_nr = state->pgc->nr_of_cells;
 
589
  last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
 
590
 
 
591
  *pos = cur_sector - first_cell->first_sector;
 
592
  *len = last_cell->last_sector - first_cell->first_sector;
 
593
 
 
594
  return DVDNAV_STATUS_OK;
 
595
}
 
596
 
 
597
uint32_t dvdnav_describe_title_chapters(dvdnav_t *this, int32_t title, uint64_t **times, uint64_t *duration) {
 
598
  int32_t retval=0;
 
599
  uint16_t parts, i;
 
600
  title_info_t *ptitle = NULL;
 
601
  ptt_info_t *ptt = NULL;
 
602
  ifo_handle_t *ifo = NULL;
 
603
  pgc_t *pgc;
 
604
  cell_playback_t *cell;
 
605
  uint64_t length, *tmp=NULL;
 
606
 
 
607
  *times = NULL;
 
608
  *duration = 0;
 
609
  pthread_mutex_lock(&this->vm_lock);
 
610
  if(!this->vm->vmgi) {
 
611
    printerr("Bad VM state or missing VTSI.");
 
612
    goto fail;
 
613
  }
 
614
  if(!this->started) {
 
615
    /* don't report an error but be nice */
 
616
    vm_start(this->vm);
 
617
    this->started = 1;
 
618
  }
 
619
  ifo = vm_get_title_ifo(this->vm, title);
 
620
  if(!ifo || !ifo->vts_pgcit) {
 
621
    printerr("Couldn't open IFO for chosen title, exit.");
 
622
    retval = 0;
 
623
    goto fail;
 
624
  }
 
625
 
 
626
  ptitle = &this->vm->vmgi->tt_srpt->title[title-1];
 
627
  parts = ptitle->nr_of_ptts;
 
628
  ptt = ifo->vts_ptt_srpt->title[ptitle->vts_ttn-1].ptt;
 
629
 
 
630
  tmp = calloc(1, sizeof(uint64_t)*parts);
 
631
  if(!tmp)
 
632
    goto fail;
 
633
 
 
634
  length = 0;
 
635
  for(i=0; i<parts; i++) {
 
636
    uint32_t cellnr, endcellnr;
 
637
    if (ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc_start_byte >= ifo->vts_pgcit->last_byte) {
 
638
      printerr("PGC start out of bounds");
 
639
      continue;
 
640
    }
 
641
    pgc = ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc;
 
642
    if (pgc == NULL) {
 
643
      printerr("PGC missing.");
 
644
      continue;
 
645
    }
 
646
    if(ptt[i].pgn > pgc->nr_of_programs) {
 
647
      printerr("WRONG part number.");
 
648
      goto fail;
 
649
    }
 
650
 
 
651
    if (pgc->nr_of_cells == 0) {
 
652
      printerr("Number of cells cannot be 0");
 
653
      continue;
 
654
    }
 
655
    if ((cellnr = pgc->program_map[ptt[i].pgn-1]) == 0) {
 
656
      printerr("Cell new row cannot be 0");
 
657
      continue;
 
658
    }
 
659
 
 
660
    if(ptt[i].pgn < pgc->nr_of_programs)
 
661
      endcellnr = pgc->program_map[ptt[i].pgn];
 
662
    else
 
663
      endcellnr = 0;
 
664
 
 
665
    do {
 
666
      cell = &pgc->cell_playback[cellnr-1];
 
667
      if(!(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK &&
 
668
           cell->block_mode != BLOCK_MODE_FIRST_CELL
 
669
      ))
 
670
      {
 
671
        tmp[i] = length + dvdnav_convert_time(&cell->playback_time);
 
672
        length = tmp[i];
 
673
      }
 
674
      cellnr++;
 
675
    } while(cellnr < endcellnr);
 
676
  }
 
677
  *duration = length;
 
678
  vm_ifo_close(ifo);
 
679
  ifo = NULL;
 
680
  retval = parts;
 
681
  *times = tmp;
 
682
 
 
683
fail:
 
684
  pthread_mutex_unlock(&this->vm_lock);
 
685
  if(!retval && ifo)
 
686
    vm_ifo_close(ifo);
 
687
  if(!retval && tmp)
 
688
    free(tmp);
 
689
  return retval;
 
690
}