You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
386 lines
15 KiB
C++
386 lines
15 KiB
C++
/*
|
|
* DVD-Audio Decoder plugin
|
|
* Copyright (c) 2009-2025 Maxim V.Anisiutkin <maxim.anisiutkin@gmail.com>
|
|
*
|
|
* DVD-Audio Decoder 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.
|
|
*
|
|
* DVD-Audio Decoder 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 FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "b2n.h"
|
|
#include "dvda_zone.h"
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <string>
|
|
|
|
auto PTS_TO_SEC = [](auto pts) {
|
|
return pts / 90000.0;
|
|
};
|
|
|
|
dvda_sector_pointer_t::dvda_sector_pointer_t(dvda_track_t& track, ats_track_sector_t& p_ats_track_sector, int sp_index) : dvda_track(track) {
|
|
index = sp_index;
|
|
first = p_ats_track_sector.first;
|
|
last = p_ats_track_sector.last;
|
|
}
|
|
|
|
double dvda_sector_pointer_t::get_time() {
|
|
return PTS_TO_SEC(get_length_pts());
|
|
}
|
|
|
|
uint32_t dvda_sector_pointer_t::get_length_pts() {
|
|
auto denom = dvda_track.get_last() - dvda_track.get_first() + 1u;
|
|
if (denom) {
|
|
auto pts = (double)dvda_track.get_length_pts() * (double)(last - first + 1u) / (double)denom;
|
|
return (uint32_t)pts;
|
|
}
|
|
return 0u;
|
|
}
|
|
|
|
dvda_track_t::dvda_track_t(ats_track_timestamp_t& ats_track_timestamp, int track_no) {
|
|
track = track_no;
|
|
index = ats_track_timestamp.n;
|
|
first_pts = ats_track_timestamp.first_pts;
|
|
length_pts = ats_track_timestamp.len_in_pts;
|
|
downmix_matrix = ats_track_timestamp.downmix_matrix < DOWNMIX_MATRICES ? ats_track_timestamp.downmix_matrix : -1;
|
|
}
|
|
|
|
double dvda_track_t::get_time() const {
|
|
return PTS_TO_SEC(length_pts);
|
|
}
|
|
|
|
uint32_t dvda_track_t::get_first() {
|
|
auto sector = (get_sector_pointers().size() > 0) ? get_sector_pointer(0).get_first() : 0u;
|
|
for (auto i = 1u; i < get_sector_pointers().size(); i++) {
|
|
sector = std::min(sector, get_sector_pointer(i).get_first());
|
|
}
|
|
return sector;
|
|
};
|
|
|
|
uint32_t dvda_track_t::get_last() {
|
|
auto sector = (get_sector_pointers().size() > 0) ? get_sector_pointer(0).get_last() : 0u;
|
|
for (auto i = 1u; i < get_sector_pointers().size(); i++) {
|
|
sector = std::max(sector, get_sector_pointer(i).get_last());
|
|
}
|
|
return sector;
|
|
};
|
|
|
|
dvda_title_t::dvda_title_t(ats_title_t* p_ats_title, ats_title_idx_t* p_ats_title_idx) {
|
|
title = p_ats_title_idx->title_nr;
|
|
indexes = p_ats_title->indexes;
|
|
tracks = p_ats_title->tracks;
|
|
length_pts = p_ats_title->len_in_pts;
|
|
}
|
|
|
|
double dvda_title_t::get_time() const {
|
|
return PTS_TO_SEC(length_pts);
|
|
}
|
|
|
|
dvda_downmix_channel_t* dvda_downmix_matrix_t::get_downmix_channel(int channel, int dmx_channel) {
|
|
if (channel >= 0 && channel < DOWNMIX_CHANNELS && dmx_channel >= 0 && dmx_channel < 2) {
|
|
return &LR_dmx[channel][dmx_channel];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
double dvda_downmix_matrix_t::get_downmix_coef(int channel, int dmx_channel) {
|
|
auto dmx_coef{ 0.0 };
|
|
dvda_downmix_channel_t* p_dmx_channel = get_downmix_channel(channel, dmx_channel);
|
|
if (p_dmx_channel) {
|
|
auto coef = p_dmx_channel->coef;
|
|
if (coef < 200) {
|
|
auto L_db = -0.2007 * coef;
|
|
dmx_coef = std::pow(10.0, L_db / 20.0);
|
|
if (p_dmx_channel->inv_phase) {
|
|
dmx_coef = -dmx_coef;
|
|
}
|
|
}
|
|
else if (coef < 255) {
|
|
auto L_db = -(2.0 * 0.2007 * (coef - 200) + 0.2007 * 200);
|
|
dmx_coef = std::pow(10.0, L_db / 20.0);
|
|
if (p_dmx_channel->inv_phase) {
|
|
dmx_coef = -dmx_coef;
|
|
}
|
|
}
|
|
}
|
|
return dmx_coef;
|
|
}
|
|
|
|
bool dvda_titleset_t::open(size_t titleset) {
|
|
dvda_titleset = titleset;
|
|
dvda_titleset_type = dvda_titleset_e::DVDTitlesetUnknown;
|
|
char file_name[13];
|
|
snprintf(file_name, sizeof(file_name), "ATS_%02d_0.IFO", (int)dvda_titleset);
|
|
auto atsi_file = dvda_zone.get_filesystem().open(file_name);
|
|
if (!atsi_file) {
|
|
snprintf(file_name, sizeof(file_name), "ATS_%02d_0.BUP", (int)dvda_titleset);
|
|
atsi_file = dvda_zone.get_filesystem().open(file_name);
|
|
if (!atsi_file) {
|
|
return is_open;
|
|
}
|
|
}
|
|
auto atsi_size = atsi_file->get_size();
|
|
if (atsi_size >= 0x0800) {
|
|
atsi_mat_t atsi_mat;
|
|
if (atsi_file->read((char*)&atsi_mat, sizeof(atsi_mat_t)) == sizeof(atsi_mat_t)) {
|
|
if (memcmp("DVDAUDIO-ATS", atsi_mat.ats_identifier, 12) == 0) {
|
|
uint32_t aob_offset{ 0 };
|
|
for (auto i = 0; i < 9; i++) {
|
|
snprintf(aobs[i].file_name, sizeof(aobs[i].file_name), "ATS_%02d_%01d.AOB", (int)dvda_titleset, i + 1);
|
|
aobs[i].dvda_fileobject = dvda_zone.get_filesystem().open(aobs[i].file_name);
|
|
if (aobs[i].dvda_fileobject) {
|
|
auto aob_size = aobs[i].dvda_fileobject->get_size();
|
|
aobs[i].block_first = aob_offset;
|
|
aobs[i].block_last = (uint32_t)(aobs[i].block_first + aob_size / DVD_BLOCK_SIZE + (aob_size % DVD_BLOCK_SIZE > 0 ? 1 : 0) - 1);
|
|
}
|
|
else {
|
|
aobs[i].block_first = aob_offset;
|
|
aobs[i].block_last = aobs[i].block_first + (1024 * 1024 - 32) * 1024 / DVD_BLOCK_SIZE - 1;
|
|
}
|
|
aob_offset = aobs[i].block_last + 1;
|
|
}
|
|
B2N_32(atsi_mat.ats_last_sector);
|
|
B2N_32(atsi_mat.atsi_last_sector);
|
|
B2N_32(atsi_mat.ats_category);
|
|
B2N_32(atsi_mat.atsi_last_byte);
|
|
B2N_32(atsi_mat.atsm_vobs);
|
|
B2N_32(atsi_mat.atstt_vobs);
|
|
B2N_32(atsi_mat.ats_ptt_srpt);
|
|
B2N_32(atsi_mat.ats_pgcit);
|
|
B2N_32(atsi_mat.atsm_pgci_ut);
|
|
B2N_32(atsi_mat.ats_tmapt);
|
|
B2N_32(atsi_mat.atsm_c_adt);
|
|
B2N_32(atsi_mat.atsm_vobu_admap);
|
|
B2N_32(atsi_mat.ats_c_adt);
|
|
B2N_32(atsi_mat.ats_vobu_admap);
|
|
for (auto i = 0; i < 8; i++) {
|
|
B2N_16(atsi_mat.ats_audio_format[i].audio_type);
|
|
}
|
|
for (auto m = 0; m < DOWNMIX_MATRICES; m++) {
|
|
for (auto ch = 0; ch < DOWNMIX_CHANNELS; ch++) {
|
|
downmix_matrices[m].get_downmix_channel(ch, 0)->inv_phase = ((atsi_mat.ats_downmix_matrices[m].phase.L >> (DOWNMIX_CHANNELS - ch - 1)) & 1) == 1;
|
|
downmix_matrices[m].get_downmix_channel(ch, 0)->coef = atsi_mat.ats_downmix_matrices[m].coef[ch].L;
|
|
downmix_matrices[m].get_downmix_channel(ch, 1)->inv_phase = ((atsi_mat.ats_downmix_matrices[m].phase.R >> (DOWNMIX_CHANNELS - ch - 1)) & 1) == 1;
|
|
downmix_matrices[m].get_downmix_channel(ch, 1)->coef = atsi_mat.ats_downmix_matrices[m].coef[ch].R;
|
|
}
|
|
}
|
|
if (atsi_mat.atsm_vobs == 0) {
|
|
dvda_titleset_type = dvda_titleset_e::DVDTitlesetAudio;
|
|
}
|
|
else {
|
|
dvda_titleset_type = dvda_titleset_e::DVDTitlesetVideo;
|
|
}
|
|
aobs_last_sector = atsi_mat.ats_last_sector - 2 * (atsi_mat.atsi_last_sector + 1);
|
|
uint32_t ats_len = (uint32_t)atsi_size - 0x0800;
|
|
atsi_file->seek(0x0800);
|
|
std::vector<uint8_t> ats_buf(ats_len, 0);
|
|
uint8_t* ats_end = ats_buf.data() + ats_len;
|
|
atsi_file->read((char*)ats_buf.data(), ats_len);
|
|
audio_pgcit_t* p_audio_pgcit = (audio_pgcit_t*)ats_buf.data();
|
|
ats_title_idx_t* p_ats_title_idx = nullptr;
|
|
if ((uint8_t*)p_audio_pgcit + AUDIO_PGCIT_SIZE > ats_end) {
|
|
goto error_exit;
|
|
}
|
|
B2N_16(p_audio_pgcit->nr_of_titles);
|
|
B2N_32(p_audio_pgcit->last_byte);
|
|
ats_end = ats_buf.data() + ((ats_len < p_audio_pgcit->last_byte + 1) ? ats_len : p_audio_pgcit->last_byte + 1);
|
|
p_ats_title_idx = (ats_title_idx_t*)((uint8_t*)p_audio_pgcit + AUDIO_PGCIT_SIZE);
|
|
for (auto i = 0u; i < p_audio_pgcit->nr_of_titles; i++) {
|
|
if ((uint8_t*)&p_ats_title_idx[i] + ATS_TITLE_IDX_SIZE > ats_end) {
|
|
goto error_exit;
|
|
}
|
|
B2N_32(p_ats_title_idx[i].title_table_offset);
|
|
ats_title_t* p_ats_title = (ats_title_t*)((uint8_t*)p_audio_pgcit + p_ats_title_idx[i].title_table_offset);
|
|
if ((uint8_t*)p_ats_title + ATS_TITLE_SIZE > ats_end) {
|
|
goto error_exit;
|
|
}
|
|
B2N_32(p_ats_title->len_in_pts);
|
|
B2N_16(p_ats_title->track_sector_table_offset);
|
|
auto p_ats_track_timestamp = (ats_track_timestamp_t*)((uint8_t*)p_ats_title + ATS_TITLE_SIZE);
|
|
auto p_ats_track_sector = (ats_track_sector_t*)((uint8_t*)p_ats_title + p_ats_title->track_sector_table_offset);
|
|
auto&& dvda_title = get_titles().emplace_back(p_ats_title, &p_ats_title_idx[i]);
|
|
for (auto j = 0; j < p_ats_title->tracks; j++) {
|
|
if ((uint8_t*)&p_ats_track_timestamp[j] + ATS_TRACK_TIMESTAMP_SIZE > ats_end) {
|
|
goto error_exit;
|
|
}
|
|
B2N_32(p_ats_track_timestamp[j].first_pts);
|
|
B2N_32(p_ats_track_timestamp[j].len_in_pts);
|
|
dvda_title.get_tracks().emplace_back(p_ats_track_timestamp[j], j + 1);
|
|
}
|
|
for (auto j = 0; j < p_ats_title->indexes; j++) {
|
|
if ((uint8_t*)&p_ats_track_sector[j] + ATS_TRACK_SECTOR_SIZE > ats_end) {
|
|
goto error_exit;
|
|
}
|
|
B2N_32(p_ats_track_sector[j].first);
|
|
B2N_32(p_ats_track_sector[j].last);
|
|
for (auto k = 0u; k < dvda_title.get_tracks().size(); k++) {
|
|
int track_curr_idx, track_next_idx;
|
|
auto&& dvda_track = dvda_title.get_track(k);
|
|
track_curr_idx = dvda_track.get_index();
|
|
track_next_idx = (k < dvda_title.get_tracks().size() - 1) ? dvda_title.get_track(k + 1).get_index() : 0;
|
|
if (j + 1 >= track_curr_idx && (j + 1 < track_next_idx || track_next_idx == 0)) {
|
|
dvda_track.get_sector_pointers().emplace_back(dvda_track, p_ats_track_sector[j], j + 1);
|
|
}
|
|
}
|
|
}
|
|
for (auto j = 0u; j < dvda_title.get_tracks().size(); j++) {
|
|
auto dvda_track = dvda_title.get_track(j);
|
|
}
|
|
}
|
|
is_open = true;
|
|
error_exit:
|
|
ats_buf.clear();
|
|
}
|
|
}
|
|
}
|
|
return is_open;
|
|
}
|
|
|
|
dvda_titleset_t::dvda_titleset_t(dvda_zone_t& zone) : dvda_zone(zone) {
|
|
is_open = false;
|
|
}
|
|
|
|
dvda_titleset_t::~dvda_titleset_t() {
|
|
close_aobs();
|
|
}
|
|
|
|
DVDAERROR dvda_titleset_t::get_block(uint32_t block, uint8_t* buf_ptr) {
|
|
for (auto i = 0; i < 9; i++) {
|
|
if (aobs[i].dvda_fileobject && block >= aobs[i].block_first && block <= aobs[i].block_last) {
|
|
if (!aobs[i].dvda_fileobject->seek((block - aobs[i].block_first) * DVD_BLOCK_SIZE)) {
|
|
return DVDAERR_CANNOT_SEEK_ATS_XX_X_AOB;
|
|
}
|
|
if (aobs[i].dvda_fileobject->read((char*)buf_ptr, DVD_BLOCK_SIZE) != DVD_BLOCK_SIZE) {
|
|
return DVDAERR_CANNOT_READ_ATS_XX_X_AOB;
|
|
}
|
|
return DVDAERR_OK;
|
|
}
|
|
}
|
|
return DVDAERR_AOB_BLOCK_NOT_FOUND;
|
|
}
|
|
|
|
size_t dvda_titleset_t::get_blocks(uint32_t block_first, uint32_t block_last, uint8_t* buf_ptr) {
|
|
auto blocks_read{ 0 };
|
|
auto aob_index{ -1 };
|
|
for (auto i = 0; i < 9; i++) {
|
|
if (block_first >= aobs[i].block_first && block_first <= aobs[i].block_last) {
|
|
aob_index = i;
|
|
break;
|
|
}
|
|
}
|
|
if (aob_index >= 0) {
|
|
if (aobs[aob_index].dvda_fileobject) {
|
|
if (aobs[aob_index].dvda_fileobject->seek((block_first - aobs[aob_index].block_first) * DVD_BLOCK_SIZE)) {
|
|
if (block_last <= aobs[aob_index].block_last) {
|
|
int bytes_to_read = (block_last + 1 - block_first) * DVD_BLOCK_SIZE;
|
|
int bytes_read = (int)aobs[aob_index].dvda_fileobject->read((char*)buf_ptr, bytes_to_read);
|
|
blocks_read += bytes_read / DVD_BLOCK_SIZE;
|
|
}
|
|
else {
|
|
int bytes_to_read = (aobs[aob_index].block_last + 1 - block_first) * DVD_BLOCK_SIZE;
|
|
int bytes_read = (int)aobs[aob_index].dvda_fileobject->read((char*)buf_ptr, bytes_to_read);
|
|
blocks_read += bytes_read / DVD_BLOCK_SIZE;
|
|
if (aob_index + 1 < 9) {
|
|
if (aobs[aob_index + 1].dvda_fileobject) {
|
|
if (aobs[aob_index + 1].dvda_fileobject->seek(0)) {
|
|
bytes_to_read = (block_last + 1 - aobs[aob_index + 1].block_first) * DVD_BLOCK_SIZE;
|
|
bytes_read = (int)aobs[aob_index + 1].dvda_fileobject->read((char*)buf_ptr + blocks_read * DVD_BLOCK_SIZE, bytes_to_read);
|
|
blocks_read += bytes_read / DVD_BLOCK_SIZE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return blocks_read;
|
|
}
|
|
|
|
void dvda_titleset_t::close_aobs() {
|
|
for (auto i = 0; i < 9; i++) {
|
|
aobs[i].dvda_fileobject.reset();
|
|
}
|
|
}
|
|
|
|
bool dvda_zone_t::open() {
|
|
close();
|
|
auto is_open{ false };
|
|
audio_titlesets = 99;
|
|
video_titlesets = 99;
|
|
auto amgi_file = dvda_filesystem.open("AUDIO_TS.IFO");
|
|
if (!amgi_file) {
|
|
amgi_file = dvda_filesystem.open("AUDIO_TS.BUP");
|
|
}
|
|
if (amgi_file) {
|
|
amgi_mat_t amgi_mat;
|
|
if (amgi_file->read((char*)&amgi_mat, sizeof(amgi_mat_t)) == sizeof(amgi_mat_t)) {
|
|
if (memcmp("DVDAUDIO-AMG", amgi_mat.amg_identifier, 12) == 0) {
|
|
B2N_32(amgi_mat.amg_last_sector);
|
|
B2N_32(amgi_mat.amgi_last_sector);
|
|
B2N_32(amgi_mat.amg_category);
|
|
B2N_16(amgi_mat.amg_nr_of_volumes);
|
|
B2N_16(amgi_mat.amg_this_volume_nr);
|
|
B2N_32(amgi_mat.amg_asvs);
|
|
B2N_64(amgi_mat.amg_pos_code);
|
|
B2N_32(amgi_mat.amgi_last_byte);
|
|
B2N_32(amgi_mat.first_play_pgc);
|
|
B2N_32(amgi_mat.amgm_vobs);
|
|
B2N_32(amgi_mat.att_srpt);
|
|
B2N_32(amgi_mat.aott_srpt);
|
|
B2N_32(amgi_mat.amgm_pgci_ut);
|
|
B2N_32(amgi_mat.ats_atrt);
|
|
B2N_32(amgi_mat.txtdt_mgi);
|
|
B2N_32(amgi_mat.amgm_c_adt);
|
|
B2N_32(amgi_mat.amgm_vobu_admap);
|
|
B2N_16(amgi_mat.amgm_audio_attr.lang_code);
|
|
B2N_16(amgi_mat.amgm_subp_attr.lang_code);
|
|
audio_titlesets = (audio_titlesets < amgi_mat.amg_nr_of_audio_title_sets) ? audio_titlesets : amgi_mat.amg_nr_of_audio_title_sets;
|
|
video_titlesets = (video_titlesets < amgi_mat.amg_nr_of_video_title_sets) ? video_titlesets : amgi_mat.amg_nr_of_video_title_sets;
|
|
for (auto i = 0u; i < audio_titlesets; i++) {
|
|
auto& dvda_titleset = get_titlesets().emplace_back(*this);
|
|
if (!dvda_titleset.open(i + 1)) {
|
|
get_titlesets().pop_back();
|
|
}
|
|
}
|
|
for (auto titleset_index = 0u; titleset_index < get_titlesets().size(); titleset_index++) {
|
|
auto dvda_titleset = get_titleset(titleset_index);
|
|
for (auto title_index = 0u; title_index < dvda_titleset.get_titles().size(); title_index++) {
|
|
auto dvda_title = dvda_titleset.get_title(title_index);
|
|
for (auto track_index = 0u; track_index < dvda_title.get_tracks().size(); track_index++) {
|
|
auto dvda_track = dvda_title.get_track(track_index);
|
|
for (auto sector_pointer_index = 0u; sector_pointer_index < dvda_track.get_sector_pointers().size(); sector_pointer_index++) {
|
|
[[maybe_unused]] auto dvda_sector_pointer = dvda_track.get_sector_pointer(sector_pointer_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
is_open = true;
|
|
}
|
|
}
|
|
}
|
|
return is_open;
|
|
}
|
|
|
|
void dvda_zone_t::close() {
|
|
dvda_titlesets.clear();
|
|
}
|
|
|
|
DVDAERROR dvda_zone_t::get_block(size_t titleset, uint32_t block_no, uint8_t* buf_ptr) {
|
|
DVDAERROR err = get_titleset(titleset).get_block(block_no, buf_ptr);
|
|
return err;
|
|
}
|
|
|
|
size_t dvda_zone_t::get_blocks(size_t titleset, uint32_t block_no, size_t blocks, uint8_t* buf_ptr) {
|
|
blocks = get_titleset(titleset).get_blocks(block_no, (int)(block_no + blocks - 1), buf_ptr);
|
|
return blocks;
|
|
}
|