diff --git a/ChangeLog b/ChangeLog index fef6021ef..0a0d2d808 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-07-03 Moritz Bunkus + + * mkvmerge: new feature: Added support for TTA lossless audio + files. + 2004-06-29 Moritz Bunkus * Released v0.9.2. diff --git a/src/common/common.h b/src/common/common.h index 79888553c..f62db96b7 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -61,6 +61,7 @@ using namespace std; #define TYPEREAL 14 #define TYPEQTMP4 15 #define TYPEFLAC 16 +#define TYPETTA 17 #define FOURCC(a, b, c, d) (uint32_t)((((unsigned char)a) << 24) + \ (((unsigned char)b) << 16) + \ diff --git a/src/common/matroska.h b/src/common/matroska.h index cd69cfbbb..74a49d4ef 100644 --- a/src/common/matroska.h +++ b/src/common/matroska.h @@ -47,6 +47,7 @@ #define MKV_A_REAL_SIPR "A_REAL/SIPR" #define MKV_A_REAL_ATRC "A_REAL/ATRC" #define MKV_A_FLAC "A_FLAC" +#define MKV_A_TTA "A_TTA1" #define MKV_V_MPEG4_SP "V_MPEG4/ISO/SP" #define MKV_V_MPEG4_ASP "V_MPEG4/ISO/ASP" diff --git a/src/input/r_matroska.cpp b/src/input/r_matroska.cpp index 3877de995..32ba79607 100644 --- a/src/input/r_matroska.cpp +++ b/src/input/r_matroska.cpp @@ -75,6 +75,7 @@ extern "C" { // for BITMAPINFOHEADER #include "p_passthrough.h" #include "p_pcm.h" #include "p_textsubs.h" +#include "p_tta.h" #include "p_video.h" #include "p_vobsub.h" #include "p_vorbis.h" @@ -535,7 +536,8 @@ kax_reader_c::verify_tracks() { "Ignoring track %u.\n", t->tnum); continue; #endif - } + } else if (!strcmp(t->codec_id, MKV_A_TTA)) + t->a_formattag = FOURCC('T', 'T', 'A', '1'); } if (t->a_sfreq == 0.0) @@ -1616,6 +1618,17 @@ kax_reader_c::create_packetizer(int64_t tid) { (int64_t)t->tnum); #endif + } else if (t->a_formattag == FOURCC('T', 'T', 'A', '1')) { + safefree(nti->private_data); + nti->private_data = NULL; + nti->private_size = 0; + t->ptzr = + add_packetizer(new tta_packetizer_c(this, t->a_channels, + t->a_bps, (int32_t)t->a_sfreq, + nti)); + mxinfo(FMT_TID "Using the TTA output module.\n", ti->fname, + (int64_t)t->tnum); + } else init_passthrough_packetizer(t); diff --git a/src/input/r_tta.cpp b/src/input/r_tta.cpp new file mode 100644 index 000000000..5b04b9c21 --- /dev/null +++ b/src/input/r_tta.cpp @@ -0,0 +1,157 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + r_tta.h + + Written by Moritz Bunkus + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html +*/ + +/*! + \file r_tta.cpp + \version $Id$ + \brief TTA demultiplexer module + \author Moritz Bunkus +*/ + +#include +#include +#include +#include + +#include "mkvmerge.h" +#include "common.h" +#include "error.h" +#include "p_tta.h" +#include "r_tta.h" + +int +tta_reader_c::probe_file(mm_io_c *mm_io, + int64_t size) { + unsigned char buf[4]; + + if (size < 26) + return 0; + try { + mm_io->setFilePointer(0, seek_beginning); + if (mm_io->read(buf, 4) != 4) + mm_io->setFilePointer(0, seek_beginning); + mm_io->setFilePointer(0, seek_beginning); + } catch (exception &ex) { + return 0; + } + if (!strncmp((char *)buf, "TTA1", 4)) + return 1; + return 0; +} + +tta_reader_c::tta_reader_c(track_info_c *nti) + throw (error_c): + generic_reader_c(nti) { + uint32_t seek_point; + int64_t seek_sum; + + try { + mm_io = new mm_io_c(ti->fname, MODE_READ); + size = mm_io->get_size(); + mm_io->setFilePointer(6, seek_beginning); + + if (identifying) + return; + + channels = mm_io->read_uint16(); + bits_per_sample = mm_io->read_uint16(); + sample_rate = mm_io->read_uint32(); + data_length = mm_io->read_uint32(); + mm_io->skip(4); + + seek_sum = mm_io->getFilePointer() + 4; + + do { + seek_point = mm_io->read_uint32(); + seek_sum += seek_point + 4; + seek_points.push_back(seek_point); + } while (seek_sum < size); + + if (seek_sum != size) + mxerror(FMT_FN "The seek table in this TTA file seems to be broken.\n", + ti->fname); + + mm_io->skip(4); + + mxverb(2, "tta: ch %u bps %u sr %u dl %u seek_sum %lld size %lld num %u\n", + channels, bits_per_sample, sample_rate, data_length, seek_sum, size, + seek_points.size()); + + bytes_processed = 0; + pos = 0; + ti->id = 0; // ID for this track. + + } catch (exception &ex) { + throw error_c("tta_reader: Could not open the file."); + } + if (verbose) + mxinfo(FMT_FN "Using the TTA demultiplexer.\n", ti->fname); +} + +tta_reader_c::~tta_reader_c() { + delete mm_io; +} + +void +tta_reader_c::create_packetizer(int64_t) { + generic_packetizer_c *ttapacketizer; + + if (NPTZR() != 0) + return; + ttapacketizer = new tta_packetizer_c(this, channels, bits_per_sample, + sample_rate, ti); + add_packetizer(ttapacketizer); + mxinfo(FMT_TID "Using the TTA output module.\n", ti->fname, (int64_t)0); +} + +int +tta_reader_c::read(generic_packetizer_c *) { + unsigned char *buf; + int nread; + + if (pos >= seek_points.size()) + return 0; + + buf = (unsigned char *)safemalloc(seek_points[pos]); + nread = mm_io->read(buf, seek_points[pos]); + if (nread <= 0) { + PTZR0->flush(); + return 0; + } + + memory_c mem(buf, nread, true); + PTZR0->process(mem); + bytes_processed += nread; + pos++; + + return EMOREDATA; +} + +int +tta_reader_c::display_priority() { + return DISPLAYPRIORITY_HIGH - 1; +} + +void +tta_reader_c::display_progress(bool final) { + if (final) + mxinfo("progress: %lld/%lld bytes (100%%)\r", size, size); + else + mxinfo("progress: %lld/%lld bytes (%d%%)\r", bytes_processed, size, + (int)(bytes_processed * 100L / size)); +} + +void +tta_reader_c::identify() { + mxinfo("File '%s': container: TTA\nTrack ID 0: audio (TTA)\n", ti->fname); +} diff --git a/src/input/r_tta.h b/src/input/r_tta.h new file mode 100644 index 000000000..ec8cedf6a --- /dev/null +++ b/src/input/r_tta.h @@ -0,0 +1,56 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + r_avi.h + + Written by Moritz Bunkus + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html +*/ + +/*! + \file r_tta.h + \version $Id$ + \brief class definitions for the TTA demultiplexer module + \author Moritz Bunkus +*/ + +#ifndef __R_TTA_H +#define __R_TTA_H + +#include "os.h" + +#include + +#include "common.h" +#include "error.h" +#include "mm_io.h" +#include "pr_generic.h" + +class tta_reader_c: public generic_reader_c { +private: + unsigned char *chunk; + mm_io_c *mm_io; + int64_t bytes_processed, size; + uint16_t channels, bits_per_sample; + uint32_t sample_rate, data_length; + vector seek_points; + int pos; + +public: + tta_reader_c(track_info_c *nti) throw (error_c); + virtual ~tta_reader_c(); + + virtual int read(generic_packetizer_c *ptzr); + virtual int display_priority(); + virtual void display_progress(bool final = false); + virtual void identify(); + virtual void create_packetizer(int64_t id); + + static int probe_file(mm_io_c *mm_io, int64_t size); +}; + +#endif // __R_TTA_H diff --git a/src/mkvmerge.cpp b/src/mkvmerge.cpp index 1b215b6b6..a3ac6acce 100644 --- a/src/mkvmerge.cpp +++ b/src/mkvmerge.cpp @@ -89,6 +89,7 @@ #include "r_real.h" #include "r_srt.h" #include "r_ssa.h" +#include "r_tta.h" #include "r_vobsub.h" #include "r_wav.h" #include "tagparser.h" @@ -218,8 +219,9 @@ file_type_t file_types[] = {"idx ", TYPEVOBSUB, "VobSub subtitles"}, {"wav ", TYPEWAV, "WAVE (uncompressed PCM)"}, #if defined(HAVE_FLAC_FORMAT_H) - {"flac", TYPEFLAC, "FLAC lossless audio (slow!)"}, + {"flac", TYPEFLAC, "FLAC lossless audio"}, #endif + {"tta", TYPETTA, "TTA lossless audio"}, {"output modules:", -1, ""}, {" ", -1, "AAC audio"}, {" ", -1, "AC3 audio"}, @@ -414,6 +416,8 @@ get_type(char *filename) { type = TYPEREAL; else if (qtmp4_reader_c::probe_file(mm_io, size)) type = TYPEQTMP4; + else if (tta_reader_c::probe_file(mm_io, size)) + type = TYPETTA; else if (mp3_reader_c::probe_file(mm_io, size)) type = TYPEMP3; else if (ac3_reader_c::probe_file(mm_io, size)) @@ -1417,6 +1421,9 @@ create_readers() { file->reader = new flac_reader_c(file->ti); break; #endif + case TYPETTA: + file->reader = new tta_reader_c(file->ti); + break; default: mxerror("EVIL internal bug! (unknown file type)\n"); break; diff --git a/src/output/p_tta.cpp b/src/output/p_tta.cpp new file mode 100644 index 000000000..6b68bbcda --- /dev/null +++ b/src/output/p_tta.cpp @@ -0,0 +1,100 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + p_tta.cpp + + Written by Moritz Bunkus + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html +*/ + +/*! + \file + \version $Id: p_tta.cpp 1657 2004-04-09 17:37:25Z mosu $ + \brief TTA output module + \author Moritz Bunkus +*/ + +#include +#include +#include +#include + +#include "mkvmerge.h" +#include "common.h" +#include "pr_generic.h" +#include "p_tta.h" +#include "matroska.h" + +using namespace libmatroska; + +static const double tta_frame_time = 1.04489795918367346939l; + +tta_packetizer_c::tta_packetizer_c(generic_reader_c *nreader, + int nchannels, + int nbits_per_sample, + int nsample_rate, + track_info_c *nti) + throw (error_c): + generic_packetizer_c(nreader, nti) { + sample_rate = nsample_rate; + channels = nchannels; + bits_per_sample = nbits_per_sample; + samples_output = 0; + + set_track_type(track_audio); + set_track_default_duration((int64_t)(1000000000.0 * ti->async.linear * + tta_frame_time)); +} + +tta_packetizer_c::~tta_packetizer_c() { +} + +void +tta_packetizer_c::set_headers() { + set_codec_id(MKV_A_TTA); + set_audio_sampling_freq((float)sample_rate); + set_audio_channels(channels); + set_audio_bit_depth(bits_per_sample); + + generic_packetizer_c::set_headers(); +} + +int +tta_packetizer_c::process(memory_c &mem, + int64_t, + int64_t, + int64_t, + int64_t) { + debug_enter("tta_packetizer_c::process"); + + add_packet(mem, samples_output * 1000000000 / sample_rate, + (int64_t)(1000000000.0 * ti->async.linear * tta_frame_time)); + samples_output += (int64_t)(tta_frame_time * sample_rate); + + debug_leave("tta_packetizer_c::process"); + + return EMOREDATA; +} + +void +tta_packetizer_c::dump_debug_info() { + mxdebug("tta_packetizer_c: queue: %d\n", packet_queue.size()); +} + +int +tta_packetizer_c::can_connect_to(generic_packetizer_c *src) { + tta_packetizer_c *psrc; + + psrc = dynamic_cast(src); + if (psrc == NULL) + return CAN_CONNECT_NO_FORMAT; + if ((sample_rate != psrc->sample_rate) || + (channels != psrc->channels) || + (bits_per_sample != psrc->bits_per_sample)) + return CAN_CONNECT_NO_PARAMETERS; + return CAN_CONNECT_YES; +} diff --git a/src/output/p_tta.h b/src/output/p_tta.h new file mode 100644 index 000000000..e3a1fc1ac --- /dev/null +++ b/src/output/p_tta.h @@ -0,0 +1,53 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + p_tta.h + + Written by Moritz Bunkus + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html +*/ + +/*! + \file p_tta.h + \version $Id$ + \brief class definition for the TTA output module + \author Moritz Bunkus +*/ + +#ifndef __P_TTA_H +#define __P_TTA_H + +#include "os.h" + +#include "common.h" +#include "pr_generic.h" + +class tta_packetizer_c: public generic_packetizer_c { +private: + int channels, bits_per_sample, sample_rate; + int64_t samples_output; + +public: + tta_packetizer_c(generic_reader_c *nreader, int nchannels, + int nbits_per_sample, int nsample_rate, track_info_c *nti) + throw (error_c); + virtual ~tta_packetizer_c(); + + virtual int process(memory_c &mem, int64_t timecode = -1, + int64_t length = -1, int64_t bref = -1, + int64_t fref = -1); + virtual void set_headers(); + + virtual void dump_debug_info(); + + virtual const char *get_format_name() { + return "TTA"; + } + virtual int can_connect_to(generic_packetizer_c *src); +}; + +#endif // __P_TTA_H