From d5ca84ac0d17b298af8488c411a4b04c9755f501 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Sat, 9 Oct 2004 14:28:58 +0000 Subject: [PATCH] Moved the external timecode stuff into their own classes. Enabled proper durations for tracks with external timecodes. --- ChangeLog | 6 + Makefile.in | 3 +- src/cluster_helper.cpp | 9 +- src/common/common.cpp | 26 ++++ src/common/common.h | 5 +- src/pr_generic.cpp | 250 +---------------------------------- src/pr_generic.h | 23 +--- src/timecode_factory.cpp | 274 +++++++++++++++++++++++++++++++++++++++ src/timecode_factory.h | 123 ++++++++++++++++++ tests/results.txt | 22 ++-- 10 files changed, 461 insertions(+), 280 deletions(-) create mode 100644 src/timecode_factory.cpp create mode 100644 src/timecode_factory.h diff --git a/ChangeLog b/ChangeLog index 68e128306..f9a1d80f1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2004-10-09 Moritz Bunkus + + * mkvmerge: Rewrote the code for the external timecode files. This + also fixes bug 99: The durations for the individual tracks were + not correct for those tracks for which --timecodes was used. + 2004-10-08 Moritz Bunkus * mmg: bug fix: Crash when saving chapters from the chapter diff --git a/Makefile.in b/Makefile.in index 2278b51a2..1fca444fe 100644 --- a/Makefile.in +++ b/Makefile.in @@ -288,7 +288,8 @@ src/output/libmtxoutput.a: $(libmtxoutput_OBJECTS) mkvmerge_SOURCES = src/mkvmerge.cpp \ src/cluster_helper.cpp \ - src/pr_generic.cpp + src/pr_generic.cpp \ + src/timecode_factory.cpp mkvmerge_OBJECTS := $(patsubst %.cpp,%.o,$(mkvmerge_SOURCES)) mkvmerge_DEPENDENCIES += $(DEP_COMMON) \ $(DEP_COMP) $(DEP_INPUT) $(DEP_OUTPUT) $(DEP_AVI) $(DEP_RMFF) diff --git a/src/cluster_helper.cpp b/src/cluster_helper.cpp index ab59474ed..c20f0aa3d 100644 --- a/src/cluster_helper.cpp +++ b/src/cluster_helper.cpp @@ -341,10 +341,13 @@ cluster_helper_c::set_duration(render_groups_t *rg) { if (rg->duration_mandatory) { if ((block_duration == 0) || - ((block_duration > 0) && (block_duration != def_duration))) + ((block_duration > 0) && + (block_duration != (rg->durations.size() * def_duration)))) group->SetBlockDuration(RND_TIMECODE_SCALE(block_duration)); - } else if (use_durations && (block_duration > 0) && - (block_duration != def_duration)) + } else if ((use_durations || (def_duration > 0)) && + (block_duration > 0) && + (RND_TIMECODE_SCALE(block_duration) != + RND_TIMECODE_SCALE(rg->durations.size() * def_duration))) group->SetBlockDuration(RND_TIMECODE_SCALE(block_duration)); } diff --git a/src/common/common.cpp b/src/common/common.cpp index 03f1601af..e76ffb061 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -1,3 +1,4 @@ + /* * mkvmerge -- utility for splicing together matroska files * from component media subtypes @@ -1496,6 +1497,31 @@ mxsprintf(const char *fmt, return dst; } +/** \brief Platform independant version of sscanf + * + * This is a platform independant version of sscanf. It first fixes the format + * string (\see fix_format) and then calls sscanf. + * + * \param str The string to parse + * \param fmt The format string + * \returns The number of elements assigned + */ +int +mxsscanf(const string &str, + const char *fmt, + ...) { + va_list ap; + string new_fmt; + int result; + + fix_format(fmt, new_fmt); + va_start(ap, fmt); + result = vsscanf(str.c_str(), new_fmt.c_str(), ap); + va_end(ap); + + return result; +} + void mxhexdump(int level, const unsigned char *buffer, diff --git a/src/common/common.h b/src/common/common.h index 13e4fb609..db7b239f5 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -143,6 +143,8 @@ void MTX_DLL_API mxverb(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void MTX_DLL_API mxdebug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +int MTX_DLL_API mxsscanf(const string &str, const char *fmt, ...) + __attribute__ ((format (scanf, 2, 3))); #else void MTX_DLL_API die(const char *fmt, ...); void MTX_DLL_API mxprint(void *stream, const char *fmt, ...); @@ -153,6 +155,7 @@ void MTX_DLL_API mxerror(const char *fmt, ...); void MTX_DLL_API mxinfo(const char *fmt, ...); void MTX_DLL_API mxverb(int level, const char *fmt, ...); void MTX_DLL_API mxdebug(const char *fmt, ...); +int MTX_DLL_API mxsscanf(const string &str, const char *fmt, ...); #endif void MTX_DLL_API mxexit(int code = -1); @@ -238,7 +241,7 @@ int MTX_DLL_API get_varg_len(const char *fmt, va_list ap); extern int MTX_DLL_API verbose; -#define foreach(it, vec) for (it = (vec).begin(); it < (vec).end(); it++) +#define foreach(it, vec) for (it = (vec).begin(); it != (vec).end(); it++) class MTX_DLL_API bit_cursor_c { private: diff --git a/src/pr_generic.cpp b/src/pr_generic.cpp index 2231225f9..4207a917b 100644 --- a/src/pr_generic.cpp +++ b/src/pr_generic.cpp @@ -244,14 +244,8 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader, dumped_packet_number = 0; - frameno = 0; - ext_timecodes_version = -1; - timecode_ranges = NULL; - ext_timecodes = NULL; - ext_timecodes_warning_printed = false; - current_tc_range = 0; - if (ti->ext_timecodes != NULL) - parse_ext_timecode_file(ti->ext_timecodes); + timecode_factory = timecode_factory_c::create(ti->ext_timecodes, + ti->fname, ti->id); } generic_packetizer_c::~generic_packetizer_c() { @@ -261,10 +255,7 @@ generic_packetizer_c::~generic_packetizer_c() { safefree(hcodec_private); if (compressor != NULL) delete compressor; - if (timecode_ranges != NULL) - delete timecode_ranges; - if (ext_timecodes != NULL) - delete ext_timecodes; + delete timecode_factory; } void @@ -595,6 +586,8 @@ generic_packetizer_c::set_headers() { *(static_cast (&GetChild(*track_entry))) = htrack_max_cache; + htrack_default_duration = + (int64_t)timecode_factory->get_default_duration(htrack_default_duration); if (htrack_default_duration != -1.0) *(static_cast (&GetChild(*track_entry))) = @@ -860,7 +853,8 @@ generic_packetizer_c::add_packet(memory_c &mem, pack->duration = duration; pack->duration_mandatory = duration_mandatory; pack->source = this; - pack->assigned_timecode = get_next_timecode(timecode) + ti->packet_delay; + timecode_factory->get_next(timecode, pack->duration); + pack->assigned_timecode = timecode + ti->packet_delay; if (reader->max_timecode_seen < (pack->assigned_timecode + pack->duration)) reader->max_timecode_seen = pack->assigned_timecode + pack->duration; @@ -907,236 +901,6 @@ generic_packetizer_c::dump_packet(const void *buffer, safefree(path); } -void -generic_packetizer_c::parse_ext_timecode_file(const char *name) { - mm_io_c *in; - string line; - - in = NULL; - try { - in = new mm_text_io_c(name); - } catch(...) { - mxerror(_("The timecode file '%s' could not be opened for reading.\n"), - name); - } - - if (!in->getline2(line) || !starts_with_case(line, "# timecode format v") || - !parse_int(&line.c_str()[strlen("# timecode format v")], - ext_timecodes_version)) - mxerror(_("The timecode file '%s' contains an unsupported/unrecognized " - "format line. The very first line must look like " - "'# timecode format v1'.\n"), name); - if (ext_timecodes_version == 1) - parse_ext_timecode_file_v1(in, name); - else if (ext_timecodes_version == 2) - parse_ext_timecode_file_v2(in, name); - else - mxerror(_("The timecode file '%s' contains an unsupported/unrecognized " - "format (version %d).\n"), name, ext_timecodes_version); - - delete in; -} - -void -generic_packetizer_c::parse_ext_timecode_file_v1(mm_io_c *in, - const char *name) { - string line; - timecode_range_c t; - vector fields; - vector::iterator iit, pit; - uint32_t i, line_no; - bool done; - double default_fps; - - line_no = 1; - do { - if (!in->getline2(line)) - mxerror(_("The timecode file '%s' does not contain a valid 'Assume' line" - " with the default number of frames per second.\n"), name); - line_no++; - strip(line); - if ((line.length() != 0) && (line[0] != '#')) - break; - } while (true); - if (!starts_with_case(line, "assume ")) - mxerror(_("The timecode file '%s' does not contain a valid 'Assume' line " - "with the default number of frames per second.\n"), name); - line.erase(0, 6); - strip(line); - if (!parse_double(line.c_str(), default_fps)) - mxerror(_("The timecode file '%s' does not contain a valid 'Assume' line " - "with the default number of frames per second.\n"), name); - if (timecode_ranges != NULL) - delete timecode_ranges; - timecode_ranges = new vector; - - while (in->getline2(line)) { - line_no++; - strip(line, true); - if ((line.length() == 0) || (line[0] == '#')) - continue; - - fields = split(line.c_str()); - strip(fields, true); - if (fields.size() != 3) { - mxwarn(_("Line %d of the timecode file '%s' could not be parsed: It " - "does not contain exactly three fields.\n"), line_no, name); - continue; - } - - if (!parse_int(fields[0].c_str(), t.start_frame)) { - mxwarn(_("Line %d of the timecode file '%s' could not be parsed: The " - "start frame number is not a valid number.\n"), line_no, name); - continue; - } - if (!parse_int(fields[1].c_str(), t.end_frame)) { - mxwarn(_("Line %d of the timecode file '%s' could not be parsed: The " - "end frame number is not a valid number.\n"), line_no, name); - continue; - } - if (!parse_double(fields[2].c_str(), t.fps)) { - mxwarn(_("Line %d of the timecode file '%s' could not be parsed: The " - "number of frames per second is not a valid floating point " - "number.\n"), line_no, name); - continue; - } - - if ((t.fps <= 0) || (t.start_frame < 0) || (t.end_frame < 0) || - (t.end_frame < t.start_frame)) { - mxwarn(_("Line %d of the timecode file '%s' contains inconsistent data " - "(e.g. the start frame number is bigger than the end frame " - "number, or some values are smaller than zero).\n"), - line_no, name); - continue; - } - - timecode_ranges->push_back(t); - } - - mxverb(3, "ext_timecodes: Version %d, default fps %f, %u entries.\n", - ext_timecodes_version, default_fps, timecode_ranges->size()); - - if (timecode_ranges->size() == 0) - mxwarn(_("The timecode file '%s' does not contain any valid entry.\n"), - name); - - sort(timecode_ranges->begin(), timecode_ranges->end()); - if (timecode_ranges->size() > 0) { - do { - done = true; - iit = timecode_ranges->begin(); - for (i = 0; i < (timecode_ranges->size() - 1); i++) { - iit++; - if ((*timecode_ranges)[i].end_frame < - ((*timecode_ranges)[i + 1].start_frame - 1)) { - t.start_frame = (*timecode_ranges)[i].end_frame + 1; - t.end_frame = (*timecode_ranges)[i + 1].start_frame - 1; - t.fps = default_fps; - timecode_ranges->insert(iit, t); - done = false; - break; - } - } - } while (!done); - if ((*timecode_ranges)[0].start_frame != 0) { - t.start_frame = 0; - t.end_frame = (*timecode_ranges)[0].start_frame - 1; - t.fps = default_fps; - timecode_ranges->insert(timecode_ranges->begin(), t); - } - t.start_frame = (*timecode_ranges) - [timecode_ranges->size() - 1].end_frame + 1; - } else - t.start_frame = 0; - t.end_frame = 0xfffffffffffffffll; - t.fps = default_fps; - timecode_ranges->push_back(t); - - (*timecode_ranges)[0].base_timecode = 0.0; - pit = timecode_ranges->begin(); - for (iit = pit + 1; iit < timecode_ranges->end(); iit++, pit++) - iit->base_timecode = pit->base_timecode + - ((double)pit->end_frame - (double)pit->start_frame + 1) * 1000000000.0 / - pit->fps; - - for (iit = timecode_ranges->begin(); iit < timecode_ranges->end(); iit++) - mxverb(3, "timecode_ranges: entry %lld -> %lld at %f with %f\n", - iit->start_frame, iit->end_frame, iit->fps, iit->base_timecode); -} - -void -generic_packetizer_c::parse_ext_timecode_file_v2(mm_io_c *in, - const char *name) { - int line_no; - string line; - double timecode; - - if (ext_timecodes != NULL) - delete ext_timecodes; - ext_timecodes = new vector; - - line_no = 0; - while (in->getline2(line)) { - line_no++; - strip(line); - if ((line.length() == 0) || (line[0] == '#')) - continue; - if (!parse_double(line.c_str(), timecode)) - mxerror(_("The line %d of the timecode file '%s' does not contain a " - "valid floating point number.\n"), line_no, name); - ext_timecodes->push_back((int64_t)timecode * 1000000); - } -} - -int64_t -generic_packetizer_c::get_next_timecode(int64_t timecode) { - int64_t new_timecode; - timecode_range_c *t; - - if (ext_timecodes_version == 1) { - if (timecode_ranges == NULL) - die("generic_packetizer: ext_timecodes_version == 1 && " - "timecodes_range == NULL. %s\n", BUGMSG); - - t = &(*timecode_ranges)[current_tc_range]; - new_timecode = (int64_t)(t->base_timecode + 1000000000.0 * - (frameno - t->start_frame) / t->fps); - frameno++; - if ((frameno > t->end_frame) && - (current_tc_range < (timecode_ranges->size() - 1))) - current_tc_range++; - - mxverb(4, "ext_timecodes v1: %lld for %lld\n", new_timecode, frameno - 1); - - return new_timecode; - - } else if (ext_timecodes_version == 2) { - if (ext_timecodes == NULL) - die("generic_packetizer: ext_timecodes_version == 2 && " - "ext_timecodes == NULL. %s\n", BUGMSG); - - if ((frameno >= ext_timecodes->size()) && !ext_timecodes_warning_printed) { - mxwarn(_("generic_packetizer: The number of external timecodes %u is " - "smaller than the number of frames in track %lld of '%s'. " - "The remaining frames of this track might not be timestamped " - "the way you intended them to be. mkvmerge might even crash." - "\n"), - ext_timecodes->size(), ti->id, ti->fname); - ext_timecodes_warning_printed = true; - return timecode; - } - - new_timecode = (*ext_timecodes)[frameno]; - frameno++; - - mxverb(4, "ext_timecodes v2: %lld for %lld\n", new_timecode, frameno - 1); - - return new_timecode; - } - - return timecode; -} - void generic_packetizer_c::displace(float by_ns) { ti->async.displacement += (int64_t)by_ns; diff --git a/src/pr_generic.h b/src/pr_generic.h index 0a3b66c39..49979dc45 100644 --- a/src/pr_generic.h +++ b/src/pr_generic.h @@ -30,6 +30,7 @@ #include "compression.h" #include "error.h" #include "mm_io.h" +#include "timecode_factory.h" using namespace libmatroska; using namespace std; @@ -238,16 +239,6 @@ public: virtual void free_contents(); }; -class timecode_range_c { -public: - int64_t start_frame, end_frame; - double fps, base_timecode; - - bool operator <(const timecode_range_c &cmp) const { - return start_frame < cmp.start_frame; - } -}; - typedef struct packetizer_container_t { generic_packetizer_c *orig; generic_packetizer_c *current; @@ -333,12 +324,7 @@ protected: int hcompression; compression_c *compressor; - vector *timecode_ranges; - vector *ext_timecodes; - uint32_t current_tc_range; - int64_t frameno; - int ext_timecodes_version; - bool ext_timecodes_warning_printed; + timecode_factory_c *timecode_factory; int64_t last_cue_timecode; @@ -460,11 +446,6 @@ public: hcompression = method; } - virtual int64_t get_next_timecode(int64_t timecode); - virtual void parse_ext_timecode_file(const char *name); - virtual void parse_ext_timecode_file_v1(mm_io_c *in, const char *name); - virtual void parse_ext_timecode_file_v2(mm_io_c *in, const char *name); - inline bool needs_negative_displacement(float) { return ((initial_displacement < 0) && (ti->async.displacement > initial_displacement)); diff --git a/src/timecode_factory.cpp b/src/timecode_factory.cpp new file mode 100644 index 000000000..be9e778a2 --- /dev/null +++ b/src/timecode_factory.cpp @@ -0,0 +1,274 @@ +/* + * mkvmerge -- utility for splicing together matroska files + * from component media subtypes + * + * Distributed under the GPL + * see the file COPYING for details + * or visit http://www.gnu.org/copyleft/gpl.html + * + * $Id$ + * + * the timecode factory + * + * Written by Moritz Bunkus . + */ + +#include + +#include "common.h" +#include "mm_io.h" +#include "pr_generic.h" +#include "timecode_factory.h" + +using namespace std; + +struct lt_int64_t { + bool operator()(int64_t i1, int64_t i2) { + return i1 < i2; + } +}; + +timecode_factory_c * +timecode_factory_c::create(const char *_file_name, + const char *_source_name, + int64_t _tid) { + mm_io_c *in; + string line; + int version; + timecode_factory_c *factory; + + if (_file_name == NULL) + return new timecode_factory_c("", _source_name, _tid); + + in = NULL; // avoid gcc warning + try { + in = new mm_text_io_c(_file_name); + } catch(...) { + mxerror(_("The timecode file '%s' could not be opened for reading.\n"), + _file_name); + } + + if (!in->getline2(line) || !starts_with_case(line, "# timecode format v") || + !parse_int(&line.c_str()[strlen("# timecode format v")], version)) + mxerror(_("The timecode file '%s' contains an unsupported/unrecognized " + "format line. The very first line must look like " + "'# timecode format v1'.\n"), _file_name); + factory = NULL; // avoid gcc warning + if (version == 1) + factory = new timecode_factory_v1_c(_file_name, _source_name, _tid); + else if (version == 2) + factory = new timecode_factory_v2_c(_file_name, _source_name, _tid); + else + mxerror(_("The timecode file '%s' contains an unsupported/unrecognized " + "format (version %d).\n"), _file_name, version); + + factory->parse(*in); + delete in; + + return factory; +} + +void +timecode_factory_v1_c::parse(mm_io_c &in) { + string line; + timecode_range_c t; + vector fields; + vector::iterator iit, pit; + uint32_t i, line_no; + bool done; + const char *name; + + line_no = 1; + do { + if (!in.getline2(line)) + mxerror(_("The timecode file '%s' does not contain a valid 'Assume' line" + " with the default number of frames per second.\n"), + file_name.c_str()); + line_no++; + strip(line); + if ((line.length() != 0) && (line[0] != '#')) + break; + } while (true); + + if (!starts_with_case(line, "assume ")) + mxerror(_("The timecode file '%s' does not contain a valid 'Assume' line " + "with the default number of frames per second.\n"), + file_name.c_str()); + line.erase(0, 6); + strip(line); + if (!parse_double(line.c_str(), default_fps)) + mxerror(_("The timecode file '%s' does not contain a valid 'Assume' line " + "with the default number of frames per second.\n"), + file_name.c_str()); + + while (in.getline2(line)) { + line_no++; + strip(line, true); + if ((line.length() == 0) || (line[0] == '#')) + continue; + + if (mxsscanf(line, "%lld,%lld,%lf", &t.start_frame, &t.end_frame, &t.fps) + != 3) { + mxwarn(_("Line %d of the timecode file '%s' could not be parsed.\n"), + line_no, file_name.c_str()); + continue; + } + + if ((t.fps <= 0) || (t.start_frame < 0) || (t.end_frame < 0) || + (t.end_frame < t.start_frame)) { + mxwarn(_("Line %d of the timecode file '%s' contains inconsistent data " + "(e.g. the start frame number is bigger than the end frame " + "number, or some values are smaller than zero).\n"), + line_no, file_name.c_str()); + continue; + } + + ranges.push_back(t); + } + + mxverb(3, "ext_timecodes: Version 1, default fps %f, %u entries.\n", + default_fps, ranges.size()); + + if (ranges.size() == 0) { + mxwarn(_("The timecode file '%s' does not contain any valid entry.\n"), + name); + t.start_frame = 0; + } else { + sort(ranges.begin(), ranges.end()); + do { + done = true; + iit = ranges.begin(); + for (i = 0; i < (ranges.size() - 1); i++) { + iit++; + if (ranges[i].end_frame < + (ranges[i + 1].start_frame - 1)) { + t.start_frame = ranges[i].end_frame + 1; + t.end_frame = ranges[i + 1].start_frame - 1; + t.fps = default_fps; + ranges.insert(iit, t); + done = false; + break; + } + } + } while (!done); + if (ranges[0].start_frame != 0) { + t.start_frame = 0; + t.end_frame = ranges[0].start_frame - 1; + t.fps = default_fps; + ranges.insert(ranges.begin(), t); + } + t.start_frame = ranges + [ranges.size() - 1].end_frame + 1; + } + t.end_frame = 0xfffffffffffffffll; + t.fps = default_fps; + ranges.push_back(t); + + ranges[0].base_timecode = 0.0; + pit = ranges.begin(); + for (iit = pit + 1; iit < ranges.end(); iit++, pit++) + iit->base_timecode = pit->base_timecode + + ((double)pit->end_frame - (double)pit->start_frame + 1) * 1000000000.0 / + pit->fps; + + for (iit = ranges.begin(); iit < ranges.end(); iit++) + mxverb(3, "ranges: entry %lld -> %lld at %f with %f\n", + iit->start_frame, iit->end_frame, iit->fps, iit->base_timecode); +} + +void +timecode_factory_v1_c::get_next(int64_t &timecode, + int64_t &duration, + bool peek_only) { + timecode = get_at(frameno); + duration = get_at(frameno + 1) - timecode; + if (!peek_only) { + frameno++; + if ((frameno > ranges[current_range].end_frame) && + (current_range < (ranges.size() - 1))) + current_range++; + } + + mxverb(4, "ext_timecodes v1: tc %lld dur %lld for %lld\n", timecode, + duration, frameno - 1); +} + +int64_t +timecode_factory_v1_c::get_at(int64_t frame) { + timecode_range_c *t; + + t = &ranges[current_range]; + if ((frame > t->end_frame) && (current_range < (ranges.size() - 1))) + t = &ranges[current_range + 1]; + return (int64_t)(t->base_timecode + 1000000000.0 * + (frame - t->start_frame) / t->fps); +} + +void +timecode_factory_v2_c::parse(mm_io_c &in) { + int line_no; + string line; + double timecode; + map dur_map; + map::const_iterator it; + int64_t duration, dur_sum; + + dur_sum = 0; + line_no = 0; + while (in.getline2(line)) { + line_no++; + strip(line); + if ((line.length() == 0) || (line[0] == '#')) + continue; + if (!parse_double(line.c_str(), timecode)) + mxerror(_("The line %d of the timecode file '%s' does not contain a " + "valid floating point number.\n"), line_no, file_name.c_str()); + timecodes.push_back((int64_t)(timecode * 1000000)); + if (timecodes.size() > 1) { + duration = timecodes[timecodes.size() - 1] - + timecodes[timecodes.size() - 2]; + if (dur_map.find(duration) == dur_map.end()) + dur_map[duration] = 1; + else + dur_map[duration] = dur_map[duration] + 1; + dur_sum += duration; + durations.push_back(duration); + } + } + if (timecodes.size() == 0) + mxerror(_("The timecode file '%s' does not contain any valid entry.\n"), + file_name.c_str()); + + dur_sum = -1; + foreach(it, dur_map) { + if ((dur_sum < 0) || (dur_map[dur_sum] < (*it).second)) + dur_sum = (*it).first; + mxverb(4, "ext_timecodes v2 dur_map %lld = %lld\n", (*it).first, + (*it).second); + } + mxverb(4, "ext_timecodes v2 max is %lld = %lld\n", dur_sum, + dur_map[dur_sum]); + if (dur_sum > 0) + default_fps = (double)1.0 / dur_sum; + durations.push_back(dur_sum); +} + +void +timecode_factory_v2_c::get_next(int64_t &timecode, + int64_t &duration, + bool peek_only) { + if ((frameno >= timecodes.size()) && !warning_printed) { + mxwarn(FMT_TID "The number of external timecodes %u is " + "smaller than the number of frames in this track. " + "The remaining frames of this track might not be timestamped " + "the way you intended them to be. mkvmerge might even crash.\n", + source_name.c_str(), tid, timecodes.size()); + warning_printed = true; + return; + } + + timecode = timecodes[frameno]; + duration = durations[frameno]; + if (!peek_only) + frameno++; +} diff --git a/src/timecode_factory.h b/src/timecode_factory.h new file mode 100644 index 000000000..03d51d050 --- /dev/null +++ b/src/timecode_factory.h @@ -0,0 +1,123 @@ +/* + * mkvmerge -- utility for splicing together matroska files + * from component media subtypes + * + * Distributed under the GPL + * see the file COPYING for details + * or visit http://www.gnu.org/copyleft/gpl.html + * + * $Id$ + * + * class definition for the timecode factory + * + * Written by Moritz Bunkus . + */ + +#ifndef __TIMECODE_FACTORY_H +#define __TIMECODE_FACTORY_H + +#include +#include + +using namespace std; + +class mm_io_c; + +class timecode_range_c { +public: + int64_t start_frame, end_frame; + double fps, base_timecode; + + bool operator <(const timecode_range_c &cmp) const { + return start_frame < cmp.start_frame; + } +}; + +class timecode_factory_c { +protected: + string file_name, source_name; + int64_t tid; + +public: + timecode_factory_c(const string &_file_name, const string &_source_name, + int64_t _tid): + file_name(_file_name), + source_name(_source_name), + tid(_tid) { + } + virtual ~timecode_factory_c() { + } + + virtual void parse(mm_io_c &) { + } + virtual void get_next(int64_t &timecode, int64_t &duration, + bool peek_only = false) { + } + virtual void peek_next(int64_t &timecode, int64_t &duration) { + get_next(timecode, duration, true); + } + virtual double get_default_duration(double proposal) { + return proposal; + } + + static timecode_factory_c *create(const char *_file_name, + const char *_source_name, + int64_t _tid); +}; + +class timecode_factory_v1_c: public timecode_factory_c { +protected: + vector ranges; + uint32_t current_range; + int64_t frameno; + double default_fps; + +public: + timecode_factory_v1_c(const string &_file_name, const string &_source_name, + int64_t _tid): + timecode_factory_c(_file_name, _source_name, _tid), + current_range(0), + frameno(0), + default_fps(0.0) { + } + virtual ~timecode_factory_v1_c() { + } + + virtual void parse(mm_io_c &in); + virtual void get_next(int64_t &timecode, int64_t &duration, + bool peek_only = false); + virtual double get_default_duration(double proposal) { + return default_fps != 0.0 ? 1 / default_fps : proposal; + } + +protected: + virtual int64_t get_at(int64_t frame); +}; + +class timecode_factory_v2_c: public timecode_factory_c { +protected: + vector timecodes, durations; + bool warning_printed; + int64_t frameno; + double default_fps; + +public: + timecode_factory_v2_c(const string &_file_name, const string &_source_name, + int64_t _tid): + timecode_factory_c(_file_name, _source_name, _tid), + warning_printed(false), + frameno(0), + default_fps(0.0) { + } + virtual ~timecode_factory_v2_c() { + } + + virtual void parse(mm_io_c &in); + virtual void get_next(int64_t &timecode, int64_t &duration, + bool peek_only = false); + virtual double get_default_duration(double proposal) { + return default_fps != 0.0 ? 1 / default_fps : proposal; + } +}; + +#endif // __TIMECODE_FACTORY_H diff --git a/tests/results.txt b/tests/results.txt index 09f81374d..d6f4a6718 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -1,34 +1,34 @@ T_001mp3:790f9415ba41cb29ba4836a035a0411e:passed:20040825-175700 T_002aac:73164d9430db643128a2a160f3d4aa14:passed:20040825-175700 T_003ac3:7ade2a219d4c5f89c6e39010a585903f:passed:20040825-175700 -T_004aacmp4:7b3a23c96c8df4c71d3dc59175680bb1:passed:20040825-175700 +T_004aacmp4:49c4bc364e62d59f19207bb091b69ad6:passed:20040825-175700 T_005flac:62571b0e2b971b181992062252987190:passed:20040825-175700 T_006oggflac:68cfdf432f987f04f454a9c222ece7d2:passed:20040825-175700 T_007oggvorbis:bec4fb34f6bf5f7542dac36c21c856ab:passed:20040825-175700 -T_008avi_divx3_mp3:767b7cad0fc95a67ecd63ae60d6771c3:passed:20040825-175700 +T_008avi_divx3_mp3:822d32b9405fcf1c0eea0988c901ca85:passed:20040825-175700 T_009realvideo_3:a0d70a589067fdd9ae67992c41fa595d:passed:20040825-175700 T_010realvideo_4:0d25e4137c50066f43fd12da6a4871af:passed:20040825-175700 T_011srt:d7ac4923916c695a9b47425a90e39a0b:passed:20040825-175700 T_012ssa:9ecbc6bdfa5dec6495f99c7a97342844:passed:20040825-175700 T_013vobsubs:8983288ea21b811fbb85d2ea272ccfe5:passed:20040825-175700 -T_014splitting_by_size:feeb1aba481b5a51d7142599359f2eac-eacf20880a6dff6fc1128c1ffd47d23b:passed:20040825-175700 -T_015splitting_by_time:d0e769a906ec4eacc9c9af929add6474-6f8dd1e514ea2a76941fb841d25a06dc:passed:20040825-175700 +T_014splitting_by_size:feeb1aba481b5a51d7142599359f2eac-5c639e8a08cf526d051fdf64f856c0d2:passed:20040825-175700 +T_015splitting_by_time:d0e769a906ec4eacc9c9af929add6474-c32e2d8c31e21e71f68875f2d9735565:passed:20040825-175700 T_016cuesheet:e2e88f91b6bf6ff411f4984ce7d89e35:passed:20040825-175700 T_017chapters:990b4b6b0931b7d52e589f715c919e10-4fff45a76f54708f881312aca04fd887:passed:20040825-175700 T_018attachments:bac27359516bab4984df3021aa295213-7e8e1f17615f157db0e98fba9ad88bad:passed:20040825-175700 -T_019attachments2:8d9ba46c4edbf3daf50175f9de2f87b4-689f99b17a166efe5457f16bff240b93-8d9ba46c4edbf3daf50175f9de2f87b4-eacf20880a6dff6fc1128c1ffd47d23b:passed:20040825-175700 -T_020languages:5e5c257edc758c326d17f11224fa8441:passed:20040825-234208 +T_019attachments2:8d9ba46c4edbf3daf50175f9de2f87b4-94349d68c9b7d493ec0d5a49f7291e40-8d9ba46c4edbf3daf50175f9de2f87b4-5c639e8a08cf526d051fdf64f856c0d2:passed:20040825-175700 +T_020languages:dc8b8792f86ac4e92e70154faef990a7:passed:20040825-234208 T_021aspect_ratio:f6e8aa4cfd776d99ff824f21d4e3c640-990a5f94678b5c8886dc8c3a5c6a22dd:passed:20040825-234244 T_022display_dimensions:108880396ffe5244465a3d25e8a57f93:passed:20040825-234339 -T_023no_x:0b7f257f1c801da7b2ef1976291e5d44-c8ff96f9dc08f98dfbebe19275aeb125-66105284756c6867756143920a242960:passed:20040825-234343 +T_023no_x:0b7f257f1c801da7b2ef1976291e5d44-39f690e6a11bc2e32b54334e67374feb-2662148f7a3d60c152c92ad9a9d5e672:passed:20040825-234343 T_024sync_mp3:a6c35d309947a52196d2a5428f87224a-6a638028d795bd48db95446f3521c302:passed:20040825-234344 T_025sync_vorbis:e9d13a6b769cf6c2e4987bf6bd160698-51fcfab150642a66484d3c6bedff697a:passed:20040825-234344 T_026sync_pcm:f337f057c059d771128206c0da3d4807-9b559191edb65655e49186c302c4b815:passed:20040825-234346 -T_027default_track:db15cd5861cdc5e43dc82bb0924ad3d9:passed:20040825-234348 +T_027default_track:e3177712483af5b129bbeb9cf0e6a820:passed:20040825-234348 T_028compression:f6c15855e42e5a0e80d22a2060ae13e8:passed:20040825-234348 -T_029link:49e174d0a7700ea2e288e20ddb85cc2a:passed:20040825-235039 -T_032cues:73c0452451a00010299e191a39c783f9:passed:20040825-235040 -T_033timecode_scale:ba6bc8dff27d7d9fd433670067fa716d-d54414d993caad6cea47ef7913cb13db:passed:20040825-235040 +T_029link:d92e5f123b799212f65bb7b394dd4f6e:passed:20040825-235039 +T_032cues:f84c58a6380b3104434b6943e0eb87e3:passed:20040825-235040 +T_033timecode_scale:ba6bc8dff27d7d9fd433670067fa716d-bbc4e4faad71330e8c6d7ec0b725898c:passed:20040825-235040 T_034ac3misdetected_as_mp2:f6765afb6d86ae09e0859f32ba65ced4:passed:20040920-100447 T_035X_vfw_video:5bcb3426b2635ae5c89a5e5fe46aa416:passed:20040920-185358 T_036X_mp3:b3bb67d316e20da12926d5c1d628f6e5:passed:20040920-190109