bluray.c 51.2 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);

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

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

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

407 408
        /* Registering overlay event handler */
        bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
409
    } else {
410
        /* set start title number */
411
        if (bluraySetTitle(p_demux, p_sys->i_longest_title) != VLC_SUCCESS) {
Rafaël Carré's avatar
Rafaël Carré committed
412
            msg_Err(p_demux, "Could not set the title %d", p_sys->i_longest_title);
413 414
            goto error;
        }
415 416
    }

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

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

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

    return VLC_SUCCESS;
432 433 434

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


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

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

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

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

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

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

struct es_out_sys_t {
    demux_t *p_demux;
};

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

500 501 502
    return -1;
}

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

509 510 511
    return -1;
}

512 513 514 515 516 517 518 519 520 521 522 523
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
524
static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
525
{
526
    demux_sys_t *p_sys = p_out->p_sys->p_demux->p_sys;
527 528
    BLURAY_TITLE_INFO *title_info = bd_get_playlist_info(p_sys->bluray, p_sys->i_playlist, 0);
    BLURAY_CLIP_INFO *clip_info = NULL;
529 530
    es_format_t fmt;

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

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

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

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

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

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

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

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

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

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

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

627 628 629
/*****************************************************************************
 * subpicture_updater_t functions:
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
630
static int subpictureUpdaterValidate(subpicture_t *p_subpic,
631 632
                                      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
633
                                      mtime_t i_ts)
634
{
Rafaël Carré's avatar
Rafaël Carré committed
635 636 637 638 639
    VLC_UNUSED(b_fmt_src);
    VLC_UNUSED(b_fmt_dst);
    VLC_UNUSED(p_fmt_src);
    VLC_UNUSED(p_fmt_dst);
    VLC_UNUSED(i_ts);
640 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

    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
698
    if (!p_src) {
699
        vlc_mutex_unlock(&p_overlay->lock);
700
        return;
701
    }
702

Rafaël Carré's avatar
Rafaël Carré committed
703
    subpicture_region_t **p_dst = &p_subpic->p_region;
704 705 706
    while (p_src != NULL) {
        *p_dst = subpicture_region_Clone(p_src);
        if (*p_dst == NULL)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
707
            break;
Rafaël Carré's avatar
Rafaël Carré committed
708
        p_dst = &(*p_dst)->p_next;
709 710 711 712 713 714 715 716
        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);
}

717 718
static void blurayCleanOverlayStruct(bluray_overlay_t *);

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

725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
/*****************************************************************************
 * 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;
}

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

754 755 756
    return VLC_SUCCESS;
}

757 758 759
/*****************************************************************************
 * libbluray overlay handling:
 *****************************************************************************/
760
static void blurayCleanOverlayStruct(bluray_overlay_t *p_overlay)
761
{
762 763
    if (!atomic_flag_test_and_set(&p_overlay->released_once))
        return;
764 765 766 767 768
    /*
     * 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
769
    subpicture_region_ChainDelete(p_overlay->p_regions);
770 771 772
    free(p_overlay);
}

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

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

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

792
    /* All overlays have been closed */
793 794 795 796
    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;
797 798 799 800 801 802 803 804 805
}

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

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

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

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

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

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

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

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

853
    p_upd_sys->p_overlay = ov;
854 855 856 857 858 859
    subpicture_updater_t updater = {
        .pf_validate = subpictureUpdaterValidate,
        .pf_update   = subpictureUpdaterUpdate,
        .pf_destroy  = subpictureUpdaterDestroy,
        .p_sys       = p_upd_sys,
    };
860 861 862 863 864 865
    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;
866 867 868 869 870 871 872 873 874 875
}

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

    vlc_mutex_lock(&ov->lock);
882

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

887
    vlc_mutex_unlock(&ov->lock);
888 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 923 924 925 926 927 928 929 930 931 932 933 934 935
}

/*
 * 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);
        video_format_Setup(&fmt, VLC_CODEC_YUVP, ov->w, ov->h, 1, 1);

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

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

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

    if (!overlay) {
966 967 968 969 970
        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);
971 972
        return;
    }
Rafaël Carré's avatar
Rafaël Carré committed
973

974
    switch (overlay->cmd) {
Rafaël Carré's avatar
Rafaël Carré committed
975 976 977 978
    case BD_OVERLAY_INIT:
        msg_Info(p_demux, "Initializing overlay");
        blurayInitOverlay(p_demux, overlay->plane, overlay->w, overlay->h);
        break;
979 980 981 982