bluray.c 76.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
# include <mntent.h>
32
#endif
33 34
#include <fcntl.h>      /* O_* */
#include <unistd.h>     /* close() */
35
#include <sys/stat.h>
36

37
#ifdef __APPLE__
38 39 40
# 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
#include <vlc_iso_lang.h>
52
#include <vlc_fs.h>
53 54 55 56

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

57 58

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

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

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

83 84 85 86
#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0,8,0)
# define BLURAY_DEMUX
#endif

87
/* Callbacks */
Rafaël Carré's avatar
Rafaël Carré committed
88 89
static int  blurayOpen (vlc_object_t *);
static void blurayClose(vlc_object_t *);
90 91

vlc_module_begin ()
Rafaël Carré's avatar
Rafaël Carré committed
92 93
    set_shortname(N_("Blu-ray"))
    set_description(N_("Blu-ray Disc support (libbluray)"))
94

Rafaël Carré's avatar
Rafaël Carré committed
95 96 97
    set_category(CAT_INPUT)
    set_subcategory(SUBCAT_INPUT_ACCESS)
    set_capability("access_demux", 200)
98
    add_bool("bluray-menu", true, BD_MENU_TEXT, BD_MENU_LONGTEXT, false)
Rafaël Carré's avatar
Rafaël Carré committed
99 100
    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)
101

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

Rafaël Carré's avatar
Rafaël Carré committed
104
    set_callbacks(blurayOpen, blurayClose)
105

106
#ifdef BLURAY_DEMUX
107 108 109 110 111 112 113
    /* demux module */
    add_submodule()
        set_description( "BluRay demuxer" )
        set_category( CAT_INPUT )
        set_subcategory( SUBCAT_INPUT_DEMUX )
        set_capability( "demux", 5 )
        set_callbacks( blurayOpen, blurayClose )
114
#endif
115

116 117
vlc_module_end ()

118 119
/* libbluray's overlay.h defines 2 types of overlay (bd_overlay_plane_e). */
#define MAX_OVERLAY 2
120

121 122 123 124 125 126 127 128
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
129
{
130
    vlc_mutex_t         lock;
131
    int                 i_channel;
132 133
    OverlayStatus       status;
    subpicture_region_t *p_regions;
134
    int                 width, height;
135 136 137 138 139 140 141

    /* 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;
142 143 144 145 146
} bluray_overlay_t;

struct  demux_sys_t
{
    BLURAY              *bluray;
147 148

    /* Titles */
149 150 151
    unsigned int        i_title;
    unsigned int        i_longest_title;
    input_title_t       **pp_title;
152

153 154 155 156
    vlc_mutex_t             pl_info_lock;
    BLURAY_TITLE_INFO      *p_pl_info;
    const BLURAY_CLIP_INFO *p_clip_info;

157 158 159 160 161
    /* Attachments */
    int                 i_attachments;
    input_attachment_t  **attachments;
    int                 i_cover_idx;

162
    /* Meta information */
163 164
    const META_DL       *p_meta;

165
    /* Menus */
166
    bluray_overlay_t    *p_overlays[MAX_OVERLAY];
167
    bool                b_fatal_error;
168
    bool                b_menu;
169 170
    bool                b_menu_open;
    bool                b_popup_available;
171
    mtime_t             i_still_end_time;
172

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

175 176
    /* */
    vout_thread_t       *p_vout;
177

178 179
    es_out_id_t         *p_dummy_video;

180
    /* TS stream */
181
    es_out_t            *p_out;
182
    vlc_array_t         es;
183 184
    int                 i_audio_stream_idx; /* Selected audio stream. -1 if default */
    int                 i_spu_stream_idx;   /* Selected subtitle stream. -1 if default */
185
    bool                b_spu_enable;       /* enabled / disabled */
186
    int                 i_video_stream;
187
    vlc_demux_chained_t *p_parser;
188
    bool                b_flushed;
189
    bool                b_pl_playing;       /* true when playing playlist */
190

191 192 193
    /* stream input */
    vlc_mutex_t         read_block_lock;

194 195
    /* Used to store bluray disc path */
    char                *psz_bd_path;
196 197 198 199
};

struct subpicture_updater_sys_t
{
200 201 202
    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)
203 204
};

205 206 207 208 209 210 211 212
/*
 * 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);
Hannes Domani's avatar
Hannes Domani committed
213
    int refs = --p_sys->ref_cnt;
214 215 216 217 218 219 220 221 222
    p_sys->p_overlay = NULL;
    vlc_mutex_unlock(&p_sys->lock);

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

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
/* 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;
}

260 261 262
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
263
static es_out_t *esOutNew(demux_t *p_demux);
264

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
265 266
static int   blurayControl(demux_t *, int, va_list);
static int   blurayDemux(demux_t *);
267

268
static void  blurayInitTitles(demux_t *p_demux, int menu_titles);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
269
static int   bluraySetTitle(demux_t *p_demux, int i_title);
270

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
271
static void  blurayOverlayProc(void *ptr, const BD_OVERLAY * const overlay);
272
static void  blurayArgbOverlayProc(void *ptr, const BD_ARGB_OVERLAY * const overlay);
273

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
274 275
static int   onMouseEvent(vlc_object_t *p_vout, const char *psz_var,
                          vlc_value_t old, vlc_value_t val, void *p_data);
276 277
static int   onIntfEvent(vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void *);
278

279
static void  blurayResetParser(demux_t *p_demux);
280
static void  notifyDiscontinuity( demux_sys_t *p_sys );
281

Petri Hintukainen's avatar
Petri Hintukainen committed
282 283
#define FROM_TICKS(a) ((a)*CLOCK_FREQ / INT64_C(90000))
#define TO_TICKS(a)   ((a)*INT64_C(90000)/CLOCK_FREQ)
284
#define CUR_LENGTH    p_sys->pp_title[p_demux->info.i_title]->i_length
285

286 287 288 289 290
/* */
static void FindMountPoint(char **file)
{
    char *device = *file;
#if defined (HAVE_MNTENT_H) && defined (HAVE_SYS_STAT_H)
291 292 293 294 295 296
    /* 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;

297
    struct stat st;
298
    if (lstat (bd_device, &st) == 0 && S_ISBLK (st.st_mode)) {
299 300 301 302 303 304 305 306 307 308 309 310 311
        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);
    }
312 313
    free(bd_device);

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
#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
331
    VLC_UNUSED( device );
332 333 334
#endif
}

335 336 337 338 339 340 341
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);
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

        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;
                }
            }
        }

362 363 364 365 366
        vlc_object_release(p_sys->p_vout);
        p_sys->p_vout = NULL;
    }
}

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
/*****************************************************************************
 * 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);

    if (!p_sys->p_dummy_video) {
Petri Hintukainen's avatar
Petri Hintukainen committed
393 394
        msg_Err(p_demux, "Error adding background ES");
        goto out;
395 396
    }

397 398
    block_t *p_block = block_Alloc(fmt.video.i_width * fmt.video.i_height *
                                   fmt.video.i_bits_per_pixel / 8);
399 400
    if (!p_block) {
        msg_Err(p_demux, "Error allocating block for background video");
Petri Hintukainen's avatar
Petri Hintukainen committed
401
        goto out;
402 403 404 405 406 407
    }

    // 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;
408 409 410
    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);
411 412

    es_out_Send(p_demux->out, p_sys->p_dummy_video, p_block);
Petri Hintukainen's avatar
Petri Hintukainen committed
413 414 415

 out:
    es_format_Clean(&fmt);
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
}

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;
}

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
/*****************************************************************************
 * 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);
}

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
/*****************************************************************************
 * 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
}

485 486 487 488 489 490 491 492
/*****************************************************************************
 * stream input
 *****************************************************************************/

static int probeStream(demux_t *p_demux)
{
    /* input must be seekable */
    bool b_canseek = false;
493
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_canseek );
494 495 496 497 498 499 500
    if (!b_canseek) {
        return VLC_EGENERIC;
    }

    /* first sector(s) should be filled with zeros */
    size_t i_peek;
    const uint8_t *p_peek;
501
    i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 2048 );
502 503 504 505 506 507 508 509 510 511 512 513
    if( i_peek != 2048 ) {
        return VLC_EGENERIC;
    }
    while (i_peek > 0) {
        if (p_peek[ --i_peek ]) {
            return VLC_EGENERIC;
        }
    }

    return VLC_SUCCESS;
}

514
#ifdef BLURAY_DEMUX
515 516 517 518 519 520 521 522 523 524
static int blurayReadBlock(void *object, void *buf, int lba, int num_blocks)
{
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys = p_demux->p_sys;
    int result = -1;

    assert(p_demux->s != NULL);

    vlc_mutex_lock(&p_sys->read_block_lock);

525
    if (vlc_stream_Seek( p_demux->s, lba * INT64_C(2048) ) == VLC_SUCCESS) {
526 527 528
        size_t  req = (size_t)2048 * num_blocks;
        ssize_t got;

529
        got = vlc_stream_Read( p_demux->s, buf, req);
530 531 532 533 534 535 536 537 538 539 540 541 542
        if (got < 0) {
            msg_Err(p_demux, "read from lba %d failed", lba);
        } else {
            result = got / 2048;
        }
    } else {
       msg_Err(p_demux, "seek to lba %d failed", lba);
    }

    vlc_mutex_unlock(&p_sys->read_block_lock);

    return result;
}
543
#endif
544

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
/*****************************************************************************
 * probing of local files
 *****************************************************************************/

/* Descriptor Tag (ECMA 167, 3/7.2) */
static int decode_descriptor_tag(const uint8_t *buf)
{
    uint16_t id;
    uint8_t  checksum = 0;
    int      i;

    id = buf[0] | (buf[1] << 8);

    /* calculate tag checksum */
    for (i = 0; i < 4; i++) {
        checksum = (uint8_t)(checksum + buf[i]);
    }
    for (i = 5; i < 16; i++) {
        checksum = (uint8_t)(checksum + buf[i]);
    }

    if (checksum != buf[4]) {
        return -1;
    }

    return id;
}

static int probeFile(const char *psz_name)
{
    struct stat stat_info;
    uint8_t peek[2048];
    unsigned i;
    int ret = VLC_EGENERIC;
    int fd;

    fd = vlc_open(psz_name, O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        return VLC_EGENERIC;
    }

    if (fstat(fd, &stat_info) == -1) {
        goto bailout;
    }
    if (!S_ISREG(stat_info.st_mode) && !S_ISBLK(stat_info.st_mode)) {
        goto bailout;
    }

    /* first sector should be filled with zeros */
    if (read(fd, peek, sizeof(peek)) != sizeof(peek)) {
        goto bailout;
    }
    for (i = 0; i < sizeof(peek); i++) {
        if (peek[ i ]) {
            goto bailout;
        }
    }

    /* Check AVDP tag checksum */
    if (lseek(fd, 256 * 2048, SEEK_SET) == -1 ||
        read(fd, peek, 16) != 16 ||
        decode_descriptor_tag(peek) != 2) {
        goto bailout;
    }

    ret = VLC_SUCCESS;

bailout:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
613
    vlc_close(fd);
614 615 616
    return ret;
}

617 618 619
/*****************************************************************************
 * blurayOpen: module init function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
620
static int blurayOpen(vlc_object_t *object)
621
{
622 623
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys;
624 625
    bool forced;
    uint64_t i_init_pos = 0;
626

627
    const char *error_msg = NULL;
628
#define BLURAY_ERROR(s) do { error_msg = s; goto error; } while(0)
629

630 631 632
    if (unlikely(!p_demux->p_input))
        return VLC_EGENERIC;

633 634 635
    forced = !strcasecmp(p_demux->psz_access, "bluray");

    if (p_demux->s) {
636
        if (!strcasecmp(p_demux->psz_access, "file")) {
637 638 639 640 641 642 643
            /* use access_demux for local files */
            return VLC_EGENERIC;
        }

        if (probeStream(p_demux) != VLC_SUCCESS) {
            return VLC_EGENERIC;
        }
644

645
    } else if (!forced) {
646 647 648 649 650 651 652
        if (!p_demux->psz_file) {
            return VLC_EGENERIC;
        }

        if (probeFile(p_demux->psz_file) != VLC_SUCCESS) {
            return VLC_EGENERIC;
        }
653 654
    }

655
    /* */
656
    p_demux->p_sys = p_sys = vlc_calloc(obj, 1, sizeof(*p_sys));
Rafaël Carré's avatar
Rafaël Carré committed
657
    if (unlikely(!p_sys))
658
        return VLC_ENOMEM;
Rafaël Carré's avatar
Rafaël Carré committed
659

660 661
    p_sys->i_audio_stream_idx = -1;
    p_sys->i_spu_stream_idx = -1;
662
    p_sys->i_video_stream = -1;
663
    p_sys->i_still_end_time = 0;
664 665 666 667 668

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

Rafaël Carré's avatar
Rafaël Carré committed
670
    TAB_INIT(p_sys->i_title, p_sys->pp_title);
671
    TAB_INIT(p_sys->i_attachments, p_sys->attachments);
672

673 674
    vlc_mutex_init(&p_sys->pl_info_lock);
    vlc_mutex_init(&p_sys->bdj_overlay_lock);
675 676
    vlc_mutex_init(&p_sys->read_block_lock); /* used during bd_open_stream() */

677 678
    var_AddCallback( p_demux->p_input, "intf-event", onIntfEvent, p_demux );

679
    /* Open BluRay */
680
#ifdef BLURAY_DEMUX
681
    if (p_demux->s) {
682
        i_init_pos = vlc_stream_Tell(p_demux->s);
683 684 685 686 687 688

        p_sys->bluray = bd_init();
        if (!bd_open_stream(p_sys->bluray, p_demux, blurayReadBlock)) {
            bd_close(p_sys->bluray);
            p_sys->bluray = NULL;
        }
689 690 691
    } else
#endif
    {
692 693 694 695 696 697 698
        if (!p_demux->psz_file) {
            /* no path provided (bluray://). use default DVD device. */
            p_sys->psz_bd_path = var_InheritString(object, "dvd");
        } else {
            /* store current bd path */
            p_sys->psz_bd_path = strdup(p_demux->psz_file);
        }
699 700 701 702 703 704

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

        p_sys->bluray = bd_open(p_sys->psz_bd_path, NULL);
    }
705
    if (!p_sys->bluray) {
706
        goto error;
707 708
    }

709 710
    /* Warning the user about AACS/BD+ */
    const BLURAY_DISC_INFO *disc_info = bd_get_disc_info(p_sys->bluray);
711 712

    /* Is it a bluray? */
713 714 715 716 717 718
    if (!disc_info->bluray_detected) {
        if (forced) {
            BLURAY_ERROR(_("Path doesn't appear to be a Blu-ray"));
        }
        goto error;
    }
719

720 721
    msg_Info(p_demux, "First play: %i, Top menu: %i\n"
                      "HDMV Titles: %i, BD-J Titles: %i, Other: %i",
722 723 724 725 726 727
             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) {
728
        msg_Dbg(p_demux, "Disc is using AACS");
729 730 731
        if (!disc_info->libaacs_detected)
            BLURAY_ERROR(_("This Blu-ray Disc needs a library for AACS decoding"
                      ", and your system does not have it."));
732
        if (!disc_info->aacs_handled) {
733 734
            if (disc_info->aacs_error_code) {
                switch (disc_info->aacs_error_code) {
735 736 737 738 739 740 741 742 743 744 745 746
                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."));
747 748
                }
            }
749 750 751 752 753
        }
    }

    /* BD+ */
    if (disc_info->bdplus_detected) {
754
        msg_Dbg(p_demux, "Disc is using BD+");
755 756 757 758 759 760
        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?"));
761 762
    }

763 764 765 766 767 768
    /* 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);

769 770 771 772 773 774 775 776
    /* 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);

Petri Hintukainen's avatar
Petri Hintukainen committed
777
    /* Get disc metadata */
778 779
    p_sys->p_meta = bd_get_meta(p_sys->bluray);
    if (!p_sys->p_meta)
Rafaël Carré's avatar
Rafaël Carré committed
780
        msg_Warn(p_demux, "Failed to get meta info.");
781

782 783 784
    p_sys->i_cover_idx = -1;
    attachThumbnail(p_demux);

785 786
    p_sys->b_menu = var_InheritBool(p_demux, "bluray-menu");

787 788 789 790 791
    /* 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);
Thomas Guillem's avatar
Thomas Guillem committed
792 793 794
        vlc_dialog_display_error(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.") : "");
795 796 797 798
        p_sys->b_menu = false;
    }

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

801 802 803 804 805
    /*
     * Initialize the event queue, so we can receive events in blurayDemux(Menu).
     */
    bd_get_event(p_sys->bluray, NULL);

806 807 808
    /* Registering overlay event handler */
    bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
809
    if (p_sys->b_menu) {
810

811
        /* Register ARGB overlay handler for BD-J */
812
        if (disc_info->num_bdj_titles)
813 814
            bd_register_argb_overlay_proc(p_sys->bluray, p_demux, blurayArgbOverlayProc, NULL);

815
        /* libbluray will start playback from "First-Title" title */
816 817 818
        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
819
    } else {
820
        /* set start title number */
821
        if (bluraySetTitle(p_demux, p_sys->i_longest_title) != VLC_SUCCESS) {
Rafaël Carré's avatar
Rafaël Carré committed
822
            msg_Err(p_demux, "Could not set the title %d", p_sys->i_longest_title);
823 824
            goto error;
        }
825 826
    }

827
    vlc_array_init(&p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
828
    p_sys->p_out = esOutNew(p_demux);
Rafaël Carré's avatar
Rafaël Carré committed
829
    if (unlikely(p_sys->p_out == NULL))
830 831
        goto error;

Rafaël Carré's avatar
Rafaël Carré committed
832
    blurayResetParser(p_demux);
833 834
    if (!p_sys->p_parser) {
        msg_Err(p_demux, "Failed to create TS demuxer");
835
        goto error;
836 837 838 839
    }

    p_demux->pf_control = blurayControl;
    p_demux->pf_demux   = blurayDemux;
840 841

    return VLC_SUCCESS;
842 843 844

error:
    if (error_msg)
Thomas Guillem's avatar
Thomas Guillem committed
845
        vlc_dialog_display_error(p_demux, _("Blu-ray error"), "%s", error_msg);
846
    blurayClose(object);
847 848 849

    if (p_demux->s != NULL) {
        /* restore stream position */
850
        if (vlc_stream_Seek(p_demux->s, i_init_pos) != VLC_SUCCESS) {
851 852 853 854 855
            msg_Err(p_demux, "Failed to seek back to stream start");
            return VLC_ETIMEOUT;
        }
    }

856
    return VLC_EGENERIC;
857
#undef BLURAY_ERROR
858 859 860 861 862 863
}


/*****************************************************************************
 * blurayClose: module destroy function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
864
static void blurayClose(vlc_object_t *object)
865
{
866 867 868
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys = p_demux->p_sys;

869 870
    var_DelCallback( p_demux->p_input, "intf-event", onIntfEvent, p_demux );

871 872
    setTitleInfo(p_sys, NULL);

873 874 875
    /*
     * Close libbluray first.
     * This will close all the overlays before we release p_vout
Rafaël Carré's avatar
Rafaël Carré committed
876
     * bd_close(NULL) can crash
877
     */
878 879 880
    if (p_sys->bluray) {
        bd_close(p_sys->bluray);
    }
881

882 883
    blurayReleaseVout(p_demux);

884
    if (p_sys->p_parser)
885
        vlc_demux_chained_Delete(p_sys->p_parser);
886 887
    if (p_sys->p_out != NULL)
        es_out_Delete(p_sys->p_out);
Rafaël Carré's avatar
Rafaël Carré committed
888 889
    assert(vlc_array_count(&p_sys->es) == 0);
    vlc_array_clear(&p_sys->es);
890

891 892 893
    /* 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
894
    TAB_CLEAN(p_sys->i_title, p_sys->pp_title);
895

896 897 898 899
    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);

900
    vlc_mutex_destroy(&p_sys->pl_info_lock);
901
    vlc_mutex_destroy(&p_sys->bdj_overlay_lock);
902
    vlc_mutex_destroy(&p_sys->read_block_lock);
903

904
    free(p_sys->psz_bd_path);
905 906
}

907 908 909 910 911 912 913 914
/*****************************************************************************
 * Elementary streams handling
 *****************************************************************************/

struct es_out_sys_t {
    demux_t *p_demux;
};

915 916 917 918 919
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
920
static int  findEsPairIndex(demux_sys_t *p_sys, int i_id)
921
{
922
    for (size_t i = 0; i < vlc_array_count(&p_sys->es); ++i)
Rafaël Carré's avatar
Rafaël Carré committed
923
        if (((fmt_es_pair_t*)vlc_array_item_at_index(&p_sys->es, i))->i_id == i_id)
924
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
925

926 927 928
    return -1;
}

Rafaël Carré's avatar
Rafaël Carré committed
929
static int  findEsPairIndexByEs(demux_sys_t *p_sys, es_out_id_t *p_es)
930
{
931
    for (size_t i = 0; i < vlc_array_count(&p_sys->es); ++i)
Rafaël Carré's avatar
Rafaël Carré committed
932
        if (((fmt_es_pair_t*)vlc_array_item_at_index(&p_sys->es, i))->p_es == p_es)
933
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
934

935 936 937
    return -1;
}

938
static void setStreamLang(demux_sys_t *p_sys, es_format_t *p_fmt)
939
{
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
    const BLURAY_STREAM_INFO *p_streams;
    int i_stream_count = 0;

    vlc_mutex_lock(&p_sys->pl_info_lock);

    if (p_sys->p_clip_info) {
        if (p_fmt->i_cat == AUDIO_ES) {
            p_streams      = p_sys->p_clip_info->audio_streams;
            i_stream_count = p_sys->p_clip_info->audio_stream_count;
        } else if (p_fmt->i_cat == SPU_ES) {
            p_streams      = p_sys->p_clip_info->pg_streams;
            i_stream_count = p_sys->p_clip_info->pg_stream_count;
        }
    }

955 956 957
    for (int i = 0; i < i_stream_count; i++) {
        if (p_fmt->i_id == p_streams[i].pid) {
            free(p_fmt->psz_language);
958
            p_fmt->psz_language = strndup((const char *)p_streams[i].lang, 3);
959
            break;
960 961
        }
    }
962 963 964 965 966 967 968 969 970 971 972 973

    vlc_mutex_unlock(&p_sys->pl_info_lock);
}

static int blurayEsPid(demux_sys_t *p_sys, int es_type, int i_es_idx)
{
    int i_pid = -1;

    vlc_mutex_lock(&p_sys->pl_info_lock);

    if (p_sys->p_clip_info) {
        if (es_type == AUDIO_ES) {
974
            if (i_es_idx >= 0 && i_es_idx < p_sys->p_clip_info->audio_stream_count) {
975 976 977 978 979 980 981 982 983 984 985 986
                i_pid = p_sys->p_clip_info->audio_streams[i_es_idx].pid;
            }
        } else if (es_type == SPU_ES) {
            if (i_es_idx >= 0 && i_es_idx < p_sys->p_clip_info->pg_stream_count) {
                i_pid = p_sys->p_clip_info->pg_streams[i_es_idx].pid;
            }
        }
    }

    vlc_mutex_unlock(&p_sys->pl_info_lock);

    return i_pid;
987 988
}

Rafaël Carré's avatar
Rafaël Carré committed
989
static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
990
{
991 992
    demux_t *p_demux = p_out->p_sys->p_demux;
    demux_sys_t *p_sys = p_demux->p_sys;
993
    es_format_t fmt;
994
    bool b_select = false;
995

996
    es_format_Init(&fmt, p_fmt->i_cat, p_fmt->i_codec);
997
    es_format_Copy(&fmt, p_fmt);
998

Rafaël Carré's avatar
Rafaël Carré committed
999
    switch (fmt.i_cat) {
1000
    case VIDEO_ES:
Rafaël Carré's avatar
Rafaël Carré committed
1001
        if (p_sys->i_video_stream != -1 && p_sys->i_video_stream != p_fmt->i_id)
1002
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
1003 1004
        break ;
    case AUDIO_ES:
1005 1006 1007
        if (p_sys->i_audio_stream_idx != -1) {
            if (blurayEsPid(p_sys, AUDIO_ES, p_sys->i_audio_stream_idx) == p_fmt->i_id)
                b_select = true;
1008
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
1009
        }
1010
        setStreamLang(p_sys, &fmt);
1011 1012
        break ;
    case SPU_ES:
1013 1014 1015
        if (p_sys->i_spu_stream_idx != -1) {
            if (blurayEsPid(p_sys, SPU_ES, p_sys->i_spu_stream_idx) == p_fmt->i_id)
                b_select = true;
1016
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
1017
        }
1018
        setStreamLang(p_sys, &fmt);
1019 1020 1021
        break ;
    }

1022
    es_out_id_t *p_es = es_out_Add(p_demux->out, &fmt);
Rafaël Carré's avatar
Rafaël Carré committed
1023
    if (p_fmt->i_id >= 0) {
1024
        /* Ensure we are not overriding anything */
1025
        int idx = findEsPairIndex(p_sys, p_fmt->i_id);
Rafaël Carré's avatar
Rafaël Carré committed
1026 1027 1028
        if (idx == -1) {
            fmt_es_pair_t *p_pair = malloc(sizeof(*p_pair));
            if (likely(p_pair != NULL)) {
1029 1030
                p_pair->i_id = p_fmt->i_id;
                p_pair->p_es = p_es;
1031
                msg_Info(p_demux, "Adding ES %d", p_fmt->i_id);
1032
                vlc_array_append(&p_sys->es, p_pair);
1033 1034 1035 1036 1037 1038 1039 1040

                if (b_select) {
                    if (fmt.i_cat == AUDIO_ES) {
                        var_SetInteger( p_demux->p_input, "audio-es", p_fmt->i_id );
                    } else if (fmt.i_cat == SPU_ES) {
                        var_SetInteger( p_demux->p_input, "spu-es", p_sys->b_spu_enable ? p_fmt->i_id : -1 );
                    }
                }
1041 1042 1043
            }
        }
    }
1044
    es_format_Clean(&fmt);
1045
    return p_es;
1046 1047
}

Rafaël Carré's avatar
Rafaël Carré committed
1048
static int esOutSend(es_out_t *p_out, es_out_id_t *p_es, block_t *p_block)
1049
{
Rafaël Carré's avatar
Rafaël Carré committed
1050
    return es_out_Send(p_out->p_sys->p_demux->out, p_es, p_block);
1051 1052
}

Rafaël Carré's avatar
Rafaël Carré committed
1053
static void esOutDel(es_out_t *p_out, es_out_id_t *p_es)
1054
{
Rafaël Carré's avatar
Rafaël Carré committed
1055 1056 1057
    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));
1058 1059
        vlc_array_remove(&p_out->p_sys->p_demux->p_sys->es, idx);
    }
Rafaël Carré's avatar
Rafaël Carré committed
1060
    es_out_Del(p_out->p_sys->p_demux->out, p_es);
1061 1062
}

Rafaël Carré's avatar
Rafaël Carré committed
1063
static int esOutControl(es_out_t *p_out, int i_query, va_list args)
1064
{
Rafaël Carré's avatar
Rafaël Carré committed
1065
    return es_out_vaControl(p_out->p_sys->p_demux->out, i_query, args);
1066 1067
}

Rafaël Carré's avatar
Rafaël Carré committed
1068
static void esOutDestroy(es_out_t *p_out)
1069
{
1070
    for (size_t i = 0; i < vlc_array_count(&p_out->p_sys->p_demux->p_sys->es); ++i)
Rafaël Carré's avatar
Rafaël Carré committed
1071
        free(vlc_array_item_at_index(&p_out->p_sys->p_demux->p_sys->es, i));
1072
    vlc_array_clear(&p_out->p_sys->p_demux->p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
1073 1074
    free(p_out->p_sys);
    free(p_out);
1075 1076
}

Rafaël Carré's avatar
Rafaël Carré committed
1077
static es_out_t *esOutNew(demux_t *p_demux)
1078
{
Rafaël Carré's avatar
Rafaël Carré committed
1079 1080 1081
    assert(vlc_array_count(&p_demux->p_sys->es) == 0);
    es_out_t    *p_out = malloc(sizeof(*p_out));
    if (unlikely(p_out == NULL))
1082 1083
        return NULL;

Rafaël Carré's avatar
Rafaël Carré committed
1084 1085 1086 1087 1088
    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;
1089

Rafaël Carré's avatar
Rafaël Carré committed
1090 1091 1092
    p_out->p_sys = malloc(sizeof(*p_out->p_sys));
    if (unlikely(p_out->p_sys == NULL)) {
        free(p_out);
1093 1094 1095 1096 1097 1098
        return NULL;
    }
    p_out->p_sys->p_demux = p_demux;
    return p_out;
}

1099 1100 1101
/*****************************************************************************
 * subpicture_updater_t functions:
 *****************************************************************************/
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127

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
1128
static int subpictureUpdaterValidate(subpicture_t *p_subpic,
1129 1130
                                      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
1131
                                      mtime_t i_ts)
1132
{
Rafaël Carré's avatar
Rafaël Carré committed
1133 1134 1135 1136 1137
    VLC_UNUSED(b_fmt_src);
    VLC_UNUSED(b_fmt_dst);
    VLC_UNUSED(p_fmt_src);
    VLC_UNUSED(p_fmt_dst);
    VLC_UNUSED(i_ts);
1138 1139

    subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
1140 1141 1142 1143 1144
    bluray_overlay_t         *p_overlay = updater_lock_overlay(p_upd_sys);

    if (!p_overlay) {
        return 1;
    }
1145 1146

    int res = p_overlay->status == Outdated;
1147 1148 1149

    updater_unlock_overlay(p_upd_sys);

1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
    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;
1162 1163 1164 1165 1166
    bluray_overlay_t         *p_overlay = updater_lock_overlay(p_upd_sys);

    if (!p_overlay) {
        return;
    }
1167 1168 1169 1170 1171 1172

    /*
     * 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
1173
    if (!p_src) {
1174
        updater_unlock_overlay(p_upd_sys);
1175
        return;
1176
    }
1177

Rafaël Carré's avatar
Rafaël Carré committed
1178
    subpicture_region_t **p_dst = &p_subpic->p_region;
1179
    while (p_src != NULL) {
1180
        *p_dst = subpicture_region_Copy(p_src);
1181
        if (*p_dst == NULL)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
1182
            break;
Rafaël Carré's avatar
Rafaël Carré committed
1183
        p_dst = &(*p_dst)->p_next;
1184 1185 1186 1187 1188 1189
        p_src = p_src->p_next;
    }
    if (*p_dst != NULL)
        (*p_dst)->p_next = NULL;
    p_overlay->status = Displayed;

1190 1191
    updater_unlock_overlay(p_upd_sys);
}
1192

1193 1194
static void subpictureUpdaterDestroy(subpicture_t *p_subpic)
{
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206
    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);
}
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234

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;

1235 1236 1237 1238 1239
    vlc_mutex_init(&p_upd_sys->lock);
    p_upd_sys->ref_cnt = 2;

    p_ov->p_updater = p_upd_sys;

1240
    return p_pic;
1241 1242
}

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
/*****************************************************************************
 * 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;
    VLC_UNUSED(old);
    VLC_UNUSED(p_vout);

    if (psz_var[6] == 'm')   //Mouse moved
1255
        bd_mouse_select(p_sys->bluray, -1, val.coords.x, val.coords.y);
1256
    else if (psz_var[6] == 'c') {
1257 1258
        bd_mouse_select(p_sys->bluray, -1, val.coords.x, val.coords.y);
        bd_user_input(p_sys->bluray, -1, BD_VK_MOUSE_ACTIVATE);
1259
    } else {
1260
        vlc_assert_unreachable();
1261 1262 1263 1264
    }
    return VLC_SUCCESS;
}

1265 1266
static int sendKeyEvent(demux_sys_t *p_sys, unsigned int key)
{
1267
    if (bd_user_input(p_sys->bluray, -1, key) < 0)
1268
        return VLC_EGENERIC;
Rafaël Carré's avatar
Rafaël Carré committed
1269

1270 1271 1272
    return VLC_SUCCESS;
}

1273 1274 1275
/*****************************************************************************
 * libbluray overlay handling:
 *****************************************************************************/
1276

1277 1278 1279 1280 1281 1282
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) {
1283 1284 1285 1286 1287 1288

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

1293 1294 1295 1296
        vlc_mutex_destroy(&ov->lock);
        subpicture_region_ChainDelete(ov->p_regions);
        free(ov);

1297 1298
        p_sys->p_overlays[plane] = NULL;
    }
1299

1300
    for (int i = 0; i < MAX_OVERLAY; i++)
1301 1302
        if (p_sys->p_overlays[i])
            return;
1303

1304
    /* All overlays have been closed */
1305
    blurayReleaseVout(p_demux);
1306 1307 1308 1309 1310 1311 1312 1313 1314
}

/*
 * 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"
 */
1315
static void blurayActivateOverlay(demux_t *p_demux, int plane)
1316 1317
{
    demux_sys_t *p_sys = p_demux->p_sys;
1318
    bluray_overlay_t *ov = p_sys->p_overlays[plane];
1319 1320 1321 1322 1323

    /*
     * If the overlay is already displayed, mark the picture as outdated.
     * We must NOT use vout_PutSubpicture if a picture is already displayed.
     */
1324 1325 1326 1327
    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
1328
        return;
1329
    }
1330

1331 1332 1333 1334 1335
    /*
     * 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
     */
1336 1337
    ov->status = ToDisplay;
    vlc_mutex_unlock(&ov->lock);
1338 1339
}

1340
static void blurayInitOverlay(demux_t *p_demux, int plane, int width, int height)
1341 1342 1343
{
    demux_sys_t *p_sys = p_demux->p_sys;

1344
    assert(p_sys->p_overlays[plane] == NULL);
1345

1346 1347
    bluray_overlay_t *ov = calloc(1, sizeof(*ov));
    if (unlikely(ov == NULL))
1348 1349
        return;

1350 1351 1352
    ov->width = width;
    ov->height = height;
    ov->i_channel = -1;
1353 1354 1355 1356

    vlc_mutex_init(&ov->lock);

    p_sys->p_overlays[plane] = ov;
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
}

/**
 * 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.
 */
1367
static void blurayClearOverlay(demux_t *p_demux, int plane)
1368 1369
{
    demux_sys_t *p_sys = p_demux->p_sys;
1370 1371 1372
    bluray_overlay_t *ov = p_sys->p_overlays[plane];

    vlc_mutex_lock(&ov->lock);
1373

1374 1375 1376
    subpicture_region_ChainDelete(ov->p_regions);
    ov->p_regions = NULL;
    ov->status = Outdated;
1377

1378
    vlc_mutex_unlock(&ov->lock);
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
}

/*
 * 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.
     */
    vlc_mutex_lock(&p_sys->p_overlays[ov->plane]->lock);

    /* Find a region to update */
1396
    subpicture_region_t **pp_reg = &p_sys->p_overlays[ov->plane]->p_regions;
1397 1398 1399 1400 1401 1402 1403
    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;
1404
        pp_reg = &p_reg->p_next;
1405 1406 1407
        p_reg = p_reg->p_next;
    }

1408 1409 1410 1411 1412 1413 1414 1415 1416 1417
    if (!ov->img) {
        if (p_reg) {
            /* drop region */
            *pp_reg = p_reg->p_next;
            subpicture_region_Delete(p_reg);
        }
        vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
        return;
    }

1418 1419 1420 1421
    /* If there is no region to update, create a new one. */
    if (!p_reg) {
        video_format_t fmt;
        video_format_Init(&fmt, 0);
1422
        video_format_Setup(&fmt, VLC_CODEC_YUVP, ov->w, ov->h, ov->w, ov->h, 1, 1);
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435

        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;