mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-23 19:31:44 +00:00
Moved the external timecode stuff into their own classes. Enabled proper durations for tracks with external timecodes.
This commit is contained in:
parent
263dfa4409
commit
d5ca84ac0d
@ -1,3 +1,9 @@
|
||||
2004-10-09 Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
* 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 <moritz@bunkus.org>
|
||||
|
||||
* mmg: bug fix: Crash when saving chapters from the chapter
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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<EbmlUInteger *>
|
||||
(&GetChild<KaxTrackMaxCache>(*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<EbmlUInteger *>
|
||||
(&GetChild<KaxTrackDefaultDuration>(*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<string> fields;
|
||||
vector<timecode_range_c>::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<timecode_range_c>;
|
||||
|
||||
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<int64_t>;
|
||||
|
||||
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;
|
||||
|
@ -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_range_c> *timecode_ranges;
|
||||
vector<int64_t> *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));
|
||||
|
274
src/timecode_factory.cpp
Normal file
274
src/timecode_factory.cpp
Normal file
@ -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 <moritz@bunkus.org>.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
|
||||
#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<string> fields;
|
||||
vector<timecode_range_c>::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<int64_t, int64_t, lt_int64_t> dur_map;
|
||||
map<int64_t, int64_t, lt_int64_t>::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++;
|
||||
}
|
123
src/timecode_factory.h
Normal file
123
src/timecode_factory.h
Normal file
@ -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 <moritz@bunkus.org>.
|
||||
*/
|
||||
|
||||
#ifndef __TIMECODE_FACTORY_H
|
||||
#define __TIMECODE_FACTORY_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<timecode_range_c> 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<int64_t> 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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user