bluray.c 23.8 KB
Newer Older
1 2 3 4 5
/*
 * This file is part of libbluray
 * Copyright (C) 2009-2010  cRTrn13
 * Copyright (C) 2009-2010  John Stebbins
 *
gates's avatar
gates committed
6 7 8 9
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
10
 *
gates's avatar
gates committed
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
gates's avatar
gates committed
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
15
 *
gates's avatar
gates committed
16 17 18
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23
 *
 * In addition, as a special exception, the copyright holders of libbluray
 * gives permission to link the code of its release of libbluray with the
 * OpenSSL project's "OpenSSL" library (or with modified versions of it
 * that use the same license as the "OpenSSL" library), and distribute
24 25 26 27 28
 * the linked library.  You must obey the GNU Lesser General Public
 * License in all respects for all of the code used other than "OpenSSL".
 * If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so.  If you do
 * not wish to do so, delete this exception statement from your version.
29 30
 */

Accident's avatar
 
Accident committed
31 32 33 34
#if HAVE_CONFIG_H
#include "config.h"
#endif

35
#include "bluray.h"
hpi1's avatar
hpi1 committed
36
#include "register.h"
37 38 39 40 41 42 43
#include "util/macro.h"
#include "util/logging.h"
#include "util/strutl.h"
#include "file/dl.h"
#include "bdnav/navigation.h"
#include "bdnav/index_parse.h"

44
#ifndef USING_DLOPEN
45
#include <libaacs/aacs.h>
46
#include <libbdplus/bdplus.h>
47
#endif
48
#include <stdlib.h>
49 50
#include <inttypes.h>
#include <string.h>
51

j45's avatar
j45 committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
static int _open_m2ts(BLURAY *bd)
{
    char *f_name;

    f_name = str_printf("%s" DIR_SEP "BDMV" DIR_SEP "STREAM" DIR_SEP "%s",
                        bd->device_path, bd->clip->name);

    bd->clip_pos = (uint64_t)bd->clip->start_pkt * 192;
    bd->clip_block_pos = (bd->clip_pos / 6144) * 6144;

    if (bd->fp != NULL) {
        file_close(bd->fp);
    }
    if ((bd->fp = file_open(f_name, "rb"))) {
        file_seek(bd->fp, 0, SEEK_END);
        if ((bd->clip_size = file_tell(bd->fp))) {
            file_seek(bd->fp, bd->clip_block_pos, SEEK_SET);
            bd->int_buf_off = 6144;
            X_FREE(f_name);

            if (bd->h_libbdplus && bd->bdplus) {
                fptr_p_void bdplus_set_title;
                bdplus_set_title = dl_dlsym(bd->h_libbdplus, "bdplus_set_title");
                if (bdplus_set_title)
                    bdplus_set_title(bd->bdplus, bd->clip->clip_id);
            }
            return 1;
        }

j45's avatar
j45 committed
81
        DEBUG(DBG_BLURAY, "Clip %s empty! (%p)\n", f_name, bd);
j45's avatar
j45 committed
82 83
    }

Accident's avatar
Accident committed
84
    DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to open clip %s! (%p)\n",
j45's avatar
j45 committed
85 86 87 88 89
          f_name, bd);

    X_FREE(f_name);
    return 0;
}
cRTrn13's avatar
cRTrn13 committed
90

91 92
static int _libaacs_open(BLURAY *bd, const char *keyfile_path)
{
93
#ifdef USING_DLOPEN
94 95 96 97 98
#ifdef __APPLE__
    if ((bd->h_libaacs = dl_dlopen("libaacs.dylib"))) {
#else
    if ((bd->h_libaacs = dl_dlopen("libaacs.so.0"))) {
#endif
99 100
        DEBUG(DBG_BLURAY, "Downloaded libaacs (%p)\n", bd->h_libaacs);

101
        fptr_p_void fptr = dl_dlsym(bd->h_libaacs, "aacs_open");
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
        bd->libaacs_decrypt_unit = dl_dlsym(bd->h_libaacs, "aacs_decrypt_unit");

        if (fptr && bd->libaacs_decrypt_unit) {
            if ((bd->aacs = fptr(bd->device_path, keyfile_path))) {
                DEBUG(DBG_BLURAY, "Opened libaacs (%p)\n", bd->aacs);
                return 1;
            }
            DEBUG(DBG_BLURAY, "aacs_open() failed!\n");
        } else {
            DEBUG(DBG_BLURAY, "libaacs dlsym failed!\n");
        }
        dl_dlclose(bd->h_libaacs);
    } else {
        DEBUG(DBG_BLURAY, "libaacs not found!\n");
    }
117
#else
118 119
    bd->h_libaacs = NULL;
    DEBUG(DBG_BLURAY, "Using libaacs via normal linking\n");
120

121
    bd->libaacs_decrypt_unit = &aacs_decrypt_unit;
122

123 124 125 126
    if (bd->libaacs_decrypt_unit) {
        if ((bd->aacs = aacs_open(bd->device_path, keyfile_path))) {
            DEBUG(DBG_BLURAY, "Opened libaacs (%p)\n", bd->aacs);
            return 1;
127
        }
128
        DEBUG(DBG_BLURAY, "aacs_open() failed!\n");
129 130
    }
#endif
131 132 133 134 135 136 137

    bd->h_libaacs = NULL;
    bd->libaacs_decrypt_unit = NULL;

    return 0;
}

138 139 140 141 142 143 144 145 146 147 148 149 150 151
int _index_open(BLURAY *bd)
{
    char *file;

    file = str_printf("%s/BDMV/index.bdmv", bd->device_path);
    bd->index = indx_parse(file);
    X_FREE(file);
    if (!bd->index) {
        return 0;
    }

    return 1;
}

cRTrn13's avatar
cRTrn13 committed
152 153
BLURAY *bd_open(const char* device_path, const char* keyfile_path)
{
154
    BLURAY *bd = calloc(1, sizeof(BLURAY));
cRTrn13's avatar
cRTrn13 committed
155

cRTrn13's avatar
cRTrn13 committed
156
    if (device_path) {
Accident's avatar
 
Accident committed
157

158 159
        bd->device_path = (char*)malloc(strlen(device_path) + 1);
        strcpy(bd->device_path, device_path);
cRTrn13's avatar
cRTrn13 committed
160

cRTrn13's avatar
cRTrn13 committed
161 162 163 164
        bd->aacs = NULL;
        bd->h_libaacs = NULL;
        bd->fp = NULL;

165
        _libaacs_open(bd, keyfile_path);
166 167 168 169

        // Take a quick stab to see if we want/need bdplus
        // we should fix this, and add various string functions.
        {
Accident's avatar
 
Accident committed
170 171 172
            uint8_t vid[16] = {
                0xC5,0x43,0xEF,0x2A,0x15,0x0E,0x50,0xC4,0xE2,0xCA,
                0x71,0x65,0xB1,0x7C,0xA7,0xCB}; // FIXME
173 174 175 176 177 178 179
            FILE_H *fd;
            char *tmp = NULL;
            tmp = str_printf("%s/BDSVM/00000.svm", bd->device_path);
            if ((fd = file_open(tmp, "rb"))) {
                file_close(fd);

                DEBUG(DBG_BDPLUS, "attempting to load libbdplus\n");
180
#ifdef USING_DLOPEN
181 182 183 184 185
#ifdef __APPLE__
                if ((bd->h_libbdplus = dl_dlopen("libbdplus.dylib"))) {
#else
                if ((bd->h_libbdplus = dl_dlopen("libbdplus.so.0"))) {
#endif
j45's avatar
j45 committed
186
                    DEBUG(DBG_BLURAY, "Downloaded libbdplus (%p)\n",
187 188 189 190 191 192 193 194 195 196 197 198
                          bd->h_libbdplus);

                    fptr_p_void bdplus_init = dl_dlsym(bd->h_libbdplus, "bdplus_init");
                    //bdplus_t *bdplus_init(path,configfile_path,*vid );
                    if (bdplus_init)
                        bd->bdplus = bdplus_init(device_path, keyfile_path, vid);

                    // Since we will call these functions a lot, we assign them
                    // now.
                    bd->bdplus_seek  = dl_dlsym(bd->h_libbdplus, "bdplus_seek");
                    bd->bdplus_fixup = dl_dlsym(bd->h_libbdplus, "bdplus_fixup");

199 200
                }
#else
201 202
                bd->h_libbdplus = NULL;
                DEBUG(DBG_BLURAY,"Using libbdplus via normal linking\n");
203

204
                bd->bdplus = bdplus_init(device_path, keyfile_path, vid);
205

206 207 208 209
                // Since we will call these functions a lot, we assign them
                // now.
                bd->bdplus_seek  = &bdplus_seek;
                bd->bdplus_fixup = &bdplus_fixup;
210
#endif
211 212 213 214
            } // file_open
            X_FREE(tmp);
        }

215
        _index_open(bd);
216

hpi1's avatar
hpi1 committed
217 218
        bd->regs = bd_registers_init();

j45's avatar
j45 committed
219
        DEBUG(DBG_BLURAY, "BLURAY initialized! (%p)\n", bd);
cRTrn13's avatar
cRTrn13 committed
220
    } else {
cRTrn13's avatar
cRTrn13 committed
221 222
        X_FREE(bd);

cRTrn13's avatar
cRTrn13 committed
223
        DEBUG(DBG_BLURAY | DBG_CRIT, "No device path provided!\n");
cRTrn13's avatar
cRTrn13 committed
224 225 226 227 228 229 230
    }

    return bd;
}

void bd_close(BLURAY *bd)
{
231 232
    if (bd->h_libaacs && bd->aacs) {
        fptr_p_void fptr = dl_dlsym(bd->h_libaacs, "aacs_close");
Accident's avatar
 
Accident committed
233
        fptr(bd->aacs);  // FIXME: NULL
cRTrn13's avatar
cRTrn13 committed
234

235
        dl_dlclose(bd->h_libaacs);
cRTrn13's avatar
cRTrn13 committed
236 237
    }

238 239 240 241 242 243 244
    if (bd->h_libbdplus && bd->bdplus) {
        fptr_p_void bdplus_free = dl_dlsym(bd->h_libbdplus, "bdplus_free");
        if (bdplus_free) bdplus_free(bd->bdplus);
        bd->bdplus = NULL;

        dl_dlclose(bd->h_libbdplus);
        bd->h_libbdplus = NULL;
cRTrn13's avatar
cRTrn13 committed
245

246 247
        bd->bdplus_seek  = NULL;
        bd->bdplus_fixup = NULL;
248
    }
cRTrn13's avatar
cRTrn13 committed
249

cRTrn13's avatar
cRTrn13 committed
250 251 252
    if (bd->fp) {
        file_close(bd->fp);
    }
cRTrn13's avatar
cRTrn13 committed
253

j45's avatar
j45 committed
254 255 256 257 258 259
    if (bd->title_list != NULL) {
        nav_free_title_list(bd->title_list);
    }
    if (bd->title != NULL) {
        nav_title_close(bd->title);
    }
260 261 262
    if (bd->index != NULL) {
        indx_free(bd->index);
    }
j45's avatar
j45 committed
263

hpi1's avatar
hpi1 committed
264 265
    bd_registers_free(bd->regs);

266 267
    X_FREE(bd->device_path);

j45's avatar
j45 committed
268
    DEBUG(DBG_BLURAY, "BLURAY destroyed! (%p)\n", bd);
cRTrn13's avatar
cRTrn13 committed
269 270

    X_FREE(bd);
cRTrn13's avatar
cRTrn13 committed
271
}
cRTrn13's avatar
cRTrn13 committed
272

j45's avatar
j45 committed
273 274 275 276 277 278 279 280 281 282 283 284 285
int _read_block(BLURAY *bd)
{
    const int len = 6144;

    if (bd->fp) {
        DEBUG(DBG_BLURAY, "Reading unit [%d bytes] at %"PRIu64"... (%p)\n",
              len, bd->clip_block_pos, bd);

        if (len + bd->clip_block_pos <= bd->clip_size) {
            int read_len;

            if ((read_len = file_read(bd->fp, bd->int_buf, len))) {
                if (read_len != len)
j45's avatar
j45 committed
286
                    DEBUG(DBG_BLURAY | DBG_CRIT, "Read %d bytes at %"PRIu64" ; requested %d ! (%p)\n", read_len, bd->clip_block_pos, len, bd);
j45's avatar
j45 committed
287

288 289 290 291 292 293
                if (bd->libaacs_decrypt_unit) {
                    if (!bd->libaacs_decrypt_unit(bd->aacs, bd->int_buf, len, bd->clip_block_pos)) {
                        DEBUG(DBG_BLURAY, "Unable decrypt unit! (%p)\n", bd);

                        return 0;
                    } // decrypt
j45's avatar
j45 committed
294 295 296 297 298 299 300 301
                } // aacs

                bd->clip_block_pos += len;

                // bdplus fixup, if required.
                if (bd->bdplus_fixup && bd->bdplus) {
                    int32_t numFixes;
                    numFixes = bd->bdplus_fixup(bd->bdplus, len, bd->int_buf);
Accident's avatar
Accident committed
302
#if 1
j45's avatar
j45 committed
303
                    if (numFixes) {
Accident's avatar
Accident committed
304
                        DEBUG(DBG_BDPLUS,
j45's avatar
j45 committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
                              "BDPLUS did %u fixups\n", numFixes);
                    }
#endif

                }

                DEBUG(DBG_BLURAY, "Read unit OK! (%p)\n", bd);

                return 1;
            }

            DEBUG(DBG_BLURAY | DBG_CRIT, "Read %d bytes at %"PRIu64" failed ! (%p)\n", len, bd->clip_block_pos, bd);

            return 0;
        }

        DEBUG(DBG_BLURAY | DBG_CRIT, "Read past EOF ! (%p)\n", bd);

        return 0;
    }

    DEBUG(DBG_BLURAY, "No valid title selected! (%p)\n", bd);

    return 0;
}

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
int64_t bd_seek_time(BLURAY *bd, uint64_t tick)
{
    uint32_t clip_pkt, out_pkt;
    NAV_CLIP *clip;

    if (bd->seamless_angle_change) {
        bd->clip = nav_set_angle(bd->title, bd->clip, bd->request_angle);
        bd->angle = bd->request_angle;
        bd->seamless_angle_change = 0;
    }
    if (tick < bd->title->duration) {
        tick /= 2;

        // Find the closest access unit to the requested position
        clip = nav_time_search(bd->title, tick, &clip_pkt, &out_pkt);
        if (clip->ref != bd->clip->ref) {
            // The position is in a new clip
            bd->clip = clip;
            if (!_open_m2ts(bd)) {
                return -1;
            }
        }
        bd->s_pos = (uint64_t)out_pkt * 192;
        bd->clip_pos = (uint64_t)clip_pkt * 192;
        bd->clip_block_pos = (bd->clip_pos / 6144) * 6144;

        file_seek(bd->fp, bd->clip_block_pos, SEEK_SET);

        bd->int_buf_off = 6144;

        DEBUG(DBG_BLURAY, "Seek to %"PRIu64" (%p)\n",
              bd->s_pos, bd);
        if (bd->bdplus_seek && bd->bdplus)
            bd->bdplus_seek(bd->bdplus, bd->clip_block_pos);

        return bd->s_pos;
    }

    return bd->s_pos;
}

j45's avatar
j45 committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
int64_t bd_seek_chapter(BLURAY *bd, int chapter)
{
    uint32_t clip_pkt, out_pkt;
    NAV_CLIP *clip;

    // Find the closest access unit to the requested position
    clip = nav_chapter_search(bd->title, chapter, &clip_pkt, &out_pkt);
    if (clip->ref != bd->clip->ref) {
        // The position is in a new clip
        bd->clip = clip;
        if (!_open_m2ts(bd)) {
            return -1;
        }
    }
    bd->s_pos = (uint64_t)out_pkt * 192;
    bd->clip_pos = (uint64_t)clip_pkt * 192;
    bd->clip_block_pos = (bd->clip_pos / 6144) * 6144;

    file_seek(bd->fp, bd->clip_block_pos, SEEK_SET);

    bd->int_buf_off = 6144;

    DEBUG(DBG_BLURAY, "Seek to %"PRIu64" (%p)\n",
          bd->s_pos, bd);
    if (bd->bdplus_seek && bd->bdplus)
        bd->bdplus_seek(bd->bdplus, bd->clip_block_pos);

    return bd->s_pos;
}

402 403 404 405 406 407 408 409 410 411
int64_t bd_chapter_pos(BLURAY *bd, int chapter)
{
    uint32_t clip_pkt, out_pkt;
    NAV_CLIP *clip;

    // Find the closest access unit to the requested position
    clip = nav_chapter_search(bd->title, chapter, &clip_pkt, &out_pkt);
    return (int64_t)out_pkt * 192;
}

j45's avatar
j45 committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
int64_t bd_seek_mark(BLURAY *bd, int mark)
{
    uint32_t clip_pkt, out_pkt;
    NAV_CLIP *clip;

    // Find the closest access unit to the requested position
    clip = nav_mark_search(bd->title, mark, &clip_pkt, &out_pkt);
    if (clip->ref != bd->clip->ref) {
        // The position is in a new clip
        bd->clip = clip;
        if (!_open_m2ts(bd)) {
            return -1;
        }
    }
    bd->s_pos = (uint64_t)out_pkt * 192;
    bd->clip_pos = (uint64_t)clip_pkt * 192;
    bd->clip_block_pos = (bd->clip_pos / 6144) * 6144;

    file_seek(bd->fp, bd->clip_block_pos, SEEK_SET);

    bd->int_buf_off = 6144;

    DEBUG(DBG_BLURAY, "Seek to %"PRIu64" (%p)\n",
          bd->s_pos, bd);
    if (bd->bdplus_seek && bd->bdplus)
        bd->bdplus_seek(bd->bdplus, bd->clip_block_pos);

    return bd->s_pos;
}

j45's avatar
j45 committed
442
int64_t bd_seek(BLURAY *bd, uint64_t pos)
cRTrn13's avatar
cRTrn13 committed
443
{
j45's avatar
j45 committed
444 445 446
    uint32_t pkt, clip_pkt, out_pkt, out_time;
    NAV_CLIP *clip;

447 448 449 450 451
    if (bd->seamless_angle_change) {
        bd->clip = nav_set_angle(bd->title, bd->clip, bd->request_angle);
        bd->angle = bd->request_angle;
        bd->seamless_angle_change = 0;
    }
cRTrn13's avatar
cRTrn13 committed
452
    if (pos < bd->s_size) {
j45's avatar
j45 committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466
        pkt = pos / 192;

        // Find the closest access unit to the requested position
        clip = nav_packet_search(bd->title, pkt, &clip_pkt, &out_pkt, &out_time);
        if (clip->ref != bd->clip->ref) {
            // The position is in a new clip
            bd->clip = clip;
            if (!_open_m2ts(bd)) {
                return -1;
            }
        }
        bd->s_pos = (uint64_t)out_pkt * 192;
        bd->clip_pos = (uint64_t)clip_pkt * 192;
        bd->clip_block_pos = (bd->clip_pos / 6144) * 6144;
cRTrn13's avatar
cRTrn13 committed
467

j45's avatar
j45 committed
468
        file_seek(bd->fp, bd->clip_block_pos, SEEK_SET);
469

470 471
        bd->int_buf_off = 6144;

j45's avatar
j45 committed
472
        DEBUG(DBG_BLURAY, "Seek to %"PRIu64" (%p)\n",
Accident's avatar
 
Accident committed
473
              bd->s_pos, bd);
474
        if (bd->bdplus_seek && bd->bdplus)
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
            bd->bdplus_seek(bd->bdplus, bd->clip_block_pos);

        return bd->s_pos;
    }

    return bd->s_pos;
}

static int64_t _clip_seek_time(BLURAY *bd, uint64_t tick)
{
    uint32_t clip_pkt, out_pkt;

    if (tick < bd->clip->out_time) {

        // Find the closest access unit to the requested position
        nav_clip_time_search(bd->clip, tick, &clip_pkt, &out_pkt);
        bd->s_pos = (uint64_t)out_pkt * 192;
        bd->clip_pos = (uint64_t)clip_pkt * 192;
        bd->clip_block_pos = (bd->clip_pos / 6144) * 6144;

        file_seek(bd->fp, bd->clip_block_pos, SEEK_SET);

        bd->int_buf_off = 6144;

        DEBUG(DBG_BLURAY, "Clip seek to %"PRIu64" (%p)\n",
              bd->s_pos, bd);
        if (bd->bdplus_seek && bd->bdplus)
            bd->bdplus_seek(bd->bdplus, bd->clip_block_pos);
503

j45's avatar
j45 committed
504
        return bd->s_pos;
cRTrn13's avatar
cRTrn13 committed
505 506 507 508 509 510 511
    }

    return bd->s_pos;
}

int bd_read(BLURAY *bd, unsigned char *buf, int len)
{
j45's avatar
j45 committed
512
    int out_len;
cRTrn13's avatar
cRTrn13 committed
513

j45's avatar
j45 committed
514 515 516 517 518 519 520
    if (bd->fp) {
        out_len = 0;
        DEBUG(DBG_BLURAY, "Reading [%d bytes] at %"PRIu64"... (%p)\n", len, bd->s_pos, bd);

        while (len > 0) {
            uint32_t clip_pkt;

521
            unsigned int size = len;
j45's avatar
j45 committed
522 523
            // Do we need to read more data?
            clip_pkt = bd->clip_pos / 192;
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
            if (bd->seamless_angle_change) {
                if (clip_pkt >= bd->angle_change_pkt) {
                    if (clip_pkt >= bd->clip->end_pkt) {
                        bd->clip = nav_next_clip(bd->title, bd->clip);
                        if (!_open_m2ts(bd)) {
                            return -1;
                        }
                        bd->s_pos = bd->clip->pos;
                    } else {
                        bd->clip = nav_set_angle(bd->title, bd->clip, bd->request_angle);
                        _clip_seek_time(bd, bd->angle_change_time);
                    }
                    bd->angle = bd->request_angle;
                    bd->seamless_angle_change = 0;
                } else {
539
                    uint64_t angle_pos;
540

541
                    angle_pos = bd->angle_change_pkt * 192;
542 543 544 545 546 547
                    if (angle_pos - bd->clip_pos < size)
                    {
                        size = angle_pos - bd->clip_pos;
                    }
                }
            }
j45's avatar
j45 committed
548 549 550
            if (bd->int_buf_off == 6144 || clip_pkt >= bd->clip->end_pkt) {

                // Do we need to get the next clip?
551 552 553 554 555
                if (bd->clip == NULL) {
                    // We previously reached the last clip.  Nothing
                    // else to read.
                    return -1;
                }
j45's avatar
j45 committed
556 557 558
                if (clip_pkt >= bd->clip->end_pkt) {
                    bd->clip = nav_next_clip(bd->title, bd->clip);
                    if (bd->clip == NULL) {
j45's avatar
j45 committed
559
                        DEBUG(DBG_BLURAY, "End of title (%p)\n", bd);
j45's avatar
j45 committed
560 561 562 563 564 565 566 567 568 569 570 571 572 573
                        return out_len;
                    }
                    if (!_open_m2ts(bd)) {
                        return -1;
                    }
                }
                if (_read_block(bd)) {

                    bd->int_buf_off = bd->clip_pos % 6144;

                } else {
                    return out_len;
                }
            }
574
            if (size > (unsigned int)6144 - bd->int_buf_off) {
j45's avatar
j45 committed
575 576 577 578 579 580 581 582 583 584
                size = 6144 - bd->int_buf_off;
            }
            memcpy(buf, bd->int_buf + bd->int_buf_off, size);
            buf += size;
            len -= size;
            out_len += size;
            bd->clip_pos += size;
            bd->int_buf_off += size;
            bd->s_pos += size;
        }
585

j45's avatar
j45 committed
586
        DEBUG(DBG_BLURAY, "%d bytes read OK! (%p)\n", out_len, bd);
cRTrn13's avatar
cRTrn13 committed
587

j45's avatar
j45 committed
588 589
        return out_len;
    }
Accident's avatar
 
Accident committed
590

j45's avatar
j45 committed
591
    DEBUG(DBG_BLURAY, "No valid title selected! (%p)\n", bd);
592

j45's avatar
j45 committed
593
    return -1;
cRTrn13's avatar
cRTrn13 committed
594
}
cRTrn13's avatar
cRTrn13 committed
595

hpi1's avatar
hpi1 committed
596
static int _open_playlist(BLURAY *bd, const char *f_name)
cRTrn13's avatar
cRTrn13 committed
597
{
598 599 600 601
    if (bd->title) {
        nav_title_close(bd->title);
    }

j45's avatar
j45 committed
602 603
    bd->title = nav_title_open(bd->device_path, f_name);
    if (bd->title == NULL) {
604
        DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to open title %s! (%p)\n",
j45's avatar
j45 committed
605 606 607
              f_name, bd);
        return 0;
    }
cRTrn13's avatar
cRTrn13 committed
608

609 610
    bd->angle = 0;
    bd->seamless_angle_change = 0;
cRTrn13's avatar
cRTrn13 committed
611
    bd->s_pos = 0;
j45's avatar
j45 committed
612
    bd->s_size = (uint64_t)bd->title->packets * 192;
cRTrn13's avatar
cRTrn13 committed
613

j45's avatar
j45 committed
614 615 616
    // Get the initial clip of the playlist
    bd->clip = nav_next_clip(bd->title, NULL);
    if (_open_m2ts(bd)) {
j45's avatar
j45 committed
617
        DEBUG(DBG_BLURAY, "Title %s selected! (%p)\n", f_name, bd);
j45's avatar
j45 committed
618 619 620 621
        return 1;
    }
    return 0;
}
622

hpi1's avatar
hpi1 committed
623 624 625 626 627 628 629 630 631 632 633
int bd_select_playlist(BLURAY *bd, uint32_t playlist)
{
    char *f_name = str_printf("%05d.mpls", playlist);
    int result;

    result = _open_playlist(bd, f_name);

    X_FREE(f_name);
    return result;
}

hpi1's avatar
hpi1 committed
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
// Select a title for playback
// The title index is an index into the list
// established by bd_get_titles()
int bd_select_title(BLURAY *bd, uint32_t title_idx)
{
    const char *f_name;

    // Open the playlist
    if (bd->title_list == NULL) {
        DEBUG(DBG_BLURAY, "Title list not yet read! (%p)\n", bd);
        return 0;
    }
    if (bd->title_list->count <= title_idx) {
        DEBUG(DBG_BLURAY, "Invalid title index %d! (%p)\n", title_idx, bd);
        return 0;
    }

    f_name = bd->title_list->title_info[title_idx].name;

    return _open_playlist(bd, f_name);
}

656 657 658 659 660 661 662 663 664 665
int bd_select_angle(BLURAY *bd, int angle)
{
    if (bd->title == NULL) {
        DEBUG(DBG_BLURAY, "Title not yet selected! (%p)\n", bd);
        return 0;
    }
    bd->clip = nav_set_angle(bd->title, bd->clip, angle);
    return 1;
}

666 667 668 669 670
void bd_seamless_angle_change(BLURAY *bd, int angle)
{
    uint32_t clip_pkt;

    clip_pkt = (bd->clip_pos + 191) / 192;
Accident's avatar
Accident committed
671
    bd->angle_change_pkt = nav_angle_change_search(bd->clip, clip_pkt,
672 673 674 675 676
                                                   &bd->angle_change_time);
    bd->request_angle = angle;
    bd->seamless_angle_change = 1;
}

j45's avatar
j45 committed
677 678 679 680
uint64_t bd_get_title_size(BLURAY *bd)
{
    return bd ? bd->s_size : UINT64_C(0);
}
cRTrn13's avatar
cRTrn13 committed
681

j45's avatar
j45 committed
682 683 684 685
uint64_t bd_tell(BLURAY *bd)
{
    return bd ? bd->s_pos : INT64_C(0);
}
686

j45's avatar
j45 committed
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
// This must be called after bd_open() and before bd_select_title().
// Populates the title list in BLURAY.
// Filtering of the returned list is controled throught flags
//  TITLES_ALL              - all titles
//  TITLES_FILTER_DUP_TITLE - remove duplicate titles
//  TITLES_FILTER_DUP_CLIP  - remove titles that have duplicated clips
//  TITLES_RELEVANT         - remove dup titles and clips
//
//  Returns the number of titles found
uint32_t bd_get_titles(BLURAY *bd, uint8_t flags)
{
    if (!bd) {
        DEBUG(DBG_BLURAY | DBG_CRIT, "bd_get_titles(NULL) failed (%p)\n", bd);
        return 0;
    }
702

j45's avatar
j45 committed
703 704
    if (bd->title_list != NULL) {
        nav_free_title_list(bd->title_list);
cRTrn13's avatar
cRTrn13 committed
705
    }
j45's avatar
j45 committed
706
    bd->title_list = nav_get_title_list(bd->device_path, flags);
cRTrn13's avatar
cRTrn13 committed
707

j45's avatar
j45 committed
708 709 710 711
    if (!bd->title_list) {
        DEBUG(DBG_BLURAY | DBG_CRIT, "nav_get_title_list(%s) failed (%p)\n", bd->device_path, bd);
        return 0;
    }
712

j45's avatar
j45 committed
713
    return bd->title_list->count;
cRTrn13's avatar
cRTrn13 committed
714
}
715

j45's avatar
j45 committed
716
BD_TITLE_INFO* bd_get_title_info(BLURAY *bd, uint32_t title_idx)
717
{
j45's avatar
j45 committed
718 719
    NAV_TITLE *title;
    BD_TITLE_INFO *title_info;
720
    unsigned int ii;
j45's avatar
j45 committed
721 722

    if (bd->title_list == NULL) {
j45's avatar
j45 committed
723
        DEBUG(DBG_BLURAY, "Title list not yet read! (%p)\n", bd);
j45's avatar
j45 committed
724 725 726
        return NULL;
    }
    if (bd->title_list->count <= title_idx) {
j45's avatar
j45 committed
727
        DEBUG(DBG_BLURAY, "Invalid title index %d! (%p)\n", title_idx, bd);
j45's avatar
j45 committed
728 729 730 731
        return NULL;
    }
    title = nav_title_open(bd->device_path, bd->title_list->title_info[title_idx].name);
    if (title == NULL) {
Accident's avatar
Accident committed
732
        DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to open title %s! (%p)\n",
j45's avatar
j45 committed
733 734 735 736 737 738 739 740 741 742 743
              bd->title_list->title_info[title_idx].name, bd);
        return NULL;
    }
    title_info = calloc(1, sizeof(BD_TITLE_INFO));
    title_info->idx = title_idx;
    title_info->duration = (uint64_t)title->duration * 2;
    title_info->angle_count = title->angle_count;
    title_info->chapter_count = title->chap_list.count;
    title_info->chapters = calloc(title_info->chapter_count, sizeof(BD_TITLE_CHAPTER));
    for (ii = 0; ii < title_info->chapter_count; ii++) {
        title_info->chapters[ii].idx = ii;
j45's avatar
j45 committed
744 745 746
        title_info->chapters[ii].start = (uint64_t)title->chap_list.mark[ii].title_time * 2;
        title_info->chapters[ii].duration = (uint64_t)title->chap_list.mark[ii].duration * 2;
        title_info->chapters[ii].offset = (uint64_t)title->chap_list.mark[ii].title_pkt * 192;
j45's avatar
j45 committed
747 748 749
    }
    nav_title_close(title);
    return title_info;
750
}
cRTrn13's avatar
cRTrn13 committed
751

j45's avatar
j45 committed
752
void bd_free_title_info(BD_TITLE_INFO *title_info)
cRTrn13's avatar
cRTrn13 committed
753
{
j45's avatar
j45 committed
754 755
    X_FREE(title_info->chapters);
    X_FREE(title_info);
cRTrn13's avatar
cRTrn13 committed
756
}
j45's avatar
j45 committed
757

hpi1's avatar
hpi1 committed
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
/*
 * player settings
 */

int bd_set_player_setting(BLURAY *bd, uint32_t idx, uint32_t value)
{
    static const struct { uint32_t idx; uint32_t  psr; } map[] = {
        { BD_PLAYER_SETTING_PARENTAL,       PSR_PARENTAL },
        { BD_PLAYER_SETTING_AUDIO_CAP,      PSR_AUDIO_CAP },
        { BD_PLAYER_SETTING_AUDIO_LANG,     PSR_AUDIO_LANG },
        { BD_PLAYER_SETTING_PG_LANG,        PSR_PG_AND_SUB_LANG },
        { BD_PLAYER_SETTING_MENU_LANG,      PSR_MENU_LANG },
        { BD_PLAYER_SETTING_COUNTRY_CODE,   PSR_COUNTRY },
        { BD_PLAYER_SETTING_REGION_CODE,    PSR_REGION },
        { BD_PLAYER_SETTING_VIDEO_CAP,      PSR_VIDEO_CAP },
        { BD_PLAYER_SETTING_TEXT_CAP,       PSR_TEXT_CAP },
        { BD_PLAYER_SETTING_PLAYER_PROFILE, PSR_PROFILE_VERSION },
    };

    unsigned i;

    if (idx == BD_PLAYER_SETTING_PLAYER_PROFILE) {
        value = ((value & 0xf) << 16) | 0x0200;  /* version fixed to BD-RO Part 3, version 2.0 */
    }

    for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
        if (idx == map[i].idx) {
            return bd_psr_setting_write(bd->regs, idx, value);
        }
    }

    return 0;
}

static uint32_t _string_to_uint(const char *s, int n)
{
    uint32_t val = 0;

    if (n > 4)
        n = 4;

    while (n--)
        val = (val << 8) | s[n];

    return val;
}

int bd_set_player_setting_str(BLURAY *bd, uint32_t idx, const char *s)
{
    switch (idx) {
        case BD_PLAYER_SETTING_AUDIO_LANG:
        case BD_PLAYER_SETTING_PG_LANG:
        case BD_PLAYER_SETTING_MENU_LANG:
            return bd_set_player_setting(bd, idx, s ? _string_to_uint(s, 3) : 0xffffff);

        case BD_PLAYER_SETTING_COUNTRY_CODE:
            return bd_set_player_setting(bd, idx, s ? _string_to_uint(s, 3) : 0xffff  );

        default:
            return 0;
    }
}