Blame src/libbluray/bluray.c

<
Packit 5e46da
/*
Packit 5e46da
 * This file is part of libbluray
Packit 5e46da
 * Copyright (C) 2009-2010  Obliter0n
Packit 5e46da
 * Copyright (C) 2009-2010  John Stebbins
Packit 5e46da
 * Copyright (C) 2010-2017  Petri Hintukainen
Packit 5e46da
 *
Packit 5e46da
 * This library is free software; you can redistribute it and/or
Packit 5e46da
 * modify it under the terms of the GNU Lesser General Public
Packit 5e46da
 * License as published by the Free Software Foundation; either
Packit 5e46da
 * version 2.1 of the License, or (at your option) any later version.
Packit 5e46da
 *
Packit 5e46da
 * This library is distributed in the hope that it will be useful,
Packit 5e46da
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5e46da
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 5e46da
 * Lesser General Public License for more details.
Packit 5e46da
 *
Packit 5e46da
 * You should have received a copy of the GNU Lesser General Public
Packit 5e46da
 * License along with this library. If not, see
Packit 5e46da
 * <http://www.gnu.org/licenses/>.
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if HAVE_CONFIG_H
Packit 5e46da
#include "config.h"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include "bluray-version.h"
Packit 5e46da
#include "bluray.h"
Packit 5e46da
#include "bluray_internal.h"
Packit 5e46da
#include "keys.h"
Packit 5e46da
#include "register.h"
Packit 5e46da
#include "util/array.h"
Packit 5e46da
#include "util/event_queue.h"
Packit 5e46da
#include "util/macro.h"
Packit 5e46da
#include "util/logging.h"
Packit 5e46da
#include "util/strutl.h"
Packit 5e46da
#include "util/mutex.h"
Packit 5e46da
#include "bdnav/bdid_parse.h"
Packit 5e46da
#include "bdnav/navigation.h"
Packit 5e46da
#include "bdnav/index_parse.h"
Packit 5e46da
#include "bdnav/meta_parse.h"
Packit 5e46da
#include "bdnav/meta_data.h"
Packit 5e46da
#include "bdnav/clpi_parse.h"
Packit 5e46da
#include "bdnav/sound_parse.h"
Packit 5e46da
#include "bdnav/uo_mask.h"
Packit 5e46da
#include "hdmv/hdmv_vm.h"
Packit 5e46da
#include "hdmv/mobj_parse.h"
Packit 5e46da
#include "decoders/graphics_controller.h"
Packit 5e46da
#include "decoders/hdmv_pids.h"
Packit 5e46da
#include "decoders/m2ts_filter.h"
Packit 5e46da
#include "decoders/overlay.h"
Packit 5e46da
#include "disc/disc.h"
Packit 5e46da
#include "disc/enc_info.h"
Packit 5e46da
#include "file/file.h"
Packit 5e46da
#include "bdj/bdj.h"
Packit 5e46da
#include "bdj/bdjo_parse.h"
Packit 5e46da
Packit 5e46da
#include <stdio.h> // SEEK_
Packit 5e46da
#include <stdlib.h>
Packit 5e46da
#include <inttypes.h>
Packit 5e46da
#include <string.h>
Packit 5e46da
Packit 5e46da
Packit 5e46da
typedef enum {
Packit 5e46da
    title_undef = 0,
Packit 5e46da
    title_hdmv,
Packit 5e46da
    title_bdj,
Packit 5e46da
} BD_TITLE_TYPE;
Packit 5e46da
Packit 5e46da
typedef struct {
Packit 5e46da
    /* current clip */
Packit 5e46da
    NAV_CLIP       *clip;
Packit 5e46da
    BD_FILE_H      *fp;
Packit 5e46da
    uint64_t       clip_size;
Packit 5e46da
    uint64_t       clip_block_pos;
Packit 5e46da
    uint64_t       clip_pos;
Packit 5e46da
Packit 5e46da
    /* current aligned unit */
Packit 5e46da
    uint16_t       int_buf_off;
Packit 5e46da
Packit 5e46da
    /* current stream UO mask (combined from playlist and current clip UO masks) */
Packit 5e46da
    BD_UO_MASK     uo_mask;
Packit 5e46da
Packit 5e46da
    /* internally handled pids */
Packit 5e46da
    uint16_t        ig_pid; /* pid of currently selected IG stream */
Packit 5e46da
    uint16_t        pg_pid; /* pid of currently selected PG stream */
Packit 5e46da
Packit 5e46da
    /* */
Packit 5e46da
    uint8_t         eof_hit;
Packit 5e46da
    uint8_t         encrypted_block_cnt;
Packit 5e46da
    uint8_t         seek_flag;  /* used to fine-tune first read after seek */
Packit 5e46da
Packit 5e46da
    M2TS_FILTER    *m2ts_filter;
Packit 5e46da
} BD_STREAM;
Packit 5e46da
Packit 5e46da
typedef struct {
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
    size_t    clip_size;
Packit 5e46da
    uint8_t  *buf;
Packit 5e46da
} BD_PRELOAD;
Packit 5e46da
Packit 5e46da
struct bluray {
Packit 5e46da
Packit 5e46da
    BD_MUTEX          mutex;  /* protect API function access to internal data */
Packit 5e46da
Packit 5e46da
    /* current disc */
Packit 5e46da
    BD_DISC          *disc;
Packit 5e46da
    BLURAY_DISC_INFO  disc_info;
Packit 5e46da
    BLURAY_TITLE    **titles;  /* titles from disc index */
Packit 5e46da
    META_ROOT        *meta;
Packit 5e46da
    NAV_TITLE_LIST   *title_list;
Packit 5e46da
Packit 5e46da
    /* current playlist */
Packit 5e46da
    NAV_TITLE      *title;
Packit 5e46da
    uint32_t       title_idx;
Packit 5e46da
    uint64_t       s_pos;
Packit 5e46da
Packit 5e46da
    /* streams */
Packit 5e46da
    BD_STREAM      st0;       /* main path */
Packit 5e46da
    BD_PRELOAD     st_ig;     /* preloaded IG stream sub path */
Packit 5e46da
    BD_PRELOAD     st_textst; /* preloaded TextST sub path */
Packit 5e46da
Packit 5e46da
    /* buffer for bd_read(): current aligned unit of main stream (st0) */
Packit 5e46da
    uint8_t        int_buf[6144];
Packit 5e46da
Packit 5e46da
    /* seamless angle change request */
Packit 5e46da
    int            seamless_angle_change;
Packit 5e46da
    uint32_t       angle_change_pkt;
Packit 5e46da
    uint32_t       angle_change_time;
Packit 5e46da
    unsigned       request_angle;
Packit 5e46da
Packit 5e46da
    /* mark tracking */
Packit 5e46da
    uint64_t       next_mark_pos;
Packit 5e46da
    int            next_mark;
Packit 5e46da
Packit 5e46da
    /* player state */
Packit 5e46da
    BD_REGISTERS   *regs;            /* player registers */
Packit 5e46da
    BD_EVENT_QUEUE *event_queue;     /* navigation mode event queue */
Packit 5e46da
    BD_UO_MASK      uo_mask;         /* Current UO mask */
Packit 5e46da
    BD_UO_MASK      title_uo_mask;   /* UO mask from current .bdjo file or Movie Object */
Packit 5e46da
    BD_TITLE_TYPE   title_type;      /* type of current title (in navigation mode) */
Packit 5e46da
    /* Pending action after playlist end
Packit 5e46da
     * BD-J: delayed sending of BDJ_EVENT_END_OF_PLAYLIST
Packit 5e46da
     *       1 - message pending. 3 - message sent.
Packit 5e46da
     */
Packit 5e46da
    uint8_t         end_of_playlist; /* 1 - reached. 3 - processed . */
Packit 5e46da
    uint8_t         app_scr;         /* 1 if application provides presentation timetamps */
Packit 5e46da
Packit 5e46da
    /* HDMV */
Packit 5e46da
    HDMV_VM        *hdmv_vm;
Packit 5e46da
    uint8_t         hdmv_suspended;
Packit 5e46da
Packit 5e46da
    /* BD-J */
Packit 5e46da
    BDJAVA         *bdjava;
Packit 5e46da
    BDJ_STORAGE     bdjstorage;
Packit 5e46da
    uint8_t         bdj_wait_start;  /* BD-J has selected playlist (prefetch) but not yet started playback */
Packit 5e46da
Packit 5e46da
    /* HDMV graphics */
Packit 5e46da
    GRAPHICS_CONTROLLER *graphics_controller;
Packit 5e46da
    SOUND_DATA          *sound_effects;
Packit 5e46da
    BD_UO_MASK           gc_uo_mask;      /* UO mask from current menu page */
Packit 5e46da
    uint32_t             gc_status;
Packit 5e46da
    uint8_t              decode_pg;
Packit 5e46da
Packit 5e46da
    /* TextST */
Packit 5e46da
    uint32_t gc_wakeup_time;  /* stream timestamp of next subtitle */
Packit 5e46da
    uint64_t gc_wakeup_pos;   /* stream position of gc_wakeup_time */
Packit 5e46da
Packit 5e46da
    /* ARGB overlay output */
Packit 5e46da
    void                *argb_overlay_proc_handle;
Packit 5e46da
    bd_argb_overlay_proc_f argb_overlay_proc;
Packit 5e46da
    BD_ARGB_BUFFER      *argb_buffer;
Packit 5e46da
    BD_MUTEX             argb_buffer_mutex;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
/* Stream Packet Number = byte offset / 192. Avoid 64-bit division. */
Packit 5e46da
#define SPN(pos) (((uint32_t)((pos) >> 6)) / 3)
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Library version
Packit 5e46da
 */
Packit 5e46da
void bd_get_version(int *major, int *minor, int *micro)
Packit 5e46da
{
Packit 5e46da
    *major = BLURAY_VERSION_MAJOR;
Packit 5e46da
    *minor = BLURAY_VERSION_MINOR;
Packit 5e46da
    *micro = BLURAY_VERSION_MICRO;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Navigation mode event queue
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _get_event(BLURAY *bd, BD_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    int result = event_queue_get(bd->event_queue, ev);
Packit 5e46da
    if (!result) {
Packit 5e46da
        ev->event = BD_EVENT_NONE;
Packit 5e46da
    }
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _queue_event(BLURAY *bd, uint32_t event, uint32_t param)
Packit 5e46da
{
Packit 5e46da
    int result = 0;
Packit 5e46da
    if (bd->event_queue) {
Packit 5e46da
        BD_EVENT ev = { event, param };
Packit 5e46da
        result = event_queue_put(bd->event_queue, &ev;;
Packit 5e46da
        if (!result) {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY|DBG_CRIT, "_queue_event(%d, %d): queue overflow !\n", event, param);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * PSR utils
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _update_time_psr(BLURAY *bd, uint32_t time)
Packit 5e46da
{
Packit 5e46da
    /*
Packit 5e46da
     * Update PSR8: Presentation Time
Packit 5e46da
     * The PSR8 represents presentation time in the playing interval from IN_time until OUT_time of
Packit 5e46da
     * the current PlayItem, measured in units of a 45 kHz clock.
Packit 5e46da
     */
Packit 5e46da
Packit 5e46da
    if (!bd->title || !bd->st0.clip) {
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
    if (time < bd->st0.clip->in_time) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_update_time_psr(): timestamp before clip start\n");
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
    if (time > bd->st0.clip->out_time) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_update_time_psr(): timestamp after clip end\n");
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_psr_write(bd->regs, PSR_TIME, time);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint32_t _update_time_psr_from_stream(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    /* update PSR_TIME from stream. Not real presentation time (except when seeking), but near enough. */
Packit 5e46da
    NAV_CLIP *clip = bd->st0.clip;
Packit 5e46da
Packit 5e46da
    if (bd->title && clip) {
Packit 5e46da
Packit 5e46da
        uint32_t clip_pkt, clip_time;
Packit 5e46da
        nav_clip_packet_search(bd->st0.clip, SPN(bd->st0.clip_pos), &clip_pkt, &clip_time);
Packit 5e46da
        if (clip_time >= clip->in_time && clip_time <= clip->out_time) {
Packit 5e46da
            _update_time_psr(bd, clip_time);
Packit 5e46da
            return clip_time;
Packit 5e46da
        } else {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY|DBG_CRIT, "%s: no timestamp for SPN %u (got %u). clip %u-%u.\n",
Packit 5e46da
                     clip->name, SPN(bd->st0.clip_pos), clip_time, clip->in_time, clip->out_time);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_stream_psr_by_lang(BD_REGISTERS *regs,
Packit 5e46da
                                       uint32_t psr_lang, uint32_t psr_stream,
Packit 5e46da
                                       uint32_t enable_flag,
Packit 5e46da
                                       MPLS_STREAM *streams, unsigned num_streams,
Packit 5e46da
                                       uint32_t *lang, uint32_t blacklist)
Packit 5e46da
{
Packit 5e46da
    uint32_t preferred_lang;
Packit 5e46da
    int      stream_idx = -1;
Packit 5e46da
    unsigned ii;
Packit 5e46da
    uint32_t stream_lang = 0;
Packit 5e46da
Packit 5e46da
    /* get preferred language */
Packit 5e46da
    preferred_lang = bd_psr_read(regs, psr_lang);
Packit 5e46da
Packit 5e46da
    /* find stream */
Packit 5e46da
    for (ii = 0; ii < num_streams; ii++) {
Packit 5e46da
        if (preferred_lang == str_to_uint32((const char *)streams[ii].lang, 3)) {
Packit 5e46da
            stream_idx = ii;
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* requested language not found ? */
Packit 5e46da
    if (stream_idx < 0) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "Stream with preferred language not found\n");
Packit 5e46da
        /* select first stream */
Packit 5e46da
        stream_idx = 0;
Packit 5e46da
        /* no subtitles if preferred language not found */
Packit 5e46da
        enable_flag = 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    stream_lang = str_to_uint32((const char *)streams[stream_idx].lang, 3);
Packit 5e46da
Packit 5e46da
    /* avoid enabling subtitles if audio is in the same language */
Packit 5e46da
    if (blacklist && blacklist == stream_lang) {
Packit 5e46da
        enable_flag = 0;
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "Subtitles disabled (audio is in the same language)\n");
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (lang) {
Packit 5e46da
        *lang = stream_lang;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* update PSR */
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "Selected stream %d (language %s)\n", stream_idx, streams[stream_idx].lang);
Packit 5e46da
Packit 5e46da
    bd_psr_write_bits(regs, psr_stream,
Packit 5e46da
                      (stream_idx + 1) | enable_flag,
Packit 5e46da
                      0x80000fff);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_clip_psrs(BLURAY *bd, NAV_CLIP *clip)
Packit 5e46da
{
Packit 5e46da
    MPLS_STN *stn = &clip->title->pl->play_item[clip->ref].stn;
Packit 5e46da
    uint32_t audio_lang = 0;
Packit 5e46da
Packit 5e46da
    bd_psr_write(bd->regs, PSR_PLAYITEM, clip->ref);
Packit 5e46da
    bd_psr_write(bd->regs, PSR_TIME,     clip->in_time);
Packit 5e46da
Packit 5e46da
    /* Update selected audio and subtitle stream PSRs when not using menus.
Packit 5e46da
     * Selection is based on language setting PSRs and clip STN.
Packit 5e46da
     */
Packit 5e46da
    if (bd->title_type == title_undef) {
Packit 5e46da
Packit 5e46da
        if (stn->num_audio) {
Packit 5e46da
            _update_stream_psr_by_lang(bd->regs,
Packit 5e46da
                                       PSR_AUDIO_LANG, PSR_PRIMARY_AUDIO_ID, 0,
Packit 5e46da
                                       stn->audio, stn->num_audio,
Packit 5e46da
                                       &audio_lang, 0);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (stn->num_pg) {
Packit 5e46da
            _update_stream_psr_by_lang(bd->regs,
Packit 5e46da
                                       PSR_PG_AND_SUB_LANG, PSR_PG_STREAM, 0x80000000,
Packit 5e46da
                                       stn->pg, stn->num_pg,
Packit 5e46da
                                       NULL, audio_lang);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
    /* Validate selected audio, subtitle and IG stream PSRs when using menus */
Packit 5e46da
    } else {
Packit 5e46da
        uint32_t psr_val;
Packit 5e46da
Packit 5e46da
        if (stn->num_audio) {
Packit 5e46da
            bd_psr_lock(bd->regs);
Packit 5e46da
            psr_val = bd_psr_read(bd->regs, PSR_PRIMARY_AUDIO_ID);
Packit 5e46da
            if (psr_val == 0 || psr_val > stn->num_audio) {
Packit 5e46da
                _update_stream_psr_by_lang(bd->regs,
Packit 5e46da
                                           PSR_AUDIO_LANG, PSR_PRIMARY_AUDIO_ID, 0,
Packit 5e46da
                                           stn->audio, stn->num_audio,
Packit 5e46da
                                           &audio_lang, 0);
Packit 5e46da
            } else {
Packit 5e46da
                audio_lang = str_to_uint32((const char *)stn->audio[psr_val - 1].lang, 3);
Packit 5e46da
            }
Packit 5e46da
            bd_psr_unlock(bd->regs);
Packit 5e46da
        }
Packit 5e46da
        if (stn->num_pg) {
Packit 5e46da
            bd_psr_lock(bd->regs);
Packit 5e46da
            psr_val = bd_psr_read(bd->regs, PSR_PG_STREAM) & 0xfff;
Packit 5e46da
            if ((psr_val == 0) || (psr_val > stn->num_pg)) {
Packit 5e46da
                _update_stream_psr_by_lang(bd->regs,
Packit 5e46da
                                           PSR_PG_AND_SUB_LANG, PSR_PG_STREAM, 0x80000000,
Packit 5e46da
                                           stn->pg, stn->num_pg,
Packit 5e46da
                                           NULL, audio_lang);
Packit 5e46da
            }
Packit 5e46da
            bd_psr_unlock(bd->regs);
Packit 5e46da
        }
Packit 5e46da
        if (stn->num_ig) {
Packit 5e46da
            bd_psr_lock(bd->regs);
Packit 5e46da
            psr_val = bd_psr_read(bd->regs, PSR_IG_STREAM_ID);
Packit 5e46da
            if ((psr_val == 0) || (psr_val > stn->num_ig)) {
Packit 5e46da
                bd_psr_write(bd->regs, PSR_IG_STREAM_ID, 1);
Packit 5e46da
                BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Selected IG stream 1 (stream %d not available)\n", psr_val);
Packit 5e46da
            }
Packit 5e46da
            bd_psr_unlock(bd->regs);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _is_interactive_title(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->titles && bd->title_type != title_undef) {
Packit 5e46da
        unsigned title = bd_psr_read(bd->regs, PSR_TITLE_NUMBER);
Packit 5e46da
        if (title == 0xffff && bd->disc_info.first_play->interactive) {
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
        if (title <= bd->disc_info.num_titles && bd->titles[title]) {
Packit 5e46da
            return bd->titles[title]->interactive;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_chapter_psr(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (!_is_interactive_title(bd) && bd->title->chap_list.count > 0) {
Packit 5e46da
        uint32_t current_chapter = bd_get_current_chapter(bd);
Packit 5e46da
        bd_psr_write(bd->regs, PSR_CHAPTER,  current_chapter + 1);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * PG
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _find_pg_stream(BLURAY *bd, uint16_t *pid, int *sub_path_idx, unsigned *sub_clip_idx, uint8_t *char_code)
Packit 5e46da
{
Packit 5e46da
    unsigned  main_clip_idx = bd->st0.clip ? bd->st0.clip->ref : 0;
Packit 5e46da
    MPLS_PI  *pi        = &bd->title->pl->play_item[main_clip_idx];
Packit 5e46da
    unsigned  pg_stream = bd_psr_read(bd->regs, PSR_PG_STREAM);
Packit 5e46da
Packit 5e46da
#if 0
Packit 5e46da
    /* Enable decoder unconditionally (required for forced subtitles).
Packit 5e46da
       Display flag is checked in graphics controller. */
Packit 5e46da
    /* check PG display flag from PSR */
Packit 5e46da
    if (!(pg_stream & 0x80000000)) {
Packit 5e46da
      return 0;
Packit 5e46da
    }
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
    pg_stream &= 0xfff;
Packit 5e46da
Packit 5e46da
    if (pg_stream > 0 && pg_stream <= pi->stn.num_pg) {
Packit 5e46da
        pg_stream--; /* stream number to table index */
Packit 5e46da
        if (pi->stn.pg[pg_stream].stream_type == 2) {
Packit 5e46da
            *sub_path_idx = pi->stn.pg[pg_stream].subpath_id;
Packit 5e46da
            *sub_clip_idx = pi->stn.pg[pg_stream].subclip_id;
Packit 5e46da
        }
Packit 5e46da
        *pid = pi->stn.pg[pg_stream].pid;
Packit 5e46da
Packit 5e46da
        if (char_code && pi->stn.pg[pg_stream].coding_type == BLURAY_STREAM_TYPE_SUB_TEXT) {
Packit 5e46da
            *char_code = pi->stn.pg[pg_stream].char_code;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "_find_pg_stream(): current PG stream pid 0x%04x sub-path %d\n",
Packit 5e46da
              *pid, *sub_path_idx);
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _init_pg_stream(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    int      pg_subpath = -1;
Packit 5e46da
    unsigned pg_subclip = 0;
Packit 5e46da
    uint16_t pg_pid     = 0;
Packit 5e46da
Packit 5e46da
    bd->st0.pg_pid = 0;
Packit 5e46da
Packit 5e46da
    if (!bd->graphics_controller) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* reset PG decoder and controller */
Packit 5e46da
    gc_run(bd->graphics_controller, GC_CTRL_PG_RESET, 0, NULL);
Packit 5e46da
Packit 5e46da
    if (!bd->decode_pg || !bd->title) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _find_pg_stream(bd, &pg_pid, &pg_subpath, &pg_subclip, NULL);
Packit 5e46da
Packit 5e46da
    /* store PID of main path embedded PG stream */
Packit 5e46da
    if (pg_subpath < 0) {
Packit 5e46da
        bd->st0.pg_pid = pg_pid;
Packit 5e46da
        return !!pg_pid;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_textst_timer(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->st_textst.clip) {
Packit 5e46da
        if (bd->st0.clip_block_pos >= bd->gc_wakeup_pos) {
Packit 5e46da
            GC_NAV_CMDS cmds = {-1, NULL, -1, 0, 0, EMPTY_UO_MASK};
Packit 5e46da
Packit 5e46da
            gc_run(bd->graphics_controller, GC_CTRL_PG_UPDATE, bd->gc_wakeup_time, &cmds);
Packit 5e46da
Packit 5e46da
            bd->gc_wakeup_time = cmds.wakeup_time;
Packit 5e46da
            bd->gc_wakeup_pos = (uint64_t)(int64_t)-1; /* no wakeup */
Packit 5e46da
Packit 5e46da
            /* next event in this clip ? */
Packit 5e46da
            if (cmds.wakeup_time >= bd->st0.clip->in_time && cmds.wakeup_time < bd->st0.clip->out_time) {
Packit 5e46da
                /* find event position in main path clip */
Packit 5e46da
                NAV_CLIP *clip = bd->st0.clip;
Packit 5e46da
                if (clip->cl) {
Packit 5e46da
                    uint32_t spn = clpi_lookup_spn(clip->cl, cmds.wakeup_time, /*before=*/1,
Packit 5e46da
                                                   bd->title->pl->play_item[clip->ref].clip[clip->angle].stc_id);
Packit 5e46da
                    if (spn) {
Packit 5e46da
                        bd->gc_wakeup_pos = (uint64_t)spn * 192L;
Packit 5e46da
                  }
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _init_textst_timer(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->st_textst.clip && bd->st0.clip->cl) {
Packit 5e46da
        uint32_t clip_time;
Packit 5e46da
        clpi_access_point(bd->st0.clip->cl, SPN(bd->st0.clip_block_pos), /*next=*/0, /*angle_change=*/0, &clip_time);
Packit 5e46da
        bd->gc_wakeup_time = clip_time;
Packit 5e46da
        bd->gc_wakeup_pos = 0;
Packit 5e46da
        _update_textst_timer(bd);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * UO mask
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static uint32_t _compressed_mask(BD_UO_MASK mask)
Packit 5e46da
{
Packit 5e46da
    return mask.menu_call | (mask.title_search << 1);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_uo_mask(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    BD_UO_MASK old_mask = bd->uo_mask;
Packit 5e46da
    BD_UO_MASK new_mask;
Packit 5e46da
Packit 5e46da
    new_mask = uo_mask_combine(bd->title_uo_mask, bd->st0.uo_mask);
Packit 5e46da
    new_mask = uo_mask_combine(bd->gc_uo_mask,    new_mask);
Packit 5e46da
    if (_compressed_mask(old_mask) != _compressed_mask(new_mask)) {
Packit 5e46da
        _queue_event(bd, BD_EVENT_UO_MASK_CHANGED, _compressed_mask(new_mask));
Packit 5e46da
    }
Packit 5e46da
    bd->uo_mask = new_mask;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_hdmv_uo_mask(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    uint32_t mask = hdmv_vm_get_uo_mask(bd->hdmv_vm);
Packit 5e46da
    bd->title_uo_mask.title_search = !!(mask & HDMV_TITLE_SEARCH_MASK);
Packit 5e46da
    bd->title_uo_mask.menu_call    = !!(mask & HDMV_MENU_CALL_MASK);
Packit 5e46da
Packit 5e46da
    _update_uo_mask(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * clip access (BD_STREAM)
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _close_m2ts(BD_STREAM *st)
Packit 5e46da
{
Packit 5e46da
    if (st->fp != NULL) {
Packit 5e46da
        file_close(st->fp);
Packit 5e46da
        st->fp = NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    m2ts_filter_close(&st->m2ts_filter);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _open_m2ts(BLURAY *bd, BD_STREAM *st)
Packit 5e46da
{
Packit 5e46da
    _close_m2ts(st);
Packit 5e46da
Packit 5e46da
    if (!st->clip) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    st->fp = disc_open_stream(bd->disc, st->clip->name);
Packit 5e46da
Packit 5e46da
    st->clip_size = 0;
Packit 5e46da
    st->clip_pos = (uint64_t)st->clip->start_pkt * 192;
Packit 5e46da
    st->clip_block_pos = (st->clip_pos / 6144) * 6144;
Packit 5e46da
    st->eof_hit = 0;
Packit 5e46da
    st->encrypted_block_cnt = 0;
Packit 5e46da
Packit 5e46da
    if (st->fp) {
Packit 5e46da
        int64_t clip_size = file_size(st->fp);
Packit 5e46da
        if (clip_size > 0) {
Packit 5e46da
Packit 5e46da
            if (file_seek(st->fp, st->clip_block_pos, SEEK_SET) < 0) {
Packit 5e46da
                BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to seek clip %s!\n", st->clip->name);
Packit 5e46da
                _close_m2ts(st);
Packit 5e46da
                return 0;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            st->clip_size   = clip_size;
Packit 5e46da
            st->int_buf_off = 6144;
Packit 5e46da
Packit 5e46da
            if (st == &bd->st0) {
Packit 5e46da
                MPLS_PL *pl = st->clip->title->pl;
Packit 5e46da
                MPLS_STN *stn = &pl->play_item[st->clip->ref].stn;
Packit 5e46da
Packit 5e46da
                st->uo_mask = uo_mask_combine(pl->app_info.uo_mask,
Packit 5e46da
                                              pl->play_item[st->clip->ref].uo_mask);
Packit 5e46da
                _update_uo_mask(bd);
Packit 5e46da
Packit 5e46da
                st->m2ts_filter = m2ts_filter_init((int64_t)st->clip->in_time << 1,
Packit 5e46da
                                                   (int64_t)st->clip->out_time << 1,
Packit 5e46da
                                                   stn->num_video, stn->num_audio,
Packit 5e46da
                                                   stn->num_ig, stn->num_pg);
Packit 5e46da
Packit 5e46da
                _update_clip_psrs(bd, st->clip);
Packit 5e46da
Packit 5e46da
                _init_pg_stream(bd);
Packit 5e46da
Packit 5e46da
                _init_textst_timer(bd);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Clip %s empty!\n", st->clip->name);
Packit 5e46da
        _close_m2ts(st);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to open clip %s!\n", st->clip->name);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _validate_unit(BLURAY *bd, BD_STREAM *st, uint8_t *buf)
Packit 5e46da
{
Packit 5e46da
    /* Check TP_extra_header Copy_permission_indicator. If != 0, unit may be encrypted. */
Packit 5e46da
    /* Check first sync byte. It should never be encrypted. */
Packit 5e46da
    if (BD_UNLIKELY(buf[0] & 0xc0 || buf[4] != 0x47)) {
Packit 5e46da
Packit 5e46da
        /* Check first sync bytes. If not OK, drop unit. */
Packit 5e46da
        if (buf[4] != 0x47 || buf [4 + 192] != 0x47 || buf[4 + 2*192] != 0x47 || buf[4 + 3*192] != 0x47) {
Packit 5e46da
Packit 5e46da
            /* Some streams have Copy_permission_indicator incorrectly set. */
Packit 5e46da
            /* Check first TS sync byte. If unit is encrypted, first 16 bytes are plain, rest not. */
Packit 5e46da
            /* not 100% accurate (can be random data too). But the unit is broken anyway ... */
Packit 5e46da
            if (buf[4] == 0x47) {
Packit 5e46da
Packit 5e46da
                /* most likely encrypted stream. Check couple of blocks before erroring out. */
Packit 5e46da
                st->encrypted_block_cnt++;
Packit 5e46da
Packit 5e46da
                if (st->encrypted_block_cnt > 10) {
Packit 5e46da
                    /* error out */
Packit 5e46da
                    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "TP header copy permission indicator != 0. Stream seems to be encrypted.\n");
Packit 5e46da
                    _queue_event(bd, BD_EVENT_ENCRYPTED, BD_ERROR_AACS);
Packit 5e46da
                    return -1;
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            /* broken block, ignore it */
Packit 5e46da
            _queue_event(bd, BD_EVENT_READ_ERROR, 1);
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    st->eof_hit = 0;
Packit 5e46da
    st->encrypted_block_cnt = 0;
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _skip_unit(BLURAY *bd, BD_STREAM *st)
Packit 5e46da
{
Packit 5e46da
    const size_t len = 6144;
Packit 5e46da
Packit 5e46da
    /* skip broken unit */
Packit 5e46da
    st->clip_block_pos += len;
Packit 5e46da
    st->clip_pos += len;
Packit 5e46da
Packit 5e46da
    _queue_event(bd, BD_EVENT_READ_ERROR, 0);
Packit 5e46da
Packit 5e46da
    /* seek to next unit start */
Packit 5e46da
    if (file_seek(st->fp, st->clip_block_pos, SEEK_SET) < 0) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to seek clip %s!\n", st->clip->name);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _read_block(BLURAY *bd, BD_STREAM *st, uint8_t *buf)
Packit 5e46da
{
Packit 5e46da
    const size_t len = 6144;
Packit 5e46da
Packit 5e46da
    if (st->fp) {
Packit 5e46da
        BD_DEBUG(DBG_STREAM, "Reading unit at %"PRIu64"...\n", st->clip_block_pos);
Packit 5e46da
Packit 5e46da
        if (len + st->clip_block_pos <= st->clip_size) {
Packit 5e46da
            size_t read_len;
Packit 5e46da
Packit 5e46da
            if ((read_len = file_read(st->fp, buf, len))) {
Packit 5e46da
                int error;
Packit 5e46da
Packit 5e46da
                if (read_len != len) {
Packit 5e46da
                    BD_DEBUG(DBG_STREAM | DBG_CRIT, "Read %d bytes at %"PRIu64" ; requested %d !\n", (int)read_len, st->clip_block_pos, (int)len);
Packit 5e46da
                    return _skip_unit(bd, st);
Packit 5e46da
                }
Packit 5e46da
                st->clip_block_pos += len;
Packit 5e46da
Packit 5e46da
                if ((error = _validate_unit(bd, st, buf)) <= 0) {
Packit 5e46da
                    /* skip broken unit */
Packit 5e46da
                    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Skipping broken unit at %"PRId64"\n", st->clip_block_pos - len);
Packit 5e46da
                    st->clip_pos += len;
Packit 5e46da
                    return error;
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
                if (st->m2ts_filter) {
Packit 5e46da
                    int result = m2ts_filter(st->m2ts_filter, buf);
Packit 5e46da
                    if (result < 0) {
Packit 5e46da
                        m2ts_filter_close(&st->m2ts_filter);
Packit 5e46da
                        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "m2ts filter error\n");
Packit 5e46da
                    }
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
                BD_DEBUG(DBG_STREAM, "Read unit OK!\n");
Packit 5e46da
Packit 5e46da
#ifdef BLURAY_READ_ERROR_TEST
Packit 5e46da
                /* simulate broken blocks */
Packit 5e46da
                if (random() % 1000)
Packit 5e46da
#else
Packit 5e46da
                return 1;
Packit 5e46da
#endif
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            BD_DEBUG(DBG_STREAM | DBG_CRIT, "Read unit at %"PRIu64" failed !\n", st->clip_block_pos);
Packit 5e46da
Packit 5e46da
            return _skip_unit(bd, st);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* This is caused by truncated .m2ts file or invalid clip length.
Packit 5e46da
         *
Packit 5e46da
         * Increase position to avoid infinite loops.
Packit 5e46da
         * Next clip won't be selected until all packets of this clip have been read.
Packit 5e46da
         */
Packit 5e46da
        st->clip_block_pos += len;
Packit 5e46da
        st->clip_pos += len;
Packit 5e46da
Packit 5e46da
        if (!st->eof_hit) {
Packit 5e46da
            BD_DEBUG(DBG_STREAM | DBG_CRIT, "Read past EOF !\n");
Packit 5e46da
            st->eof_hit = 1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "No valid title selected!\n");
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * clip preload (BD_PRELOAD)
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _close_preload(BD_PRELOAD *p)
Packit 5e46da
{
Packit 5e46da
    X_FREE(p->buf);
Packit 5e46da
    memset(p, 0, sizeof(*p));
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
#define PRELOAD_SIZE_LIMIT  (512*1024*1024)  /* do not preload clips larger than 512M */
Packit 5e46da
Packit 5e46da
static int _preload_m2ts(BLURAY *bd, BD_PRELOAD *p)
Packit 5e46da
{
Packit 5e46da
    /* setup and open BD_STREAM */
Packit 5e46da
Packit 5e46da
    BD_STREAM st;
Packit 5e46da
Packit 5e46da
    memset(&st, 0, sizeof(st));
Packit 5e46da
    st.clip = p->clip;
Packit 5e46da
Packit 5e46da
    if (st.clip_size > PRELOAD_SIZE_LIMIT) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY|DBG_CRIT, "_preload_m2ts(): too large clip (%"PRId64")\n", st.clip_size);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!_open_m2ts(bd, &st)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* allocate buffer */
Packit 5e46da
    p->clip_size = (size_t)st.clip_size;
Packit 5e46da
    uint8_t* tmp = (uint8_t*)realloc(p->buf, p->clip_size);
Packit 5e46da
    if (!tmp) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_preload_m2ts(): out of memory\n");
Packit 5e46da
        _close_m2ts(&st);
Packit 5e46da
        _close_preload(p);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->buf = tmp;
Packit 5e46da
Packit 5e46da
    /* read clip to buffer */
Packit 5e46da
Packit 5e46da
    uint8_t *buf = p->buf;
Packit 5e46da
    uint8_t *end = p->buf + p->clip_size;
Packit 5e46da
Packit 5e46da
    for (; buf < end; buf += 6144) {
Packit 5e46da
        if (_read_block(bd, &st, buf) <= 0) {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY|DBG_CRIT, "_preload_m2ts(): error loading %s at %"PRIu64"\n",
Packit 5e46da
                  st.clip->name, (uint64_t)(buf - p->buf));
Packit 5e46da
            _close_m2ts(&st);
Packit 5e46da
            _close_preload(p);
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* */
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "_preload_m2ts(): loaded %"PRIu64" bytes from %s\n",
Packit 5e46da
          st.clip_size, st.clip->name);
Packit 5e46da
Packit 5e46da
    _close_m2ts(&st);
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int64_t _seek_stream(BLURAY *bd, BD_STREAM *st,
Packit 5e46da
                            NAV_CLIP *clip, uint32_t clip_pkt)
Packit 5e46da
{
Packit 5e46da
    if (!clip)
Packit 5e46da
        return -1;
Packit 5e46da
Packit 5e46da
    if (!st->fp || !st->clip || clip->ref != st->clip->ref) {
Packit 5e46da
        // The position is in a new clip
Packit 5e46da
        st->clip = clip;
Packit 5e46da
        if (!_open_m2ts(bd, st)) {
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (st->m2ts_filter) {
Packit 5e46da
        m2ts_filter_seek(st->m2ts_filter, 0, (int64_t)st->clip->in_time << 1);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    st->clip_pos = (uint64_t)clip_pkt * 192;
Packit 5e46da
    st->clip_block_pos = (st->clip_pos / 6144) * 6144;
Packit 5e46da
Packit 5e46da
    if (file_seek(st->fp, st->clip_block_pos, SEEK_SET) < 0) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to seek clip %s!\n", st->clip->name);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    st->int_buf_off = 6144;
Packit 5e46da
    st->seek_flag = 1;
Packit 5e46da
Packit 5e46da
    return st->clip_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Graphics controller interface
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _run_gc(BLURAY *bd, gc_ctrl_e msg, uint32_t param)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bd->graphics_controller && bd->hdmv_vm) {
Packit 5e46da
        GC_NAV_CMDS cmds = {-1, NULL, -1, 0, 0, EMPTY_UO_MASK};
Packit 5e46da
Packit 5e46da
        result = gc_run(bd->graphics_controller, msg, param, &cmds);
Packit 5e46da
Packit 5e46da
        if (cmds.num_nav_cmds > 0) {
Packit 5e46da
            hdmv_vm_set_object(bd->hdmv_vm, cmds.num_nav_cmds, cmds.nav_cmds);
Packit 5e46da
            bd->hdmv_suspended = !hdmv_vm_running(bd->hdmv_vm);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (cmds.status != bd->gc_status) {
Packit 5e46da
            uint32_t changed_flags = cmds.status ^ bd->gc_status;
Packit 5e46da
            bd->gc_status = cmds.status;
Packit 5e46da
            if (changed_flags & GC_STATUS_MENU_OPEN) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_MENU, !!(bd->gc_status & GC_STATUS_MENU_OPEN));
Packit 5e46da
            }
Packit 5e46da
            if (changed_flags & GC_STATUS_POPUP) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_POPUP, !!(bd->gc_status & GC_STATUS_POPUP));
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (cmds.sound_id_ref >= 0 && cmds.sound_id_ref < 0xff) {
Packit 5e46da
            _queue_event(bd, BD_EVENT_SOUND_EFFECT, cmds.sound_id_ref);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        bd->gc_uo_mask = cmds.page_uo_mask;
Packit 5e46da
        _update_uo_mask(bd);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        if (bd->gc_status & GC_STATUS_MENU_OPEN) {
Packit 5e46da
            _queue_event(bd, BD_EVENT_MENU, 0);
Packit 5e46da
        }
Packit 5e46da
        if (bd->gc_status & GC_STATUS_POPUP) {
Packit 5e46da
            _queue_event(bd, BD_EVENT_POPUP, 0);
Packit 5e46da
        }
Packit 5e46da
        bd->gc_status = GC_STATUS_NONE;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * disc info
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _check_bdj(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (!bd->disc_info.bdj_handled) {
Packit 5e46da
        if (!bd->disc || bd->disc_info.bdj_detected) {
Packit 5e46da
Packit 5e46da
            /* Check if jvm + jar can be loaded ? */
Packit 5e46da
            switch (bdj_jvm_available(&bd->bdjstorage)) {
Packit 5e46da
            case 2: bd->disc_info.bdj_handled = 1;
Packit 5e46da
                    /* fall thru */
Packit 5e46da
            case 1: bd->disc_info.libjvm_detected = 1;
Packit 5e46da
            default:;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _fill_disc_info(BLURAY *bd, BD_ENC_INFO *enc_info)
Packit 5e46da
{
Packit 5e46da
    INDX_ROOT *index = NULL;
Packit 5e46da
Packit 5e46da
    if (enc_info) {
Packit 5e46da
        bd->disc_info.aacs_detected      = enc_info->aacs_detected;
Packit 5e46da
        bd->disc_info.libaacs_detected   = enc_info->libaacs_detected;
Packit 5e46da
        bd->disc_info.aacs_error_code    = enc_info->aacs_error_code;
Packit 5e46da
        bd->disc_info.aacs_handled       = enc_info->aacs_handled;
Packit 5e46da
        bd->disc_info.aacs_mkbv          = enc_info->aacs_mkbv;
Packit 5e46da
        memcpy(bd->disc_info.disc_id, enc_info->disc_id, 20);
Packit 5e46da
        bd->disc_info.bdplus_detected    = enc_info->bdplus_detected;
Packit 5e46da
        bd->disc_info.libbdplus_detected = enc_info->libbdplus_detected;
Packit 5e46da
        bd->disc_info.bdplus_handled     = enc_info->bdplus_handled;
Packit 5e46da
        bd->disc_info.bdplus_gen         = enc_info->bdplus_gen;
Packit 5e46da
        bd->disc_info.bdplus_date        = enc_info->bdplus_date;
Packit 5e46da
        bd->disc_info.no_menu_support    = enc_info->no_menu_support;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->disc_info.bluray_detected        = 0;
Packit 5e46da
    bd->disc_info.top_menu_supported     = 0;
Packit 5e46da
    bd->disc_info.first_play_supported   = 0;
Packit 5e46da
    bd->disc_info.num_hdmv_titles        = 0;
Packit 5e46da
    bd->disc_info.num_bdj_titles         = 0;
Packit 5e46da
    bd->disc_info.num_unsupported_titles = 0;
Packit 5e46da
Packit 5e46da
    bd->disc_info.bdj_detected    = 0;
Packit 5e46da
    bd->disc_info.bdj_supported   = 1;
Packit 5e46da
Packit 5e46da
    bd->disc_info.num_titles  = 0;
Packit 5e46da
    bd->disc_info.titles      = NULL;
Packit 5e46da
    bd->disc_info.top_menu    = NULL;
Packit 5e46da
    bd->disc_info.first_play  = NULL;
Packit 5e46da
Packit 5e46da
    array_free((void**)&bd->titles);
Packit 5e46da
Packit 5e46da
    memset(bd->disc_info.bdj_org_id,  0, sizeof(bd->disc_info.bdj_org_id));
Packit 5e46da
    memset(bd->disc_info.bdj_disc_id, 0, sizeof(bd->disc_info.bdj_disc_id));
Packit 5e46da
Packit 5e46da
    if (bd->disc) {
Packit 5e46da
        bd->disc_info.udf_volume_id = disc_volume_id(bd->disc);
Packit 5e46da
        index = indx_get(bd->disc);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (index) {
Packit 5e46da
        INDX_PLAY_ITEM *pi;
Packit 5e46da
        unsigned        ii;
Packit 5e46da
Packit 5e46da
        bd->disc_info.bluray_detected = 1;
Packit 5e46da
Packit 5e46da
        /* application info */
Packit 5e46da
        bd->disc_info.video_format     = index->app_info.video_format;
Packit 5e46da
        bd->disc_info.frame_rate       = index->app_info.frame_rate;
Packit 5e46da
        bd->disc_info.content_exist_3D = index->app_info.content_exist_flag;
Packit 5e46da
        bd->disc_info.initial_output_mode_preference = index->app_info.initial_output_mode_preference;
Packit 5e46da
        memcpy(bd->disc_info.provider_data, index->app_info.user_data, sizeof(bd->disc_info.provider_data));
Packit 5e46da
Packit 5e46da
        /* allocate array for title info */
Packit 5e46da
        BLURAY_TITLE **titles = (BLURAY_TITLE**)array_alloc(index->num_titles + 2, sizeof(BLURAY_TITLE));
Packit 5e46da
        if (!titles) {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Can't allocate memory\n");
Packit 5e46da
            indx_free(&index);
Packit 5e46da
            return;
Packit 5e46da
        }
Packit 5e46da
        bd->titles = titles;
Packit 5e46da
        bd->disc_info.titles = (const BLURAY_TITLE * const *)titles;
Packit 5e46da
        bd->disc_info.num_titles = index->num_titles;
Packit 5e46da
Packit 5e46da
        /* count titles and fill title info */
Packit 5e46da
Packit 5e46da
        for (ii = 0; ii < index->num_titles; ii++) {
Packit 5e46da
            if (index->titles[ii].object_type == indx_object_type_hdmv) {
Packit 5e46da
                bd->disc_info.num_hdmv_titles++;
Packit 5e46da
                titles[ii + 1]->interactive = (index->titles[ii].hdmv.playback_type == indx_hdmv_playback_type_interactive);
Packit 5e46da
                titles[ii + 1]->id_ref = index->titles[ii].hdmv.id_ref;
Packit 5e46da
            }
Packit 5e46da
            if (index->titles[ii].object_type == indx_object_type_bdj) {
Packit 5e46da
                bd->disc_info.num_bdj_titles++;
Packit 5e46da
                bd->disc_info.bdj_detected = 1;
Packit 5e46da
                titles[ii + 1]->bdj = 1;
Packit 5e46da
                titles[ii + 1]->interactive = (index->titles[ii].bdj.playback_type == indx_bdj_playback_type_interactive);
Packit 5e46da
                titles[ii + 1]->id_ref = atoi(index->titles[ii].bdj.name);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            titles[ii + 1]->accessible =  !(index->titles[ii].access_type & INDX_ACCESS_PROHIBITED_MASK);
Packit 5e46da
            titles[ii + 1]->hidden     = !!(index->titles[ii].access_type & INDX_ACCESS_HIDDEN_MASK);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        pi = &index->first_play;
Packit 5e46da
        if (pi->object_type == indx_object_type_bdj) {
Packit 5e46da
            bd->disc_info.bdj_detected = 1;
Packit 5e46da
            titles[index->num_titles + 1]->bdj = 1;
Packit 5e46da
            titles[index->num_titles + 1]->interactive = (pi->bdj.playback_type == indx_bdj_playback_type_interactive);
Packit 5e46da
            titles[index->num_titles + 1]->id_ref = atoi(pi->bdj.name);
Packit 5e46da
        }
Packit 5e46da
        if (pi->object_type == indx_object_type_hdmv && pi->hdmv.id_ref != 0xffff) {
Packit 5e46da
            titles[index->num_titles + 1]->interactive = (pi->hdmv.playback_type == indx_hdmv_playback_type_interactive);
Packit 5e46da
            titles[index->num_titles + 1]->id_ref = pi->hdmv.id_ref;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        pi = &index->top_menu;
Packit 5e46da
        if (pi->object_type == indx_object_type_bdj) {
Packit 5e46da
            bd->disc_info.bdj_detected = 1;
Packit 5e46da
            titles[0]->bdj = 1;
Packit 5e46da
            titles[0]->interactive = (pi->bdj.playback_type == indx_bdj_playback_type_interactive);
Packit 5e46da
            titles[0]->id_ref = atoi(pi->bdj.name);
Packit 5e46da
        }
Packit 5e46da
        if (pi->object_type == indx_object_type_hdmv && pi->hdmv.id_ref != 0xffff) {
Packit 5e46da
            titles[0]->interactive = (pi->hdmv.playback_type == indx_hdmv_playback_type_interactive);
Packit 5e46da
            titles[0]->id_ref = pi->hdmv.id_ref;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* mark supported titles */
Packit 5e46da
Packit 5e46da
        _check_bdj(bd);
Packit 5e46da
Packit 5e46da
        if (bd->disc_info.bdj_detected && !bd->disc_info.bdj_handled) {
Packit 5e46da
            bd->disc_info.num_unsupported_titles = bd->disc_info.num_bdj_titles;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        pi = &index->first_play;
Packit 5e46da
        if (pi->object_type == indx_object_type_hdmv && pi->hdmv.id_ref != 0xffff) {
Packit 5e46da
            bd->disc_info.first_play_supported = 1;
Packit 5e46da
        }
Packit 5e46da
        if (pi->object_type == indx_object_type_bdj) {
Packit 5e46da
            bd->disc_info.first_play_supported = bd->disc_info.bdj_handled;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        pi = &index->top_menu;
Packit 5e46da
        if (pi->object_type == indx_object_type_hdmv && pi->hdmv.id_ref != 0xffff) {
Packit 5e46da
            bd->disc_info.top_menu_supported = 1;
Packit 5e46da
        }
Packit 5e46da
        if (pi->object_type == indx_object_type_bdj) {
Packit 5e46da
            bd->disc_info.top_menu_supported = bd->disc_info.bdj_handled;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* */
Packit 5e46da
Packit 5e46da
        if (bd->disc_info.first_play_supported) {
Packit 5e46da
            titles[index->num_titles + 1]->accessible = 1;
Packit 5e46da
            bd->disc_info.first_play = titles[index->num_titles + 1];
Packit 5e46da
        }
Packit 5e46da
        if (bd->disc_info.top_menu_supported) {
Packit 5e46da
            titles[0]->accessible = 1;
Packit 5e46da
            bd->disc_info.top_menu = titles[0];
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* populate title names */
Packit 5e46da
        bd_get_meta(bd);
Packit 5e46da
Packit 5e46da
        /* no BD-J menu support for profile 6 */
Packit 5e46da
        if (bd->disc_info.num_bdj_titles) {
Packit 5e46da
            // XXX actually, should check from bdjo files ...
Packit 5e46da
            if (index->indx_version >= ('0' << 24 | '3' << 16 | '0' << 8 | '0')) {
Packit 5e46da
                BD_DEBUG(DBG_CRIT | DBG_BLURAY, "WARNING: BluRay profile 6 BD-J menus are not supported\n");
Packit 5e46da
                bd->disc_info.no_menu_support = 1;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        indx_free(&index);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
#if 0
Packit 5e46da
    if (!bd->disc_info.first_play_supported || !bd->disc_info.top_menu_supported) {
Packit 5e46da
        bd->disc_info.no_menu_support = 1;
Packit 5e46da
    }
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
    if (bd->disc_info.bdj_detected) {
Packit 5e46da
        BDID_DATA *bdid = bdid_get(bd->disc); /* parse id.bdmv */
Packit 5e46da
        if (bdid) {
Packit 5e46da
            memcpy(bd->disc_info.bdj_org_id,  bdid->org_id,  sizeof(bd->disc_info.bdj_org_id));
Packit 5e46da
            memcpy(bd->disc_info.bdj_disc_id, bdid->disc_id, sizeof(bd->disc_info.bdj_disc_id));
Packit 5e46da
            bdid_free(&bdid);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _check_bdj(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
const BLURAY_DISC_INFO *bd_get_disc_info(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (!bd->disc) {
Packit 5e46da
        BD_ENC_INFO enc_info;
Packit 5e46da
        memset(&enc_info, 0, sizeof(enc_info));
Packit 5e46da
        _fill_disc_info(bd, &enc_info);
Packit 5e46da
    }
Packit 5e46da
    return &bd->disc_info;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * bdj callbacks
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
void bd_set_bdj_uo_mask(BLURAY *bd, unsigned mask)
Packit 5e46da
{
Packit 5e46da
    bd->title_uo_mask.title_search = !!(mask & BDJ_TITLE_SEARCH_MASK);
Packit 5e46da
    bd->title_uo_mask.menu_call    = !!(mask & BDJ_MENU_CALL_MASK);
Packit 5e46da
Packit 5e46da
    _update_uo_mask(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
const uint8_t *bd_get_aacs_data(BLURAY *bd, int type)
Packit 5e46da
{
Packit 5e46da
    return disc_get_data(bd->disc, type);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint64_t bd_get_uo_mask(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    /* internal function. Used by BD-J. */
Packit 5e46da
    union {
Packit 5e46da
      uint64_t u64;
Packit 5e46da
      BD_UO_MASK mask;
Packit 5e46da
    } mask = {0};
Packit 5e46da
Packit 5e46da
    //bd_mutex_lock(&bd->mutex);
Packit 5e46da
    memcpy(&mask.mask, &bd->uo_mask, sizeof(BD_UO_MASK));
Packit 5e46da
    //bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return mask.u64;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void bd_set_bdj_kit(BLURAY *bd, int mask)
Packit 5e46da
{
Packit 5e46da
    _queue_event(bd, BD_EVENT_KEY_INTEREST_TABLE, mask);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_bdj_sound_effect(BLURAY *bd, int id)
Packit 5e46da
{
Packit 5e46da
    if (bd->sound_effects && id >= bd->sound_effects->num_sounds) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (id < 0 || id > 0xff) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _queue_event(bd, BD_EVENT_SOUND_EFFECT, id);
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void bd_select_rate(BLURAY *bd, float rate, int reason)
Packit 5e46da
{
Packit 5e46da
    if (reason == BDJ_PLAYBACK_STOP) {
Packit 5e46da
        /* playback stop. Might want to wait for buffers empty here. */
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (reason == BDJ_PLAYBACK_START) {
Packit 5e46da
        /* playback is triggered by bd_select_rate() */
Packit 5e46da
        bd->bdj_wait_start = 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (rate < 0.5) {
Packit 5e46da
        _queue_event(bd, BD_EVENT_STILL, 1);
Packit 5e46da
    } else {
Packit 5e46da
        _queue_event(bd, BD_EVENT_STILL, 0);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_bdj_seek(BLURAY *bd, int playitem, int playmark, int64_t time)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (playitem > 0) {
Packit 5e46da
        bd_seek_playitem(bd, playitem);
Packit 5e46da
    }
Packit 5e46da
    if (playmark >= 0) {
Packit 5e46da
        bd_seek_mark(bd, playmark);
Packit 5e46da
    }
Packit 5e46da
    if (time >= 0) {
Packit 5e46da
        bd_seek_time(bd, time);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_set_virtual_package(BLURAY *bd, const char *vp_path, int psr_init_backup)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_set_virtual_package() failed: playlist is playing\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (bd->title_type != title_bdj) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_set_virtual_package() failed: HDMV title\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (psr_init_backup) {
Packit 5e46da
        bd_psr_reset_backup_registers(bd->regs);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    disc_update(bd->disc, vp_path);
Packit 5e46da
Packit 5e46da
    /* TODO: reload all cached information, update disc info, notify app */
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BD_DISC *bd_get_disc(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    return bd ? bd->disc : NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint32_t bd_reg_read(BLURAY *bd, int psr, int reg)
Packit 5e46da
{
Packit 5e46da
    if (psr) {
Packit 5e46da
        return bd_psr_read(bd->regs, reg);
Packit 5e46da
    } else {
Packit 5e46da
        return bd_gpr_read(bd->regs, reg);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_reg_write(BLURAY *bd, int psr, int reg, uint32_t value, uint32_t psr_value_mask)
Packit 5e46da
{
Packit 5e46da
    if (psr) {
Packit 5e46da
        if (psr < 102) {
Packit 5e46da
            /* avoid deadlocks (psr_write triggers callbacks that may lock this mutex) */
Packit 5e46da
            bd_mutex_lock(&bd->mutex);
Packit 5e46da
        }
Packit 5e46da
        int res = bd_psr_write_bits(bd->regs, reg, value, psr_value_mask);
Packit 5e46da
        if (psr < 102) {
Packit 5e46da
            bd_mutex_unlock(&bd->mutex);
Packit 5e46da
        }
Packit 5e46da
        return res;
Packit 5e46da
    } else {
Packit 5e46da
        return bd_gpr_write(bd->regs, reg, value);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BD_ARGB_BUFFER *bd_lock_osd_buffer(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_lock(&bd->argb_buffer_mutex);
Packit 5e46da
    return bd->argb_buffer;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void bd_unlock_osd_buffer(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_unlock(&bd->argb_buffer_mutex);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * handle graphics updates from BD-J layer
Packit 5e46da
 */
Packit 5e46da
void bd_bdj_osd_cb(BLURAY *bd, const unsigned *img, int w, int h,
Packit 5e46da
                   int x0, int y0, int x1, int y1)
Packit 5e46da
{
Packit 5e46da
    BD_ARGB_OVERLAY aov;
Packit 5e46da
Packit 5e46da
    if (!bd->argb_overlay_proc) {
Packit 5e46da
        _queue_event(bd, BD_EVENT_MENU, 0);
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    memset(&aov, 0, sizeof(aov));
Packit 5e46da
    aov.pts   = -1;
Packit 5e46da
    aov.plane = BD_OVERLAY_IG;
Packit 5e46da
Packit 5e46da
    /* no image data -> init or close */
Packit 5e46da
    if (!img) {
Packit 5e46da
        if (w > 0 && h > 0) {
Packit 5e46da
            aov.cmd = BD_ARGB_OVERLAY_INIT;
Packit 5e46da
            aov.w   = w;
Packit 5e46da
            aov.h   = h;
Packit 5e46da
            _queue_event(bd, BD_EVENT_MENU, 1);
Packit 5e46da
        } else {
Packit 5e46da
            aov.cmd = BD_ARGB_OVERLAY_CLOSE;
Packit 5e46da
            _queue_event(bd, BD_EVENT_MENU, 0);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        bd->argb_overlay_proc(bd->argb_overlay_proc_handle, &aov;;
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* no changed pixels ? */
Packit 5e46da
    if (x1 < x0 || y1 < y0) {
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* pass only changed region */
Packit 5e46da
    if (bd->argb_buffer && (bd->argb_buffer->width < w || bd->argb_buffer->height < h)) {
Packit 5e46da
        aov.argb   = img;
Packit 5e46da
    } else {
Packit 5e46da
        aov.argb   = img + x0 + y0 * w;
Packit 5e46da
    }
Packit 5e46da
    aov.stride = w;
Packit 5e46da
    aov.x      = x0;
Packit 5e46da
    aov.y      = y0;
Packit 5e46da
    aov.w      = x1 - x0 + 1;
Packit 5e46da
    aov.h      = y1 - y0 + 1;
Packit 5e46da
Packit 5e46da
    if (bd->argb_buffer) {
Packit 5e46da
        /* set dirty region */
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].x0 = x0;
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].x1 = x1;
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].y0 = y0;
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].y1 = y1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* draw */
Packit 5e46da
    aov.cmd = BD_ARGB_OVERLAY_DRAW;
Packit 5e46da
    bd->argb_overlay_proc(bd->argb_overlay_proc_handle, &aov;;
Packit 5e46da
Packit 5e46da
    /* commit changes */
Packit 5e46da
    aov.cmd = BD_ARGB_OVERLAY_FLUSH;
Packit 5e46da
    bd->argb_overlay_proc(bd->argb_overlay_proc_handle, &aov;;
Packit 5e46da
Packit 5e46da
    if (bd->argb_buffer) {
Packit 5e46da
        /* reset dirty region */
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].x0 = bd->argb_buffer->width;
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].x1 = bd->argb_buffer->height;
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].y0 = 0;
Packit 5e46da
        bd->argb_buffer->dirty[BD_OVERLAY_IG].y1 = 0;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * BD-J
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _start_bdj(BLURAY *bd, unsigned title)
Packit 5e46da
{
Packit 5e46da
    if (bd->bdjava == NULL) {
Packit 5e46da
        const char *root = disc_root(bd->disc);
Packit 5e46da
        bd->bdjava = bdj_open(root, bd, bd->disc_info.bdj_disc_id, &bd->bdjstorage);
Packit 5e46da
        if (!bd->bdjava) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return !bdj_process_event(bd->bdjava, BDJ_EVENT_START, title);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _bdj_event(BLURAY *bd, unsigned ev, unsigned param)
Packit 5e46da
{
Packit 5e46da
    if (bd->bdjava != NULL) {
Packit 5e46da
        return bdj_process_event(bd->bdjava, ev, param);
Packit 5e46da
    }
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _stop_bdj(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->bdjava != NULL) {
Packit 5e46da
        bdj_process_event(bd->bdjava, BDJ_EVENT_STOP, 0);
Packit 5e46da
        _queue_event(bd, BD_EVENT_STILL, 0);
Packit 5e46da
        _queue_event(bd, BD_EVENT_KEY_INTEREST_TABLE, 0);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _close_bdj(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->bdjava != NULL) {
Packit 5e46da
        bdj_close(bd->bdjava);
Packit 5e46da
        bd->bdjava = NULL;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * open / close
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
BLURAY *bd_init(void)
Packit 5e46da
{
Packit 5e46da
    char *env;
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "libbluray version "BLURAY_VERSION_STRING"\n");
Packit 5e46da
Packit 5e46da
    BLURAY *bd = calloc(1, sizeof(BLURAY));
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Can't allocate memory\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->regs = bd_registers_init();
Packit 5e46da
    if (!bd->regs) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "bd_registers_init() failed\n");
Packit 5e46da
        X_FREE(bd);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_init(&bd->mutex);
Packit 5e46da
    bd_mutex_init(&bd->argb_buffer_mutex);
Packit 5e46da
Packit 5e46da
    env = getenv("LIBBLURAY_PERSISTENT_STORAGE");
Packit 5e46da
    if (env) {
Packit 5e46da
        int v = (!strcmp(env, "yes")) ? 1 : (!strcmp(env, "no")) ? 0 : atoi(env);
Packit 5e46da
        bd->bdjstorage.no_persistent_storage = !v;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "BLURAY initialized!\n");
Packit 5e46da
Packit 5e46da
    return bd;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _bd_open(BLURAY *bd,
Packit 5e46da
                    const char *device_path, const char *keyfile_path,
Packit 5e46da
                    fs_access *p_fs)
Packit 5e46da
{
Packit 5e46da
    BD_ENC_INFO enc_info;
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
    if (bd->disc) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Disc already open\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->disc = disc_open(device_path, p_fs,
Packit 5e46da
                         &enc_info, keyfile_path,
Packit 5e46da
                         (void*)bd->regs, (void*)bd_psr_read, (void*)bd_psr_write);
Packit 5e46da
Packit 5e46da
    if (!bd->disc) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _fill_disc_info(bd, &enc_info);
Packit 5e46da
Packit 5e46da
    return bd->disc_info.bluray_detected;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_open_disc(BLURAY *bd, const char *device_path, const char *keyfile_path)
Packit 5e46da
{
Packit 5e46da
    if (!device_path) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "No device path provided!\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return _bd_open(bd, device_path, keyfile_path, NULL);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_open_stream(BLURAY *bd,
Packit 5e46da
                   void *read_blocks_handle,
Packit 5e46da
                   int (*read_blocks)(void *handle, void *buf, int lba, int num_blocks))
Packit 5e46da
{
Packit 5e46da
    if (!read_blocks) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    fs_access fs = { read_blocks_handle, read_blocks, NULL, NULL };
Packit 5e46da
    return _bd_open(bd, NULL, NULL, &fs);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_open_files(BLURAY *bd,
Packit 5e46da
                  void *handle,
Packit 5e46da
                  struct bd_dir_s *(*open_dir)(void *handle, const char *rel_path),
Packit 5e46da
                  struct bd_file_s *(*open_file)(void *handle, const char *rel_path))
Packit 5e46da
{
Packit 5e46da
    if (!open_dir || !open_file) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    fs_access fs = { handle, NULL, open_dir, open_file };
Packit 5e46da
    return _bd_open(bd, NULL, NULL, &fs);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BLURAY *bd_open(const char *device_path, const char *keyfile_path)
Packit 5e46da
{
Packit 5e46da
    BLURAY *bd;
Packit 5e46da
Packit 5e46da
    bd = bd_init();
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!bd_open_disc(bd, device_path, keyfile_path)) {
Packit 5e46da
        bd_close(bd);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return bd;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void bd_close(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _close_bdj(bd);
Packit 5e46da
Packit 5e46da
    _close_m2ts(&bd->st0);
Packit 5e46da
    _close_preload(&bd->st_ig);
Packit 5e46da
    _close_preload(&bd->st_textst);
Packit 5e46da
Packit 5e46da
    nav_free_title_list(&bd->title_list);
Packit 5e46da
    nav_title_close(&bd->title);
Packit 5e46da
Packit 5e46da
    hdmv_vm_free(&bd->hdmv_vm);
Packit 5e46da
Packit 5e46da
    gc_free(&bd->graphics_controller);
Packit 5e46da
    meta_free(&bd->meta);
Packit 5e46da
    sound_free(&bd->sound_effects);
Packit 5e46da
    bd_registers_free(bd->regs);
Packit 5e46da
Packit 5e46da
    event_queue_destroy(&bd->event_queue);
Packit 5e46da
    array_free((void**)&bd->titles);
Packit 5e46da
    bdj_storage_cleanup(&bd->bdjstorage);
Packit 5e46da
Packit 5e46da
    disc_close(&bd->disc);
Packit 5e46da
Packit 5e46da
    bd_mutex_destroy(&bd->mutex);
Packit 5e46da
    bd_mutex_destroy(&bd->argb_buffer_mutex);
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "BLURAY destroyed!\n");
Packit 5e46da
Packit 5e46da
    X_FREE(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * PlayMark tracking
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _find_next_playmark(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    bd->next_mark = -1;
Packit 5e46da
    bd->next_mark_pos = (uint64_t)-1;
Packit 5e46da
    for (ii = 0; ii < bd->title->mark_list.count; ii++) {
Packit 5e46da
        uint64_t pos = (uint64_t)bd->title->mark_list.mark[ii].title_pkt * 192L;
Packit 5e46da
        if (pos > bd->s_pos) {
Packit 5e46da
            bd->next_mark = ii;
Packit 5e46da
            bd->next_mark_pos = pos;
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _update_chapter_psr(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _playmark_reached(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "PlayMark %d reached (%"PRIu64")\n", bd->next_mark, bd->next_mark_pos);
Packit 5e46da
Packit 5e46da
    _queue_event(bd, BD_EVENT_PLAYMARK, bd->next_mark);
Packit 5e46da
    _bdj_event(bd, BDJ_EVENT_MARK, bd->next_mark);
Packit 5e46da
Packit 5e46da
    /* update next mark */
Packit 5e46da
    bd->next_mark++;
Packit 5e46da
    if ((unsigned)bd->next_mark < bd->title->mark_list.count) {
Packit 5e46da
        bd->next_mark_pos = (uint64_t)bd->title->mark_list.mark[bd->next_mark].title_pkt * 192L;
Packit 5e46da
    } else {
Packit 5e46da
        bd->next_mark = -1;
Packit 5e46da
        bd->next_mark_pos = (uint64_t)-1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* chapter tracking */
Packit 5e46da
    _update_chapter_psr(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * seeking and current position
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _seek_internal(BLURAY *bd,
Packit 5e46da
                           NAV_CLIP *clip, uint32_t title_pkt, uint32_t clip_pkt)
Packit 5e46da
{
Packit 5e46da
    if (_seek_stream(bd, &bd->st0, clip, clip_pkt) >= 0) {
Packit 5e46da
        uint32_t media_time;
Packit 5e46da
Packit 5e46da
        /* update title position */
Packit 5e46da
        bd->s_pos = (uint64_t)title_pkt * 192;
Packit 5e46da
Packit 5e46da
        /* Update PSR_TIME */
Packit 5e46da
        media_time = _update_time_psr_from_stream(bd);
Packit 5e46da
Packit 5e46da
        /* emit notification events */
Packit 5e46da
        if (media_time >= clip->in_time) {
Packit 5e46da
            media_time = media_time - clip->in_time + clip->title_time;
Packit 5e46da
        }
Packit 5e46da
        _queue_event(bd, BD_EVENT_SEEK, media_time);
Packit 5e46da
        _bdj_event(bd, BDJ_EVENT_SEEK, media_time);
Packit 5e46da
Packit 5e46da
        /* playmark tracking */
Packit 5e46da
        _find_next_playmark(bd);
Packit 5e46da
Packit 5e46da
        /* reset PG decoder and controller */
Packit 5e46da
        if (bd->graphics_controller) {
Packit 5e46da
            gc_run(bd->graphics_controller, GC_CTRL_PG_RESET, 0, NULL);
Packit 5e46da
Packit 5e46da
            _init_textst_timer(bd);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "Seek to %"PRIu64"\n", bd->s_pos);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* _change_angle() should be used only before call to _seek_internal() ! */
Packit 5e46da
static void _change_angle(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->seamless_angle_change) {
Packit 5e46da
        bd->st0.clip = nav_set_angle(bd->title, bd->st0.clip, bd->request_angle);
Packit 5e46da
        bd->seamless_angle_change = 0;
Packit 5e46da
        bd_psr_write(bd->regs, PSR_ANGLE_NUMBER, bd->title->angle + 1);
Packit 5e46da
Packit 5e46da
        /* force re-opening .m2ts file in _seek_internal() */
Packit 5e46da
        _close_m2ts(&bd->st0);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t bd_seek_time(BLURAY *bd, uint64_t tick)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt, out_pkt;
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
Packit 5e46da
    if (tick >> 33) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_seek_time(%"PRIu64") failed: invalid timestamp\n", tick);
Packit 5e46da
        return bd->s_pos;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    tick /= 2;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title &&
Packit 5e46da
        tick < bd->title->duration) {
Packit 5e46da
Packit 5e46da
        _change_angle(bd);
Packit 5e46da
Packit 5e46da
        // Find the closest access unit to the requested position
Packit 5e46da
        clip = nav_time_search(bd->title, (uint32_t)tick, &clip_pkt, &out_pkt);
Packit 5e46da
Packit 5e46da
        _seek_internal(bd, clip, out_pkt, clip_pkt);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_seek_time(%u) failed\n", (unsigned int)tick);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return bd->s_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint64_t bd_tell_time(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt = 0, out_pkt = 0, out_time = 0;
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title) {
Packit 5e46da
        clip = nav_packet_search(bd->title, SPN(bd->s_pos), &clip_pkt, &out_pkt, &out_time);
Packit 5e46da
        if (clip) {
Packit 5e46da
            out_time += clip->title_time;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return ((uint64_t)out_time) * 2;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t bd_seek_chapter(BLURAY *bd, unsigned chapter)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt, out_pkt;
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title &&
Packit 5e46da
        chapter < bd->title->chap_list.count) {
Packit 5e46da
Packit 5e46da
        _change_angle(bd);
Packit 5e46da
Packit 5e46da
        // Find the closest access unit to the requested position
Packit 5e46da
        clip = nav_chapter_search(bd->title, chapter, &clip_pkt, &out_pkt);
Packit 5e46da
Packit 5e46da
        _seek_internal(bd, clip, out_pkt, clip_pkt);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_seek_chapter(%u) failed\n", chapter);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return bd->s_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t bd_chapter_pos(BLURAY *bd, unsigned chapter)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt, out_pkt;
Packit 5e46da
    int64_t ret = -1;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title &&
Packit 5e46da
        chapter < bd->title->chap_list.count) {
Packit 5e46da
Packit 5e46da
        // Find the closest access unit to the requested position
Packit 5e46da
        nav_chapter_search(bd->title, chapter, &clip_pkt, &out_pkt);
Packit 5e46da
        ret = (int64_t)out_pkt * 192;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint32_t bd_get_current_chapter(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    uint32_t ret = 0;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title) {
Packit 5e46da
        ret = nav_chapter_get_current(bd->title, SPN(bd->s_pos));
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t bd_seek_playitem(BLURAY *bd, unsigned clip_ref)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt, out_pkt;
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title &&
Packit 5e46da
        clip_ref < bd->title->clip_list.count) {
Packit 5e46da
Packit 5e46da
      _change_angle(bd);
Packit 5e46da
Packit 5e46da
      clip     = &bd->title->clip_list.clip[clip_ref];
Packit 5e46da
      clip_pkt = clip->start_pkt;
Packit 5e46da
      out_pkt  = clip->title_pkt;
Packit 5e46da
Packit 5e46da
      _seek_internal(bd, clip, out_pkt, clip_pkt);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_seek_playitem(%u) failed\n", clip_ref);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return bd->s_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t bd_seek_mark(BLURAY *bd, unsigned mark)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt, out_pkt;
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title &&
Packit 5e46da
        mark < bd->title->mark_list.count) {
Packit 5e46da
Packit 5e46da
        _change_angle(bd);
Packit 5e46da
Packit 5e46da
        // Find the closest access unit to the requested position
Packit 5e46da
        clip = nav_mark_search(bd->title, mark, &clip_pkt, &out_pkt);
Packit 5e46da
Packit 5e46da
        _seek_internal(bd, clip, out_pkt, clip_pkt);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_seek_mark(%u) failed\n", mark);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return bd->s_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t bd_seek(BLURAY *bd, uint64_t pos)
Packit 5e46da
{
Packit 5e46da
    uint32_t pkt, clip_pkt, out_pkt, out_time;
Packit 5e46da
    NAV_CLIP *clip;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title &&
Packit 5e46da
        pos < (uint64_t)bd->title->packets * 192) {
Packit 5e46da
Packit 5e46da
        pkt = SPN(pos);
Packit 5e46da
Packit 5e46da
        _change_angle(bd);
Packit 5e46da
Packit 5e46da
        // Find the closest access unit to the requested position
Packit 5e46da
        clip = nav_packet_search(bd->title, pkt, &clip_pkt, &out_pkt, &out_time);
Packit 5e46da
Packit 5e46da
        _seek_internal(bd, clip, out_pkt, clip_pkt);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return bd->s_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint64_t bd_get_title_size(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    uint64_t ret = 0;
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title) {
Packit 5e46da
        ret = (uint64_t)bd->title->packets * 192;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint64_t bd_tell(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    uint64_t ret = 0;
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    ret = bd->s_pos;
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * read
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int64_t _clip_seek_time(BLURAY *bd, uint32_t tick)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt, out_pkt;
Packit 5e46da
Packit 5e46da
    if (!bd->title || !bd->st0.clip) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_clip_seek_time(): no playlist playing\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (tick >= bd->st0.clip->out_time) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_clip_seek_time(): timestamp after clip end (%u < %u)\n",
Packit 5e46da
                 bd->st0.clip->out_time, tick);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    // Find the closest access unit to the requested position
Packit 5e46da
    nav_clip_time_search(bd->st0.clip, tick, &clip_pkt, &out_pkt);
Packit 5e46da
Packit 5e46da
    _seek_internal(bd, bd->st0.clip, out_pkt, clip_pkt);
Packit 5e46da
Packit 5e46da
    return bd->s_pos;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _bd_read(BLURAY *bd, unsigned char *buf, int len)
Packit 5e46da
{
Packit 5e46da
    BD_STREAM *st = &bd->st0;
Packit 5e46da
    int out_len;
Packit 5e46da
Packit 5e46da
    if (st->fp) {
Packit 5e46da
        out_len = 0;
Packit 5e46da
        BD_DEBUG(DBG_STREAM, "Reading [%d bytes] at %"PRIu64"...\n", len, bd->s_pos);
Packit 5e46da
Packit 5e46da
        if (st->clip == NULL) {
Packit 5e46da
            // We previously reached the last clip.  Nothing
Packit 5e46da
            // else to read.
Packit 5e46da
            _queue_event(bd, BD_EVENT_END_OF_TITLE, 0);
Packit 5e46da
            bd->end_of_playlist |= 1;
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        while (len > 0) {
Packit 5e46da
            uint32_t clip_pkt;
Packit 5e46da
Packit 5e46da
            unsigned int size = len;
Packit 5e46da
            // Do we need to read more data?
Packit 5e46da
            clip_pkt = SPN(st->clip_pos);
Packit 5e46da
            if (bd->seamless_angle_change) {
Packit 5e46da
                if (clip_pkt >= bd->angle_change_pkt) {
Packit 5e46da
                    if (clip_pkt >= st->clip->end_pkt) {
Packit 5e46da
                        st->clip = nav_next_clip(bd->title, st->clip);
Packit 5e46da
                        if (!_open_m2ts(bd, st)) {
Packit 5e46da
                            return -1;
Packit 5e46da
                        }
Packit 5e46da
                        bd->s_pos = (uint64_t)st->clip->title_pkt * 192L;
Packit 5e46da
                    } else {
Packit 5e46da
                        _change_angle(bd);
Packit 5e46da
                        _clip_seek_time(bd, bd->angle_change_time);
Packit 5e46da
                    }
Packit 5e46da
                    bd->seamless_angle_change = 0;
Packit 5e46da
                } else {
Packit 5e46da
                    uint64_t angle_pos;
Packit 5e46da
Packit 5e46da
                    angle_pos = (uint64_t)bd->angle_change_pkt * 192L;
Packit 5e46da
                    if (angle_pos - st->clip_pos < size) {
Packit 5e46da
                        size = (unsigned int)(angle_pos - st->clip_pos);
Packit 5e46da
                    }
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
            if (st->int_buf_off == 6144 || clip_pkt >= st->clip->end_pkt) {
Packit 5e46da
Packit 5e46da
                // Do we need to get the next clip?
Packit 5e46da
                if (clip_pkt >= st->clip->end_pkt) {
Packit 5e46da
Packit 5e46da
                    // split read()'s at clip boundary
Packit 5e46da
                    if (out_len) {
Packit 5e46da
                        return out_len;
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                    MPLS_PI *pi = &st->clip->title->pl->play_item[st->clip->ref];
Packit 5e46da
Packit 5e46da
                    // handle still mode clips
Packit 5e46da
                    if (pi->still_mode == BLURAY_STILL_INFINITE) {
Packit 5e46da
                        _queue_event(bd, BD_EVENT_STILL_TIME, 0);
Packit 5e46da
                        return 0;
Packit 5e46da
                    }
Packit 5e46da
                    if (pi->still_mode == BLURAY_STILL_TIME) {
Packit 5e46da
                        if (bd->event_queue) {
Packit 5e46da
                            _queue_event(bd, BD_EVENT_STILL_TIME, pi->still_time);
Packit 5e46da
                            return 0;
Packit 5e46da
                        }
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                    // find next clip
Packit 5e46da
                    st->clip = nav_next_clip(bd->title, st->clip);
Packit 5e46da
                    if (st->clip == NULL) {
Packit 5e46da
                        BD_DEBUG(DBG_BLURAY | DBG_STREAM, "End of title\n");
Packit 5e46da
                        _queue_event(bd, BD_EVENT_END_OF_TITLE, 0);
Packit 5e46da
                        bd->end_of_playlist |= 1;
Packit 5e46da
                        return 0;
Packit 5e46da
                    }
Packit 5e46da
                    if (!_open_m2ts(bd, st)) {
Packit 5e46da
                        return -1;
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                    if (st->clip->connection == CONNECT_NON_SEAMLESS) {
Packit 5e46da
                        /* application layer demuxer buffers must be reset here */
Packit 5e46da
                        _queue_event(bd, BD_EVENT_DISCONTINUITY, st->clip->in_time);
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
                int r = _read_block(bd, st, bd->int_buf);
Packit 5e46da
                if (r > 0) {
Packit 5e46da
Packit 5e46da
                    if (st->ig_pid > 0) {
Packit 5e46da
                        if (gc_decode_ts(bd->graphics_controller, st->ig_pid, bd->int_buf, 1, -1) > 0) {
Packit 5e46da
                            /* initialize menus */
Packit 5e46da
                            _run_gc(bd, GC_CTRL_INIT_MENU, 0);
Packit 5e46da
                        }
Packit 5e46da
                    }
Packit 5e46da
                    if (st->pg_pid > 0) {
Packit 5e46da
                        if (gc_decode_ts(bd->graphics_controller, st->pg_pid, bd->int_buf, 1, -1) > 0) {
Packit 5e46da
                            /* render subtitles */
Packit 5e46da
                            gc_run(bd->graphics_controller, GC_CTRL_PG_UPDATE, 0, NULL);
Packit 5e46da
                        }
Packit 5e46da
                    }
Packit 5e46da
                    if (bd->st_textst.clip) {
Packit 5e46da
                        _update_textst_timer(bd);
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                    st->int_buf_off = st->clip_pos % 6144;
Packit 5e46da
Packit 5e46da
                } else if (r == 0) {
Packit 5e46da
                    /* recoverable error (EOF, broken block) */
Packit 5e46da
                    return out_len;
Packit 5e46da
                } else {
Packit 5e46da
                    /* fatal error */
Packit 5e46da
                    return -1;
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
                /* finetune seek point (avoid skipping PAT/PMT/PCR) */
Packit 5e46da
                if (BD_UNLIKELY(st->seek_flag)) {
Packit 5e46da
                    st->seek_flag = 0;
Packit 5e46da
Packit 5e46da
                    /* rewind if previous packets contain PAT/PMT/PCR */
Packit 5e46da
                    while (st->int_buf_off >= 192 && TS_PID(bd->int_buf + st->int_buf_off - 192) <= HDMV_PID_PCR) {
Packit 5e46da
                        st->clip_pos -= 192;
Packit 5e46da
                        st->int_buf_off -= 192;
Packit 5e46da
                        bd->s_pos -= 192;
Packit 5e46da
                    }
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
            }
Packit 5e46da
            if (size > (unsigned int)6144 - st->int_buf_off) {
Packit 5e46da
                size = 6144 - st->int_buf_off;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            /* cut read at clip end packet */
Packit 5e46da
            uint32_t new_clip_pkt = SPN(st->clip_pos + size);
Packit 5e46da
            if (new_clip_pkt > st->clip->end_pkt) {
Packit 5e46da
                BD_DEBUG(DBG_STREAM, "cut %d bytes at end of block\n", (new_clip_pkt - st->clip->end_pkt) * 192);
Packit 5e46da
                size -= (new_clip_pkt - st->clip->end_pkt) * 192;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            /* copy chunk */
Packit 5e46da
            memcpy(buf, bd->int_buf + st->int_buf_off, size);
Packit 5e46da
            buf += size;
Packit 5e46da
            len -= size;
Packit 5e46da
            out_len += size;
Packit 5e46da
            st->clip_pos += size;
Packit 5e46da
            st->int_buf_off += size;
Packit 5e46da
            bd->s_pos += size;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* mark tracking */
Packit 5e46da
        if (bd->next_mark >= 0 && bd->s_pos > bd->next_mark_pos) {
Packit 5e46da
            _playmark_reached(bd);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        BD_DEBUG(DBG_STREAM, "%d bytes read OK!\n", out_len);
Packit 5e46da
        return out_len;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_STREAM | DBG_CRIT, "bd_read(): no valid title selected!\n");
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_read(BLURAY *bd, unsigned char *buf, int len)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
    result = _bd_read(bd, buf, len);
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_read_skip_still(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    BD_STREAM *st = &bd->st0;
Packit 5e46da
    int ret = 0;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (st->clip) {
Packit 5e46da
        MPLS_PI *pi = &st->clip->title->pl->play_item[st->clip->ref];
Packit 5e46da
Packit 5e46da
        if (pi->still_mode == BLURAY_STILL_TIME) {
Packit 5e46da
            st->clip = nav_next_clip(bd->title, st->clip);
Packit 5e46da
            if (st->clip) {
Packit 5e46da
                ret = _open_m2ts(bd, st);
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * synchronous sub paths
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _preload_textst_subpath(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    uint8_t        char_code      = BLURAY_TEXT_CHAR_CODE_UTF8;
Packit 5e46da
    int            textst_subpath = -1;
Packit 5e46da
    unsigned       textst_subclip = 0;
Packit 5e46da
    uint16_t       textst_pid     = 0;
Packit 5e46da
    unsigned       ii;
Packit 5e46da
Packit 5e46da
    if (!bd->graphics_controller) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!bd->decode_pg || !bd->title) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _find_pg_stream(bd, &textst_pid, &textst_subpath, &textst_subclip, &char_code);
Packit 5e46da
    if (textst_subpath < 0) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (textst_subclip >= bd->title->sub_path[textst_subpath].clip_list.count) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_preload_textst_subpath(): invalid subclip id\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bd->st_textst.clip == &bd->title->sub_path[textst_subpath].clip_list.clip[textst_subclip]) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "_preload_textst_subpath(): subpath already loaded");
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    gc_run(bd->graphics_controller, GC_CTRL_PG_RESET, 0, NULL);
Packit 5e46da
Packit 5e46da
    bd->st_textst.clip = &bd->title->sub_path[textst_subpath].clip_list.clip[textst_subclip];
Packit 5e46da
    if (!bd->st_textst.clip->cl) {
Packit 5e46da
        /* required for fonts */
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_preload_textst_subpath(): missing clip data\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!_preload_m2ts(bd, &bd->st_textst)) {
Packit 5e46da
        _close_preload(&bd->st_textst);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    gc_decode_ts(bd->graphics_controller, 0x1800, bd->st_textst.buf, SPN(bd->st_textst.clip_size) / 32, -1);
Packit 5e46da
Packit 5e46da
    /* set fonts and encoding from clip info */
Packit 5e46da
    gc_add_font(bd->graphics_controller, NULL, -1);
Packit 5e46da
    for (ii = 0; ii < bd->st_textst.clip->cl->clip.font_info.font_count; ii++) {
Packit 5e46da
        char *file = str_printf("%s.otf", bd->st_textst.clip->cl->clip.font_info.font[ii].file_id);
Packit 5e46da
        if (file) {
Packit 5e46da
            uint8_t *data = NULL;
Packit 5e46da
            size_t size = disc_read_file(bd->disc, "BDMV" DIR_SEP "AUXDATA", file, &data);
Packit 5e46da
            if (data && size > 0 && gc_add_font(bd->graphics_controller, data, size) < 0) {
Packit 5e46da
                X_FREE(data);
Packit 5e46da
            }
Packit 5e46da
            X_FREE(file);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    gc_run(bd->graphics_controller, GC_CTRL_PG_CHARCODE, char_code, NULL);
Packit 5e46da
Packit 5e46da
    /* start presentation timer */
Packit 5e46da
    _init_textst_timer(bd);
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * preloader for asynchronous sub paths
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _find_ig_stream(BLURAY *bd, uint16_t *pid, int *sub_path_idx, unsigned *sub_clip_idx)
Packit 5e46da
{
Packit 5e46da
    unsigned  main_clip_idx = bd->st0.clip ? bd->st0.clip->ref : 0;
Packit 5e46da
    MPLS_PI  *pi        = &bd->title->pl->play_item[main_clip_idx];
Packit 5e46da
    unsigned  ig_stream = bd_psr_read(bd->regs, PSR_IG_STREAM_ID);
Packit 5e46da
Packit 5e46da
    if (ig_stream > 0 && ig_stream <= pi->stn.num_ig) {
Packit 5e46da
        ig_stream--; /* stream number to table index */
Packit 5e46da
        if (pi->stn.ig[ig_stream].stream_type == 2) {
Packit 5e46da
            *sub_path_idx = pi->stn.ig[ig_stream].subpath_id;
Packit 5e46da
            *sub_clip_idx = pi->stn.ig[ig_stream].subclip_id;
Packit 5e46da
        }
Packit 5e46da
        *pid = pi->stn.ig[ig_stream].pid;
Packit 5e46da
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "_find_ig_stream(): current IG stream pid 0x%04x sub-path %d\n",
Packit 5e46da
              *pid, *sub_path_idx);
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _preload_ig_subpath(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    int      ig_subpath = -1;
Packit 5e46da
    unsigned ig_subclip = 0;
Packit 5e46da
    uint16_t ig_pid     = 0;
Packit 5e46da
Packit 5e46da
    if (!bd->graphics_controller) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _find_ig_stream(bd, &ig_pid, &ig_subpath, &ig_subclip);
Packit 5e46da
Packit 5e46da
    if (ig_subpath < 0) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (ig_subclip >= bd->title->sub_path[ig_subpath].clip_list.count) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_preload_ig_subpath(): invalid subclip id\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bd->st_ig.clip == &bd->title->sub_path[ig_subpath].clip_list.clip[ig_subclip]) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_preload_ig_subpath(): subpath already loaded");
Packit 5e46da
        //return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->st_ig.clip = &bd->title->sub_path[ig_subpath].clip_list.clip[ig_subclip];
Packit 5e46da
Packit 5e46da
    if (bd->title->sub_path[ig_subpath].clip_list.count > 1) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_preload_ig_subpath(): multi-clip sub paths not supported\n");
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!_preload_m2ts(bd, &bd->st_ig)) {
Packit 5e46da
        _close_preload(&bd->st_ig);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _preload_subpaths(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    _close_preload(&bd->st_ig);
Packit 5e46da
    _close_preload(&bd->st_textst);
Packit 5e46da
Packit 5e46da
    if (bd->title->pl->sub_count <= 0) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return _preload_ig_subpath(bd) | _preload_textst_subpath(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _init_ig_stream(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    int      ig_subpath = -1;
Packit 5e46da
    unsigned ig_subclip = 0;
Packit 5e46da
    uint16_t ig_pid     = 0;
Packit 5e46da
Packit 5e46da
    bd->st0.ig_pid = 0;
Packit 5e46da
Packit 5e46da
    if (!bd->graphics_controller) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _find_ig_stream(bd, &ig_pid, &ig_subpath, &ig_subclip);
Packit 5e46da
Packit 5e46da
    /* decode already preloaded IG sub-path */
Packit 5e46da
    if (bd->st_ig.clip) {
Packit 5e46da
        gc_decode_ts(bd->graphics_controller, ig_pid, bd->st_ig.buf, SPN(bd->st_ig.clip_size) / 32, -1);
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* store PID of main path embedded IG stream */
Packit 5e46da
    if (ig_subpath < 0) {
Packit 5e46da
        bd->st0.ig_pid = ig_pid;
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * select title / angle
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _close_playlist(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (bd->graphics_controller) {
Packit 5e46da
        gc_run(bd->graphics_controller, GC_CTRL_RESET, 0, NULL);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* stopping playback in middle of playlist ? */
Packit 5e46da
    if (bd->title && bd->st0.clip) {
Packit 5e46da
        if (bd->st0.clip->ref < bd->title->clip_list.count - 1) {
Packit 5e46da
            /* not last clip of playlist */
Packit 5e46da
            BD_DEBUG(DBG_BLURAY, "close playlist (not last clip)\n");
Packit 5e46da
            _queue_event(bd, BD_EVENT_PLAYLIST_STOP, 0);
Packit 5e46da
        } else {
Packit 5e46da
            /* last clip of playlist */
Packit 5e46da
            int clip_pkt = SPN(bd->st0.clip_pos);
Packit 5e46da
            int skip = bd->st0.clip->end_pkt - clip_pkt;
Packit 5e46da
            BD_DEBUG(DBG_BLURAY, "close playlist (last clip), packets skipped %d\n", skip);
Packit 5e46da
            if (skip > 100) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_PLAYLIST_STOP, 0);
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _close_m2ts(&bd->st0);
Packit 5e46da
    _close_preload(&bd->st_ig);
Packit 5e46da
    _close_preload(&bd->st_textst);
Packit 5e46da
Packit 5e46da
    nav_title_close(&bd->title);
Packit 5e46da
Packit 5e46da
    /* reset UO mask */
Packit 5e46da
    memset(&bd->st0.uo_mask, 0, sizeof(BD_UO_MASK));
Packit 5e46da
    memset(&bd->gc_uo_mask,  0, sizeof(BD_UO_MASK));
Packit 5e46da
    _update_uo_mask(bd);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _add_known_playlist(BD_DISC *p, const char *mpls_id)
Packit 5e46da
{
Packit 5e46da
    char *old_mpls_ids;
Packit 5e46da
    char *new_mpls_ids = NULL;
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    old_mpls_ids = disc_property_get(p, DISC_PROPERTY_PLAYLISTS);
Packit 5e46da
    if (!old_mpls_ids) {
Packit 5e46da
        return disc_property_put(p, DISC_PROPERTY_PLAYLISTS, mpls_id);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* no duplicates */
Packit 5e46da
    if (str_strcasestr(old_mpls_ids, mpls_id)) {
Packit 5e46da
        goto out;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    new_mpls_ids = str_printf("%s,%s", old_mpls_ids, mpls_id);
Packit 5e46da
    if (new_mpls_ids) {
Packit 5e46da
        result = disc_property_put(p, DISC_PROPERTY_PLAYLISTS, new_mpls_ids);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
 out:
Packit 5e46da
    X_FREE(old_mpls_ids);
Packit 5e46da
    X_FREE(new_mpls_ids);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _open_playlist(BLURAY *bd, const char *f_name, unsigned angle)
Packit 5e46da
{
Packit 5e46da
    _close_playlist(bd);
Packit 5e46da
Packit 5e46da
    bd->title = nav_title_open(bd->disc, f_name, angle);
Packit 5e46da
    if (bd->title == NULL) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to open title %s!\n", f_name);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->seamless_angle_change = 0;
Packit 5e46da
    bd->s_pos = 0;
Packit 5e46da
    bd->end_of_playlist = 0;
Packit 5e46da
    bd->st0.ig_pid = 0;
Packit 5e46da
Packit 5e46da
    bd_psr_write(bd->regs, PSR_PLAYLIST, atoi(bd->title->name));
Packit 5e46da
    bd_psr_write(bd->regs, PSR_ANGLE_NUMBER, bd->title->angle + 1);
Packit 5e46da
    bd_psr_write(bd->regs, PSR_CHAPTER, 0xffff);
Packit 5e46da
Packit 5e46da
    // Get the initial clip of the playlist
Packit 5e46da
    bd->st0.clip = nav_next_clip(bd->title, NULL);
Packit 5e46da
    if (_open_m2ts(bd, &bd->st0)) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "Title %s selected\n", f_name);
Packit 5e46da
Packit 5e46da
        _find_next_playmark(bd);
Packit 5e46da
Packit 5e46da
        _preload_subpaths(bd);
Packit 5e46da
Packit 5e46da
        bd->st0.seek_flag = 1;
Packit 5e46da
Packit 5e46da
        /* remember played playlists when using menus */
Packit 5e46da
        if (bd->title_type != title_undef) {
Packit 5e46da
            _add_known_playlist(bd->disc, bd->title->name);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_select_playlist(BLURAY *bd, uint32_t playlist)
Packit 5e46da
{
Packit 5e46da
    char *f_name;
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    f_name = str_printf("%05d.mpls", playlist);
Packit 5e46da
    if (!f_name) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    if (bd->title_list) {
Packit 5e46da
        /* update current title */
Packit 5e46da
        unsigned i;
Packit 5e46da
        for (i = 0; i < bd->title_list->count; i++) {
Packit 5e46da
            if (playlist == bd->title_list->title_info[i].mpls_id) {
Packit 5e46da
                bd->title_idx = i;
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = _open_playlist(bd, f_name, 0);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    X_FREE(f_name);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* BD-J callback */
Packit 5e46da
static int _play_playlist_at(BLURAY *bd, int playlist, int playitem, int playmark, int64_t time)
Packit 5e46da
{
Packit 5e46da
    if (playlist < 0) {
Packit 5e46da
        _close_playlist(bd);
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!bd_select_playlist(bd, playlist)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->bdj_wait_start = 1;  /* playback is triggered by bd_select_rate() */
Packit 5e46da
Packit 5e46da
    bd_bdj_seek(bd, playitem, playmark, time);
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* BD-J callback */
Packit 5e46da
int bd_play_playlist_at(BLURAY *bd, int playlist, int playitem, int playmark, int64_t time)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    /* select + seek should be atomic (= player can't read data between select and seek to start position) */
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
    result = _play_playlist_at(bd, playlist, playitem, playmark, time);
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
// Select a title for playback
Packit 5e46da
// The title index is an index into the list
Packit 5e46da
// established by bd_get_titles()
Packit 5e46da
int bd_select_title(BLURAY *bd, uint32_t title_idx)
Packit 5e46da
{
Packit 5e46da
    const char *f_name;
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    // Open the playlist
Packit 5e46da
    if (bd->title_list == NULL) {
Packit 5e46da
        BD_DEBUG(DBG_CRIT | DBG_BLURAY, "Title list not yet read!\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
    if (bd->title_list->count <= title_idx) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Invalid title index %d!\n", title_idx);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    bd->title_idx = title_idx;
Packit 5e46da
    f_name = bd->title_list->title_info[title_idx].name;
Packit 5e46da
Packit 5e46da
    result = _open_playlist(bd, f_name, 0);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint32_t bd_get_current_title(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    return bd->title_idx;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _bd_select_angle(BLURAY *bd, unsigned angle)
Packit 5e46da
{
Packit 5e46da
    unsigned orig_angle;
Packit 5e46da
Packit 5e46da
    if (bd->title == NULL) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Can't select angle: title not yet selected!\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    orig_angle = bd->title->angle;
Packit 5e46da
Packit 5e46da
    bd->st0.clip = nav_set_angle(bd->title, bd->st0.clip, angle);
Packit 5e46da
Packit 5e46da
    if (orig_angle == bd->title->angle) {
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_psr_write(bd->regs, PSR_ANGLE_NUMBER, bd->title->angle + 1);
Packit 5e46da
Packit 5e46da
    if (!_open_m2ts(bd, &bd->st0)) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Error selecting angle %d !\n", angle);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_select_angle(BLURAY *bd, unsigned angle)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
    result = _bd_select_angle(bd, angle);
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
unsigned bd_get_current_angle(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    int angle = 0;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
    if (bd->title) {
Packit 5e46da
        angle = bd->title->angle;
Packit 5e46da
    }
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return angle;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
void bd_seamless_angle_change(BLURAY *bd, unsigned angle)
Packit 5e46da
{
Packit 5e46da
    uint32_t clip_pkt;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    clip_pkt = SPN(bd->st0.clip_pos + 191);
Packit 5e46da
    bd->angle_change_pkt = nav_angle_change_search(bd->st0.clip, clip_pkt,
Packit 5e46da
                                                   &bd->angle_change_time);
Packit 5e46da
    bd->request_angle = angle;
Packit 5e46da
    bd->seamless_angle_change = 1;
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * title lists
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
uint32_t bd_get_titles(BLURAY *bd, uint8_t flags, uint32_t min_title_length)
Packit 5e46da
{
Packit 5e46da
    if (!bd) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_get_titles(NULL) failed\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    nav_free_title_list(&bd->title_list);
Packit 5e46da
    bd->title_list = nav_get_title_list(bd->disc, flags, min_title_length);
Packit 5e46da
Packit 5e46da
    if (!bd->title_list) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "nav_get_title_list(%s) failed\n", disc_root(bd->disc));
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    disc_event(bd->disc, DISC_EVENT_START, bd->disc_info.num_titles);
Packit 5e46da
Packit 5e46da
    return bd->title_list->count;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_get_main_title(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (bd->title_type != title_undef) {
Packit 5e46da
        BD_DEBUG(DBG_CRIT | DBG_BLURAY, "bd_get_main_title() can't be used with BluRay menus\n");
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bd->title_list == NULL) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Title list not yet read!\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return bd->title_list->main_title_idx;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _copy_streams(NAV_CLIP *clip, BLURAY_STREAM_INFO **pstreams, MPLS_STREAM *si, int count)
Packit 5e46da
{
Packit 5e46da
    BLURAY_STREAM_INFO *streams;
Packit 5e46da
    int ii;
Packit 5e46da
Packit 5e46da
    if (!count) {
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
    streams = *pstreams = calloc(count, sizeof(BLURAY_STREAM_INFO));
Packit 5e46da
    if (!streams) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < count; ii++) {
Packit 5e46da
        streams[ii].coding_type = si[ii].coding_type;
Packit 5e46da
        streams[ii].format = si[ii].format;
Packit 5e46da
        streams[ii].rate = si[ii].rate;
Packit 5e46da
        streams[ii].char_code = si[ii].char_code;
Packit 5e46da
        memcpy(streams[ii].lang, si[ii].lang, 4);
Packit 5e46da
        streams[ii].pid = si[ii].pid;
Packit 5e46da
        streams[ii].aspect = nav_lookup_aspect(clip, si[ii].pid);
Packit 5e46da
        if ((si->stream_type == 2) || (si->stream_type == 3))
Packit 5e46da
            streams[ii].subpath_id = si->subpath_id;
Packit 5e46da
        else
Packit 5e46da
            streams[ii].subpath_id = -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BLURAY_TITLE_INFO* _fill_title_info(NAV_TITLE* title, uint32_t title_idx, uint32_t playlist)
Packit 5e46da
{
Packit 5e46da
    BLURAY_TITLE_INFO *title_info;
Packit 5e46da
    unsigned int ii;
Packit 5e46da
Packit 5e46da
    title_info = calloc(1, sizeof(BLURAY_TITLE_INFO));
Packit 5e46da
    if (!title_info) {
Packit 5e46da
        goto error;
Packit 5e46da
    }
Packit 5e46da
    title_info->idx = title_idx;
Packit 5e46da
    title_info->playlist = playlist;
Packit 5e46da
    title_info->duration = (uint64_t)title->duration * 2;
Packit 5e46da
    title_info->angle_count = title->angle_count;
Packit 5e46da
    title_info->chapter_count = title->chap_list.count;
Packit 5e46da
    if (title_info->chapter_count) {
Packit 5e46da
        title_info->chapters = calloc(title_info->chapter_count, sizeof(BLURAY_TITLE_CHAPTER));
Packit 5e46da
        if (!title_info->chapters) {
Packit 5e46da
            goto error;
Packit 5e46da
        }
Packit 5e46da
        for (ii = 0; ii < title_info->chapter_count; ii++) {
Packit 5e46da
            title_info->chapters[ii].idx = ii;
Packit 5e46da
            title_info->chapters[ii].start = (uint64_t)title->chap_list.mark[ii].title_time * 2;
Packit 5e46da
            title_info->chapters[ii].duration = (uint64_t)title->chap_list.mark[ii].duration * 2;
Packit 5e46da
            title_info->chapters[ii].offset = (uint64_t)title->chap_list.mark[ii].title_pkt * 192L;
Packit 5e46da
            title_info->chapters[ii].clip_ref = title->chap_list.mark[ii].clip_ref;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    title_info->mark_count = title->mark_list.count;
Packit 5e46da
    if (title_info->mark_count) {
Packit 5e46da
        title_info->marks = calloc(title_info->mark_count, sizeof(BLURAY_TITLE_MARK));
Packit 5e46da
        if (!title_info->marks) {
Packit 5e46da
            goto error;
Packit 5e46da
        }
Packit 5e46da
        for (ii = 0; ii < title_info->mark_count; ii++) {
Packit 5e46da
            title_info->marks[ii].idx = ii;
Packit 5e46da
            title_info->marks[ii].type = title->mark_list.mark[ii].mark_type;
Packit 5e46da
            title_info->marks[ii].start = (uint64_t)title->mark_list.mark[ii].title_time * 2;
Packit 5e46da
            title_info->marks[ii].duration = (uint64_t)title->mark_list.mark[ii].duration * 2;
Packit 5e46da
            title_info->marks[ii].offset = (uint64_t)title->mark_list.mark[ii].title_pkt * 192L;
Packit 5e46da
            title_info->marks[ii].clip_ref = title->mark_list.mark[ii].clip_ref;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    title_info->clip_count = title->clip_list.count;
Packit 5e46da
    if (title_info->clip_count) {
Packit 5e46da
        title_info->clips = calloc(title_info->clip_count, sizeof(BLURAY_CLIP_INFO));
Packit 5e46da
        if (!title_info->clips) {
Packit 5e46da
            goto error;
Packit 5e46da
        }
Packit 5e46da
        for (ii = 0; ii < title_info->clip_count; ii++) {
Packit 5e46da
            MPLS_PI *pi = &title->pl->play_item[ii];
Packit 5e46da
            BLURAY_CLIP_INFO *ci = &title_info->clips[ii];
Packit 5e46da
            NAV_CLIP *nc = &title->clip_list.clip[ii];
Packit 5e46da
Packit 5e46da
            memcpy(ci->clip_id, pi->clip->clip_id, sizeof(ci->clip_id));
Packit 5e46da
            ci->pkt_count = nc->end_pkt - nc->start_pkt;
Packit 5e46da
            ci->start_time = (uint64_t)nc->title_time * 2;
Packit 5e46da
            ci->in_time = (uint64_t)pi->in_time * 2;
Packit 5e46da
            ci->out_time = (uint64_t)pi->out_time * 2;
Packit 5e46da
            ci->still_mode = pi->still_mode;
Packit 5e46da
            ci->still_time = pi->still_time;
Packit 5e46da
            ci->video_stream_count = pi->stn.num_video;
Packit 5e46da
            ci->audio_stream_count = pi->stn.num_audio;
Packit 5e46da
            ci->pg_stream_count = pi->stn.num_pg + pi->stn.num_pip_pg;
Packit 5e46da
            ci->ig_stream_count = pi->stn.num_ig;
Packit 5e46da
            ci->sec_video_stream_count = pi->stn.num_secondary_video;
Packit 5e46da
            ci->sec_audio_stream_count = pi->stn.num_secondary_audio;
Packit 5e46da
            if (!_copy_streams(nc, &ci->video_streams, pi->stn.video, ci->video_stream_count) ||
Packit 5e46da
                !_copy_streams(nc, &ci->audio_streams, pi->stn.audio, ci->audio_stream_count) ||
Packit 5e46da
                !_copy_streams(nc, &ci->pg_streams, pi->stn.pg, ci->pg_stream_count) ||
Packit 5e46da
                !_copy_streams(nc, &ci->ig_streams, pi->stn.ig, ci->ig_stream_count) ||
Packit 5e46da
                !_copy_streams(nc, &ci->sec_video_streams, pi->stn.secondary_video, ci->sec_video_stream_count) ||
Packit 5e46da
                !_copy_streams(nc, &ci->sec_audio_streams, pi->stn.secondary_audio, ci->sec_audio_stream_count)) {
Packit 5e46da
Packit 5e46da
                goto error;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return title_info;
Packit 5e46da
Packit 5e46da
 error:
Packit 5e46da
    BD_DEBUG(DBG_CRIT, "Out of memory\n");
Packit 5e46da
    bd_free_title_info(title_info);
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BLURAY_TITLE_INFO *_get_title_info(BLURAY *bd, uint32_t title_idx, uint32_t playlist, const char *mpls_name,
Packit 5e46da
                                          unsigned angle)
Packit 5e46da
{
Packit 5e46da
    NAV_TITLE *title;
Packit 5e46da
    BLURAY_TITLE_INFO *title_info;
Packit 5e46da
Packit 5e46da
    /* current title ? => no need to load mpls file */
Packit 5e46da
    if (bd->title && bd->title->angle == angle && !strcmp(bd->title->name, mpls_name)) {
Packit 5e46da
        return _fill_title_info(bd->title, title_idx, playlist);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    title = nav_title_open(bd->disc, mpls_name, angle);
Packit 5e46da
    if (title == NULL) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Unable to open title %s!\n", mpls_name);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    title_info = _fill_title_info(title, title_idx, playlist);
Packit 5e46da
Packit 5e46da
    nav_title_close(&title);
Packit 5e46da
    return title_info;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BLURAY_TITLE_INFO* bd_get_title_info(BLURAY *bd, uint32_t title_idx, unsigned angle)
Packit 5e46da
{
Packit 5e46da
    if (bd->title_list == NULL) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Title list not yet read!\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
    if (bd->title_list->count <= title_idx) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Invalid title index %d!\n", title_idx);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return _get_title_info(bd,
Packit 5e46da
                           title_idx, bd->title_list->title_info[title_idx].mpls_id,
Packit 5e46da
                           bd->title_list->title_info[title_idx].name,
Packit 5e46da
                           angle);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BLURAY_TITLE_INFO* bd_get_playlist_info(BLURAY *bd, uint32_t playlist, unsigned angle)
Packit 5e46da
{
Packit 5e46da
    char *f_name;
Packit 5e46da
    BLURAY_TITLE_INFO *title_info;
Packit 5e46da
Packit 5e46da
    f_name = str_printf("%05d.mpls", playlist);
Packit 5e46da
    if (!f_name) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    title_info = _get_title_info(bd, 0, playlist, f_name, angle);
Packit 5e46da
Packit 5e46da
    X_FREE(f_name);
Packit 5e46da
Packit 5e46da
    return title_info;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void bd_free_title_info(BLURAY_TITLE_INFO *title_info)
Packit 5e46da
{
Packit 5e46da
    unsigned int ii;
Packit 5e46da
Packit 5e46da
    if (title_info) {
Packit 5e46da
        X_FREE(title_info->chapters);
Packit 5e46da
        X_FREE(title_info->marks);
Packit 5e46da
        if (title_info->clips) {
Packit 5e46da
            for (ii = 0; ii < title_info->clip_count; ii++) {
Packit 5e46da
                X_FREE(title_info->clips[ii].video_streams);
Packit 5e46da
                X_FREE(title_info->clips[ii].audio_streams);
Packit 5e46da
                X_FREE(title_info->clips[ii].pg_streams);
Packit 5e46da
                X_FREE(title_info->clips[ii].ig_streams);
Packit 5e46da
                X_FREE(title_info->clips[ii].sec_video_streams);
Packit 5e46da
                X_FREE(title_info->clips[ii].sec_audio_streams);
Packit 5e46da
            }
Packit 5e46da
            X_FREE(title_info->clips);
Packit 5e46da
        }
Packit 5e46da
        X_FREE(title_info);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * player settings
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
int bd_set_player_setting(BLURAY *bd, uint32_t idx, uint32_t value)
Packit 5e46da
{
Packit 5e46da
    static const struct { uint32_t idx; uint32_t  psr; } map[] = {
Packit 5e46da
        { BLURAY_PLAYER_SETTING_PARENTAL,       PSR_PARENTAL },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_AUDIO_CAP,      PSR_AUDIO_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_AUDIO_LANG,     PSR_AUDIO_LANG },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_PG_LANG,        PSR_PG_AND_SUB_LANG },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_MENU_LANG,      PSR_MENU_LANG },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_COUNTRY_CODE,   PSR_COUNTRY },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_REGION_CODE,    PSR_REGION },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_OUTPUT_PREFER,  PSR_OUTPUT_PREFER },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_DISPLAY_CAP,    PSR_DISPLAY_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_3D_CAP,         PSR_3D_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_UHD_CAP,         PSR_UHD_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_UHD_DISPLAY_CAP, PSR_UHD_DISPLAY_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_HDR_PREFERENCE,  PSR_UHD_HDR_PREFER },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_SDR_CONV_PREFER, PSR_UHD_SDR_CONV_PREFER },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_VIDEO_CAP,      PSR_VIDEO_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_TEXT_CAP,       PSR_TEXT_CAP },
Packit 5e46da
        { BLURAY_PLAYER_SETTING_PLAYER_PROFILE, PSR_PROFILE_VERSION },
Packit 5e46da
    };
Packit 5e46da
Packit 5e46da
    unsigned i;
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (idx == BLURAY_PLAYER_SETTING_DECODE_PG) {
Packit 5e46da
        bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
        bd->decode_pg = !!value;
Packit 5e46da
        result = !bd_psr_write_bits(bd->regs, PSR_PG_STREAM,
Packit 5e46da
                                    (!!value) << 31,
Packit 5e46da
                                    0x80000000);
Packit 5e46da
Packit 5e46da
        bd_mutex_unlock(&bd->mutex);
Packit 5e46da
        return result;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (idx == BLURAY_PLAYER_SETTING_PERSISTENT_STORAGE) {
Packit 5e46da
        if (bd->title_type != title_undef) {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Can't disable persistent storage during playback\n");
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
        bd->bdjstorage.no_persistent_storage = !value;
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
Packit 5e46da
        if (idx == map[i].idx) {
Packit 5e46da
            bd_mutex_lock(&bd->mutex);
Packit 5e46da
            result = !bd_psr_setting_write(bd->regs, map[i].psr, value);
Packit 5e46da
            bd_mutex_unlock(&bd->mutex);
Packit 5e46da
            return result;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_set_player_setting_str(BLURAY *bd, uint32_t idx, const char *s)
Packit 5e46da
{
Packit 5e46da
    switch (idx) {
Packit 5e46da
        case BLURAY_PLAYER_SETTING_AUDIO_LANG:
Packit 5e46da
        case BLURAY_PLAYER_SETTING_PG_LANG:
Packit 5e46da
        case BLURAY_PLAYER_SETTING_MENU_LANG:
Packit 5e46da
            return bd_set_player_setting(bd, idx, str_to_uint32(s, 3));
Packit 5e46da
Packit 5e46da
        case BLURAY_PLAYER_SETTING_COUNTRY_CODE:
Packit 5e46da
            return bd_set_player_setting(bd, idx, str_to_uint32(s, 2));
Packit 5e46da
Packit 5e46da
        case BLURAY_PLAYER_CACHE_ROOT:
Packit 5e46da
            bd_mutex_lock(&bd->mutex);
Packit 5e46da
            X_FREE(bd->bdjstorage.cache_root);
Packit 5e46da
            bd->bdjstorage.cache_root = str_dup(s);
Packit 5e46da
            bd_mutex_unlock(&bd->mutex);
Packit 5e46da
            BD_DEBUG(DBG_BDJ, "Cache root dir set to %s\n", bd->bdjstorage.cache_root);
Packit 5e46da
            return 1;
Packit 5e46da
Packit 5e46da
        case BLURAY_PLAYER_PERSISTENT_ROOT:
Packit 5e46da
            bd_mutex_lock(&bd->mutex);
Packit 5e46da
            X_FREE(bd->bdjstorage.persistent_root);
Packit 5e46da
            bd->bdjstorage.persistent_root = str_dup(s);
Packit 5e46da
            bd_mutex_unlock(&bd->mutex);
Packit 5e46da
            BD_DEBUG(DBG_BDJ, "Persistent root dir set to %s\n", bd->bdjstorage.persistent_root);
Packit 5e46da
            return 1;
Packit 5e46da
Packit 5e46da
        default:
Packit 5e46da
            return 0;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void bd_select_stream(BLURAY *bd, uint32_t stream_type, uint32_t stream_id, uint32_t enable_flag)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    switch (stream_type) {
Packit 5e46da
        case BLURAY_AUDIO_STREAM:
Packit 5e46da
            bd_psr_write(bd->regs, PSR_PRIMARY_AUDIO_ID, stream_id & 0xff);
Packit 5e46da
            break;
Packit 5e46da
        case BLURAY_PG_TEXTST_STREAM:
Packit 5e46da
            bd_psr_write_bits(bd->regs, PSR_PG_STREAM,
Packit 5e46da
                              ((!!enable_flag)<<31) | (stream_id & 0xfff),
Packit 5e46da
                              0x80000fff);
Packit 5e46da
            break;
Packit 5e46da
        /*
Packit 5e46da
        case BLURAY_SECONDARY_VIDEO_STREAM:
Packit 5e46da
        case BLURAY_SECONDARY_AUDIO_STREAM:
Packit 5e46da
        */
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * BD-J testing
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
int bd_start_bdj(BLURAY *bd, const char *start_object)
Packit 5e46da
{
Packit 5e46da
    const BLURAY_TITLE *t;
Packit 5e46da
    unsigned int title_num = atoi(start_object);
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    if (!bd) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* first play object ? */
Packit 5e46da
    if (bd->disc_info.first_play_supported) {
Packit 5e46da
        t = bd->disc_info.first_play;
Packit 5e46da
        if (t && t->bdj && t->id_ref == title_num) {
Packit 5e46da
            return _start_bdj(bd, BLURAY_TITLE_FIRST_PLAY);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* valid BD-J title from disc index ? */
Packit 5e46da
    if (bd->disc_info.titles) {
Packit 5e46da
        for (ii = 0; ii <= bd->disc_info.num_titles; ii++) {
Packit 5e46da
            t = bd->disc_info.titles[ii];
Packit 5e46da
            if (t && t->bdj && t->id_ref == title_num) {
Packit 5e46da
                return _start_bdj(bd, ii);
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "No %s.bdjo in disc index\n", start_object);
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "No disc index\n");
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
 }
Packit 5e46da
Packit 5e46da
void bd_stop_bdj(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
    _close_bdj(bd);
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Navigation mode interface
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _set_scr(BLURAY *bd, int64_t pts)
Packit 5e46da
{
Packit 5e46da
    if (pts >= 0) {
Packit 5e46da
        uint32_t tick = (uint32_t)(((uint64_t)pts) >> 1);
Packit 5e46da
        _update_time_psr(bd, tick);
Packit 5e46da
Packit 5e46da
    } else if (!bd->app_scr) {
Packit 5e46da
        _update_time_psr_from_stream(bd);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _process_psr_restore_event(BLURAY *bd, BD_PSR_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    /* PSR restore events are handled internally.
Packit 5e46da
     * Restore stored playback position.
Packit 5e46da
     */
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "PSR restore: psr%u = %u\n", ev->psr_idx, ev->new_val);
Packit 5e46da
Packit 5e46da
    switch (ev->psr_idx) {
Packit 5e46da
        case PSR_ANGLE_NUMBER:
Packit 5e46da
            /* can't set angle before playlist is opened */
Packit 5e46da
            return;
Packit 5e46da
        case PSR_TITLE_NUMBER:
Packit 5e46da
            /* pass to the application */
Packit 5e46da
            _queue_event(bd, BD_EVENT_TITLE, ev->new_val);
Packit 5e46da
            return;
Packit 5e46da
        case PSR_CHAPTER:
Packit 5e46da
            /* will be selected automatically */
Packit 5e46da
            return;
Packit 5e46da
        case PSR_PLAYLIST:
Packit 5e46da
            bd_select_playlist(bd, ev->new_val);
Packit 5e46da
            nav_set_angle(bd->title, bd->st0.clip, bd_psr_read(bd->regs, PSR_ANGLE_NUMBER) - 1);
Packit 5e46da
            return;
Packit 5e46da
        case PSR_PLAYITEM:
Packit 5e46da
            bd_seek_playitem(bd, ev->new_val);
Packit 5e46da
            return;
Packit 5e46da
        case PSR_TIME:
Packit 5e46da
            _clip_seek_time(bd, ev->new_val);
Packit 5e46da
            _init_ig_stream(bd);
Packit 5e46da
            _run_gc(bd, GC_CTRL_INIT_MENU, 0);
Packit 5e46da
            return;
Packit 5e46da
Packit 5e46da
        case PSR_SELECTED_BUTTON_ID:
Packit 5e46da
        case PSR_MENU_PAGE_ID:
Packit 5e46da
            /* handled by graphics controller */
Packit 5e46da
            return;
Packit 5e46da
Packit 5e46da
        default:
Packit 5e46da
            /* others: ignore */
Packit 5e46da
            return;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * notification events to APP
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _process_psr_write_event(BLURAY *bd, BD_PSR_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    if (ev->ev_type == BD_PSR_WRITE) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "PSR write: psr%u = %u\n", ev->psr_idx, ev->new_val);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    switch (ev->psr_idx) {
Packit 5e46da
Packit 5e46da
        /* current playback position */
Packit 5e46da
Packit 5e46da
        case PSR_ANGLE_NUMBER:
Packit 5e46da
            _bdj_event  (bd, BDJ_EVENT_ANGLE,   ev->new_val);
Packit 5e46da
            _queue_event(bd, BD_EVENT_ANGLE,    ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
        case PSR_TITLE_NUMBER:
Packit 5e46da
            _queue_event(bd, BD_EVENT_TITLE,    ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
        case PSR_PLAYLIST:
Packit 5e46da
            _bdj_event  (bd, BDJ_EVENT_PLAYLIST,ev->new_val);
Packit 5e46da
            _queue_event(bd, BD_EVENT_PLAYLIST, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
        case PSR_PLAYITEM:
Packit 5e46da
            _bdj_event  (bd, BDJ_EVENT_PLAYITEM,ev->new_val);
Packit 5e46da
            _queue_event(bd, BD_EVENT_PLAYITEM, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
        case PSR_TIME:
Packit 5e46da
            _bdj_event  (bd, BDJ_EVENT_PTS,     ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case 102:
Packit 5e46da
            _bdj_event  (bd, BDJ_EVENT_PSR102,  ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
        case 103:
Packit 5e46da
            disc_event(bd->disc, DISC_EVENT_APPLICATION, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        default:;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _process_psr_change_event(BLURAY *bd, BD_PSR_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "PSR change: psr%u = %u\n", ev->psr_idx, ev->new_val);
Packit 5e46da
Packit 5e46da
    _process_psr_write_event(bd, ev);
Packit 5e46da
Packit 5e46da
    switch (ev->psr_idx) {
Packit 5e46da
Packit 5e46da
        /* current playback position */
Packit 5e46da
Packit 5e46da
        case PSR_TITLE_NUMBER:
Packit 5e46da
            disc_event(bd->disc, DISC_EVENT_TITLE, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case PSR_CHAPTER:
Packit 5e46da
            _bdj_event  (bd, BDJ_EVENT_CHAPTER, ev->new_val);
Packit 5e46da
            if (ev->new_val != 0xffff) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_CHAPTER,  ev->new_val);
Packit 5e46da
            }
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        /* stream selection */
Packit 5e46da
Packit 5e46da
        case PSR_IG_STREAM_ID:
Packit 5e46da
            _queue_event(bd, BD_EVENT_IG_STREAM, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case PSR_PRIMARY_AUDIO_ID:
Packit 5e46da
            _bdj_event(bd, BDJ_EVENT_AUDIO_STREAM, ev->new_val);
Packit 5e46da
            _queue_event(bd, BD_EVENT_AUDIO_STREAM, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case PSR_PG_STREAM:
Packit 5e46da
            _bdj_event(bd, BDJ_EVENT_SUBTITLE, ev->new_val);
Packit 5e46da
            if ((ev->new_val & 0x80000fff) != (ev->old_val & 0x80000fff)) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_PG_TEXTST,        !!(ev->new_val & 0x80000000));
Packit 5e46da
                _queue_event(bd, BD_EVENT_PG_TEXTST_STREAM,    ev->new_val & 0xfff);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            bd_mutex_lock(&bd->mutex);
Packit 5e46da
            if (bd->st0.clip) {
Packit 5e46da
                _init_pg_stream(bd);
Packit 5e46da
                if (bd->st_textst.clip) {
Packit 5e46da
                    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Changing TextST stream\n");
Packit 5e46da
                    _preload_textst_subpath(bd);
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
            bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case PSR_SECONDARY_AUDIO_VIDEO:
Packit 5e46da
            /* secondary video */
Packit 5e46da
            if ((ev->new_val & 0x8f00ff00) != (ev->old_val & 0x8f00ff00)) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_SECONDARY_VIDEO, !!(ev->new_val & 0x80000000));
Packit 5e46da
                _queue_event(bd, BD_EVENT_SECONDARY_VIDEO_SIZE, (ev->new_val >> 24) & 0xf);
Packit 5e46da
                _queue_event(bd, BD_EVENT_SECONDARY_VIDEO_STREAM, (ev->new_val & 0xff00) >> 8);
Packit 5e46da
            }
Packit 5e46da
            /* secondary audio */
Packit 5e46da
            if ((ev->new_val & 0x400000ff) != (ev->old_val & 0x400000ff)) {
Packit 5e46da
                _queue_event(bd, BD_EVENT_SECONDARY_AUDIO, !!(ev->new_val & 0x40000000));
Packit 5e46da
                _queue_event(bd, BD_EVENT_SECONDARY_AUDIO_STREAM, ev->new_val & 0xff);
Packit 5e46da
            }
Packit 5e46da
            _bdj_event(bd, BDJ_EVENT_SECONDARY_STREAM, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        /* 3D status */
Packit 5e46da
        case PSR_3D_STATUS:
Packit 5e46da
            _queue_event(bd, BD_EVENT_STEREOSCOPIC_STATUS, ev->new_val & 1);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        default:;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _process_psr_event(void *handle, BD_PSR_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    BLURAY *bd = (BLURAY*)handle;
Packit 5e46da
Packit 5e46da
    switch(ev->ev_type) {
Packit 5e46da
        case BD_PSR_WRITE:
Packit 5e46da
            _process_psr_write_event(bd, ev);
Packit 5e46da
            break;
Packit 5e46da
        case BD_PSR_CHANGE:
Packit 5e46da
            _process_psr_change_event(bd, ev);
Packit 5e46da
            break;
Packit 5e46da
        case BD_PSR_RESTORE:
Packit 5e46da
            _process_psr_restore_event(bd, ev);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case BD_PSR_SAVE:
Packit 5e46da
            BD_DEBUG(DBG_BLURAY, "PSR save event\n");
Packit 5e46da
            break;
Packit 5e46da
        default:
Packit 5e46da
            BD_DEBUG(DBG_BLURAY, "PSR event %d: psr%u = %u\n", ev->ev_type, ev->psr_idx, ev->new_val);
Packit 5e46da
            break;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _queue_initial_psr_events(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    const uint32_t psrs[] = {
Packit 5e46da
        PSR_ANGLE_NUMBER,
Packit 5e46da
        PSR_TITLE_NUMBER,
Packit 5e46da
        PSR_IG_STREAM_ID,
Packit 5e46da
        PSR_PRIMARY_AUDIO_ID,
Packit 5e46da
        PSR_PG_STREAM,
Packit 5e46da
        PSR_SECONDARY_AUDIO_VIDEO,
Packit 5e46da
    };
Packit 5e46da
    unsigned ii;
Packit 5e46da
    BD_PSR_EVENT ev;
Packit 5e46da
Packit 5e46da
    ev.ev_type = BD_PSR_CHANGE;
Packit 5e46da
    ev.old_val = 0;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < sizeof(psrs) / sizeof(psrs[0]); ii++) {
Packit 5e46da
        ev.psr_idx = psrs[ii];
Packit 5e46da
        ev.new_val = bd_psr_read(bd->regs, psrs[ii]);
Packit 5e46da
Packit 5e46da
        _process_psr_change_event(bd, &ev;;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _play_bdj(BLURAY *bd, unsigned title)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    bd->title_type = title_bdj;
Packit 5e46da
Packit 5e46da
    result = _start_bdj(bd, title);
Packit 5e46da
    if (result <= 0) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Can't play BD-J title %d\n", title);
Packit 5e46da
        bd->title_type = title_undef;
Packit 5e46da
        _queue_event(bd, BD_EVENT_ERROR, BD_ERROR_BDJ);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _play_hdmv(BLURAY *bd, unsigned id_ref)
Packit 5e46da
{
Packit 5e46da
    int result = 1;
Packit 5e46da
Packit 5e46da
    _stop_bdj(bd);
Packit 5e46da
Packit 5e46da
    bd->title_type = title_hdmv;
Packit 5e46da
Packit 5e46da
    if (!bd->hdmv_vm) {
Packit 5e46da
        bd->hdmv_vm = hdmv_vm_init(bd->disc, bd->regs, bd->disc_info.num_titles,
Packit 5e46da
                                   bd->disc_info.first_play_supported, bd->disc_info.top_menu_supported);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (hdmv_vm_select_object(bd->hdmv_vm, id_ref)) {
Packit 5e46da
        result = 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd->hdmv_suspended = !hdmv_vm_running(bd->hdmv_vm);
Packit 5e46da
Packit 5e46da
    if (result <= 0) {
Packit 5e46da
        bd->title_type = title_undef;
Packit 5e46da
        _queue_event(bd, BD_EVENT_ERROR, BD_ERROR_HDMV);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _play_title(BLURAY *bd, unsigned title)
Packit 5e46da
{
Packit 5e46da
    if (!bd->disc_info.titles) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_play_title(#%d): No disc index\n", title);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bd->disc_info.no_menu_support) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_play_title(): no menu support\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* first play object ? */
Packit 5e46da
    if (title == BLURAY_TITLE_FIRST_PLAY) {
Packit 5e46da
Packit 5e46da
        bd_psr_write(bd->regs, PSR_TITLE_NUMBER, 0xffff); /* 5.2.3.3 */
Packit 5e46da
Packit 5e46da
        if (!bd->disc_info.first_play_supported) {
Packit 5e46da
            /* no first play title (5.2.3.3) */
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_play_title(): No first play title\n");
Packit 5e46da
            bd->title_type = title_hdmv;
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (bd->disc_info.first_play->bdj) {
Packit 5e46da
            return _play_bdj(bd, title);
Packit 5e46da
        } else {
Packit 5e46da
            return _play_hdmv(bd, bd->disc_info.first_play->id_ref);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* bd_play not called ? */
Packit 5e46da
    if (bd->title_type == title_undef) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY|DBG_CRIT, "bd_call_title(): bd_play() not called !\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* top menu ? */
Packit 5e46da
    if (title == BLURAY_TITLE_TOP_MENU) {
Packit 5e46da
Packit 5e46da
        bd_psr_write(bd->regs, PSR_TITLE_NUMBER, 0); /* 5.2.3.3 */
Packit 5e46da
Packit 5e46da
        if (!bd->disc_info.top_menu_supported) {
Packit 5e46da
            /* no top menu (5.2.3.3) */
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_play_title(): No top menu title\n");
Packit 5e46da
            bd->title_type = title_hdmv;
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (bd->disc_info.top_menu->bdj) {
Packit 5e46da
            return _play_bdj(bd, title);
Packit 5e46da
        } else {
Packit 5e46da
            return _play_hdmv(bd, bd->disc_info.top_menu->id_ref);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* valid title from disc index ? */
Packit 5e46da
    if (title > 0 && title <= bd->disc_info.num_titles) {
Packit 5e46da
Packit 5e46da
        bd_psr_write(bd->regs, PSR_TITLE_NUMBER, title); /* 5.2.3.3 */
Packit 5e46da
        if (bd->disc_info.titles[title]->bdj) {
Packit 5e46da
            return _play_bdj(bd, title);
Packit 5e46da
        } else {
Packit 5e46da
            return _play_hdmv(bd, bd->disc_info.titles[title]->id_ref);
Packit 5e46da
        }
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "_play_title(#%d): Title not found\n", title);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* BD-J callback */
Packit 5e46da
int bd_play_title_internal(BLURAY *bd, unsigned title)
Packit 5e46da
{
Packit 5e46da
    /* used by BD-J. Like bd_play_title() but bypasses UO mask checks. */
Packit 5e46da
    int ret;
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
    ret = _play_title(bd, title);
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int bd_play(BLURAY *bd)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    /* reset player state */
Packit 5e46da
Packit 5e46da
    bd->title_type = title_undef;
Packit 5e46da
Packit 5e46da
    if (bd->hdmv_vm) {
Packit 5e46da
        hdmv_vm_free(&bd->hdmv_vm);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!bd->event_queue) {
Packit 5e46da
        bd->event_queue = event_queue_new(sizeof(BD_EVENT));
Packit 5e46da
Packit 5e46da
        bd_psr_lock(bd->regs);
Packit 5e46da
        bd_psr_register_cb(bd->regs, _process_psr_event, bd);
Packit 5e46da
        _queue_initial_psr_events(bd);
Packit 5e46da
        bd_psr_unlock(bd->regs);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    disc_event(bd->disc, DISC_EVENT_START, 0);
Packit 5e46da
Packit 5e46da
    /* start playback from FIRST PLAY title */
Packit 5e46da
Packit 5e46da
    result = _play_title(bd, BLURAY_TITLE_FIRST_PLAY);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&bd->mutex);
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _try_play_title(BLURAY *bd, unsigned title)
Packit 5e46da
{
Packit 5e46da
    if (bd->title_type == title_undef && title != BLURAY_TITLE_FIRST_PLAY) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_play_title(): bd_play() not called\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bd->uo_mask.title_search) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "title search masked\n");
Packit 5e46da
        _bdj_event(bd, BDJ_EVENT_UO_MASKED, UO_MASK_TITLE_SEARCH_INDEX);
Packit 5e46da
        return 0;