/*
* This file is part of libbluray
* Copyright (C) 2010-2017 Petri Hintukainen <phintuka@users.sourceforge.net>
*
* This library 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 (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "register.h"
#include "player_settings.h"
#include "util/attributes.h"
#include "util/macro.h"
#include "util/logging.h"
#include "util/mutex.h"
#include <stdlib.h>
#include <string.h>
/*
* Initial values for player status/setting registers (5.8.2).
*
* PS in comment indicates player setting -> register can't be changed from movie object code.
*/
static const uint32_t bd_psr_init[BD_PSR_COUNT] = {
1, /* PSR0: Interactive graphics stream number */
0xff, /* PSR1: Primary audio stream number */
0x0fff0fff, /* PSR2: PG TextST stream number and PiP PG stream number*/
1, /* PSR3: Angle number */
0xffff, /* PSR4: Title number */
0xffff, /* PSR5: Chapter number */
0, /* PSR6: PlayList ID */
0, /* PSR7: PlayItem ID */
0, /* PSR8: Presentation time */
0, /* PSR9: Navigation timer */
0xffff, /* PSR10: Selected button ID */
0, /* PSR11: Page ID */
0xff, /* PSR12: User style number */
0xff, /* PS: PSR13: User age */
0xffff, /* PSR14: Secondary audio stream number and secondary video stream number */
/* PS: PSR15: player capability for audio */
BLURAY_ACAP_LPCM_48_96_SURROUND |
BLURAY_ACAP_LPCM_192_SURROUND |
BLURAY_ACAP_DDPLUS_SURROUND |
BLURAY_ACAP_DDPLUS_DEP_SURROUND |
BLURAY_ACAP_DTSHD_CORE_SURROUND |
BLURAY_ACAP_DTSHD_EXT_SURROUND |
BLURAY_ACAP_DD_SURROUND |
BLURAY_ACAP_MLP_SURROUND,
0xffffff, /* PS: PSR16: Language code for audio */
0xffffff, /* PS: PSR17: Language code for PG and Text subtitles */
0xffffff, /* PS: PSR18: Menu description language code */
0xffff, /* PS: PSR19: Country code */
/* PS: PSR20: Region code */ /* 1 - A, 2 - B, 4 - C */
BLURAY_REGION_B,
/* PS: PSR21: Output mode preference */
BLURAY_OUTPUT_PREFER_2D,
0, /* PSR22: Stereoscopic status */
0, /* PS: PSR23: Display capability */
0, /* PS: PSR24: 3D capability */
0, /* PS: PSR25: UHD capability */
0, /* PS: PSR26: UHD display capability */
0, /* PS: PSR27: HDR preference */
0, /* PS: PSR28: SDR conversion preference */
/* PS: PSR29: player capability for video */
BLURAY_VCAP_SECONDARY_HD |
BLURAY_VCAP_25Hz_50Hz,
0x1ffff, /* PS: PSR30: player capability for text subtitle */
/* PS: PSR31: Player profile and version */
BLURAY_PLAYER_PROFILE_2_v2_0,
0, /* PSR32 */
0, /* PSR33 */
0, /* PSR34 */
0, /* PSR35 */
0xffff, /* PSR36: backup PSR4 */
0xffff, /* PSR37: backup PSR5 */
0, /* PSR38: backup PSR6 */
0, /* PSR39: backup PSR7 */
0, /* PSR40: backup PSR8 */
0, /* PSR41: */
0xffff, /* PSR42: backup PSR10 */
0, /* PSR43: backup PSR11 */
0xff, /* PSR44: backup PSR12 */
0, /* PSR45: */
0, /* PSR46: */
0, /* PSR47: */
0xffffffff, /* PS: PSR48: Characteristic text caps */
0xffffffff, /* PS: PSR49: Characteristic text caps */
0xffffffff, /* PS: PSR50: Characteristic text caps */
0xffffffff, /* PS: PSR51: Characteristic text caps */
0xffffffff, /* PS: PSR52: Characteristic text caps */
0xffffffff, /* PS: PSR53: Characteristic text caps */
0xffffffff, /* PS: PSR54: Characteristic text caps */
0xffffffff, /* PS: PSR55: Characteristic text caps */
0xffffffff, /* PS: PSR56: Characteristic text caps */
0xffffffff, /* PS: PSR57: Characteristic text caps */
0xffffffff, /* PS: PSR58: Characteristic text caps */
0xffffffff, /* PS: PSR59: Characteristic text caps */
0xffffffff, /* PS: PSR60: Characteristic text caps */
0xffffffff, /* PS: PSR61: Characteristic text caps */
/* 62-95: reserved */
/* 96-111: reserved for BD system use */
/* 112-127: reserved */
};
/*
* PSR ids for debugging
*/
static const char * const bd_psr_name[BD_PSR_COUNT] = {
"IG_STREAM_ID",
"PRIMARY_AUDIO_ID",
"PG_STREAM",
"ANGLE_NUMBER",
"TITLE_NUMBER",
"CHAPTER",
"PLAYLIST",
"PLAYITEM",
"TIME",
"NAV_TIMER",
"SELECTED_BUTTON_ID",
"MENU_PAGE_ID",
"STYLE",
"PARENTAL",
"SECONDARY_AUDIO_VIDEO",
"AUDIO_CAP",
"AUDIO_LANG",
"PG_AND_SUB_LANG",
"MENU_LANG",
"COUNTRY",
"REGION",
"OUTPUT_PREFER",
"3D_STATUS",
"DISPLAY_CAP",
"3D_CAP",
//"PSR_VIDEO_CAP",
};
/*
* data
*/
typedef struct {
void *handle;
void (*cb)(void *, BD_PSR_EVENT*);
} PSR_CB_DATA;
struct bd_registers_s
{
uint32_t psr[BD_PSR_COUNT];
uint32_t gpr[BD_GPR_COUNT];
/* callbacks */
unsigned num_cb;
PSR_CB_DATA *cb;
BD_MUTEX mutex;
};
/*
* init / free
*/
BD_REGISTERS *bd_registers_init(void)
{
BD_REGISTERS *p = calloc(1, sizeof(BD_REGISTERS));
if (p) {
memcpy(p->psr, bd_psr_init, sizeof(bd_psr_init));
bd_mutex_init(&p->mutex);
}
return p;
}
void bd_registers_free(BD_REGISTERS *p)
{
if (p) {
bd_mutex_destroy(&p->mutex);
X_FREE(p->cb);
}
X_FREE(p);
}
/*
* PSR lock / unlock
*/
void bd_psr_lock(BD_REGISTERS *p)
{
bd_mutex_lock(&p->mutex);
}
void bd_psr_unlock(BD_REGISTERS *p)
{
bd_mutex_unlock(&p->mutex);
}
/*
* PSR change callback register / unregister
*/
void bd_psr_register_cb (BD_REGISTERS *p, void (*callback)(void*,BD_PSR_EVENT*), void *cb_handle)
{
/* no duplicates ! */
PSR_CB_DATA *cb;
unsigned i;
bd_psr_lock(p);
for (i = 0; i < p->num_cb; i++) {
if (p->cb[i].handle == cb_handle && p->cb[i].cb == callback) {
bd_psr_unlock(p);
return;
}
}
cb = realloc(p->cb, (p->num_cb + 1) * sizeof(PSR_CB_DATA));
if (cb) {
p->cb = cb;
p->cb[p->num_cb].cb = callback;
p->cb[p->num_cb].handle = cb_handle;
p->num_cb++;
} else {
BD_DEBUG(DBG_BLURAY|DBG_CRIT, "bd_psr_register_cb(): realloc failed\n");
}
bd_psr_unlock(p);
}
void bd_psr_unregister_cb(BD_REGISTERS *p, void (*callback)(void*,BD_PSR_EVENT*), void *cb_handle)
{
unsigned i = 0;
bd_psr_lock(p);
while (i < p->num_cb) {
if (p->cb[i].handle == cb_handle && p->cb[i].cb == callback) {
if (--p->num_cb && i < p->num_cb) {
memmove(p->cb + i, p->cb + i + 1, sizeof(PSR_CB_DATA) * (p->num_cb - i));
continue;
}
}
i++;
}
bd_psr_unlock(p);
}
/*
* PSR state save / restore
*/
void bd_psr_save_state(BD_REGISTERS *p)
{
/* store registers 4-8 and 10-12 to backup registers */
bd_psr_lock(p);
memcpy(p->psr + 36, p->psr + 4, sizeof(uint32_t) * 5);
memcpy(p->psr + 42, p->psr + 10, sizeof(uint32_t) * 3);
/* generate save event */
if (p->num_cb) {
BD_PSR_EVENT ev;
ev.ev_type = BD_PSR_SAVE;
ev.psr_idx = -1;
ev.old_val = 0;
ev.new_val = 0;
unsigned j;
for (j = 0; j < p->num_cb; j++) {
p->cb[j].cb(p->cb[j].handle, &ev);
}
}
bd_psr_unlock(p);
}
void bd_psr_reset_backup_registers(BD_REGISTERS *p)
{
bd_psr_lock(p);
/* init backup registers to default */
memcpy(p->psr + 36, bd_psr_init + 36, sizeof(uint32_t) * 5);
memcpy(p->psr + 42, bd_psr_init + 42, sizeof(uint32_t) * 3);
bd_psr_unlock(p);
}
void bd_psr_restore_state(BD_REGISTERS *p)
{
uint32_t old_psr[13];
uint32_t new_psr[13];
bd_psr_lock(p);
if (p->num_cb) {
memcpy(old_psr, p->psr, sizeof(old_psr[0]) * 13);
}
/* restore backup registers */
memcpy(p->psr + 4, p->psr + 36, sizeof(uint32_t) * 5);
memcpy(p->psr + 10, p->psr + 42, sizeof(uint32_t) * 3);
if (p->num_cb) {
memcpy(new_psr, p->psr, sizeof(new_psr[0]) * 13);
}
/* init backup registers to default */
memcpy(p->psr + 36, bd_psr_init + 36, sizeof(uint32_t) * 5);
memcpy(p->psr + 42, bd_psr_init + 42, sizeof(uint32_t) * 3);
/* generate restore events */
if (p->num_cb) {
BD_PSR_EVENT ev;
unsigned i, j;
ev.ev_type = BD_PSR_RESTORE;
for (i = 4; i < 13; i++) {
if (i != PSR_NAV_TIMER) {
ev.psr_idx = i;
ev.old_val = old_psr[i];
ev.new_val = new_psr[i];
for (j = 0; j < p->num_cb; j++) {
p->cb[j].cb(p->cb[j].handle, &ev);
}
}
}
}
bd_psr_unlock(p);
}
/*
* GPR read / write
*/
int bd_gpr_write(BD_REGISTERS *p, unsigned int reg, uint32_t val)
{
if (reg >= BD_GPR_COUNT) {
BD_DEBUG(DBG_BLURAY, "bd_gpr_write(%d): invalid register\n", reg);
return -1;
}
p->gpr[reg] = val;
return 0;
}
uint32_t bd_gpr_read(BD_REGISTERS *p, unsigned int reg)
{
if (reg >= BD_GPR_COUNT) {
BD_DEBUG(DBG_BLURAY, "bd_gpr_read(%d): invalid register\n", reg);
return 0;
}
return p->gpr[reg];
}
/*
* PSR read / write
*/
uint32_t bd_psr_read(BD_REGISTERS *p, unsigned int reg)
{
uint32_t val;
if (reg >= BD_PSR_COUNT) {
BD_DEBUG(DBG_BLURAY, "bd_psr_read(%d): invalid register\n", reg);
return -1;
}
bd_psr_lock(p);
val = p->psr[reg];
bd_psr_unlock(p);
return val;
}
int bd_psr_setting_write(BD_REGISTERS *p, unsigned int reg, uint32_t val)
{
if (reg >= BD_PSR_COUNT) {
BD_DEBUG(DBG_BLURAY, "bd_psr_write(%d, %d): invalid register\n", reg, val);
return -1;
}
bd_psr_lock(p);
if (p->psr[reg] == val) {
BD_DEBUG(DBG_BLURAY, "bd_psr_write(%d, %d): no change in value\n", reg, val);
} else if (bd_psr_name[reg]) {
BD_DEBUG(DBG_BLURAY, "bd_psr_write(): PSR%-4d (%s) 0x%x -> 0x%x\n", reg, bd_psr_name[reg], p->psr[reg], val);
} else {
BD_DEBUG(DBG_BLURAY, "bd_psr_write(): PSR%-4d 0x%x -> 0x%x\n", reg, p->psr[reg], val);
}
if (p->num_cb) {
BD_PSR_EVENT ev;
unsigned i;
ev.ev_type = p->psr[reg] == val ? BD_PSR_WRITE : BD_PSR_CHANGE;
ev.psr_idx = reg;
ev.old_val = p->psr[reg];
ev.new_val = val;
p->psr[reg] = val;
for (i = 0; i < p->num_cb; i++) {
p->cb[i].cb(p->cb[i].handle, &ev);
}
} else {
p->psr[reg] = val;
}
bd_psr_unlock(p);
return 0;
}
int bd_psr_write(BD_REGISTERS *p, unsigned int reg, uint32_t val)
{
if ((reg == 13) ||
(reg >= 15 && reg <= 21) ||
(reg >= 23 && reg <= 31) ||
(reg >= 48 && reg <= 61)) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bd_psr_write(%d, %d): read-only register !\n", reg, val);
return -2;
}
return bd_psr_setting_write(p, reg, val);
}
int bd_psr_write_bits(BD_REGISTERS *p, unsigned int reg, uint32_t val, uint32_t mask)
{
int result;
if (mask == 0xffffffff) {
return bd_psr_write(p, reg, val);
}
bd_psr_lock(p);
uint32_t psr_value = bd_psr_read(p, reg);
psr_value = (psr_value & (~mask)) | (val & mask);
result = bd_psr_write(p, reg, psr_value);
bd_psr_unlock(p);
return result;
}
/*
* save / restore registers between playback sessions
*/
void registers_save(BD_REGISTERS *p, uint32_t *psr, uint32_t *gpr)
{
bd_psr_lock(p);
memcpy(gpr, p->gpr, sizeof(p->gpr));
memcpy(psr, p->psr, sizeof(p->psr));
bd_psr_unlock(p);
}
void registers_restore(BD_REGISTERS *p, const uint32_t *psr, const uint32_t *gpr)
{
uint32_t new_psr[13];
bd_psr_lock(p);
memcpy(p->gpr, gpr, sizeof(p->gpr));
memcpy(p->psr, psr, sizeof(p->psr));
memcpy(new_psr, p->psr, sizeof(new_psr[0]) * 13);
/* generate restore events */
if (p->num_cb) {
BD_PSR_EVENT ev;
unsigned i, j;
ev.ev_type = BD_PSR_RESTORE;
ev.old_val = 0; /* not used with BD_PSR_RESTORE */
for (i = 4; i < 13; i++) {
if (i != PSR_NAV_TIMER) {
p->psr[i] = new_psr[i];
ev.psr_idx = i;
ev.new_val = new_psr[i];
for (j = 0; j < p->num_cb; j++) {
p->cb[j].cb(p->cb[j].handle, &ev);
}
}
}
}
bd_psr_unlock(p);
}
/*
*
*/
int psr_init_3D(BD_REGISTERS *p, int initial_mode, int force)
{
bd_psr_lock(p);
/* make automatic initialization to fail if app has already changed player profile */
if (!force) {
if ((bd_psr_read(p, PSR_PROFILE_VERSION) & BLURAY_PLAYER_PROFILE_VERSION_MASK) >= 0x0300) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "psr_init_3D() failed: profile version already set to >= 0x0300 (profile 6)\n");
bd_psr_unlock(p);
return -1;
}
if (bd_psr_read(p, PSR_PROFILE_VERSION) & BLURAY_PLAYER_PROFILE_3D_FLAG) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "psr_init_3D() failed: 3D already set in profile\n");
bd_psr_unlock(p);
return -1;
}
}
bd_psr_setting_write(p, PSR_OUTPUT_PREFER,
BLURAY_OUTPUT_PREFER_3D);
bd_psr_setting_write(p, PSR_DISPLAY_CAP,
BLURAY_DCAP_1080p_720p_3D |
BLURAY_DCAP_720p_50Hz_3D |
BLURAY_DCAP_NO_3D_CLASSES_REQUIRED |
BLURAY_DCAP_INTERLACED_3D |
0);
bd_psr_setting_write(p, PSR_3D_CAP,
/* TODO */ 0xffffffff );
bd_psr_setting_write(p, PSR_PROFILE_VERSION,
BLURAY_PLAYER_PROFILE_5_v2_4);
bd_psr_write(p, PSR_3D_STATUS,
!!initial_mode);
bd_psr_unlock(p);
return 0;
}
int psr_init_UHD(BD_REGISTERS *p, int force)
{
bd_psr_lock(p);
/* make automatic initialization to fail if app has already changed player profile */
if (!force) {
if ((bd_psr_read(p, PSR_PROFILE_VERSION) & BLURAY_PLAYER_PROFILE_VERSION_MASK) >= 0x0300) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "psr_init_UHD() failed: profile version already >= 0x0300\n");
bd_psr_unlock(p);
return -1;
}
if (bd_psr_read(p, PSR_PROFILE_VERSION) & BLURAY_PLAYER_PROFILE_3D_FLAG) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "psr_init_UHD() failed: 3D already set in profile\n");
bd_psr_unlock(p);
return -1;
}
}
bd_psr_setting_write(p, PSR_UHD_CAP,
/* TODO */ 0xffffffff );
bd_psr_setting_write(p, PSR_UHD_DISPLAY_CAP,
/* TODO */ 0xffffffff );
bd_psr_setting_write(p, PSR_UHD_HDR_PREFER,
/* TODO */ 0xffffffff );
bd_psr_setting_write(p, PSR_UHD_SDR_CONV_PREFER,
/* TODO */ 0 );
bd_psr_setting_write(p, PSR_PROFILE_VERSION,
BLURAY_PLAYER_PROFILE_6_v3_1);
bd_psr_unlock(p);
return 0;
}