mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-25 04:11:44 +00:00
MP4 reader: re-derive MP3 parameters if track headers are invalid
This commit is contained in:
parent
3f83e0c533
commit
5c45673d37
@ -1,3 +1,10 @@
|
||||
2015-02-23 Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
* mkvmerge: bug fix: If the MP4 track headers for MP3 tracks
|
||||
contain invalid values (number of channels is 0 or the sampling
|
||||
rate is 0) then mkvmerge will re-derive these parameters from the
|
||||
MP3 bitstream instead of ignoring that track.
|
||||
|
||||
2015-02-18 Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
* mkvmerge: bug fix: Matroska reader: track-specific tags weren't
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "common/endian.h"
|
||||
#include "common/hacks.h"
|
||||
#include "common/iso639.h"
|
||||
#include "common/mp3.h"
|
||||
#include "common/strings/formatting.h"
|
||||
#include "common/strings/parsing.h"
|
||||
#include "input/r_qtmp4.h"
|
||||
@ -131,6 +132,7 @@ qtmp4_reader_c::qtmp4_reader_c(const track_info_c &ti,
|
||||
, m_fragment_implicit_offset{}
|
||||
, m_fragment{}
|
||||
, m_track_for_fragment{}
|
||||
, m_timecodes_calculated{}
|
||||
, m_debug_chapters{ "qtmp4|qtmp4_full|qtmp4_chapters"}
|
||||
, m_debug_headers{ "qtmp4|qtmp4_full|qtmp4_headers"}
|
||||
, m_debug_tables{ "qtmp4_full|qtmp4_tables"}
|
||||
@ -305,12 +307,15 @@ qtmp4_reader_c::verify_track_parameters_and_update_indexes() {
|
||||
else if (dmx->is_unknown())
|
||||
continue;
|
||||
|
||||
dmx->ok = dmx->update_tables(m_time_scale);
|
||||
dmx->ok = dmx->update_tables();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
qtmp4_reader_c::calculate_timecodes() {
|
||||
if (m_timecodes_calculated)
|
||||
return;
|
||||
|
||||
int64_t min_timecode = 0;
|
||||
|
||||
for (auto &dmx : m_demuxers) {
|
||||
@ -325,6 +330,8 @@ qtmp4_reader_c::calculate_timecodes() {
|
||||
|
||||
for (auto &dmx : m_demuxers)
|
||||
dmx->build_index();
|
||||
|
||||
m_timecodes_calculated = true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -582,7 +589,7 @@ qtmp4_reader_c::handle_moov_atom(qt_atom_t parent,
|
||||
handle_mvex_atom(atom.to_parent(), level + 1);
|
||||
|
||||
else if (atom.fourcc == "trak") {
|
||||
qtmp4_demuxer_cptr new_dmx(new qtmp4_demuxer_c);
|
||||
auto new_dmx = std::make_shared<qtmp4_demuxer_c>(*this);
|
||||
new_dmx->id = m_demuxers.size();
|
||||
|
||||
handle_trak_atom(new_dmx, atom.to_parent(), level + 1);
|
||||
@ -1435,9 +1442,9 @@ qtmp4_reader_c::create_bitmap_info_header(qtmp4_demuxer_cptr &dmx,
|
||||
|
||||
bool
|
||||
qtmp4_reader_c::create_audio_packetizer_ac3(qtmp4_demuxer_cptr &dmx) {
|
||||
memory_cptr buf = memory_c::alloc(64);
|
||||
auto buf = dmx->read_first_bytes(64);
|
||||
|
||||
if (!dmx->read_first_bytes(buf, 64, m_in) || (-1 == dmx->m_ac3_header.find_in(buf))) {
|
||||
if (!buf || (-1 == dmx->m_ac3_header.find_in(buf))) {
|
||||
mxwarn_tid(m_ti.m_fname, dmx->id, Y("No AC3 header found in first frame; track will be skipped.\n"));
|
||||
dmx->ok = false;
|
||||
|
||||
@ -1462,9 +1469,9 @@ qtmp4_reader_c::create_audio_packetizer_alac(qtmp4_demuxer_cptr &dmx) {
|
||||
bool
|
||||
qtmp4_reader_c::create_audio_packetizer_dts(qtmp4_demuxer_cptr &dmx) {
|
||||
auto const bytes_to_read = 8192u;
|
||||
auto buf = memory_c::alloc(bytes_to_read);
|
||||
auto buf = dmx->read_first_bytes(bytes_to_read);
|
||||
|
||||
if (!dmx->read_first_bytes(buf, bytes_to_read, m_in) || (-1 == find_dts_header(buf->get_buffer(), bytes_to_read, &dmx->m_dts_header, false))) {
|
||||
if (!buf || (-1 == find_dts_header(buf->get_buffer(), bytes_to_read, &dmx->m_dts_header, false))) {
|
||||
mxwarn_tid(m_ti.m_fname, dmx->id, Y("No DTS header found in first frames; track will be skipped.\n"));
|
||||
dmx->ok = false;
|
||||
|
||||
@ -1995,7 +2002,10 @@ qtmp4_demuxer_c::min_timecode()
|
||||
}
|
||||
|
||||
bool
|
||||
qtmp4_demuxer_c::update_tables(int64_t global_m_time_scale) {
|
||||
qtmp4_demuxer_c::update_tables() {
|
||||
if (m_tables_updated)
|
||||
return true;
|
||||
|
||||
uint64_t last = chunk_table.size();
|
||||
|
||||
if (!last)
|
||||
@ -2043,6 +2053,8 @@ qtmp4_demuxer_c::update_tables(int64_t global_m_time_scale) {
|
||||
else
|
||||
mxerror(Y("Quicktime/MP4 reader: Constant samplesize & variable duration not yet supported. Contact the author if you have such a sample file.\n"));
|
||||
|
||||
m_tables_updated = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2086,16 +2098,19 @@ qtmp4_demuxer_c::update_tables(int64_t global_m_time_scale) {
|
||||
mxdebug(boost::format(" %1%: pts %2% size %3% pos %4%\n") % i++ % sample.pts % sample.size % sample.pos);
|
||||
}
|
||||
|
||||
update_editlist_table(global_m_time_scale);
|
||||
update_editlist_table();
|
||||
|
||||
m_tables_updated = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
qtmp4_demuxer_c::update_editlist_table(int64_t global_time_scale) {
|
||||
qtmp4_demuxer_c::update_editlist_table() {
|
||||
if (editlist_table.empty())
|
||||
return;
|
||||
|
||||
auto global_time_scale = m_reader.m_time_scale;
|
||||
auto simple_editlist_type = 0u;
|
||||
int64_t raw_offset = 0;
|
||||
auto offset_in_global_time_scale = false;
|
||||
@ -2240,10 +2255,14 @@ qtmp4_demuxer_c::build_index_chunk_mode() {
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
qtmp4_demuxer_c::read_first_bytes(memory_cptr &buf,
|
||||
int num_bytes,
|
||||
mm_io_cptr in) {
|
||||
memory_cptr
|
||||
qtmp4_demuxer_c::read_first_bytes(int num_bytes) {
|
||||
if (!update_tables())
|
||||
return memory_cptr{};
|
||||
|
||||
m_reader.calculate_timecodes();
|
||||
|
||||
auto buf = memory_c::alloc(num_bytes);
|
||||
size_t buf_pos = 0;
|
||||
size_t idx_pos = 0;
|
||||
|
||||
@ -2251,16 +2270,16 @@ qtmp4_demuxer_c::read_first_bytes(memory_cptr &buf,
|
||||
qt_index_t &index = m_index[idx_pos];
|
||||
uint64_t num_bytes_to_read = std::min((int64_t)num_bytes, index.size);
|
||||
|
||||
in->setFilePointer(index.file_pos);
|
||||
if (in->read(buf->get_buffer() + buf_pos, num_bytes_to_read) < num_bytes_to_read)
|
||||
return false;
|
||||
m_reader.m_in->setFilePointer(index.file_pos);
|
||||
if (m_reader.m_in->read(buf->get_buffer() + buf_pos, num_bytes_to_read) < num_bytes_to_read)
|
||||
return memory_cptr{};
|
||||
|
||||
num_bytes -= num_bytes_to_read;
|
||||
buf_pos += num_bytes_to_read;
|
||||
++idx_pos;
|
||||
}
|
||||
|
||||
return 0 == num_bytes;
|
||||
return 0 == num_bytes ? buf : memory_cptr{};
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2687,8 +2706,28 @@ qtmp4_demuxer_c::parse_esds_atom(mm_mem_io_c &memio,
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
qtmp4_demuxer_c::derive_track_params_from_mp3_audio_bitstream() {
|
||||
auto buf = read_first_bytes(64);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
mp3_header_t header;
|
||||
auto offset = find_mp3_header(buf->get_buffer(), buf->get_size());
|
||||
if ((-1 == offset) || !decode_mp3_header(&buf->get_buffer()[offset], &header))
|
||||
return;
|
||||
|
||||
a_channels = header.channels;
|
||||
a_samplerate = header.sampling_frequency;
|
||||
}
|
||||
|
||||
bool
|
||||
qtmp4_demuxer_c::verify_audio_parameters() {
|
||||
if ((0 == a_channels) || (0.0 == a_samplerate)) {
|
||||
if (codec.is(codec_c::A_MP3))
|
||||
derive_track_params_from_mp3_audio_bitstream();
|
||||
}
|
||||
|
||||
if ((0 == a_channels) || (0.0 == a_samplerate)) {
|
||||
mxwarn(boost::format(Y("Quicktime/MP4 reader: Track %1% is missing some data. Broken header atoms?\n")) % id);
|
||||
return false;
|
||||
|
@ -207,8 +207,12 @@ struct qt_fragment_t {
|
||||
{}
|
||||
};
|
||||
|
||||
class qtmp4_reader_c;
|
||||
|
||||
struct qtmp4_demuxer_c {
|
||||
bool ok;
|
||||
qtmp4_reader_c &m_reader;
|
||||
|
||||
bool ok, m_tables_updated;
|
||||
|
||||
char type;
|
||||
uint32_t id, container_id;
|
||||
@ -261,8 +265,10 @@ struct qtmp4_demuxer_c {
|
||||
|
||||
debugging_option_c m_debug_tables, m_debug_fps, m_debug_headers, m_debug_editlists;
|
||||
|
||||
qtmp4_demuxer_c()
|
||||
: ok{false}
|
||||
qtmp4_demuxer_c(qtmp4_reader_c &reader)
|
||||
: m_reader{reader}
|
||||
, ok{}
|
||||
, m_tables_updated{}
|
||||
, type{'?'}
|
||||
, id{0}
|
||||
, container_id{0}
|
||||
@ -305,12 +311,12 @@ struct qtmp4_demuxer_c {
|
||||
void calculate_timecodes();
|
||||
void adjust_timecodes(int64_t delta);
|
||||
|
||||
bool update_tables(int64_t global_time_scale);
|
||||
void update_editlist_table(int64_t global_time_scale);
|
||||
bool update_tables();
|
||||
void update_editlist_table();
|
||||
|
||||
void build_index();
|
||||
|
||||
bool read_first_bytes(memory_cptr &buf, int num_bytes, mm_io_cptr in);
|
||||
memory_cptr read_first_bytes(int num_bytes);
|
||||
|
||||
bool is_audio() const;
|
||||
bool is_video() const;
|
||||
@ -339,6 +345,8 @@ struct qtmp4_demuxer_c {
|
||||
bool verify_subtitles_parameters();
|
||||
bool verify_vobsub_subtitles_parameters();
|
||||
|
||||
void derive_track_params_from_mp3_audio_bitstream();
|
||||
|
||||
int64_t min_timecode() const;
|
||||
|
||||
void determine_codec();
|
||||
@ -413,7 +421,7 @@ private:
|
||||
std::unordered_map<unsigned int, bool> m_chapter_track_ids;
|
||||
std::unordered_map<unsigned int, qt_track_defaults_t> m_track_defaults;
|
||||
|
||||
uint32_t m_time_scale;
|
||||
int64_t m_time_scale;
|
||||
fourcc_c m_compression_algorithm;
|
||||
int m_main_dmx;
|
||||
|
||||
@ -423,8 +431,12 @@ private:
|
||||
qt_fragment_t *m_fragment;
|
||||
qtmp4_demuxer_c *m_track_for_fragment;
|
||||
|
||||
bool m_timecodes_calculated;
|
||||
|
||||
debugging_option_c m_debug_chapters, m_debug_headers, m_debug_tables, m_debug_interleaving, m_debug_resync;
|
||||
|
||||
friend class qtmp4_demuxer_c;
|
||||
|
||||
public:
|
||||
qtmp4_reader_c(const track_info_c &ti, const mm_io_cptr &in);
|
||||
virtual ~qtmp4_reader_c();
|
||||
|
@ -309,3 +309,4 @@ T_460truehd:33f8c0f013c71529281179cf8669c567-33f8c0f013c71529281179cf8669c567-ab
|
||||
T_461truehd_from_mpeg_ts:c1ee4dc0746b9a3bea90ab49dd65e6a1-29f5c91656812569fb5e1fdbbbf85c39-c7272bed949872038798042ebbd31a2e:passed:20150212-134650:22.377720277
|
||||
T_462dtshd_reduce_to_core:c284b0e29c3b7040e14b89a7e4790ce1-1e8b4d2eac574607bbaa54e4b8eee9e0-1e8b4d2eac574607bbaa54e4b8eee9e0:passed:20150212-223839:1.367529862
|
||||
T_463a_ms_acm_with_track_tags:7766fe047ed88b8561caa4fba7c40ea6-45aa68321f4c3785d2e38ddbfd7cd850:passed:20150218-142924:0.139369636
|
||||
T_464mp4_mp3_track_sampling_rate_0:51ec17a6dcfae5d8ac0f5fcf44e31eb6-b2c1dca03505c75c694c0de113a450f6:passed:20150223-190257:0.939161518
|
||||
|
8
tests/test-464mp4_mp3_track_sampling_rate_0.rb
Executable file
8
tests/test-464mp4_mp3_track_sampling_rate_0.rb
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/ruby -w
|
||||
|
||||
describe "mkvmerge / MP3 in MP4, sample rate == 0 in track headers"
|
||||
|
||||
file = "data/mp4/mp3-samplerate-0.mp4"
|
||||
|
||||
test_identify file
|
||||
test_merge file, :exit_code => :warning
|
Loading…
Reference in New Issue
Block a user