bluray.c 51.4 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
LGPL  
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
#include <limits.h>                         /* PATH_MAX */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
30

31
#if defined (HAVE_MNTENT_H) && defined(HAVE_SYS_STAT_H)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
32 33
# include <mntent.h>
# include <sys/stat.h>
34
#endif
35

36
#ifdef __APPLE__
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
37 38 39 40
# include <sys/stat.h>
# include <sys/param.h>
# include <sys/ucred.h>
# include <sys/mount.h>
41 42
#endif

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

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

56 57

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

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

Rafaël Carré's avatar
Rafaël Carré committed
67 68 69 70 71
#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. "\
72 73 74 75 76 77 78 79
                                "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) */
80
#define LANGUAGE_DEFAULT ("eng")
81

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

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

Rafaël Carré's avatar
Rafaël Carré committed
90 91 92 93 94 95
    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)
96

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

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

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

105 106 107 108 109 110 111 112
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
113
{
114
    atomic_flag         released_once;
115 116 117 118 119 120 121 122 123
    vlc_mutex_t         lock;
    subpicture_t        *p_pic;
    OverlayStatus       status;
    subpicture_region_t *p_regions;
} bluray_overlay_t;

struct  demux_sys_t
{
    BLURAY              *bluray;
124 125

    /* Titles */
126 127
    unsigned int        i_title;
    unsigned int        i_longest_title;
128
    int                 i_playlist;          /* -1 = no playlist playing */
129
    unsigned int        i_current_clip;
130
    input_title_t       **pp_title;
131

132
    /* Meta information */
133 134
    const META_DL       *p_meta;

135
    /* Menus */
136 137 138 139 140 141 142
    bluray_overlay_t    *p_overlays[MAX_OVERLAY];
    int                 current_overlay; // -1 if no current overlay;
    bool                b_menu;

    /* */
    input_thread_t      *p_input;
    vout_thread_t       *p_vout;
143

144
    /* TS stream */
145
    es_out_t            *p_out;
146
    vlc_array_t         es;
147
    int                 i_audio_stream; /* Selected audio stream. -1 if default */
148
    int                 i_spu_stream;   /* Selected subtitle stream. -1 if default */
149
    int                 i_video_stream;
150
    stream_t            *p_parser;
151 152 153

    /* Used to store bluray disc path */
    char                *psz_bd_path;
154 155 156 157 158
};

struct subpicture_updater_sys_t
{
    bluray_overlay_t    *p_overlay;
159 160
};

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
/* 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;
}

198 199 200
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
201
static es_out_t *esOutNew(demux_t *p_demux);
202

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
203 204
static int   blurayControl(demux_t *, int, va_list);
static int   blurayDemux(demux_t *);
205

206
static void  blurayInitTitles(demux_t *p_demux, int menu_titles);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
207
static int   bluraySetTitle(demux_t *p_demux, int i_title);
208

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
209
static void  blurayOverlayProc(void *ptr, const BD_OVERLAY * const overlay);
210
static void  blurayArgbOverlayProc(void *ptr, const BD_ARGB_OVERLAY * const overlay);
211

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

215 216
static void  blurayResetParser(demux_t *p_demux);

217 218 219
#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
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
/* */
static void FindMountPoint(char **file)
{
    char *device = *file;
#if defined (HAVE_MNTENT_H) && defined (HAVE_SYS_STAT_H)
    struct stat st;
    if (!stat (device, &st) && S_ISBLK (st.st_mode)) {
        FILE *mtab = setmntent ("/proc/self/mounts", "r");
        struct mntent *m, mbuf;
        char buf [8192];
        /* 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)
            bd_device = strdup(device);

        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;
            }
        }
        free(bd_device);
        endmntent (mtab);
    }
#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
#endif
}

267 268 269
/*****************************************************************************
 * blurayOpen: module init function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
270
static int blurayOpen(vlc_object_t *object)
271
{
272 273
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys;
274

275
    const char *error_msg = NULL;
276
#define BLURAY_ERROR(s) do { error_msg = s; goto error; } while(0)
277

278
    if (strcmp(p_demux->psz_access, "bluray")) {
279
        // TODO BDMV support, once we figure out what to do in libbluray
280 281 282
        return VLC_EGENERIC;
    }

283
    /* */
284
    p_demux->p_sys = p_sys = calloc(1, sizeof(*p_sys));
Rafaël Carré's avatar
Rafaël Carré committed
285
    if (unlikely(!p_sys))
286
        return VLC_ENOMEM;
Rafaël Carré's avatar
Rafaël Carré committed
287

288
    p_sys->current_overlay = -1;
289
    p_sys->i_audio_stream = -1;
290
    p_sys->i_spu_stream = -1;
291
    p_sys->i_video_stream = -1;
292 293 294 295 296

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

Rafaël Carré's avatar
Rafaël Carré committed
298
    TAB_INIT(p_sys->i_title, p_sys->pp_title);
299

300
    /* store current bd path */
Rafaël Carré's avatar
Rafaël Carré committed
301 302
    if (p_demux->psz_file)
        p_sys->psz_bd_path = strdup(p_demux->psz_file);
303

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

307
    p_sys->bluray = bd_open(p_sys->psz_bd_path, NULL);
308
    if (!p_sys->bluray) {
309
        free(p_sys->psz_bd_path);
310 311 312 313
        free(p_sys);
        return VLC_EGENERIC;
    }

314 315
    /* Warning the user about AACS/BD+ */
    const BLURAY_DISC_INFO *disc_info = bd_get_disc_info(p_sys->bluray);
316 317

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

321 322
    msg_Info(p_demux, "First play: %i, Top menu: %i\n"
                      "HDMV Titles: %i, BD-J Titles: %i, Other: %i",
323 324 325 326 327 328
             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) {
329
        msg_Dbg(p_demux, "Disc is using AACS");
330 331 332
        if (!disc_info->libaacs_detected)
            BLURAY_ERROR(_("This Blu-ray Disc needs a library for AACS decoding"
                      ", and your system does not have it."));
333
        if (!disc_info->aacs_handled) {
334 335
            if (disc_info->aacs_error_code) {
                switch (disc_info->aacs_error_code) {
336 337 338 339 340 341 342 343 344 345 346 347
                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."));
348 349
                }
            }
350 351 352 353 354
        }
    }

    /* BD+ */
    if (disc_info->bdplus_detected) {
355
        msg_Dbg(p_demux, "Disc is using BD+");
356 357 358 359 360 361
        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?"));
362 363
    }

364 365 366 367 368 369
    /* 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);

370 371 372 373 374 375 376 377
    /* 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);

378
    /* Get titles and chapters */
379 380
    p_sys->p_meta = bd_get_meta(p_sys->bluray);
    if (!p_sys->p_meta)
Rafaël Carré's avatar
Rafaël Carré committed
381
        msg_Warn(p_demux, "Failed to get meta info.");
382

383 384 385
    p_sys->b_menu = var_InheritBool(p_demux, "bluray-menu");

    blurayInitTitles(p_demux, disc_info->num_hdmv_titles + disc_info->num_bdj_titles + 1/*Top Menu*/ + 1/*First Play*/);
386

387 388 389 390 391
    /*
     * Initialize the event queue, so we can receive events in blurayDemux(Menu).
     */
    bd_get_event(p_sys->bluray, NULL);

392 393 394
    /* Registering overlay event handler */
    bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
395
    if (p_sys->b_menu) {
396
        p_sys->p_input = demux_GetParentInput(p_demux);
Rafaël Carré's avatar
Rafaël Carré committed
397
        if (unlikely(!p_sys->p_input)) {
398
            msg_Err(p_demux, "Could not get parent input");
399
            goto error;
Rafaël Carré's avatar
Rafaël Carré committed
400
        }
401

402
        /* Register ARGB overlay handler for BD-J */
403
        if (disc_info->num_bdj_titles)
404 405
            bd_register_argb_overlay_proc(p_sys->bluray, p_demux, blurayArgbOverlayProc, NULL);

406
        /* libbluray will start playback from "First-Title" title */
407 408 409
        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
410
    } else {
411
        /* set start title number */
412
        if (bluraySetTitle(p_demux, p_sys->i_longest_title) != VLC_SUCCESS) {
Rafaël Carré's avatar
Rafaël Carré committed
413
            msg_Err(p_demux, "Could not set the title %d", p_sys->i_longest_title);
414 415
            goto error;
        }
416 417
    }

418
    vlc_array_init(&p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
419
    p_sys->p_out = esOutNew(p_demux);
Rafaël Carré's avatar
Rafaël Carré committed
420
    if (unlikely(p_sys->p_out == NULL))
421 422
        goto error;

Rafaël Carré's avatar
Rafaël Carré committed
423
    blurayResetParser(p_demux);
424 425
    if (!p_sys->p_parser) {
        msg_Err(p_demux, "Failed to create TS demuxer");
426
        goto error;
427 428 429 430
    }

    p_demux->pf_control = blurayControl;
    p_demux->pf_demux   = blurayDemux;
431 432

    return VLC_SUCCESS;
433 434 435

error:
    if (error_msg)
Christoph Miebach's avatar
Christoph Miebach committed
436
        dialog_Fatal(p_demux, _("Blu-ray error"), "%s", error_msg);
437 438
    blurayClose(object);
    return VLC_EGENERIC;
439
#undef BLURAY_ERROR
440 441 442 443 444 445
}


/*****************************************************************************
 * blurayClose: module destroy function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
446
static void blurayClose(vlc_object_t *object)
447
{
448 449 450
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys = p_demux->p_sys;

451 452 453
    /*
     * Close libbluray first.
     * This will close all the overlays before we release p_vout
Rafaël Carré's avatar
Rafaël Carré committed
454
     * bd_close(NULL) can crash
455 456 457 458
     */
    assert(p_sys->bluray);
    bd_close(p_sys->bluray);

459
    if (p_sys->p_vout != NULL) {
Rafaël Carré's avatar
Rafaël Carré committed
460 461
        var_DelCallback(p_sys->p_vout, "mouse-moved", onMouseEvent, p_demux);
        var_DelCallback(p_sys->p_vout, "mouse-clicked", onMouseEvent, p_demux);
462
        vlc_object_release(p_sys->p_vout);
463
    }
464 465
    if (p_sys->p_input != NULL)
        vlc_object_release(p_sys->p_input);
466 467
    if (p_sys->p_parser)
        stream_Delete(p_sys->p_parser);
468 469
    if (p_sys->p_out != NULL)
        es_out_Delete(p_sys->p_out);
Rafaël Carré's avatar
Rafaël Carré committed
470 471
    assert(vlc_array_count(&p_sys->es) == 0);
    vlc_array_clear(&p_sys->es);
472

473 474 475
    /* 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
476
    TAB_CLEAN(p_sys->i_title, p_sys->pp_title);
477

478
    free(p_sys->psz_bd_path);
479 480 481
    free(p_sys);
}

482 483 484 485 486 487 488 489
/*****************************************************************************
 * Elementary streams handling
 *****************************************************************************/

struct es_out_sys_t {
    demux_t *p_demux;
};

490 491 492 493 494
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
495
static int  findEsPairIndex(demux_sys_t *p_sys, int i_id)
496
{
Rafaël Carré's avatar
Rafaël Carré committed
497 498
    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)
499
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
500

501 502 503
    return -1;
}

Rafaël Carré's avatar
Rafaël Carré committed
504
static int  findEsPairIndexByEs(demux_sys_t *p_sys, es_out_id_t *p_es)
505
{
Rafaël Carré's avatar
Rafaël Carré committed
506 507
    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)
508
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
509

510 511 512
    return -1;
}

513 514 515 516 517 518 519 520 521 522 523 524
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);
            p_fmt->psz_language = strndup(p_streams[i].lang, 3);
            return;
        }
    }
}

Rafaël Carré's avatar
Rafaël Carré committed
525
static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
526
{
527
    demux_sys_t *p_sys = p_out->p_sys->p_demux->p_sys;
528 529
    BLURAY_TITLE_INFO *title_info = bd_get_playlist_info(p_sys->bluray, p_sys->i_playlist, 0);
    BLURAY_CLIP_INFO *clip_info = NULL;
530 531
    es_format_t fmt;

532 533 534 535
    if (title_info && p_sys->i_current_clip < title_info->clip_count) {
        clip_info = &title_info->clips[p_sys->i_current_clip];
    }

536
    es_format_Copy(&fmt, p_fmt);
Rafaël Carré's avatar
Rafaël Carré committed
537
    switch (fmt.i_cat) {
538
    case VIDEO_ES:
Rafaël Carré's avatar
Rafaël Carré committed
539
        if (p_sys->i_video_stream != -1 && p_sys->i_video_stream != p_fmt->i_id)
540
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
541 542
        break ;
    case AUDIO_ES:
Rafaël Carré's avatar
Rafaël Carré committed
543
        if (p_sys->i_audio_stream != -1 && p_sys->i_audio_stream != p_fmt->i_id)
544
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
545 546
        if (clip_info)
            setStreamLang(&fmt, clip_info->audio_streams, clip_info->audio_stream_count);
547 548
        break ;
    case SPU_ES:
549 550
        if (p_sys->i_spu_stream != -1 && p_sys->i_spu_stream != p_fmt->i_id)
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
551 552
        if (clip_info)
            setStreamLang(&fmt, clip_info->pg_streams, clip_info->pg_stream_count);
553 554 555
        break ;
    }

556 557 558
    if (title_info)
        bd_free_title_info(title_info);

Rafaël Carré's avatar
Rafaël Carré committed
559 560
    es_out_id_t *p_es = es_out_Add(p_out->p_sys->p_demux->out, &fmt);
    if (p_fmt->i_id >= 0) {
561
        /* Ensure we are not overriding anything */
562
        int idx = findEsPairIndex(p_sys, p_fmt->i_id);
Rafaël Carré's avatar
Rafaël Carré committed
563 564 565
        if (idx == -1) {
            fmt_es_pair_t *p_pair = malloc(sizeof(*p_pair));
            if (likely(p_pair != NULL)) {
566 567
                p_pair->i_id = p_fmt->i_id;
                p_pair->p_es = p_es;
Rafaël Carré's avatar
Rafaël Carré committed
568
                msg_Info(p_out->p_sys->p_demux, "Adding ES %d", p_fmt->i_id);
569
                vlc_array_append(&p_sys->es, p_pair);
570 571 572
            }
        }
    }
573
    es_format_Clean(&fmt);
574
    return p_es;
575 576
}

Rafaël Carré's avatar
Rafaël Carré committed
577
static int esOutSend(es_out_t *p_out, es_out_id_t *p_es, block_t *p_block)
578
{
Rafaël Carré's avatar
Rafaël Carré committed
579
    return es_out_Send(p_out->p_sys->p_demux->out, p_es, p_block);
580 581
}

Rafaël Carré's avatar
Rafaël Carré committed
582
static void esOutDel(es_out_t *p_out, es_out_id_t *p_es)
583
{
Rafaël Carré's avatar
Rafaël Carré committed
584 585 586
    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));
587 588
        vlc_array_remove(&p_out->p_sys->p_demux->p_sys->es, idx);
    }
Rafaël Carré's avatar
Rafaël Carré committed
589
    es_out_Del(p_out->p_sys->p_demux->out, p_es);
590 591
}

Rafaël Carré's avatar
Rafaël Carré committed
592
static int esOutControl(es_out_t *p_out, int i_query, va_list args)
593
{
Rafaël Carré's avatar
Rafaël Carré committed
594
    return es_out_vaControl(p_out->p_sys->p_demux->out, i_query, args);
595 596
}

Rafaël Carré's avatar
Rafaël Carré committed
597
static void esOutDestroy(es_out_t *p_out)
598
{
Rafaël Carré's avatar
Rafaël Carré committed
599 600
    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));
601
    vlc_array_clear(&p_out->p_sys->p_demux->p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
602 603
    free(p_out->p_sys);
    free(p_out);
604 605
}

Rafaël Carré's avatar
Rafaël Carré committed
606
static es_out_t *esOutNew(demux_t *p_demux)
607
{
Rafaël Carré's avatar
Rafaël Carré committed
608 609 610
    assert(vlc_array_count(&p_demux->p_sys->es) == 0);
    es_out_t    *p_out = malloc(sizeof(*p_out));
    if (unlikely(p_out == NULL))
611 612
        return NULL;

Rafaël Carré's avatar
Rafaël Carré committed
613 614 615 616 617
    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;
618

Rafaël Carré's avatar
Rafaël Carré committed
619 620 621
    p_out->p_sys = malloc(sizeof(*p_out->p_sys));
    if (unlikely(p_out->p_sys == NULL)) {
        free(p_out);
622 623 624 625 626 627
        return NULL;
    }
    p_out->p_sys->p_demux = p_demux;
    return p_out;
}

628 629 630
/*****************************************************************************
 * subpicture_updater_t functions:
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
631
static int subpictureUpdaterValidate(subpicture_t *p_subpic,
632 633
                                      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
634
                                      mtime_t i_ts)
635
{
Rafaël Carré's avatar
Rafaël Carré committed
636 637 638 639 640
    VLC_UNUSED(b_fmt_src);
    VLC_UNUSED(b_fmt_dst);
    VLC_UNUSED(p_fmt_src);
    VLC_UNUSED(p_fmt_dst);
    VLC_UNUSED(i_ts);
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

    subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
    bluray_overlay_t         *p_overlay = p_upd_sys->p_overlay;

    vlc_mutex_lock(&p_overlay->lock);
    int res = p_overlay->status == Outdated;
    vlc_mutex_unlock(&p_overlay->lock);
    return res;
}

/* This should probably be moved to subpictures.c afterward */
static subpicture_region_t* subpicture_region_Clone(subpicture_region_t *p_region_src)
{
    if (!p_region_src)
        return NULL;
    subpicture_region_t *p_region_dst = subpicture_region_New(&p_region_src->fmt);
    if (unlikely(!p_region_dst))
        return NULL;

    p_region_dst->i_x      = p_region_src->i_x;
    p_region_dst->i_y      = p_region_src->i_y;
    p_region_dst->i_align  = p_region_src->i_align;
    p_region_dst->i_alpha  = p_region_src->i_alpha;

    p_region_dst->psz_text = p_region_src->psz_text ? strdup(p_region_src->psz_text) : NULL;
    p_region_dst->psz_html = p_region_src->psz_html ? strdup(p_region_src->psz_html) : NULL;
    if (p_region_src->p_style != NULL) {
        p_region_dst->p_style = malloc(sizeof(*p_region_dst->p_style));
        p_region_dst->p_style = text_style_Copy(p_region_dst->p_style,
                                                p_region_src->p_style);
    }

    //Palette is already copied by subpicture_region_New, we just have to duplicate p_pixels
    for (int i = 0; i < p_region_src->p_picture->i_planes; i++)
        memcpy(p_region_dst->p_picture->p[i].p_pixels,
               p_region_src->p_picture->p[i].p_pixels,
               p_region_src->p_picture->p[i].i_lines * p_region_src->p_picture->p[i].i_pitch);
    return p_region_dst;
}

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;
    bluray_overlay_t         *p_overlay = p_upd_sys->p_overlay;

    /*
     * When this function is called, all p_subpic regions are gone.
     * We need to duplicate our regions (stored internaly) to this subpic.
     */
    vlc_mutex_lock(&p_overlay->lock);

    subpicture_region_t *p_src = p_overlay->p_regions;
Rafaël Carré's avatar
Rafaël Carré committed
699
    if (!p_src) {
700
        vlc_mutex_unlock(&p_overlay->lock);
701
        return;
702
    }
703

Rafaël Carré's avatar
Rafaël Carré committed
704
    subpicture_region_t **p_dst = &p_subpic->p_region;
705 706 707
    while (p_src != NULL) {
        *p_dst = subpicture_region_Clone(p_src);
        if (*p_dst == NULL)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
708
            break;
Rafaël Carré's avatar
Rafaël Carré committed
709
        p_dst = &(*p_dst)->p_next;
710 711 712 713 714 715 716 717
        p_src = p_src->p_next;
    }
    if (*p_dst != NULL)
        (*p_dst)->p_next = NULL;
    p_overlay->status = Displayed;
    vlc_mutex_unlock(&p_overlay->lock);
}

718 719
static void blurayCleanOverlayStruct(bluray_overlay_t *);

720 721
static void subpictureUpdaterDestroy(subpicture_t *p_subpic)
{
722
    blurayCleanOverlayStruct(p_subpic->updater.p_sys->p_overlay);
Rafaël Carré's avatar
Rafaël Carré committed
723
    free(p_subpic->updater.p_sys);
724 725
}

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
/*****************************************************************************
 * 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 {
        assert(0);
    }
    return VLC_SUCCESS;
}

749 750 751
static int sendKeyEvent(demux_sys_t *p_sys, unsigned int key)
{
    mtime_t now = mdate();
Rafaël Carré's avatar
Rafaël Carré committed
752
    if (bd_user_input(p_sys->bluray, now, key) < 0)
753
        return VLC_EGENERIC;
Rafaël Carré's avatar
Rafaël Carré committed
754

755 756 757
    return VLC_SUCCESS;
}

758 759 760
/*****************************************************************************
 * libbluray overlay handling:
 *****************************************************************************/
761
static void blurayCleanOverlayStruct(bluray_overlay_t *p_overlay)
762
{
763 764
    if (!atomic_flag_test_and_set(&p_overlay->released_once))
        return;
765 766 767 768 769
    /*
     * This will be called when destroying the picture.
     * Don't delete it again from here!
     */
    vlc_mutex_destroy(&p_overlay->lock);
Rafaël Carré's avatar
Rafaël Carré committed
770
    subpicture_region_ChainDelete(p_overlay->p_regions);
771 772 773
    free(p_overlay);
}

774 775 776 777 778 779
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) {
780 781
        if (p_sys->p_vout)
            vout_FlushSubpictureChannel(p_sys->p_vout, ov->p_pic->i_channel);
782 783 784
        blurayCleanOverlayStruct(ov);
        if (p_sys->current_overlay == plane)
            p_sys->current_overlay = -1;
785

786 787
        p_sys->p_overlays[plane] = NULL;
    }
788

789
    for (int i = 0; i < MAX_OVERLAY; i++)
790 791
        if (p_sys->p_overlays[i])
            return;
792

793
    /* All overlays have been closed */
794 795 796 797
    var_DelCallback(p_sys->p_vout, "mouse-moved", onMouseEvent, p_demux);
    var_DelCallback(p_sys->p_vout, "mouse-clicked", onMouseEvent, p_demux);
    vlc_object_release(p_sys->p_vout);
    p_sys->p_vout = NULL;
798 799 800 801 802 803 804 805 806
}

/*
 * 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"
 */
807
static void blurayActivateOverlay(demux_t *p_demux, int plane)
808 809
{
    demux_sys_t *p_sys = p_demux->p_sys;
810
    bluray_overlay_t *ov = p_sys->p_overlays[plane];
811 812 813 814 815

    /*
     * If the overlay is already displayed, mark the picture as outdated.
     * We must NOT use vout_PutSubpicture if a picture is already displayed.
     */
816 817 818 819
    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
820
        return;
821
    }
822

823 824 825 826 827
    /*
     * 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
     */
828
    p_sys->current_overlay = plane;
829 830
    ov->status = ToDisplay;
    vlc_mutex_unlock(&ov->lock);
831 832
}

833
static void blurayInitOverlay(demux_t *p_demux, int plane, int width, int height)
834 835 836
{
    demux_sys_t *p_sys = p_demux->p_sys;

837
    assert(p_sys->p_overlays[plane] == NULL);
838

839 840
    p_sys->p_overlays[plane] = calloc(1, sizeof(**p_sys->p_overlays));
    if (unlikely(!p_sys->p_overlays[plane]))
841 842
        return;

843 844
    bluray_overlay_t *ov = p_sys->p_overlays[plane];

845 846
    subpicture_updater_sys_t *p_upd_sys = malloc(sizeof(*p_upd_sys));
    if (unlikely(!p_upd_sys)) {
847
        free(ov);
848
        p_sys->p_overlays[plane] = NULL;
849 850
        return;
    }
851
    /* two references: vout + demux */
852
    ov->released_once = ATOMIC_FLAG_INIT;
853

854
    p_upd_sys->p_overlay = ov;
855 856 857 858 859 860
    subpicture_updater_t updater = {
        .pf_validate = subpictureUpdaterValidate,
        .pf_update   = subpictureUpdaterUpdate,
        .pf_destroy  = subpictureUpdaterDestroy,
        .p_sys       = p_upd_sys,
    };
861 862 863 864 865 866
    vlc_mutex_init(&ov->lock);
    ov->p_pic = subpicture_New(&updater);
    ov->p_pic->i_original_picture_width = width;
    ov->p_pic->i_original_picture_height = height;
    ov->p_pic->b_ephemer = true;
    ov->p_pic->b_absolute = true;
867 868 869 870 871 872 873 874 875 876
}

/**
 * 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.
 */
877
static void blurayClearOverlay(demux_t *p_demux, int plane)
878 879
{
    demux_sys_t *p_sys = p_demux->p_sys;
880 881 882
    bluray_overlay_t *ov = p_sys->p_overlays[plane];

    vlc_mutex_lock(&ov->lock);
883

884 885 886
    subpicture_region_ChainDelete(ov->p_regions);
    ov->p_regions = NULL;
    ov->status = Outdated;
887

888
    vlc_mutex_unlock(&ov->lock);
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
}

/*
 * 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);
923
        video_format_Setup(&fmt, VLC_CODEC_YUVP, ov->w, ov->h, ov->w, ov->h, 1, 1);
924 925 926 927 928 929 930 931 932 933 934 935 936

        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
937
    for (int y = 0; y < ov->h; y++)
938
        for (int x = 0; x < ov->w;) {
939 940
            plane_t *p = &p_reg->p_picture->p[0];
            memset(&p->p_pixels[y * p->i_pitch + x], img->color, img->len);
941 942 943
            x += img->len;
            img++;
        }
Rafaël Carré's avatar
Rafaël Carré committed
944

945 946 947 948 949 950 951 952 953
    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
954

955 956 957 958 959 960 961 962 963
    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;
964
    demux_sys_t *p_sys = p_demux->p_sys;
965 966

    if (!overlay) {
967 968 969 970 971
        msg_Info(p_demux, "Closing overlays.");
        p_sys->current_overlay = -1;
        if (p_sys->p_vout)
            for (int i = 0; i < MAX_OVERLAY; i++)
                blurayCloseOverlay(p_demux, i);
972 973
        return;
    }
Rafaël Carré's avatar
Rafaël Carré committed
974

975
    switch (overlay->cmd) {
Rafaël Carré's avatar
Rafaël Carré committed
976 977 978 979