mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
parent
5594270cd9
commit
8e270adcef
1
NEWS.md
1
NEWS.md
@ -18,6 +18,7 @@
|
||||
timestamps. Implements #1887.
|
||||
* mkvmerge: AAC: implemented support for AAC with 960 samples per
|
||||
frame. Implements #2031.
|
||||
* mkvmerge: WAV reader: added support for Wave64 files. Implements #2042.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "common/dts_parser.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/id_info.h"
|
||||
#include "common/mm_io_x.h"
|
||||
#include "common/strings/formatting.h"
|
||||
#include "input/r_wav.h"
|
||||
#include "input/wav_ac3acm_demuxer.h"
|
||||
@ -40,69 +41,109 @@ wav_demuxer_c::wav_demuxer_c(wav_reader_c *reader,
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
int
|
||||
wav_reader_c::probe_file(mm_io_c *in,
|
||||
uint64_t size) {
|
||||
wave_header wheader;
|
||||
static unsigned char const s_wave64_guid_riff[16] = {
|
||||
'r', 'i', 'f', 'f',
|
||||
0x2e, 0x91, 0xcf, 0x11, 0xa5, 0xd6, 0x28, 0xdb, 0x04, 0xc1, 0x00, 0x00
|
||||
};
|
||||
|
||||
if (sizeof(wave_header) > size)
|
||||
return 0;
|
||||
try {
|
||||
in->setFilePointer(0, seek_beginning);
|
||||
if (in->read(&wheader.riff, sizeof(wheader.riff)) != sizeof(wheader.riff))
|
||||
return 0;
|
||||
in->setFilePointer(0, seek_beginning);
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
static unsigned char const s_wave64_guid_wave[16] = {
|
||||
'w', 'a', 'v', 'e',
|
||||
0xf3, 0xac, 0xd3, 0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f, 0x8e, 0xdb, 0x8a
|
||||
};
|
||||
|
||||
if (strncmp((char *)wheader.riff.id, "RIFF", 4) ||
|
||||
strncmp((char *)wheader.riff.wave_id, "WAVE", 4))
|
||||
return 0;
|
||||
static unsigned char const s_wave64_guid_fmt [16] = {
|
||||
'f', 'm', 't', ' ',
|
||||
0xf3, 0xac, 0xd3, 0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f, 0x8e, 0xdb, 0x8a
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
static unsigned char const s_wave64_guid_fact[16] = {
|
||||
'f', 'a', 'c', 't',
|
||||
0xf3, 0xac, 0xd3, 0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f, 0x8e, 0xdb, 0x8a
|
||||
};
|
||||
|
||||
static unsigned char const s_wave64_guid_data[16] = {
|
||||
'd', 'a', 't', 'a',
|
||||
0xf3, 0xac, 0xd3, 0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f, 0x8e, 0xdb, 0x8a
|
||||
};
|
||||
|
||||
static unsigned char const s_wave64_guid_summarylist[16] = {
|
||||
0xbc, 0x94, 0x5f, 0x92,
|
||||
0x5a, 0x52, 0xd2, 0x11, 0x86, 0xdc, 0x00, 0xc0, 0x4f, 0x8e, 0xdb, 0x8a
|
||||
};
|
||||
|
||||
wav_reader_c::wav_reader_c(const track_info_c &ti,
|
||||
const mm_io_cptr &in)
|
||||
: generic_reader_c(ti, in)
|
||||
, m_bytes_in_data_chunks(0)
|
||||
, m_remaining_bytes_in_current_data_chunk(0)
|
||||
, m_cur_data_chunk_idx(0)
|
||||
: generic_reader_c{ti, in}
|
||||
, m_type{type_e::unknown}
|
||||
, m_bytes_in_data_chunks{}
|
||||
, m_remaining_bytes_in_current_data_chunk{}
|
||||
{
|
||||
}
|
||||
|
||||
wav_reader_c::~wav_reader_c() {
|
||||
}
|
||||
|
||||
wav_reader_c::type_e
|
||||
wav_reader_c::determine_type(mm_io_c &in,
|
||||
uint64_t size) {
|
||||
if (sizeof(wave64_header_t) > size)
|
||||
return type_e::unknown;
|
||||
|
||||
try {
|
||||
wave_header wheader;
|
||||
wave64_header_t w64_header;
|
||||
|
||||
in.setFilePointer(0, seek_beginning);
|
||||
|
||||
if (in.read(&w64_header, sizeof(w64_header)) != sizeof(w64_header))
|
||||
return type_e::unknown;
|
||||
|
||||
in.setFilePointer(0, seek_beginning);
|
||||
|
||||
std::memcpy(&wheader.riff, &w64_header, sizeof(wheader.riff));
|
||||
|
||||
if ( !std::memcmp(&wheader.riff.id, "RIFF", 4)
|
||||
&& !std::memcmp(&wheader.riff.wave_id, "WAVE", 4))
|
||||
return type_e::wave;
|
||||
|
||||
if ( !std::memcmp(w64_header.riff.guid, s_wave64_guid_riff, 16)
|
||||
&& !std::memcmp(w64_header.wave_guid, s_wave64_guid_wave, 16))
|
||||
return type_e::wave64;
|
||||
|
||||
} catch (mtx::mm_io::exception &) {
|
||||
return type_e::unknown;
|
||||
}
|
||||
|
||||
return type_e::unknown;
|
||||
}
|
||||
|
||||
int
|
||||
wav_reader_c::probe_file(mm_io_c *in,
|
||||
uint64_t size) {
|
||||
return determine_type(*in, size) != type_e::unknown;
|
||||
}
|
||||
|
||||
void
|
||||
wav_reader_c::read_headers() {
|
||||
if (!wav_reader_c::probe_file(m_in.get(), m_size))
|
||||
m_type = determine_type(*m_in, m_size);
|
||||
if (m_type == type_e::unknown)
|
||||
throw mtx::input::invalid_format_x();
|
||||
|
||||
parse_file();
|
||||
create_demuxer();
|
||||
}
|
||||
|
||||
wav_reader_c::~wav_reader_c() {
|
||||
}
|
||||
|
||||
void
|
||||
wav_reader_c::parse_file() {
|
||||
int chunk_idx;
|
||||
wav_reader_c::parse_fmt_chunk() {
|
||||
auto chunk_idx = find_chunk("fmt ");
|
||||
|
||||
if (m_in->read(&m_wheader.riff, sizeof(m_wheader.riff)) != sizeof(m_wheader.riff))
|
||||
if (!chunk_idx)
|
||||
throw mtx::input::header_parsing_x();
|
||||
|
||||
scan_chunks();
|
||||
|
||||
if ((chunk_idx = find_chunk("fmt ")) == -1)
|
||||
throw mtx::input::header_parsing_x();
|
||||
|
||||
m_in->setFilePointer(m_chunks[chunk_idx].pos, seek_beginning);
|
||||
|
||||
try {
|
||||
if (m_in->read(&m_wheader.format, sizeof(m_wheader.format)) != sizeof(m_wheader.format))
|
||||
throw false;
|
||||
m_in->setFilePointer(m_chunks[*chunk_idx].pos, seek_beginning);
|
||||
|
||||
if (static_cast<uint64_t>(m_chunks[chunk_idx].len) >= sizeof(alWAVEFORMATEXTENSIBLE)) {
|
||||
if (static_cast<uint64_t>(m_chunks[*chunk_idx].len) >= sizeof(alWAVEFORMATEXTENSIBLE)) {
|
||||
alWAVEFORMATEXTENSIBLE format;
|
||||
if (m_in->read(&format, sizeof(format)) != sizeof(format))
|
||||
throw false;
|
||||
@ -121,37 +162,47 @@ wav_reader_c::parse_file() {
|
||||
} catch (...) {
|
||||
throw mtx::input::header_parsing_x();
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_cur_data_chunk_idx = find_chunk("data", 0, false)) == -1)
|
||||
void
|
||||
wav_reader_c::parse_file() {
|
||||
scan_chunks();
|
||||
|
||||
parse_fmt_chunk();
|
||||
|
||||
m_cur_data_chunk_idx = find_chunk("data", 0, false);
|
||||
if (!m_cur_data_chunk_idx)
|
||||
throw mtx::input::header_parsing_x();
|
||||
|
||||
if (debugging_c::requested("wav_reader|wav_reader_headers"))
|
||||
dump_headers();
|
||||
|
||||
m_in->setFilePointer(m_chunks[m_cur_data_chunk_idx].pos + sizeof(struct chunk_struct), seek_beginning);
|
||||
m_in->setFilePointer(m_chunks[*m_cur_data_chunk_idx].pos, seek_beginning);
|
||||
|
||||
m_remaining_bytes_in_current_data_chunk = m_chunks[m_cur_data_chunk_idx].len;
|
||||
m_remaining_bytes_in_current_data_chunk = m_chunks[*m_cur_data_chunk_idx].len;
|
||||
}
|
||||
|
||||
void
|
||||
wav_reader_c::dump_headers() {
|
||||
mxinfo(boost::format("File '%1%' wave_header dump\n"
|
||||
" riff:\n"
|
||||
" id: %2%%3%%4%%5%\n"
|
||||
" len: %6%\n"
|
||||
" wave_id: %7%%8%%9%%10%\n"
|
||||
" common:\n"
|
||||
" wFormatTag: %|11$04x|\n"
|
||||
" wChannels: %12%\n"
|
||||
" dwSamplesPerSec: %13%\n"
|
||||
" dwAvgBytesPerSec: %14%\n"
|
||||
" wBlockAlign: %15%\n"
|
||||
" wBitsPerSample: %16%\n"
|
||||
" actual format_tag: %17%\n")
|
||||
% m_ti.m_fname
|
||||
% char(m_wheader.riff.id[0]) % char(m_wheader.riff.id[1]) % char(m_wheader.riff.id[2]) % char(m_wheader.riff.id[3])
|
||||
% get_uint32_le(&m_wheader.riff.len)
|
||||
% char(m_wheader.riff.wave_id[0]) % char(m_wheader.riff.wave_id[1]) % char(m_wheader.riff.wave_id[2]) % char(m_wheader.riff.wave_id[3])
|
||||
mxinfo(boost::format("File '%1%' header dump (mode: %2%)\n") % m_ti.m_fname % (m_type == type_e::wave ? "WAV" : "Wave64"));
|
||||
|
||||
if (m_type == type_e::wave)
|
||||
mxinfo(boost::format(" riff:\n"
|
||||
" id: %1%%2%%3%%4%\n"
|
||||
" len: %5%\n"
|
||||
" wave_id: %6%%7%%8%%9%\n")
|
||||
% char(m_wheader.riff.id[0]) % char(m_wheader.riff.id[1]) % char(m_wheader.riff.id[2]) % char(m_wheader.riff.id[3])
|
||||
% get_uint32_le(&m_wheader.riff.len)
|
||||
% char(m_wheader.riff.wave_id[0]) % char(m_wheader.riff.wave_id[1]) % char(m_wheader.riff.wave_id[2]) % char(m_wheader.riff.wave_id[3]));
|
||||
|
||||
mxinfo(boost::format(" common:\n"
|
||||
" wFormatTag: %|1$04x|\n"
|
||||
" wChannels: %2%\n"
|
||||
" dwSamplesPerSec: %3%\n"
|
||||
" dwAvgBytesPerSec: %4%\n"
|
||||
" wBlockAlign: %5%\n"
|
||||
" wBitsPerSample: %6%\n"
|
||||
" actual format_tag: %7%\n")
|
||||
% get_uint16_le(&m_wheader.common.wFormatTag)
|
||||
% get_uint16_le(&m_wheader.common.wChannels)
|
||||
% get_uint32_le(&m_wheader.common.dwSamplesPerSec)
|
||||
@ -217,14 +268,14 @@ wav_reader_c::read(generic_packetizer_c *,
|
||||
m_remaining_bytes_in_current_data_chunk -= num_read;
|
||||
|
||||
if (!m_remaining_bytes_in_current_data_chunk) {
|
||||
m_cur_data_chunk_idx = find_chunk("data", m_cur_data_chunk_idx + 1, false);
|
||||
m_cur_data_chunk_idx = find_chunk("data", *m_cur_data_chunk_idx + 1, false);
|
||||
|
||||
if (-1 == m_cur_data_chunk_idx)
|
||||
if (!m_cur_data_chunk_idx)
|
||||
return flush_packetizers();
|
||||
|
||||
m_in->setFilePointer(m_chunks[m_cur_data_chunk_idx].pos + sizeof(struct chunk_struct), seek_beginning);
|
||||
m_in->setFilePointer(m_chunks[*m_cur_data_chunk_idx].pos, seek_beginning);
|
||||
|
||||
m_remaining_bytes_in_current_data_chunk = m_chunks[m_cur_data_chunk_idx].len;
|
||||
m_remaining_bytes_in_current_data_chunk = m_chunks[*m_cur_data_chunk_idx].len;
|
||||
}
|
||||
|
||||
return FILE_STATUS_MOREDATA;
|
||||
@ -232,37 +283,50 @@ wav_reader_c::read(generic_packetizer_c *,
|
||||
|
||||
void
|
||||
wav_reader_c::scan_chunks() {
|
||||
if (m_type == type_e::wave)
|
||||
scan_chunks_wave();
|
||||
else
|
||||
scan_chunks_wave64();
|
||||
}
|
||||
|
||||
void
|
||||
wav_reader_c::scan_chunks_wave() {
|
||||
m_in->setFilePointer(0);
|
||||
|
||||
if (m_in->read(&m_wheader.riff, sizeof(m_wheader.riff)) != sizeof(m_wheader.riff))
|
||||
throw mtx::input::header_parsing_x();
|
||||
|
||||
wav_chunk_t new_chunk;
|
||||
bool debug_chunks = debugging_c::requested("wav_reader|wav_reader_chunks");
|
||||
|
||||
try {
|
||||
int64_t file_size = m_in->get_size();
|
||||
auto file_size = m_in->get_size();
|
||||
char id[4];
|
||||
|
||||
while (true) {
|
||||
new_chunk.pos = m_in->getFilePointer();
|
||||
new_chunk.pos = m_in->getFilePointer() + 8;
|
||||
|
||||
if (m_in->read(new_chunk.id, 4) != 4)
|
||||
if (m_in->read(id, 4) != 4)
|
||||
return;
|
||||
|
||||
new_chunk.id = memory_c::clone(id, 4);
|
||||
new_chunk.len = m_in->read_uint32_le();
|
||||
|
||||
if (debug_chunks)
|
||||
mxinfo(boost::format("wav_reader_c::scan_chunks() new chunk at %1% type %2% length %3%\n")
|
||||
% new_chunk.pos % get_displayable_string(new_chunk.id, 4) % new_chunk.len);
|
||||
mxdebug_if(debug_chunks, boost::format("wav_reader_c::scan_chunks() new chunk at %1% type %2% length %3%\n") % new_chunk.pos % get_displayable_string(id, 4) % new_chunk.len);
|
||||
|
||||
if (!strncasecmp(new_chunk.id, "data", 4))
|
||||
if (!strncasecmp(id, "data", 4))
|
||||
m_bytes_in_data_chunks += new_chunk.len;
|
||||
|
||||
else if (!m_chunks.empty() && !strncasecmp(m_chunks.back().id, "data", 4) && (file_size > 0x100000000ll)) {
|
||||
else if ( !m_chunks.empty()
|
||||
&& !strncasecmp(reinterpret_cast<char *>(m_chunks.back().id->get_buffer()), "data", 4)
|
||||
&& (file_size > 0x100000000ll)) {
|
||||
wav_chunk_t &previous_chunk = m_chunks.back();
|
||||
int64_t this_chunk_len = file_size - previous_chunk.pos - sizeof(struct chunk_struct);
|
||||
int64_t this_chunk_len = file_size - previous_chunk.pos;
|
||||
m_bytes_in_data_chunks -= previous_chunk.len;
|
||||
m_bytes_in_data_chunks += this_chunk_len;
|
||||
previous_chunk.len = this_chunk_len;
|
||||
|
||||
if (debug_chunks)
|
||||
mxinfo(boost::format("wav_reader_c::scan_chunks() hugh data chunk with wrong length at %1%; re-calculated from file size; new length %2%\n")
|
||||
% previous_chunk.pos % previous_chunk.len);
|
||||
mxdebug_if(debug_chunks, boost::format("wav_reader_c::scan_chunks() hugh data chunk with wrong length at %1%; re-calculated from file size; new length %2%\n") % previous_chunk.pos % previous_chunk.len);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -275,17 +339,46 @@ wav_reader_c::scan_chunks() {
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
wav_reader_c::scan_chunks_wave64() {
|
||||
wav_chunk_t new_chunk;
|
||||
bool debug_chunks = debugging_c::requested("wav_reader|wav_reader_chunks");
|
||||
|
||||
m_in->setFilePointer(sizeof(wave64_header_t));
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
new_chunk.pos = m_in->getFilePointer() + sizeof(wave64_chunk_t);
|
||||
new_chunk.id = m_in->read(16);
|
||||
new_chunk.len = m_in->read_uint64_le();
|
||||
|
||||
if (!new_chunk.id || (new_chunk.len < sizeof(wave64_chunk_t)))
|
||||
return;
|
||||
|
||||
new_chunk.len -= sizeof(wave64_chunk_t);
|
||||
|
||||
mxdebug_if(debug_chunks, boost::format("wav_reader_c::scan_chunks() new chunk at %1% type %2% length %3%\n") % new_chunk.pos % get_displayable_string(reinterpret_cast<char *>(new_chunk.id->get_buffer()), 4) % new_chunk.len);
|
||||
|
||||
if (!strncasecmp(reinterpret_cast<char *>(new_chunk.id->get_buffer()), "data", 4))
|
||||
m_bytes_in_data_chunks += new_chunk.len;
|
||||
|
||||
m_chunks.push_back(new_chunk);
|
||||
m_in->setFilePointer(new_chunk.len, seek_current);
|
||||
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<std::size_t>
|
||||
wav_reader_c::find_chunk(const char *id,
|
||||
int start_idx,
|
||||
bool allow_empty) {
|
||||
size_t idx;
|
||||
|
||||
for (idx = start_idx; idx < m_chunks.size(); ++idx)
|
||||
if (!strncasecmp(m_chunks[idx].id, id, 4) && (allow_empty || m_chunks[idx].len))
|
||||
for (std::size_t idx = start_idx, end = m_chunks.size(); idx < end; ++idx)
|
||||
if (!strncasecmp(reinterpret_cast<char *>(m_chunks[idx].id->get_buffer()), id, 4) && (allow_empty || m_chunks[idx].len))
|
||||
return idx;
|
||||
|
||||
return -1;
|
||||
return {};
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -54,18 +54,35 @@ public:
|
||||
using wav_demuxer_cptr = std::shared_ptr<wav_demuxer_c>;
|
||||
|
||||
struct wav_chunk_t {
|
||||
int64_t pos;
|
||||
char id[4];
|
||||
int64_t len;
|
||||
uint64_t pos, len;
|
||||
memory_cptr id;
|
||||
};
|
||||
|
||||
struct wave64_chunk_t {
|
||||
unsigned char guid[16];
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct wave64_header_t {
|
||||
wave64_chunk_t riff;
|
||||
unsigned char wave_guid[16];
|
||||
};
|
||||
|
||||
class wav_reader_c: public generic_reader_c {
|
||||
public:
|
||||
enum class type_e {
|
||||
unknown,
|
||||
wave,
|
||||
wave64,
|
||||
};
|
||||
|
||||
private:
|
||||
type_e m_type;
|
||||
struct wave_header m_wheader;
|
||||
int64_t m_bytes_in_data_chunks, m_remaining_bytes_in_current_data_chunk;
|
||||
|
||||
std::vector<wav_chunk_t> m_chunks;
|
||||
int m_cur_data_chunk_idx;
|
||||
boost::optional<std::size_t> m_cur_data_chunk_idx;
|
||||
|
||||
wav_demuxer_cptr m_demuxer;
|
||||
|
||||
@ -90,12 +107,19 @@ public:
|
||||
static int probe_file(mm_io_c *in, uint64_t size);
|
||||
|
||||
protected:
|
||||
static type_e determine_type(mm_io_c &in, uint64_t size);
|
||||
|
||||
boost::optional<std::size_t> find_chunk(const char *id, int start_idx = 0, bool allow_empty = true);
|
||||
|
||||
void scan_chunks();
|
||||
int find_chunk(const char *id, int start_idx = 0, bool allow_empty = true);
|
||||
void scan_chunks_wave();
|
||||
void scan_chunks_wave64();
|
||||
|
||||
void dump_headers();
|
||||
|
||||
void parse_file();
|
||||
void parse_fmt_chunk();
|
||||
|
||||
void create_demuxer();
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@ bool
|
||||
wav_dts_demuxer_c::probe(mm_io_cptr &io) {
|
||||
io->save_pos();
|
||||
auto read_buf = memory_c::alloc(DTS_READ_SIZE);
|
||||
read_buf->set_size(io->read(read_buf, DTS_READ_SIZE));
|
||||
read_buf->set_size(io->read(read_buf->get_buffer(), DTS_READ_SIZE));
|
||||
io->restore_pos();
|
||||
|
||||
if (!m_parser.detect(*read_buf, 5))
|
||||
|
@ -452,3 +452,4 @@ T_603mpeg_ps_ac3_not_enough_data_in_first_packet:1+189+128+AC_3/E_AC_3-2+189+129
|
||||
T_604append_only_one_video_track_with_codec_private:994b07e01a07bdfa5d571ac0edac9ff0:passed:20170624-105837:0.144927484
|
||||
T_605h264_changing_sps_pps_wrong_frame_order_and_timestamps:1af409bd8266567525c54a0799de44cc:passed:20170704-211727:0.912982996
|
||||
T_606aac_960_samples_per_frame:69b0ad71348f27421ec3a2fbba9ed33d-a4bcfeaa69074c2ecf5d672c5a21284a:passed:20170720-215449:0.065007779
|
||||
T_607wave64:567b45caf96e2914012453a72227e4df-a2b74f962f91921d05bf1b6d65d4350e:passed:20170721-221321:0.021307011
|
||||
|
8
tests/test-607wave64.rb
Executable file
8
tests/test-607wave64.rb
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/ruby -w
|
||||
|
||||
files = %w{w64-pcm16.w64 w64-pcm32.w64}
|
||||
|
||||
# T_607wave64
|
||||
describe "mkvmerge / Wave64"
|
||||
|
||||
files.each { |file| test_merge "data/wav/#{file}" }
|
Loading…
Reference in New Issue
Block a user