bluray.c 63.9 KB
Newer Older
1 2 3
/*****************************************************************************
 * bluray.c: Blu-ray disc support plugin
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright © 2010-2012 VideoLAN, VLC authors and libbluray AUTHORS
5 6
 *
 * Authors: Jean-Baptiste Kempf <jb@videolan.org>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
7
 *          Hugo Beauzée-Luyssen <hugo@videolan.org>
8
 *
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12 13 14 15
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19 20 21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27 28
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>
29

30
#if defined (HAVE_MNTENT_H) && defined(HAVE_SYS_STAT_H)
31 32
# include <mntent.h>
# include <sys/stat.h>
33
#endif
34

35
#ifdef __APPLE__
36 37 38 39
# include <sys/stat.h>
# include <sys/param.h>
# include <sys/ucred.h>
# include <sys/mount.h>
40 41
#endif

42 43
#include <vlc_common.h>
#include <vlc_plugin.h>
44 45
#include <vlc_demux.h>                      /* demux_t */
#include <vlc_input.h>                      /* Seekpoints, chapters */
46
#include <vlc_atomic.h>
47
#include <vlc_dialog.h>                     /* BD+/AACS warnings */
48
#include <vlc_vout.h>                       /* vout_PutSubpicture / subpicture_t */
49
#include <vlc_url.h>                        /* vlc_path2uri */
50 51 52 53 54
#include <vlc_iso_lang.h>

/* FIXME we should find a better way than including that */
#include "../../src/text/iso-639_def.h"

55 56

#include <libbluray/bluray.h>
57
#include <libbluray/bluray-version.h>
58
#include <libbluray/keys.h>
59
#include <libbluray/meta_data.h>
60
#include <libbluray/overlay.h>
61 62 63 64 65

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

Rafaël Carré's avatar
Rafaël Carré committed
66 67 68 69 70
#define BD_MENU_TEXT        N_("Blu-ray menus")
#define BD_MENU_LONGTEXT    N_("Use Blu-ray menus. If disabled, "\
                                "the movie will start directly")
#define BD_REGION_TEXT      N_("Region code")
#define BD_REGION_LONGTEXT  N_("Blu-Ray player region code. "\
71 72 73 74 75 76 77 78
                                "Some discs can be played only with a correct region code.")

static const char *const ppsz_region_code[] = {
    "A", "B", "C" };
static const char *const ppsz_region_code_text[] = {
    "Region A", "Region B", "Region C" };

#define REGION_DEFAULT   1   /* Index to region list. Actual region code is (1<<REGION_DEFAULT) */
79
#define LANGUAGE_DEFAULT ("eng")
80

81
/* Callbacks */
Rafaël Carré's avatar
Rafaël Carré committed
82 83
static int  blurayOpen (vlc_object_t *);
static void blurayClose(vlc_object_t *);
84 85

vlc_module_begin ()
Rafaël Carré's avatar
Rafaël Carré committed
86 87
    set_shortname(N_("Blu-ray"))
    set_description(N_("Blu-ray Disc support (libbluray)"))
88

Rafaël Carré's avatar
Rafaël Carré committed
89 90 91 92 93 94
    set_category(CAT_INPUT)
    set_subcategory(SUBCAT_INPUT_ACCESS)
    set_capability("access_demux", 200)
    add_bool("bluray-menu", false, BD_MENU_TEXT, BD_MENU_LONGTEXT, false)
    add_string("bluray-region", ppsz_region_code[REGION_DEFAULT], BD_REGION_TEXT, BD_REGION_LONGTEXT, false)
        change_string_list(ppsz_region_code, ppsz_region_code_text)
95

Rafaël Carré's avatar
Rafaël Carré committed
96
    add_shortcut("bluray", "file")
97

Rafaël Carré's avatar
Rafaël Carré committed
98
    set_callbacks(blurayOpen, blurayClose)
99 100
vlc_module_end ()

101 102
/* libbluray's overlay.h defines 2 types of overlay (bd_overlay_plane_e). */
#define MAX_OVERLAY 2
103

104 105 106 107 108 109 110 111
typedef enum OverlayStatus {
    Closed = 0,
    ToDisplay,  //Used to mark the overlay to be displayed the first time.
    Displayed,
    Outdated    //used to update the overlay after it has been sent to the vout
} OverlayStatus;

typedef struct bluray_overlay_t
112
{
113
    vlc_mutex_t         lock;
114
    int                 i_channel;
115 116
    OverlayStatus       status;
    subpicture_region_t *p_regions;
117
    int                 width, height;
118 119 120 121 122 123 124

    /* pointer to last subpicture updater.
     * used to disconnect this overlay from vout when:
     * - the overlay is closed
     * - vout is changed and this overlay is sent to the new vout
     */
    struct subpicture_updater_sys_t *p_updater;
125 126 127 128 129
} bluray_overlay_t;

struct  demux_sys_t
{
    BLURAY              *bluray;
130 131

    /* Titles */
132 133 134
    unsigned int        i_title;
    unsigned int        i_longest_title;
    input_title_t       **pp_title;
135

136 137 138 139
    vlc_mutex_t             pl_info_lock;
    BLURAY_TITLE_INFO      *p_pl_info;
    const BLURAY_CLIP_INFO *p_clip_info;

140 141 142 143 144
    /* Attachments */
    int                 i_attachments;
    input_attachment_t  **attachments;
    int                 i_cover_idx;

145
    /* Meta information */
146 147
    const META_DL       *p_meta;

148
    /* Menus */
149
    bluray_overlay_t    *p_overlays[MAX_OVERLAY];
150
    bool                b_fatal_error;
151
    bool                b_menu;
152 153
    bool                b_menu_open;
    bool                b_popup_available;
154
    mtime_t             i_still_end_time;
155

156 157
    vlc_mutex_t         bdj_overlay_lock; /* used to lock BD-J overlay open/close while overlays are being sent to vout */

158 159
    /* */
    vout_thread_t       *p_vout;
160

161 162
    es_out_id_t         *p_dummy_video;

163
    /* TS stream */
164
    es_out_t            *p_out;
165
    vlc_array_t         es;
166
    int                 i_audio_stream; /* Selected audio stream. -1 if default */
167
    int                 i_spu_stream;   /* Selected subtitle stream. -1 if default */
168
    int                 i_video_stream;
169
    stream_t            *p_parser;
170
    bool                b_flushed;
171 172 173

    /* Used to store bluray disc path */
    char                *psz_bd_path;
174 175 176 177
};

struct subpicture_updater_sys_t
{
178 179 180
    vlc_mutex_t          lock;      // protect p_overlay pointer and ref_cnt
    bluray_overlay_t    *p_overlay; // NULL if overlay has been closed
    int                  ref_cnt;   // one reference in vout (subpicture_t), one in input (bluray_overlay_t)
181 182
};

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
/*
 * cut the connection between vout and overlay.
 * - called when vout is closed or overlay is closed.
 * - frees subpicture_updater_sys_t when both sides have been closed.
 */
static void unref_subpicture_updater(subpicture_updater_sys_t *p_sys)
{
    vlc_mutex_lock(&p_sys->lock);
    int refs = p_sys->ref_cnt--;
    p_sys->p_overlay = NULL;
    vlc_mutex_unlock(&p_sys->lock);

    if (refs < 1) {
        vlc_mutex_destroy(&p_sys->lock);
        free(p_sys);
    }
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
/* Get a 3 char code
 * FIXME: partiallyy duplicated from src/input/es_out.c
 */
static const char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var )
{
    const iso639_lang_t *pl;
    char *psz_lang;
    char *p;

    psz_lang = var_CreateGetString( p_demux, psz_var );
    if( !psz_lang )
        return LANGUAGE_DEFAULT;

    /* XXX: we will use only the first value
     * (and ignore other ones in case of a list) */
    if( ( p = strchr( psz_lang, ',' ) ) )
        *p = '\0';

    for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
    {
        if( *psz_lang == '\0' )
            continue;
        if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
            !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
            !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
            !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
            break;
    }

    free( psz_lang );

    if( pl->psz_eng_name != NULL )
        return pl->psz_iso639_2T;

    return LANGUAGE_DEFAULT;
}

238 239 240
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
241
static es_out_t *esOutNew(demux_t *p_demux);
242

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
243 244
static int   blurayControl(demux_t *, int, va_list);
static int   blurayDemux(demux_t *);
245

246
static void  blurayInitTitles(demux_t *p_demux, int menu_titles);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
247
static int   bluraySetTitle(demux_t *p_demux, int i_title);
248

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
249
static void  blurayOverlayProc(void *ptr, const BD_OVERLAY * const overlay);
250
static void  blurayArgbOverlayProc(void *ptr, const BD_ARGB_OVERLAY * const overlay);
251

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
252 253
static int   onMouseEvent(vlc_object_t *p_vout, const char *psz_var,
                          vlc_value_t old, vlc_value_t val, void *p_data);
254

255 256
static void  blurayResetParser(demux_t *p_demux);

257 258 259
#define FROM_TICKS(a) (a*CLOCK_FREQ / INT64_C(90000))
#define TO_TICKS(a)   (a*INT64_C(90000)/CLOCK_FREQ)
#define CUR_LENGTH    p_sys->pp_title[p_demux->info.i_title]->i_length
260

261 262 263 264 265
/* */
static void FindMountPoint(char **file)
{
    char *device = *file;
#if defined (HAVE_MNTENT_H) && defined (HAVE_SYS_STAT_H)
266 267 268 269 270 271
    /* bd path may be a symlink (e.g. /dev/dvd -> /dev/sr0), so make sure
     * we look up the real device */
    char *bd_device = realpath(device, NULL);
    if (bd_device == NULL)
        return;

272
    struct stat st;
273
    if (lstat (bd_device, &st) == 0 && S_ISBLK (st.st_mode)) {
274 275 276 277 278 279 280 281 282 283 284 285 286
        FILE *mtab = setmntent ("/proc/self/mounts", "r");
        struct mntent *m, mbuf;
        char buf [8192];

        while ((m = getmntent_r (mtab, &mbuf, buf, sizeof(buf))) != NULL) {
            if (!strcmp (m->mnt_fsname, bd_device)) {
                free(device);
                *file = strdup(m->mnt_dir);
                break;
            }
        }
        endmntent (mtab);
    }
287 288
    free(bd_device);

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
#elif defined(__APPLE__)
    struct stat st;
    if (!stat (device, &st) && S_ISBLK (st.st_mode)) {
        int fs_count = getfsstat (NULL, 0, MNT_NOWAIT);
        if (fs_count > 0) {
            struct statfs mbuf[128];
            getfsstat (mbuf, fs_count * sizeof(mbuf[0]), MNT_NOWAIT);
            for (int i = 0; i < fs_count; ++i)
                if (!strcmp (mbuf[i].f_mntfromname, device)) {
                    free(device);
                    *file = strdup(mbuf[i].f_mntonname);
                    return;
                }
        }
    }
#else
# warning Disc device to mount point not implemented
306
    VLC_UNUSED( device );
307 308 309
#endif
}

310 311 312 313 314 315 316
static void blurayReleaseVout(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (p_sys->p_vout != NULL) {
        var_DelCallback(p_sys->p_vout, "mouse-moved", onMouseEvent, p_demux);
        var_DelCallback(p_sys->p_vout, "mouse-clicked", onMouseEvent, p_demux);
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

        for (int i = 0; i < MAX_OVERLAY; i++) {
            bluray_overlay_t *p_ov = p_sys->p_overlays[i];
            if (p_ov) {
                vlc_mutex_lock(&p_ov->lock);
                if (p_ov->i_channel != -1) {
                    msg_Err(p_demux, "blurayReleaseVout: subpicture channel exists\n");
                    vout_FlushSubpictureChannel(p_sys->p_vout, p_ov->i_channel);
                }
                p_ov->i_channel = -1;
                p_ov->status = ToDisplay;
                vlc_mutex_unlock(&p_ov->lock);

                if (p_ov->p_updater) {
                    unref_subpicture_updater(p_ov->p_updater);
                    p_ov->p_updater = NULL;
                }
            }
        }

337 338 339 340 341
        vlc_object_release(p_sys->p_vout);
        p_sys->p_vout = NULL;
    }
}

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
/*****************************************************************************
 * BD-J background video
 *****************************************************************************/

static void startBackground(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (p_sys->p_dummy_video) {
        return;
    }

    msg_Info(p_demux, "Start background");

    /* */
    es_format_t fmt;
    es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_I420 );
    video_format_Setup( &fmt.video, VLC_CODEC_I420,
                        1920, 1080, 1920, 1080, 1, 1);
    fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN;
    fmt.i_id = 4115; /* 4113 = main video. 4114 = MVC. 4115 = unused. */
    fmt.i_group = 1;

    p_sys->p_dummy_video = es_out_Add(p_demux->out, &fmt);
    es_format_Clean(&fmt);

    if (!p_sys->p_dummy_video) {
      msg_Err(p_demux, "Error adding background ES");
      return;
    }

373 374
    block_t *p_block = block_Alloc(fmt.video.i_width * fmt.video.i_height *
                                   fmt.video.i_bits_per_pixel / 8);
375 376 377 378 379 380 381 382 383
    if (!p_block) {
        msg_Err(p_demux, "Error allocating block for background video");
        return;
    }

    // XXX TODO: what would be correct timestamp ???
    p_block->i_dts = p_block->i_pts = mdate() + CLOCK_FREQ/25;

    uint8_t *p = p_block->p_buffer;
384 385 386
    memset(p, 0, fmt.video.i_width * fmt.video.i_height);
    p += fmt.video.i_width * fmt.video.i_height;
    memset(p, 0x80, fmt.video.i_width * fmt.video.i_height / 2);
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

    es_out_Send(p_demux->out, p_sys->p_dummy_video, p_block);
}

static void stopBackground(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (!p_sys->p_dummy_video) {
        return;
    }

    msg_Info(p_demux, "Stop background");

    es_out_Del(p_demux->out, p_sys->p_dummy_video);
    p_sys->p_dummy_video = NULL;
}

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
/*****************************************************************************
 * cache current playlist (title) information
 *****************************************************************************/

static void setTitleInfo(demux_sys_t *p_sys, BLURAY_TITLE_INFO *info)
{
    vlc_mutex_lock(&p_sys->pl_info_lock);

    if (p_sys->p_pl_info) {
        bd_free_title_info(p_sys->p_pl_info);
    }
    p_sys->p_pl_info   = info;
    p_sys->p_clip_info = NULL;

    if (p_sys->p_pl_info && p_sys->p_pl_info->clip_count) {
        p_sys->p_clip_info = &p_sys->p_pl_info->clips[0];
    }

    vlc_mutex_unlock(&p_sys->pl_info_lock);
}

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
/*****************************************************************************
 * create input attachment for thumbnail
 *****************************************************************************/

static void attachThumbnail(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (!p_sys->p_meta)
        return;

#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0,9,0)
    if (p_sys->p_meta->thumb_count > 0 && p_sys->p_meta->thumbnails) {
        int64_t size;
        void *data;
        if (bd_get_meta_file(p_sys->bluray, p_sys->p_meta->thumbnails[0].path, &data, &size) > 0) {
            char psz_name[64];
            input_attachment_t *p_attachment;

            snprintf(psz_name, sizeof(psz_name), "picture%d_%s", p_sys->i_attachments, p_sys->p_meta->thumbnails[0].path);

            p_attachment = vlc_input_attachment_New(psz_name, NULL, "Album art", data, size);
            if (p_attachment) {
                p_sys->i_cover_idx = p_sys->i_attachments;
                TAB_APPEND(p_sys->i_attachments, p_sys->attachments, p_attachment);
            }
        }
        free(data);
    }
#endif
}

458 459 460
/*****************************************************************************
 * blurayOpen: module init function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
461
static int blurayOpen(vlc_object_t *object)
462
{
463 464
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys;
465

466
    const char *error_msg = NULL;
467
#define BLURAY_ERROR(s) do { error_msg = s; goto error; } while(0)
468

469
    if (strcmp(p_demux->psz_access, "bluray")) {
470
        // TODO BDMV support, once we figure out what to do in libbluray
471 472 473
        return VLC_EGENERIC;
    }

474
    /* */
475
    p_demux->p_sys = p_sys = calloc(1, sizeof(*p_sys));
Rafaël Carré's avatar
Rafaël Carré committed
476
    if (unlikely(!p_sys))
477
        return VLC_ENOMEM;
Rafaël Carré's avatar
Rafaël Carré committed
478

479
    p_sys->i_audio_stream = -1;
480
    p_sys->i_spu_stream = -1;
481
    p_sys->i_video_stream = -1;
482
    p_sys->i_still_end_time = 0;
483 484 485 486 487

    /* init demux info fields */
    p_demux->info.i_update    = 0;
    p_demux->info.i_title     = 0;
    p_demux->info.i_seekpoint = 0;
488

Rafaël Carré's avatar
Rafaël Carré committed
489
    TAB_INIT(p_sys->i_title, p_sys->pp_title);
490
    TAB_INIT(p_sys->i_attachments, p_sys->attachments);
491

492
    /* store current bd path */
493 494
    if (p_demux->psz_file)
        p_sys->psz_bd_path = strdup(p_demux->psz_file);
495

496
    /* If we're passed a block device, try to convert it to the mount point. */
497 498
    FindMountPoint(&p_sys->psz_bd_path);

499
    p_sys->bluray = bd_open(p_sys->psz_bd_path, NULL);
500
    if (!p_sys->bluray) {
501
        free(p_sys->psz_bd_path);
502 503 504 505
        free(p_sys);
        return VLC_EGENERIC;
    }

506
    vlc_mutex_init(&p_sys->pl_info_lock);
507
    vlc_mutex_init(&p_sys->bdj_overlay_lock);
508

509 510
    /* Warning the user about AACS/BD+ */
    const BLURAY_DISC_INFO *disc_info = bd_get_disc_info(p_sys->bluray);
511 512

    /* Is it a bluray? */
513 514
    if (!disc_info->bluray_detected)
        BLURAY_ERROR(_("Path doesn't appear to be a Blu-ray"));
515

516 517
    msg_Info(p_demux, "First play: %i, Top menu: %i\n"
                      "HDMV Titles: %i, BD-J Titles: %i, Other: %i",
518 519 520 521 522 523
             disc_info->first_play_supported, disc_info->top_menu_supported,
             disc_info->num_hdmv_titles, disc_info->num_bdj_titles,
             disc_info->num_unsupported_titles);

    /* AACS */
    if (disc_info->aacs_detected) {
524
        msg_Dbg(p_demux, "Disc is using AACS");
525 526 527
        if (!disc_info->libaacs_detected)
            BLURAY_ERROR(_("This Blu-ray Disc needs a library for AACS decoding"
                      ", and your system does not have it."));
528
        if (!disc_info->aacs_handled) {
529 530
            if (disc_info->aacs_error_code) {
                switch (disc_info->aacs_error_code) {
531 532 533 534 535 536 537 538 539 540 541 542
                case BD_AACS_CORRUPTED_DISC:
                    BLURAY_ERROR(_("Blu-ray Disc is corrupted."));
                case BD_AACS_NO_CONFIG:
                    BLURAY_ERROR(_("Missing AACS configuration file!"));
                case BD_AACS_NO_PK:
                    BLURAY_ERROR(_("No valid processing key found in AACS config file."));
                case BD_AACS_NO_CERT:
                    BLURAY_ERROR(_("No valid host certificate found in AACS config file."));
                case BD_AACS_CERT_REVOKED:
                    BLURAY_ERROR(_("AACS Host certificate revoked."));
                case BD_AACS_MMC_FAILED:
                    BLURAY_ERROR(_("AACS MMC failed."));
543 544
                }
            }
545 546 547 548 549
        }
    }

    /* BD+ */
    if (disc_info->bdplus_detected) {
550
        msg_Dbg(p_demux, "Disc is using BD+");
551 552 553 554 555 556
        if (!disc_info->libbdplus_detected)
            BLURAY_ERROR(_("This Blu-ray Disc needs a library for BD+ decoding"
                      ", and your system does not have it."));
        if (!disc_info->bdplus_handled)
            BLURAY_ERROR(_("Your system BD+ decoding library does not work. "
                      "Missing configuration?"));
557 558
    }

559 560 561 562 563 564
    /* set player region code */
    char *psz_region = var_InheritString(p_demux, "bluray-region");
    unsigned int region = psz_region ? (psz_region[0] - 'A') : REGION_DEFAULT;
    free(psz_region);
    bd_set_player_setting(p_sys->bluray, BLURAY_PLAYER_SETTING_REGION_CODE, 1<<region);

565 566 567 568 569 570 571 572
    /* set preferred languages */
    const char *psz_code = DemuxGetLanguageCode( p_demux, "audio-language" );
    bd_set_player_setting_str(p_sys->bluray, BLURAY_PLAYER_SETTING_AUDIO_LANG, psz_code);
    psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
    bd_set_player_setting_str(p_sys->bluray, BLURAY_PLAYER_SETTING_PG_LANG,    psz_code);
    psz_code = DemuxGetLanguageCode( p_demux, "menu-language" );
    bd_set_player_setting_str(p_sys->bluray, BLURAY_PLAYER_SETTING_MENU_LANG,  psz_code);

573
    /* Get titles and chapters */
574 575
    p_sys->p_meta = bd_get_meta(p_sys->bluray);
    if (!p_sys->p_meta)
Rafaël Carré's avatar
Rafaël Carré committed
576
        msg_Warn(p_demux, "Failed to get meta info.");
577

578 579 580
    p_sys->i_cover_idx = -1;
    attachThumbnail(p_demux);

581 582
    p_sys->b_menu = var_InheritBool(p_demux, "bluray-menu");

583 584 585 586 587 588 589 590 591 592 593 594
    /* Check BD-J capability */
    if (p_sys->b_menu && disc_info->bdj_detected && !disc_info->bdj_handled) {
        msg_Err(p_demux, "BD-J menus not supported. Playing without menus. "
                "BD-J support: %d, JVM found: %d, JVM usable: %d",
                disc_info->bdj_supported, disc_info->libjvm_detected, disc_info->bdj_handled);
        dialog_Fatal(p_demux, _("Java required"),
                     _("This Blu-ray disc needs Java for menus.%s\nDisc is played without menus."),
                     !disc_info->libjvm_detected ? _(" Java was not found from your system.") : "");
        p_sys->b_menu = false;
    }

    /* Get titles and chapters */
595
    blurayInitTitles(p_demux, disc_info->num_hdmv_titles + disc_info->num_bdj_titles + 1/*Top Menu*/ + 1/*First Play*/);
596

597 598 599 600 601
    /*
     * Initialize the event queue, so we can receive events in blurayDemux(Menu).
     */
    bd_get_event(p_sys->bluray, NULL);

602 603
    /* Registering overlay event handler */
    bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);
604
    if (unlikely(!p_demux->p_input)) {
605 606 607
        msg_Err(p_demux, "Could not get parent input");
        goto error;
    }
608

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
609
    if (p_sys->b_menu) {
610

611
        /* Register ARGB overlay handler for BD-J */
612
        if (disc_info->num_bdj_titles)
613 614
            bd_register_argb_overlay_proc(p_sys->bluray, p_demux, blurayArgbOverlayProc, NULL);

615
        /* libbluray will start playback from "First-Title" title */
616 617 618
        if (bd_play(p_sys->bluray) == 0)
            BLURAY_ERROR(_("Failed to start bluray playback. Please try without menu support."));

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
619
    } else {
620
        /* set start title number */
621
        if (bluraySetTitle(p_demux, p_sys->i_longest_title) != VLC_SUCCESS) {
Rafaël Carré's avatar
Rafaël Carré committed
622
            msg_Err(p_demux, "Could not set the title %d", p_sys->i_longest_title);
623 624
            goto error;
        }
625 626
    }

627
    vlc_array_init(&p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
628
    p_sys->p_out = esOutNew(p_demux);
Rafaël Carré's avatar
Rafaël Carré committed
629
    if (unlikely(p_sys->p_out == NULL))
630 631
        goto error;

Rafaël Carré's avatar
Rafaël Carré committed
632
    blurayResetParser(p_demux);
633 634
    if (!p_sys->p_parser) {
        msg_Err(p_demux, "Failed to create TS demuxer");
635
        goto error;
636 637 638 639
    }

    p_demux->pf_control = blurayControl;
    p_demux->pf_demux   = blurayDemux;
640 641

    return VLC_SUCCESS;
642 643 644

error:
    if (error_msg)
Christoph Miebach's avatar
Christoph Miebach committed
645
        dialog_Fatal(p_demux, _("Blu-ray error"), "%s", error_msg);
646 647
    blurayClose(object);
    return VLC_EGENERIC;
648
#undef BLURAY_ERROR
649 650 651 652 653 654
}


/*****************************************************************************
 * blurayClose: module destroy function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
655
static void blurayClose(vlc_object_t *object)
656
{
657 658 659
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys = p_demux->p_sys;

660 661
    setTitleInfo(p_sys, NULL);

662 663 664
    /*
     * Close libbluray first.
     * This will close all the overlays before we release p_vout
Rafaël Carré's avatar
Rafaël Carré committed
665
     * bd_close(NULL) can crash
666 667 668 669
     */
    assert(p_sys->bluray);
    bd_close(p_sys->bluray);

670 671
    blurayReleaseVout(p_demux);

672 673
    if (p_sys->p_parser)
        stream_Delete(p_sys->p_parser);
674 675
    if (p_sys->p_out != NULL)
        es_out_Delete(p_sys->p_out);
Rafaël Carré's avatar
Rafaël Carré committed
676 677
    assert(vlc_array_count(&p_sys->es) == 0);
    vlc_array_clear(&p_sys->es);
678

679 680 681
    /* Titles */
    for (unsigned int i = 0; i < p_sys->i_title; i++)
        vlc_input_title_Delete(p_sys->pp_title[i]);
Rafaël Carré's avatar
Rafaël Carré committed
682
    TAB_CLEAN(p_sys->i_title, p_sys->pp_title);
683

684 685 686 687
    for (int i = 0; i < p_sys->i_attachments; i++)
      vlc_input_attachment_Delete(p_sys->attachments[i]);
    TAB_CLEAN(p_sys->i_attachments, p_sys->attachments);

688
    vlc_mutex_destroy(&p_sys->pl_info_lock);
689
    vlc_mutex_destroy(&p_sys->bdj_overlay_lock);
690

691
    free(p_sys->psz_bd_path);
692 693 694
    free(p_sys);
}

695 696 697 698 699 700 701 702
/*****************************************************************************
 * Elementary streams handling
 *****************************************************************************/

struct es_out_sys_t {
    demux_t *p_demux;
};

703 704 705 706 707
typedef struct  fmt_es_pair {
    int         i_id;
    es_out_id_t *p_es;
}               fmt_es_pair_t;

Rafaël Carré's avatar
Rafaël Carré committed
708
static int  findEsPairIndex(demux_sys_t *p_sys, int i_id)
709
{
Rafaël Carré's avatar
Rafaël Carré committed
710 711
    for (int i = 0; i < vlc_array_count(&p_sys->es); ++i)
        if (((fmt_es_pair_t*)vlc_array_item_at_index(&p_sys->es, i))->i_id == i_id)
712
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
713

714 715 716
    return -1;
}

Rafaël Carré's avatar
Rafaël Carré committed
717
static int  findEsPairIndexByEs(demux_sys_t *p_sys, es_out_id_t *p_es)
718
{
Rafaël Carré's avatar
Rafaël Carré committed
719 720
    for (int i = 0; i < vlc_array_count(&p_sys->es); ++i)
        if (((fmt_es_pair_t*)vlc_array_item_at_index(&p_sys->es, i))->p_es == p_es)
721
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
722

723 724 725
    return -1;
}

726 727 728 729 730 731
static void setStreamLang(es_format_t *p_fmt,
                          const BLURAY_STREAM_INFO *p_streams, int i_stream_count)
{
    for (int i = 0; i < i_stream_count; i++) {
        if (p_fmt->i_id == p_streams[i].pid) {
            free(p_fmt->psz_language);
732
            p_fmt->psz_language = strndup((const char *)p_streams[i].lang, 3);
733 734 735 736 737
            return;
        }
    }
}

Rafaël Carré's avatar
Rafaël Carré committed
738
static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
739
{
740 741 742 743
    demux_sys_t *p_sys = p_out->p_sys->p_demux->p_sys;
    es_format_t fmt;

    es_format_Copy(&fmt, p_fmt);
744 745 746

    vlc_mutex_lock(&p_sys->pl_info_lock);

Rafaël Carré's avatar
Rafaël Carré committed
747
    switch (fmt.i_cat) {
748
    case VIDEO_ES:
Rafaël Carré's avatar
Rafaël Carré committed
749
        if (p_sys->i_video_stream != -1 && p_sys->i_video_stream != p_fmt->i_id)
750
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
751 752
        break ;
    case AUDIO_ES:
Rafaël Carré's avatar
Rafaël Carré committed
753
        if (p_sys->i_audio_stream != -1 && p_sys->i_audio_stream != p_fmt->i_id)
754
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
755 756
        if (p_sys->p_clip_info)
            setStreamLang(&fmt, p_sys->p_clip_info->audio_streams, p_sys->p_clip_info->audio_stream_count);
757 758
        break ;
    case SPU_ES:
759 760
        if (p_sys->i_spu_stream != -1 && p_sys->i_spu_stream != p_fmt->i_id)
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
761 762
        if (p_sys->p_clip_info)
            setStreamLang(&fmt, p_sys->p_clip_info->pg_streams, p_sys->p_clip_info->pg_stream_count);
763 764 765
        break ;
    }

766
    vlc_mutex_unlock(&p_sys->pl_info_lock);
767

Rafaël Carré's avatar
Rafaël Carré committed
768 769
    es_out_id_t *p_es = es_out_Add(p_out->p_sys->p_demux->out, &fmt);
    if (p_fmt->i_id >= 0) {
770
        /* Ensure we are not overriding anything */
771
        int idx = findEsPairIndex(p_sys, p_fmt->i_id);
Rafaël Carré's avatar
Rafaël Carré committed
772 773 774
        if (idx == -1) {
            fmt_es_pair_t *p_pair = malloc(sizeof(*p_pair));
            if (likely(p_pair != NULL)) {
775 776
                p_pair->i_id = p_fmt->i_id;
                p_pair->p_es = p_es;
Rafaël Carré's avatar
Rafaël Carré committed
777
                msg_Info(p_out->p_sys->p_demux, "Adding ES %d", p_fmt->i_id);
778
                vlc_array_append(&p_sys->es, p_pair);
779 780 781
            }
        }
    }
782
    es_format_Clean(&fmt);
783
    return p_es;
784 785
}

Rafaël Carré's avatar
Rafaël Carré committed
786
static int esOutSend(es_out_t *p_out, es_out_id_t *p_es, block_t *p_block)
787
{
Rafaël Carré's avatar
Rafaël Carré committed
788
    return es_out_Send(p_out->p_sys->p_demux->out, p_es, p_block);
789 790
}

Rafaël Carré's avatar
Rafaël Carré committed
791
static void esOutDel(es_out_t *p_out, es_out_id_t *p_es)
792
{
Rafaël Carré's avatar
Rafaël Carré committed
793 794 795
    int idx = findEsPairIndexByEs(p_out->p_sys->p_demux->p_sys, p_es);
    if (idx >= 0) {
        free(vlc_array_item_at_index(&p_out->p_sys->p_demux->p_sys->es, idx));
796 797
        vlc_array_remove(&p_out->p_sys->p_demux->p_sys->es, idx);
    }
Rafaël Carré's avatar
Rafaël Carré committed
798
    es_out_Del(p_out->p_sys->p_demux->out, p_es);
799 800
}

Rafaël Carré's avatar
Rafaël Carré committed
801
static int esOutControl(es_out_t *p_out, int i_query, va_list args)
802
{
Rafaël Carré's avatar
Rafaël Carré committed
803
    return es_out_vaControl(p_out->p_sys->p_demux->out, i_query, args);
804 805
}

Rafaël Carré's avatar
Rafaël Carré committed
806
static void esOutDestroy(es_out_t *p_out)
807
{
Rafaël Carré's avatar
Rafaël Carré committed
808 809
    for (int i = 0; i < vlc_array_count(&p_out->p_sys->p_demux->p_sys->es); ++i)
        free(vlc_array_item_at_index(&p_out->p_sys->p_demux->p_sys->es, i));
810
    vlc_array_clear(&p_out->p_sys->p_demux->p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
811 812
    free(p_out->p_sys);
    free(p_out);
813 814
}

Rafaël Carré's avatar
Rafaël Carré committed
815
static es_out_t *esOutNew(demux_t *p_demux)
816
{
Rafaël Carré's avatar
Rafaël Carré committed
817 818 819
    assert(vlc_array_count(&p_demux->p_sys->es) == 0);
    es_out_t    *p_out = malloc(sizeof(*p_out));
    if (unlikely(p_out == NULL))
820 821
        return NULL;

Rafaël Carré's avatar
Rafaël Carré committed
822 823 824 825 826
    p_out->pf_add       = esOutAdd;
    p_out->pf_control   = esOutControl;
    p_out->pf_del       = esOutDel;
    p_out->pf_destroy   = esOutDestroy;
    p_out->pf_send      = esOutSend;
827

Rafaël Carré's avatar
Rafaël Carré committed
828 829 830
    p_out->p_sys = malloc(sizeof(*p_out->p_sys));
    if (unlikely(p_out->p_sys == NULL)) {
        free(p_out);
831 832 833 834 835 836
        return NULL;
    }
    p_out->p_sys->p_demux = p_demux;
    return p_out;
}

837 838 839
/*****************************************************************************
 * subpicture_updater_t functions:
 *****************************************************************************/
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865

static bluray_overlay_t *updater_lock_overlay(subpicture_updater_sys_t *p_upd_sys)
{
    /* this lock is held while vout accesses overlay. => overlay can't be closed. */
    vlc_mutex_lock(&p_upd_sys->lock);

    bluray_overlay_t *ov = p_upd_sys->p_overlay;
    if (ov) {
        /* this lock is held while vout accesses overlay. => overlay can't be modified. */
        vlc_mutex_lock(&ov->lock);
        return ov;
    }

    /* overlay has been closed */
    vlc_mutex_unlock(&p_upd_sys->lock);
    return NULL;
}

static void updater_unlock_overlay(subpicture_updater_sys_t *p_upd_sys)
{
    assert (p_upd_sys->p_overlay);

    vlc_mutex_unlock(&p_upd_sys->p_overlay->lock);
    vlc_mutex_unlock(&p_upd_sys->lock);
}

Rafaël Carré's avatar
Rafaël Carré committed
866
static int subpictureUpdaterValidate(subpicture_t *p_subpic,
867 868
                                      bool b_fmt_src, const video_format_t *p_fmt_src,
                                      bool b_fmt_dst, const video_format_t *p_fmt_dst,
Rafaël Carré's avatar
Rafaël Carré committed
869
                                      mtime_t i_ts)
870
{
Rafaël Carré's avatar
Rafaël Carré committed
871 872 873 874 875
    VLC_UNUSED(b_fmt_src);
    VLC_UNUSED(b_fmt_dst);
    VLC_UNUSED(p_fmt_src);
    VLC_UNUSED(p_fmt_dst);
    VLC_UNUSED(i_ts);
876 877

    subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
878 879 880 881 882
    bluray_overlay_t         *p_overlay = updater_lock_overlay(p_upd_sys);

    if (!p_overlay) {
        return 1;
    }
883 884

    int res = p_overlay->status == Outdated;
885 886 887

    updater_unlock_overlay(p_upd_sys);

888 889 890 891 892 893 894 895 896 897 898 899
    return res;
}

static void subpictureUpdaterUpdate(subpicture_t *p_subpic,
                                    const video_format_t *p_fmt_src,
                                    const video_format_t *p_fmt_dst,
                                    mtime_t i_ts)
{
    VLC_UNUSED(p_fmt_src);
    VLC_UNUSED(p_fmt_dst);
    VLC_UNUSED(i_ts);
    subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
900 901 902 903 904
    bluray_overlay_t         *p_overlay = updater_lock_overlay(p_upd_sys);

    if (!p_overlay) {
        return;
    }
905 906 907 908 909 910

    /*
     * When this function is called, all p_subpic regions are gone.
     * We need to duplicate our regions (stored internaly) to this subpic.
     */
    subpicture_region_t *p_src = p_overlay->p_regions;
Rafaël Carré's avatar
Rafaël Carré committed
911
    if (!p_src) {
912
        updater_unlock_overlay(p_upd_sys);
913
        return;
914
    }
915

Rafaël Carré's avatar
Rafaël Carré committed
916
    subpicture_region_t **p_dst = &p_subpic->p_region;
917
    while (p_src != NULL) {
918
        *p_dst = subpicture_region_Copy(p_src);
919
        if (*p_dst == NULL)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
920
            break;
Rafaël Carré's avatar
Rafaël Carré committed
921
        p_dst = &(*p_dst)->p_next;
922 923 924 925 926 927
        p_src = p_src->p_next;
    }
    if (*p_dst != NULL)
        (*p_dst)->p_next = NULL;
    p_overlay->status = Displayed;

928 929
    updater_unlock_overlay(p_upd_sys);
}
930

931 932
static void subpictureUpdaterDestroy(subpicture_t *p_subpic)
{
933 934 935 936 937 938 939 940 941 942 943 944
    subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
    bluray_overlay_t         *p_overlay = updater_lock_overlay(p_upd_sys);

    if (p_overlay) {
        /* vout is closed (seek, new clip, ?). Overlay must be redrawn. */
        p_overlay->status = ToDisplay;
        p_overlay->i_channel = -1;
        updater_unlock_overlay(p_upd_sys);
    }

    unref_subpicture_updater(p_upd_sys);
}
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972

static subpicture_t *bluraySubpictureCreate(bluray_overlay_t *p_ov)
{
    subpicture_updater_sys_t *p_upd_sys = malloc(sizeof(*p_upd_sys));
    if (unlikely(p_upd_sys == NULL)) {
        return NULL;
    }

    p_upd_sys->p_overlay = p_ov;

    subpicture_updater_t updater = {
        .pf_validate = subpictureUpdaterValidate,
        .pf_update   = subpictureUpdaterUpdate,
        .pf_destroy  = subpictureUpdaterDestroy,
        .p_sys       = p_upd_sys,
    };

    subpicture_t *p_pic = subpicture_New(&updater);
    if (p_pic == NULL) {
        free(p_upd_sys);
        return NULL;
    }

    p_pic->i_original_picture_width = p_ov->width;
    p_pic->i_original_picture_height = p_ov->height;
    p_pic->b_ephemer = true;
    p_pic->b_absolute = true;

973 974 975 976 977
    vlc_mutex_init(&p_upd_sys->lock);
    p_upd_sys->ref_cnt = 2;

    p_ov->p_updater = p_upd_sys;

978
    return p_pic;
979 980
}

981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
/*****************************************************************************
 * User input events:
 *****************************************************************************/
static int onMouseEvent(vlc_object_t *p_vout, const char *psz_var, vlc_value_t old,
                        vlc_value_t val, void *p_data)
{
    demux_t     *p_demux = (demux_t*)p_data;
    demux_sys_t *p_sys   = p_demux->p_sys;
    mtime_t     now      = mdate();
    VLC_UNUSED(old);
    VLC_UNUSED(p_vout);

    if (psz_var[6] == 'm')   //Mouse moved
        bd_mouse_select(p_sys->bluray, now, val.coords.x, val.coords.y);
    else if (psz_var[6] == 'c') {
        bd_mouse_select(p_sys->bluray, now, val.coords.x, val.coords.y);
        bd_user_input(p_sys->bluray, now, BD_VK_MOUSE_ACTIVATE);
    } else {
999
        vlc_assert_unreachable();
1000 1001 1002 1003
    }
    return VLC_SUCCESS;
}

1004 1005 1006
static int sendKeyEvent(demux_sys_t *p_sys, unsigned int key)
{
    mtime_t now = mdate();
Rafaël Carré's avatar
Rafaël Carré committed
1007
    if (bd_user_input(p_sys->bluray, now, key) < 0)
1008
        return VLC_EGENERIC;
Rafaël Carré's avatar
Rafaël Carré committed
1009

1010 1011 1012
    return VLC_SUCCESS;
}

1013 1014 1015
/*****************************************************************************
 * libbluray overlay handling:
 *****************************************************************************/
1016

1017 1018 1019 1020 1021 1022
static void blurayCloseOverlay(demux_t *p_demux, int plane)
{
    demux_sys_t *p_sys = p_demux->p_sys;
    bluray_overlay_t *ov = p_sys->p_overlays[plane];

    if (ov != NULL) {
1023 1024 1025 1026 1027 1028

        /* drop overlay from vout */
        if (ov->p_updater) {
            unref_subpicture_updater(ov->p_updater);
        }
        /* no references to this overlay exist in vo anymore */
1029 1030 1031 1032
        if (p_sys->p_vout && ov->i_channel != -1) {
            vout_FlushSubpictureChannel(p_sys->p_vout, ov->i_channel);
        }

1033 1034 1035 1036
        vlc_mutex_destroy(&ov->lock);
        subpicture_region_ChainDelete(ov->p_regions);
        free(ov);

1037 1038
        p_sys->p_overlays[plane] = NULL;
    }
1039

1040
    for (int i = 0; i < MAX_OVERLAY; i++)
1041 1042
        if (p_sys->p_overlays[i])
            return;
1043

1044
    /* All overlays have been closed */
1045
    blurayReleaseVout(p_demux);
1046 1047 1048 1049 1050 1051 1052 1053 1054
}

/*
 * Mark the overlay as "ToDisplay" status.
 * This will not send the overlay to the vout instantly, as the vout
 * may not be acquired (not acquirable) yet.
 * If is has already been acquired, the overlay has already been sent to it,
 * therefore, we only flag the overlay as "Outdated"
 */
1055
static void blurayActivateOverlay(demux_t *p_demux, int plane)
1056 1057
{
    demux_sys_t *p_sys = p_demux->p_sys;
1058
    bluray_overlay_t *ov = p_sys->p_overlays[plane];
1059 1060 1061 1062 1063

    /*
     * If the overlay is already displayed, mark the picture as outdated.
     * We must NOT use vout_PutSubpicture if a picture is already displayed.
     */
1064 1065 1066 1067
    vlc_mutex_lock(&ov->lock);
    if (ov->status >= Displayed && p_sys->p_vout) {
        ov->status = Outdated;
        vlc_mutex_unlock(&ov->lock);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
1068
        return;
1069
    }
1070

1071 1072 1073 1074 1075
    /*
     * Mark the overlay as available, but don't display it right now.
     * the blurayDemuxMenu will send it to vout, as it may be unavailable when
     * the overlay is computed
     */
1076 1077
    ov->status = ToDisplay;
    vlc_mutex_unlock(&ov->lock);
1078 1079
}

1080
static void blurayInitOverlay(demux_t *p_demux, int plane, int width, int height)
1081 1082 1083
{
    demux_sys_t *p_sys = p_demux->p_sys;

1084
    assert(p_sys->p_overlays[plane] == NULL);
1085

1086 1087
    bluray_overlay_t *ov = calloc(1, sizeof(*ov));
    if (unlikely(ov == NULL))
1088 1089
        return;

1090 1091 1092
    ov->width = width;
    ov->height = height;
    ov->i_channel = -1;
1093 1094 1095 1096

    vlc_mutex_init(&ov->lock);

    p_sys->p_overlays[plane] = ov;
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
}

/**
 * Destroy every regions in the subpicture.
 * This is done in two steps:
 * - Wiping our private regions list
 * - Flagging the overlay as outdated, so the changes are replicated from
 *   the subpicture_updater_t::pf_update
 * This doesn't destroy the subpicture, as the overlay may be used again by libbluray.
 */
1107
static void blurayClearOverlay(demux_t *p_demux, int plane)
1108 1109
{
    demux_sys_t *p_sys = p_demux->p_sys;
1110 1111 1112
    bluray_overlay_t *ov = p_sys->p_overlays[plane];

    vlc_mutex_lock(&ov->lock);
1113

1114 1115 1116
    subpicture_region_ChainDelete(ov->p_regions);
    ov->p_regions = NULL;
    ov->status = Outdated;
1117

1118
    vlc_mutex_unlock(&ov->lock);
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
}

/*
 * This will draw to the overlay by adding a region to our region list
 * This will have to be copied to the subpicture used to render the overlay.
 */
static void blurayDrawOverlay(demux_t *p_demux, const BD_OVERLAY* const ov)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    /*
     * Compute a subpicture_region_t.
     * It will be copied and sent to the vout later.
     */
    if (!ov->img)
        return;

    vlc_mutex_lock(&p_sys->p_overlays[ov->plane]->lock);

    /* Find a region to update */
    subpicture_region_t *p_reg = p_sys->p_overlays[ov->plane]->p_regions;
    subpicture_region_t *p_last = NULL;
    while (p_reg != NULL) {
        p_last = p_reg;
        if (p_reg->i_x == ov->x && p_reg->i_y == ov->y &&
                p_reg->fmt.i_width == ov->w && p_reg->fmt.i_height == ov->h)
            break;
        p_reg = p_reg->p_next;
    }

    /* If there is no region to update, create a new one. */
    if (!p_reg) {
        video_format_t fmt;
        video_format_Init(&fmt, 0);
1153
        video_format_Setup(&fmt, VLC_CODEC_YUVP, ov->w, ov->h, ov->w, ov->h, 1, 1);
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

        p_reg = subpicture_region_New(&fmt);
        p_reg->i_x = ov->x;
        p_reg->i_y = ov->y;
        /* Append it to our list. */
        if (p_last != NULL)
            p_last->p_next = p_reg;
        else /* If we don't have a last region, then our list empty */
            p_sys->p_overlays[ov->plane]->p_regions = p_reg;
    }

    /* Now we can update the region, regardless it's an update or an insert */
    const BD_PG_RLE_ELEM *img = ov->img;
Rafaël Carré's avatar
Rafaël Carré committed
1167
    for (int y = 0; y < ov->h; y++)
1168
        for (int x = 0; x < ov->w;) {
1169 1170
            plane_t *p = &p_reg->p_picture->p[0];
            memset(&p->p_pixels[y * p->i_pitch + x], img->color, img->len);
1171 1172 1173
            x += img->len;
            img++;
        }
Rafaël Carré's avatar
Rafaël Carré committed
1174

1175 1176 1177 1178 1179 1180 1181 1182 1183
    if (ov->palette) {
        p_reg->fmt.p_palette->i_entries = 256;
        for (int i = 0; i < 256; ++i) {
            p_reg->fmt.p_palette->palette[i][0] = ov->palette[i].Y;
            p_reg->fmt.p_palette->palette[i][1] = ov->palette[i].Cb;
            p_reg->fmt.p_palette->palette[i][2] = ov->palette[i].Cr;
            p_reg->fmt.p_palette->palette[i][3] = ov->palette[i].T;
        }
    }
Rafaël Carré's avatar
Rafaël Carré committed
1184

1185 1186 1187 1188 1189 1190 1191 1192 1193
    vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
    /*
     * /!\ The region is now stored in our internal list, but not in the subpicture /!\
     */
}

static void blurayOverlayProc(void *ptr, const BD_OVERLAY *const overlay)
{
    demux_t *p_demux = (demux_t*)ptr;
1194
    demux_sys_t *p_sys = p_demux->p_sys;
1195 1196

    if (!overlay) {
1197 1198 1199 1200
        msg_Info(p_demux, "Closing overlays.");
        if (p_sys->p_vout)
            for (int i = 0; i < MAX_OVERLAY; i++)
                blurayCloseOverlay(p_demux, i);
1201 1202
        return;
    }
Rafaël Carré's avatar
Rafaël Carré committed
1203

1204
    switch (overlay->cmd) {
Rafaël Carré's avatar
Rafaël Carré committed
1205 1206 1207 1208
    case BD_OVERLAY_INIT:
        msg_Info(p_demux, "Initializing overlay");
        blurayInitOverlay(p_demux, overlay->plane, overlay->w, overlay->h);
        break;
1209 1210 1211 1212
    case BD_OVERLAY_CLOSE:
        blurayClearOverlay(p_demux, overlay->plane);
        blurayCloseOverlay(p_demux, overlay->plane);
        break;
Rafaël Carré's avatar
Rafaël Carré committed
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224
    case BD_OVERLAY_CLEAR:
        blurayClearOverlay(p_demux, overlay->plane);
        break;
    case BD_OVERLAY_FLUSH:
        blurayActivateOverlay(p_demux, overlay->plane);
        break;
    case BD_OVERLAY_DRAW:
        blurayDrawOverlay(p_demux, overlay);
        break;
    default:
        msg_Warn(p_demux, "Unknown BD overlay command: %u", overlay->cmd);
        break;
1225 1226 1227
    }
}

1228 1229 1230 1231 1232 1233 1234 1235 1236
/*
 * ARGB overlay (BD-J)
 */
static void blurayInitArgbOverlay(demux_t *p_demux, int plane, int width, int height)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    blurayInitOverlay(p_demux, plane, width, height);

1237
    if (!p_sys->p_overlays[plane]->p_regions) {
1238 1239
        video_format_t fmt;
        video_format_Init(&fmt, 0);
1240
        video_format_Setup(&fmt, VLC_CODEC_RGBA, width, height, width, height, 1, 1);
1241

1242
        p_sys->p_overlays[plane]->p_regions = subpicture_region_New(&fmt);
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
    }
}

static void blurayDrawArgbOverlay(demux_t *p_demux, const BD_ARGB_OVERLAY* const ov)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    vlc_mutex_lock(&p_sys->p_overlays[ov->plane]->lock);

    /* Find a region to update */
    subpicture_region_t *p_reg = p_sys->p_overlays[ov->plane]->p_regions;
    if (!p_reg) {
        vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
        return;
    }

    /* Now we can update the region */
    const uint32_t *src0 = ov->argb;
    uint8_t        *dst0 = p_reg->p_picture->p[0].p_pixels +
                           p_reg->p_picture->p[0].i_pitch * ov->y +
                           ov->x * 4;

    for (int y = 0; y < ov->h; y++) {
        // XXX: add support for this format ? Should be possible with OPENGL/VDPAU/...
        // - or add libbluray option to select the format ?
        for (int x = 0; x < ov->w; x++) {
            dst0[x*4  ] = src0[x]>>16; /* R */
            dst0[x*4+1] = src0[x]>>8;  /* G */
            dst0[x*4+2] = src0[x];     /* B */
            dst0[x*4+3] = src0[x]>>24; /* A */
        }

        src0 += ov->stride;
        dst0 += p_reg->p_picture->p[0].i_pitch;
    }

    vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
    /*
     * /!\ The region is now stored in our internal list, but not in the subpicture /!\
     */
}

static void blurayArgbOverlayProc(void *ptr, const BD_ARGB_OVERLAY *const overlay)
{
    demux_t *p_demux = (demux_t*)ptr;
1288
    demux_sys_t *p_sys = p_demux->p_sys;
1289 1290

    switch (overlay->cmd) {
Rafaël Carré's avatar
Rafaël Carré committed
1291
    case BD_ARGB_OVERLAY_INIT:
1292
        vlc_mutex_lock(&p_sys->bdj_overlay_lock);
Rafaël Carré's avatar
Rafaël Carré committed
1293
        blurayInitArgbOverlay(p_demux, overlay->plane, overlay->w, overlay->h);
1294
        vlc_mutex_unlock(&p_sys->bdj_overlay_lock);
Rafaël Carré's avatar
Rafaël Carré committed
1295 1296
        break;
    case BD_ARGB_OVERLAY_CLOSE:
1297
        vlc_mutex_lock(&p_sys->bdj_overlay_lock);
Rafaël Carré's avatar
Rafaël Carré committed
1298
        blurayClearOverlay(p_demux, overlay->plane);
1299
        blurayCloseOverlay(p_demux, overlay->plane);
1300
        vlc_mutex_unlock(&p_sys->bdj_overlay_lock);
Rafaël Carré's avatar
Rafaël Carré committed
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310
        break;
    case BD_ARGB_OVERLAY_FLUSH:
        blurayActivateOverlay(p_demux, overlay->plane);
        break;
    case BD_ARGB_OVERLAY_DRAW:
        blurayDrawArgbOverlay(p_demux, overlay);
        break;
    default:
        msg_Warn(p_demux, "Unknown BD ARGB overlay command: %u", overlay->cmd);
        break;
1311 1312 1313
    }
}

1314
static void bluraySendOverlayToVout(demux_t *p_demux, bluray_overlay_t *p_ov)
1315 1316 1317
{
    demux_sys_t *p_sys = p_demux->p_sys;

1318
    assert(p_ov != NULL);
1319
    assert(p_ov->i_channel == -1);
Petri Hintukainen's avatar
Petri Hintukainen committed
1320 1321 1322 1323 1324

    if (p_ov->p_updater) {
        unref_subpicture_updater(p_ov->p_updater);
        p_ov->p_updater = NULL;
    }
1325

1326 1327
    subpicture_t *p_pic = bluraySubpictureCreate(p_ov);
    if (!p_pic) {
1328
        msg_Err(p_demux, "bluraySubpictureCreate() failed");
1329 1330 1331 1332 1333 1334
        return;
    }

    p_pic->i_start = p_pic->i_stop = mdate();
    p_pic->i_channel = vout_RegisterSubpictureChannel(p_sys->p_vout);
    p_ov->i_channel = p_pic->i_channel;
1335 1336 1337

    /*
     * After this point, the picture should not be accessed from the demux thread,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
1338
     * as it is held by the vout thread.
1339 1340 1341
     * This must be done only once per subpicture, ie. only once between each
     * blurayInitOverlay & blurayCloseOverlay call.
     */
1342 1343
    vout_PutSubpicture(p_sys->p_vout, p_pic);

1344 1345 1346 1347
    /*
     * Mark the picture as Outdated, as it contains no region for now.
     * This will make the subpicture_updater_t call pf_update
     */
1348
    p_ov->status = Outdated;
1349
}
1350

1351
static void blurayUpdateTitleInfo(input_title_t *t, BLURAY_TITLE_INFO *title_info)
1352
{
1353
    t->i_length = FROM_TICKS(title_info->duration);
1354

1355
    if (!t->i_seekpoint) {
Rafaël Carré's avatar
Rafaël Carré committed
1356
        for (unsigned int j = 0; j < title_info->chapter_count; j++) {
1357
            seekpoint_t *s = vlc_seekpoint_New();
1358
            if (!s) {
1359
                break;
1360
            }
1361 1362
            s->i_time_offset = title_info->chapters[j].offset;

Rafaël Carré's avatar
Rafaël Carré committed
1363
            TAB_APPEND(t->i_seekpoint, t->seekpoint, s);
1364
        }
1365 1366 1367 1368 1369 1370 1371 1372 1373
    }
}

static void blurayInitTitles(demux_t *p_demux, int menu_titles)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    /* get and set the titles */
    unsigned i_title = menu_titles;
1374

1375
    if (!p_sys->b_menu) {
1376
        i_title = bd_get_titles(p_sys->bluray, TITLES_RELEVANT, 60);
1377 1378
        p_sys->i_longest_title = bd_get_main_title(p_sys->bluray);
    }
1379 1380 1381 1382 1383 1384 1385

    for (unsigned int i = 0; i < i_title; i++) {
        input_title_t *t = vlc_input_title_New();
        if (!t)
            break;

        if (!p_sys->b_menu) {
1386 1387 1388
            BLURAY_TITLE_INFO *title_info = bd_get_title_info(p_sys->bluray, i, 0);
            blurayUpdateTitleInfo(t, title_info);
            bd_free_title_info(title_info);
1389 1390 1391 1392 1393

        } else if (i == 0) {
            t->psz_name = strdup(_("Top Menu"));
        } else if (i == i_title - 1) {
            t->psz_name = strdup(_("First Play"));
1394 1395
        }

Rafaël Carré's avatar
Rafaël Carré committed
1396
        TAB_APPEND(p_sys->i_title, p_sys->pp_title, t);
1397 1398
    }
}
1399

Rafaël Carré's avatar
Rafaël Carré committed
1400
static void blurayResetParser(demux_t *p_demux)
1401 1402 1403 1404 1405 1406 1407
{
    /*
     * This is a hack and will have to be removed.
     * The parser should be flushed, and not destroy/created each time
     * we are changing title.
     */
    demux_sys_t *p_sys = p_demux->p_sys;
1408 1409
    if (p_sys->p_parser)
        stream_Delete(p_sys->p_parser);
Rafaël Carré's avatar
Rafaël Carré committed
1410

1411
    p_sys->p_parser = stream_DemuxNew(p_demux, "ts", p_sys->p_out);
Rafaël Carré's avatar
Rafaël Carré committed
1412 1413

    if (!p_sys->p_parser)
1414 1415