Blame src/libbluray/disc/dec.c

Packit 5e46da
/*
Packit 5e46da
 * This file is part of libbluray
Packit 5e46da
 * Copyright (C) 2014  VideoLAN
Packit 5e46da
 *
Packit 5e46da
 * This library is free software; you can redistribute it and/or
Packit 5e46da
 * modify it under the terms of the GNU Lesser General Public
Packit 5e46da
 * License as published by the Free Software Foundation; either
Packit 5e46da
 * version 2.1 of the License, or (at your option) any later version.
Packit 5e46da
 *
Packit 5e46da
 * This library is distributed in the hope that it will be useful,
Packit 5e46da
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5e46da
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 5e46da
 * Lesser General Public License for more details.
Packit 5e46da
 *
Packit 5e46da
 * You should have received a copy of the GNU Lesser General Public
Packit 5e46da
 * License along with this library. If not, see
Packit 5e46da
 * <http://www.gnu.org/licenses/>.
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if HAVE_CONFIG_H
Packit 5e46da
#include "config.h"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include "dec.h"
Packit 5e46da
Packit 5e46da
#include "enc_info.h"
Packit 5e46da
#include "aacs.h"
Packit 5e46da
#include "bdplus.h"
Packit 5e46da
Packit 5e46da
#include "file/file.h"
Packit 5e46da
#include "util/logging.h"
Packit 5e46da
#include "util/macro.h"
Packit 5e46da
#include "util/strutl.h"
Packit 5e46da
Packit 5e46da
#include <string.h>
Packit 5e46da
Packit 5e46da
struct bd_dec {
Packit 5e46da
    int        use_menus;
Packit 5e46da
    BD_AACS   *aacs;
Packit 5e46da
    BD_BDPLUS *bdplus;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * stream
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
typedef struct {
Packit 5e46da
    BD_FILE_H    *fp;
Packit 5e46da
    BD_AACS      *aacs;
Packit 5e46da
    BD_BDPLUS_ST *bdplus;
Packit 5e46da
} DEC_STREAM;
Packit 5e46da
Packit 5e46da
static int64_t _stream_read(BD_FILE_H *fp, uint8_t *buf, int64_t size)
Packit 5e46da
{
Packit 5e46da
    DEC_STREAM *st = (DEC_STREAM *)fp->internal;
Packit 5e46da
    int64_t     result;
Packit 5e46da
Packit 5e46da
    if (size != 6144) {
Packit 5e46da
        BD_DEBUG(DBG_CRIT, "read size != unit size\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = st->fp->read(st->fp, buf, size);
Packit 5e46da
    if (result <= 0) {
Packit 5e46da
        return result;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (st->aacs) {
Packit 5e46da
        if (libaacs_decrypt_unit(st->aacs, buf)) {
Packit 5e46da
            /* failure is detected from TP header */
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (st->bdplus) {
Packit 5e46da
        if (libbdplus_fixup(st->bdplus, buf, (int)size) < 0) {
Packit 5e46da
          /* there's no way to verify if the stream was decoded correctly */
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int64_t _stream_seek(BD_FILE_H *fp, int64_t offset, int32_t origin)
Packit 5e46da
{
Packit 5e46da
    DEC_STREAM *st = (DEC_STREAM *)fp->internal;
Packit 5e46da
    int64_t result = st->fp->seek(st->fp, offset, origin);
Packit 5e46da
    if (result >= 0 && st->bdplus) {
Packit 5e46da
        libbdplus_seek(st->bdplus, st->fp->tell(st->fp));
Packit 5e46da
    }
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int64_t _stream_tell(BD_FILE_H *fp)
Packit 5e46da
{
Packit 5e46da
    DEC_STREAM *st = (DEC_STREAM *)fp->internal;
Packit 5e46da
    return st->fp->tell(st->fp);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _stream_close(BD_FILE_H *fp)
Packit 5e46da
{
Packit 5e46da
    DEC_STREAM *st = (DEC_STREAM *)fp->internal;
Packit 5e46da
    if (st->bdplus) {
Packit 5e46da
        libbdplus_m2ts_close(&st->bdplus);
Packit 5e46da
    }
Packit 5e46da
    st->fp->close(st->fp);
Packit 5e46da
    X_FREE(fp->internal);
Packit 5e46da
    X_FREE(fp);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BD_FILE_H *dec_open_stream(BD_DEC *dec, BD_FILE_H *fp, uint32_t clip_id)
Packit 5e46da
{
Packit 5e46da
    DEC_STREAM *st;
Packit 5e46da
    BD_FILE_H  *p = calloc(1, sizeof(BD_FILE_H));
Packit 5e46da
    if (!p) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    st = calloc(1, sizeof(DEC_STREAM));
Packit 5e46da
    if (!st) {
Packit 5e46da
        X_FREE(p);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
    st->fp = fp;
Packit 5e46da
Packit 5e46da
    if (dec->bdplus) {
Packit 5e46da
        st->bdplus = libbdplus_m2ts(dec->bdplus, clip_id, 0);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (dec->aacs) {
Packit 5e46da
        st->aacs = dec->aacs;
Packit 5e46da
        if (!dec->use_menus) {
Packit 5e46da
            /* There won't be title events --> need to manually reset AACS CPS */
Packit 5e46da
            libaacs_select_title(dec->aacs, 0xffff);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->internal = st;
Packit 5e46da
    p->read  = _stream_read;
Packit 5e46da
    p->seek  = _stream_seek;
Packit 5e46da
    p->tell  = _stream_tell;
Packit 5e46da
    p->close = _stream_close;
Packit 5e46da
Packit 5e46da
    return p;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _bdrom_have_file(void *p, const char *dir, const char *file)
Packit 5e46da
{
Packit 5e46da
    struct dec_dev *dev = (struct dec_dev *)p;
Packit 5e46da
    BD_FILE_H *fp;
Packit 5e46da
    char *path;
Packit 5e46da
Packit 5e46da
    path = str_printf("%s" DIR_SEP "%s", dir, file);
Packit 5e46da
    if (!path) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    fp = dev->pf_file_open_bdrom(dev->file_open_bdrom_handle, path);
Packit 5e46da
    X_FREE(path);
Packit 5e46da
Packit 5e46da
    if (fp) {
Packit 5e46da
        file_close(fp);
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _libaacs_init(BD_DEC *dec, struct dec_dev *dev,
Packit 5e46da
                         BD_ENC_INFO *i, const char *keyfile_path)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
    const uint8_t *disc_id;
Packit 5e46da
Packit 5e46da
    if (!dec->aacs) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = libaacs_open(dec->aacs, dev->device, dev->file_open_vfs_handle, (void*)dev->pf_file_open_vfs, keyfile_path);
Packit 5e46da
Packit 5e46da
    i->aacs_error_code = result;
Packit 5e46da
    i->aacs_handled    = !result;
Packit 5e46da
    i->aacs_mkbv       = libaacs_get_mkbv(dec->aacs);
Packit 5e46da
    disc_id = libaacs_get_aacs_data(dec->aacs, BD_AACS_DISC_ID);
Packit 5e46da
    if (disc_id) {
Packit 5e46da
        memcpy(i->disc_id, disc_id, 20);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (result) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_open() failed: %d!\n", result);
Packit 5e46da
        libaacs_unload(&dec->aacs);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "Opened libaacs\n");
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _libbdplus_init(BD_DEC *dec, struct dec_dev *dev,
Packit 5e46da
                           BD_ENC_INFO *i,
Packit 5e46da
                           void *regs, void *psr_read, void *psr_write)
Packit 5e46da
{
Packit 5e46da
    if (!dec->bdplus) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    const uint8_t *vid = libaacs_get_aacs_data(dec->aacs, BD_AACS_MEDIA_VID);
Packit 5e46da
    const uint8_t *mk  = libaacs_get_aacs_data(dec->aacs, BD_AACS_MEDIA_KEY);
Packit 5e46da
    if (!vid) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ initialization failed (no AACS ?)\n");
Packit 5e46da
        libbdplus_unload(&dec->bdplus);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (libbdplus_init(dec->bdplus, dev->root, dev->device, dev->file_open_bdrom_handle, (void*)dev->pf_file_open_bdrom, vid, mk)) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bdplus_init() failed\n");
Packit 5e46da
Packit 5e46da
        i->bdplus_handled = 0;
Packit 5e46da
        libbdplus_unload(&dec->bdplus);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "libbdplus initialized\n");
Packit 5e46da
Packit 5e46da
    /* map player memory regions */
Packit 5e46da
    libbdplus_mmap(dec->bdplus, 0, regs);
Packit 5e46da
    libbdplus_mmap(dec->bdplus, 1, (void*)((uint8_t *)regs + sizeof(uint32_t) * 128));
Packit 5e46da
Packit 5e46da
    /* connect registers */
Packit 5e46da
    libbdplus_psr(dec->bdplus, regs, psr_read, psr_write);
Packit 5e46da
Packit 5e46da
    i->bdplus_gen     = libbdplus_get_gen(dec->bdplus);
Packit 5e46da
    i->bdplus_date    = libbdplus_get_date(dec->bdplus);
Packit 5e46da
    i->bdplus_handled = 1;
Packit 5e46da
Packit 5e46da
    if (i->bdplus_date == 0) {
Packit 5e46da
        // libmmbd -> no menu support
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "WARNING: using libmmbd for BD+. On-disc menus will not work.\n");
Packit 5e46da
        //i->no_menu_support = 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _dec_detect(struct dec_dev *dev, BD_ENC_INFO *i)
Packit 5e46da
{
Packit 5e46da
    /* Check for AACS */
Packit 5e46da
    i->aacs_detected = libaacs_required((void*)dev, _bdrom_have_file);
Packit 5e46da
    if (!i->aacs_detected) {
Packit 5e46da
        /* No AACS (=> no BD+) */
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* check for BD+ */
Packit 5e46da
    i->bdplus_detected = libbdplus_required((void*)dev, _bdrom_have_file);
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _dec_load(BD_DEC *dec, BD_ENC_INFO *i)
Packit 5e46da
{
Packit 5e46da
    int force_mmbd_aacs = 0;
Packit 5e46da
Packit 5e46da
    if (i->bdplus_detected) {
Packit 5e46da
        /* load BD+ library and check BD+ library type. libmmbd doesn't work with libaacs */
Packit 5e46da
        dec->bdplus = libbdplus_load();
Packit 5e46da
        force_mmbd_aacs = dec->bdplus && libbdplus_is_mmbd(dec->bdplus);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* load AACS library */
Packit 5e46da
    dec->aacs = libaacs_load(force_mmbd_aacs);
Packit 5e46da
Packit 5e46da
    i->libaacs_detected   = !!dec->aacs;
Packit 5e46da
    i->libbdplus_detected = !!dec->bdplus;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
BD_DEC *dec_init(struct dec_dev *dev, BD_ENC_INFO *enc_info,
Packit 5e46da
                 const char *keyfile_path,
Packit 5e46da
                 void *regs, void *psr_read, void *psr_write)
Packit 5e46da
{
Packit 5e46da
    BD_DEC *dec = NULL;
Packit 5e46da
Packit 5e46da
    memset(enc_info, 0, sizeof(*enc_info));
Packit 5e46da
Packit 5e46da
    /* detect AACS/BD+ */
Packit 5e46da
    if (!_dec_detect(dev, enc_info)) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    dec = calloc(1, sizeof(BD_DEC));
Packit 5e46da
    if (!dec) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* load compatible libraries */
Packit 5e46da
    _dec_load(dec, enc_info);
Packit 5e46da
Packit 5e46da
    /* init decoding libraries */
Packit 5e46da
    /* BD+ won't help unless AACS works ... */
Packit 5e46da
    if (_libaacs_init(dec, dev, enc_info, keyfile_path)) {
Packit 5e46da
        _libbdplus_init(dec, dev, enc_info, regs, psr_read, psr_write);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!enc_info->aacs_handled) {
Packit 5e46da
        /* AACS failed, clean up */
Packit 5e46da
        dec_close(&dec;;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* BD+ failure may be non-fatal (not all titles in disc use BD+).
Packit 5e46da
     * Keep working AACS decoder even if BD+ init failed
Packit 5e46da
     */
Packit 5e46da
Packit 5e46da
    return dec;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void dec_close(BD_DEC **pp)
Packit 5e46da
{
Packit 5e46da
    if (pp && *pp) {
Packit 5e46da
        BD_DEC *p = *pp;
Packit 5e46da
        libaacs_unload(&p->aacs);
Packit 5e46da
        libbdplus_unload(&p->bdplus);
Packit 5e46da
        X_FREE(*pp);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
const uint8_t *dec_data(BD_DEC *dec, int type)
Packit 5e46da
{
Packit 5e46da
    const uint8_t *ret = NULL;
Packit 5e46da
Packit 5e46da
    if (type >= 0x1000) {
Packit 5e46da
        if (dec->bdplus) {
Packit 5e46da
            ret = libbdplus_get_data(dec->bdplus, type);
Packit 5e46da
        }
Packit 5e46da
    } else {
Packit 5e46da
        if (dec->aacs) {
Packit 5e46da
            ret = libaacs_get_aacs_data(dec->aacs, type);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
const uint8_t *dec_disc_id(BD_DEC *dec)
Packit 5e46da
{
Packit 5e46da
    return dec_data(dec, BD_AACS_DISC_ID);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void dec_start(BD_DEC *dec, uint32_t num_titles)
Packit 5e46da
{
Packit 5e46da
    if (num_titles == 0) {
Packit 5e46da
        dec->use_menus = 1;
Packit 5e46da
        if (dec->bdplus) {
Packit 5e46da
            libbdplus_start(dec->bdplus);
Packit 5e46da
            libbdplus_event(dec->bdplus, 0x110, 0xffff, 0);
Packit 5e46da
        }
Packit 5e46da
    } else {
Packit 5e46da
        if (dec->bdplus) {
Packit 5e46da
            libbdplus_event(dec->bdplus, 0xffffffff, num_titles, 0);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void dec_title(BD_DEC *dec, uint32_t title)
Packit 5e46da
{
Packit 5e46da
    if (dec->aacs) {
Packit 5e46da
        libaacs_select_title(dec->aacs, title);
Packit 5e46da
    }
Packit 5e46da
    if (dec->bdplus) {
Packit 5e46da
        libbdplus_event(dec->bdplus, 0x110, title, 0);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void dec_application(BD_DEC *dec, uint32_t data)
Packit 5e46da
{
Packit 5e46da
    if (dec->bdplus) {
Packit 5e46da
        libbdplus_event(dec->bdplus, 0x210, data, 0);
Packit 5e46da
    }
Packit 5e46da
}