mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
TrueHD reader: treat embedded AC3 data as its own track
Part of an implementation of #1107.
This commit is contained in:
parent
ac58b5547e
commit
e72a2307c6
@ -1,3 +1,10 @@
|
||||
2015-02-10 Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
* mkvmerge: new feature: mkvmerge will now recognize TrueHD+AC3
|
||||
files as consisting of two tracks. Instead of always dropping the
|
||||
AC3 part the user can simply select which tracks to
|
||||
keep. Part of the implementation of #1107.
|
||||
|
||||
2015-02-08 Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
* Released v7.6.0.
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include "common/ac3.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/memory.h"
|
||||
#include "common/truehd.h"
|
||||
@ -96,15 +95,14 @@ truehd_parser_c::parse(bool end_of_stream) {
|
||||
frame->m_channels = mlp_channels[data[offset + 11] & 0x1f];
|
||||
}
|
||||
|
||||
} else if (get_uint16_be(&data[offset]) == 0x0b77) {
|
||||
ac3::frame_c ac3_frame;
|
||||
if (ac3_frame.decode_header(&data[offset], size - offset)) {
|
||||
if (((size - offset) < ac3_frame.m_bytes) && !end_of_stream)
|
||||
} else if (get_uint16_be(&data[offset]) == AC3_SYNC_WORD) {
|
||||
if (frame->m_ac3_header.decode_header(&data[offset], size - offset)) {
|
||||
if (((size - offset) < frame->m_ac3_header.m_bytes) && !end_of_stream)
|
||||
break;
|
||||
|
||||
if (((size - offset) >= ac3_frame.m_bytes) && verify_ac3_checksum(&data[offset], size - offset)) {
|
||||
if (((size - offset) >= frame->m_ac3_header.m_bytes) && verify_ac3_checksum(&data[offset], size - offset)) {
|
||||
frame->m_type = truehd_frame_t::ac3;
|
||||
frame->m_size = ac3_frame.m_bytes;
|
||||
frame->m_size = frame->m_ac3_header.m_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -170,7 +168,7 @@ truehd_parser_c::resync(unsigned int offset) {
|
||||
|
||||
for (offset = offset + 4; (offset + 4) < size; ++offset) {
|
||||
uint32_t sync_word = get_uint32_be(&data[offset]);
|
||||
if ((TRUEHD_SYNC_WORD == sync_word) || (MLP_SYNC_WORD == sync_word)) {
|
||||
if ((TRUEHD_SYNC_WORD == sync_word) || (MLP_SYNC_WORD == sync_word) || (AC3_SYNC_WORD == get_uint16_be(&data[offset - 4]))) {
|
||||
m_sync_state = state_synced;
|
||||
return offset - 4;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "common/ac3.h"
|
||||
#include "common/byte_buffer.h"
|
||||
|
||||
#define TRUEHD_SYNC_WORD 0xf8726fba
|
||||
@ -41,6 +42,8 @@ struct truehd_frame_t {
|
||||
int m_channels;
|
||||
int m_samples_per_frame;
|
||||
|
||||
ac3::frame_c m_ac3_header;
|
||||
|
||||
memory_cptr m_data;
|
||||
|
||||
truehd_frame_t()
|
||||
|
@ -32,6 +32,11 @@ public:
|
||||
virtual ~packet_converter_c() {}
|
||||
|
||||
virtual bool convert(packet_cptr const &packet) = 0;
|
||||
virtual void flush() {}
|
||||
|
||||
virtual void set_packetizer(generic_packetizer_c *ptzr) {
|
||||
m_ptzr = ptzr;
|
||||
}
|
||||
};
|
||||
typedef std::shared_ptr<packet_converter_c> packet_converter_cptr;
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "common/mm_io_x.h"
|
||||
#include "input/r_truehd.h"
|
||||
#include "merge/input_x.h"
|
||||
#include "output/p_ac3.h"
|
||||
#include "output/p_truehd.h"
|
||||
|
||||
#define TRUEHD_READ_SIZE (1024 * 1024)
|
||||
@ -40,6 +41,7 @@ truehd_reader_c::truehd_reader_c(const track_info_c &ti,
|
||||
const mm_io_cptr &in)
|
||||
: generic_reader_c(ti, in)
|
||||
, m_chunk(memory_c::alloc(TRUEHD_READ_SIZE))
|
||||
, m_ac3_ptzr{-1}
|
||||
{
|
||||
}
|
||||
|
||||
@ -63,7 +65,30 @@ truehd_reader_c::read_headers() {
|
||||
|
||||
truehd_parser_c parser;
|
||||
parser.add_data(m_chunk->get_buffer(), init_read_len);
|
||||
m_header = parser.get_next_frame();
|
||||
|
||||
|
||||
auto found_truehd = false;
|
||||
auto found_ac3 = false;
|
||||
|
||||
while (parser.frame_available() && (!found_truehd || !found_ac3)) {
|
||||
auto frame = parser.get_next_frame();
|
||||
|
||||
if (frame->is_ac3()) {
|
||||
if (!found_ac3) {
|
||||
found_ac3 = true;
|
||||
m_ac3_header = frame->m_ac3_header;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!frame->is_sync())
|
||||
continue;
|
||||
|
||||
m_header = frame;
|
||||
found_truehd = true;
|
||||
}
|
||||
|
||||
m_ti.m_id = 0; // ID for this track.
|
||||
|
||||
show_demuxer_info();
|
||||
@ -77,31 +102,65 @@ truehd_reader_c::~truehd_reader_c() {
|
||||
}
|
||||
|
||||
void
|
||||
truehd_reader_c::create_packetizer(int64_t) {
|
||||
if (!demuxing_requested('a', 0) || (NPTZR() != 0))
|
||||
truehd_reader_c::add_available_track_ids() {
|
||||
add_available_track_id(0);
|
||||
if (m_ac3_header.m_valid)
|
||||
add_available_track_id(1);
|
||||
}
|
||||
|
||||
void
|
||||
truehd_reader_c::create_packetizers() {
|
||||
create_packetizer(0);
|
||||
create_packetizer(1);
|
||||
}
|
||||
|
||||
void
|
||||
truehd_reader_c::create_packetizer(int64_t tid) {
|
||||
if (!demuxing_requested('a', tid))
|
||||
return;
|
||||
|
||||
add_packetizer(new truehd_packetizer_c(this, m_ti, m_header->m_codec, m_header->m_sampling_rate, m_header->m_channels));
|
||||
show_packetizer_info(0, PTZR0);
|
||||
if (0 == tid) {
|
||||
m_truehd_ptzr = add_packetizer(new truehd_packetizer_c(this, m_ti, m_header->m_codec, m_header->m_sampling_rate, m_header->m_channels));
|
||||
show_packetizer_info(0, PTZR(m_truehd_ptzr));
|
||||
m_converter.set_packetizer(PTZR(m_truehd_ptzr));
|
||||
|
||||
} else if ((1 == tid) && m_ac3_header.m_valid) {
|
||||
m_ac3_ptzr = add_packetizer(new ac3_packetizer_c(this, m_ti, m_ac3_header.m_sample_rate, m_ac3_header.m_channels, m_ac3_header.m_bs_id, true));
|
||||
show_packetizer_info(m_ti.m_id, PTZR(m_ac3_ptzr));
|
||||
m_converter.set_ac3_packetizer(PTZR(m_ac3_ptzr));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
file_status_e
|
||||
truehd_reader_c::read(generic_packetizer_c *,
|
||||
bool) {
|
||||
int64_t remaining_bytes = m_size - m_in->getFilePointer();
|
||||
int64_t read_len = std::min((int64_t)TRUEHD_READ_SIZE, remaining_bytes);
|
||||
int num_read = m_in->read(m_chunk->get_buffer(), read_len);
|
||||
auto remaining_bytes = m_size - m_in->getFilePointer();
|
||||
auto read_len = std::min<int64_t>(TRUEHD_READ_SIZE, remaining_bytes);
|
||||
|
||||
if (0 <= num_read)
|
||||
PTZR0->process(new packet_t(new memory_c(m_chunk->get_buffer(), num_read, false)));
|
||||
if (0 >= read_len) {
|
||||
m_converter.flush();
|
||||
return FILE_STATUS_DONE;
|
||||
}
|
||||
|
||||
return (0 >= (remaining_bytes - num_read)) ? flush_packetizers() : FILE_STATUS_MOREDATA;
|
||||
auto num_read = m_in->read(m_chunk->get_buffer(), read_len);
|
||||
|
||||
if (0 < num_read)
|
||||
m_converter.convert(std::make_shared<packet_t>(new memory_c(m_chunk->get_buffer(), num_read, false)));
|
||||
|
||||
if (num_read == read_len)
|
||||
return FILE_STATUS_MOREDATA;
|
||||
|
||||
m_converter.flush();
|
||||
return FILE_STATUS_DONE;
|
||||
}
|
||||
|
||||
void
|
||||
truehd_reader_c::identify() {
|
||||
id_result_container();
|
||||
id_result_track(0, ID_RESULT_TRACK_AUDIO, codec_c::get_name(m_header->is_truehd() ? codec_c::A_TRUEHD : codec_c::A_MLP, m_header->is_truehd() ? "TrueHD" : "MLP"));
|
||||
if (m_ac3_header.m_valid)
|
||||
id_result_track(1, ID_RESULT_TRACK_AUDIO, codec_c::get_name(codec_c::A_AC3, "AC3"));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -20,13 +20,18 @@
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/mm_io.h"
|
||||
#include "input/truehd_ac3_splitting_packet_converter.h"
|
||||
#include "merge/generic_reader.h"
|
||||
#include "common/ac3.h"
|
||||
#include "common/truehd.h"
|
||||
|
||||
class truehd_reader_c: public generic_reader_c {
|
||||
private:
|
||||
memory_cptr m_chunk;
|
||||
truehd_frame_cptr m_header;
|
||||
int m_truehd_ptzr, m_ac3_ptzr;
|
||||
ac3::frame_c m_ac3_header;
|
||||
truehd_ac3_splitting_packet_converter_c m_converter;
|
||||
|
||||
public:
|
||||
truehd_reader_c(const track_info_c &ti, const mm_io_cptr &in);
|
||||
@ -39,7 +44,9 @@ public:
|
||||
virtual void read_headers();
|
||||
virtual file_status_e read(generic_packetizer_c *ptzr, bool force = false);
|
||||
virtual void identify();
|
||||
virtual void create_packetizer(int64_t id);
|
||||
virtual void create_packetizers();
|
||||
virtual void create_packetizer(int64_t tid);
|
||||
virtual void add_available_track_ids();
|
||||
virtual bool is_providing_timecodes() const {
|
||||
return false;
|
||||
}
|
||||
|
68
src/input/truehd_ac3_splitting_packet_converter.cpp
Normal file
68
src/input/truehd_ac3_splitting_packet_converter.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
mkvmerge -- utility for splicing together matroska files
|
||||
from component media subtypes
|
||||
|
||||
Distributed under the GPL v2
|
||||
see the file COPYING for details
|
||||
or visit http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
Splitting TrueHD/AC3 into to packetizers
|
||||
|
||||
Written by Moritz Bunkus <moritz@bunkus.org>.
|
||||
*/
|
||||
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include "input/truehd_ac3_splitting_packet_converter.h"
|
||||
#include "merge/generic_packetizer.h"
|
||||
#include "output/p_truehd.h"
|
||||
|
||||
truehd_ac3_splitting_packet_converter_c::truehd_ac3_splitting_packet_converter_c(generic_packetizer_c *truehd_ptzr,
|
||||
generic_packetizer_c *ac3_ptzr)
|
||||
: packet_converter_c{truehd_ptzr}
|
||||
, m_ac3_ptzr{ac3_ptzr}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
truehd_ac3_splitting_packet_converter_c::convert(packet_cptr const &packet) {
|
||||
m_parser.add_data(packet->data->get_buffer(), packet->data->get_size());
|
||||
|
||||
process_frames(packet->timecode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
truehd_ac3_splitting_packet_converter_c::set_ac3_packetizer(generic_packetizer_c *ac3_ptzr) {
|
||||
m_ac3_ptzr = ac3_ptzr;
|
||||
}
|
||||
|
||||
void
|
||||
truehd_ac3_splitting_packet_converter_c::flush() {
|
||||
m_parser.parse(true);
|
||||
|
||||
process_frames();
|
||||
|
||||
if (m_ptzr)
|
||||
m_ptzr->flush();
|
||||
|
||||
if (m_ac3_ptzr)
|
||||
m_ac3_ptzr->flush();
|
||||
}
|
||||
|
||||
void
|
||||
truehd_ac3_splitting_packet_converter_c::process_frames(int64_t timecode) {
|
||||
while (m_parser.frame_available()) {
|
||||
auto frame = m_parser.get_next_frame();
|
||||
|
||||
if (frame->is_ac3()) {
|
||||
if (m_ac3_ptzr) {
|
||||
auto packet = std::make_shared<packet_t>(frame->m_data, timecode);
|
||||
m_ac3_ptzr->process(packet);
|
||||
}
|
||||
|
||||
} else if (m_ptzr)
|
||||
static_cast<truehd_packetizer_c *>(m_ptzr)->process_framed(frame);
|
||||
}
|
||||
}
|
41
src/input/truehd_ac3_splitting_packet_converter.h
Normal file
41
src/input/truehd_ac3_splitting_packet_converter.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
mkvmerge -- utility for splicing together matroska files
|
||||
from component media subtypes
|
||||
|
||||
Distributed under the GPL v2
|
||||
see the file COPYING for details
|
||||
or visit http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
class definitions for the TrueHD/AC3 splitting converter
|
||||
|
||||
Written by Moritz Bunkus <moritz@bunkus.org>.
|
||||
*/
|
||||
|
||||
#ifndef MTX_INPUT_TRUEHD_AC3_SPLITTING_PACKET_CONVERTER_H
|
||||
#define MTX_INPUT_TRUEHD_AC3_SPLITTING_PACKET_CONVERTER_H
|
||||
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include "common/truehd.h"
|
||||
#include "input/packet_converter.h"
|
||||
|
||||
class truehd_ac3_splitting_packet_converter_c: public packet_converter_c {
|
||||
protected:
|
||||
truehd_parser_c m_parser;
|
||||
generic_packetizer_c *m_ac3_ptzr;
|
||||
|
||||
public:
|
||||
truehd_ac3_splitting_packet_converter_c(generic_packetizer_c *truehd_ptzr = nullptr, generic_packetizer_c *ac3_ptzr = nullptr);
|
||||
virtual ~truehd_ac3_splitting_packet_converter_c() {};
|
||||
|
||||
virtual bool convert(packet_cptr const &packet);
|
||||
virtual void set_ac3_packetizer(generic_packetizer_c *ac3_ptzr);
|
||||
virtual void flush();
|
||||
|
||||
protected:
|
||||
virtual void process_frames(int64_t timecode = -1);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<truehd_ac3_splitting_packet_converter_c> truehd_ac3_splitting_packet_converter_cptr;
|
||||
|
||||
#endif // MTX_INPUT_TRUEHD_AC3_SPLITTING_PACKET_CONVERTER_H
|
@ -58,6 +58,17 @@ truehd_packetizer_c::set_headers() {
|
||||
m_track_entry->EnableLacing(false);
|
||||
}
|
||||
|
||||
void
|
||||
truehd_packetizer_c::process_framed(truehd_frame_cptr const &frame) {
|
||||
if (frame->is_sync()) {
|
||||
adjust_header_values(frame);
|
||||
flush();
|
||||
}
|
||||
|
||||
if (!frame->is_ac3())
|
||||
m_frames.push_back(frame);
|
||||
}
|
||||
|
||||
int
|
||||
truehd_packetizer_c::process(packet_cptr packet) {
|
||||
m_parser.add_data(packet->data->get_buffer(), packet->data->get_size());
|
||||
@ -84,7 +95,7 @@ truehd_packetizer_c::handle_frames() {
|
||||
}
|
||||
|
||||
void
|
||||
truehd_packetizer_c::adjust_header_values(truehd_frame_cptr &frame) {
|
||||
truehd_packetizer_c::adjust_header_values(truehd_frame_cptr const &frame) {
|
||||
if (!m_first_frame)
|
||||
return;
|
||||
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
virtual ~truehd_packetizer_c();
|
||||
|
||||
virtual int process(packet_cptr packet);
|
||||
virtual void process_framed(truehd_frame_cptr const &frame);
|
||||
virtual void handle_frames();
|
||||
virtual void set_headers();
|
||||
|
||||
@ -45,7 +46,7 @@ public:
|
||||
virtual connection_result_e can_connect_to(generic_packetizer_c *src, std::string &error_message);
|
||||
|
||||
protected:
|
||||
virtual void adjust_header_values(truehd_frame_cptr &frame);
|
||||
virtual void adjust_header_values(truehd_frame_cptr const &frame);
|
||||
|
||||
virtual void flush_impl();
|
||||
virtual void flush_frames();
|
||||
|
@ -305,4 +305,4 @@ T_456tta:4b92a305f440778a3128685cefd4bd5d-a3f2a14c15e709027c05445ac91b0659:passe
|
||||
T_457mpeg_ts_all_pmts_with_crc_errors:0fefd1b95985d5f5deea17d20e05f6e9:passed:20150104-133628:1.586689715
|
||||
T_458pcm_big_endian_in_matroska:c83ea37c1a80696b342859467d863f2a:passed:20150202-193826:0.080955865
|
||||
T_459append_chapters_same_uid_with_sub_chapters:656362feddd9e89d28df6078361bf9d0:passed:20150202-214722:0.072671062
|
||||
T_460truehd:08e418b5bc96a1c746a14a70ddf8f2b1-8a5f8851733848f5505361d043ae8eb8:passed:20150210-130114:3.848084902
|
||||
T_460truehd:08e418b5bc96a1c746a14a70ddf8f2b1-08e418b5bc96a1c746a14a70ddf8f2b1-dd6d54ed6acbe10803bb910551b45ed9-8a5f8851733848f5505361d043ae8eb8-b2089ce04a1239127d8a3a0ccb864346-dd6d54ed6acbe10803bb910551b45ed9:passed:20150210-130114:10.654546371
|
||||
|
@ -3,5 +3,10 @@
|
||||
# T_460truehd
|
||||
describe "mkvmerge / TrueHD"
|
||||
|
||||
test_merge "data/truehd/blueplanet.thd"
|
||||
test_merge "data/truehd/blueplanet.thd", :args => "-a 0"
|
||||
|
||||
test_merge "data/truehd/blueplanet.thd+ac3"
|
||||
test_merge "data/truehd/blueplanet.thd+ac3", :args => "-a 0"
|
||||
test_merge "data/truehd/blueplanet.thd+ac3", :args => "-a 1"
|
||||
test_merge "data/truehd/blueplanet.thd+ac3", :args => "-a 0,1"
|
||||
|
Loading…
Reference in New Issue
Block a user