/* * This file is part of libbluray * Copyright (C) 2010-2017 Petri Hintukainen * * 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 * . */ #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 #include /* * 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; }