Increased resolution for chapters to ns.

This commit is contained in:
Moritz Bunkus 2004-04-30 16:42:59 +00:00
parent 61581b676e
commit a519d8b84b
7 changed files with 106 additions and 103 deletions

View File

@ -1,3 +1,9 @@
2004-04-30 Moritz Bunkus <moritz@bunkus.org>
* all: Increased the precision for timecodes in chapter files to
nanoseconds (optionally, you can still use fewer digits after the
'.').
2004-04-26 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: Fixes for compilation with gcc 3.4.

View File

@ -132,51 +132,20 @@ el_get_utf8string(parser_data_t *pdata,
cstrutf8_to_UTFstring(pdata->bin->c_str());
}
#define iscolon(s) (*(s) == ':')
#define isdot(s) (*(s) == '.')
#define istwodigits(s) (isdigit(*(s)) && isdigit(*(s + 1)))
#define isthreedigits(s) (istwodigits(s) && isdigit(*(s + 2)))
#define istimestamp(s) ((strlen(s) == 12) && \
istwodigits(s) && iscolon(s + 2) && \
istwodigits(s + 3) && iscolon(s + 5) && \
istwodigits(s + 6) && isdot(s + 8) && \
isthreedigits(s + 9))
static void
el_get_time(parser_data_t *pdata,
EbmlElement *el) {
const char *errmsg = "Expected a time in the following format: HH:MM:SS.mmm"
" (HH = hour, MM = minute, SS = second, mmm = millisecond). Found '%s' "
"instead.";
char *p;
int64_t hour, minute, second, msec;
const char *errmsg = "Expected a time in the following format: HH:MM:SS.nnn"
" (HH = hour, MM = minute, SS = second, nnn = millisecond up to "
"nanosecond. You may use up to nine digits for 'n' which would mean "
"nanosecond precision). Found '%s' instead. Additional error message: %s";
int64_t usec;
strip(*pdata->bin);
p = safestrdup(pdata->bin->c_str());
if (!parse_timecode(pdata->bin->c_str(), &usec))
cperror(pdata, errmsg, pdata->bin->c_str(), timecode_parser_error);
if (!istimestamp(p))
cperror(pdata, errmsg, pdata->bin->c_str());
p[2] = 0;
p[5] = 0;
p[8] = 0;
if (!parse_int(p, hour) || !parse_int(&p[3], minute) ||
!parse_int(&p[6], second) || !parse_int(&p[9], msec))
cperror(pdata, errmsg, pdata->bin->c_str());
if ((hour < 0) || (hour > 23))
cperror(pdata, "Invalid hour given (%d).", hour);
if ((minute < 0) || (minute > 59))
cperror(pdata, "Invalid minute given (%d).", minute);
if ((second < 0) || (second > 59))
cperror(pdata, "Invalid second given (%d).", second);
safefree(p);
*(static_cast<EbmlUInteger *>(el)) =
((hour * 60 * 60 * 1000) + (minute * 60 * 1000) +
(second * 1000) + msec) * 1000000;
*(static_cast<EbmlUInteger *>(el)) = usec;
}
static void

View File

@ -295,18 +295,16 @@ static void write_chapter_atom_xml(KaxChapterAtom *atom, int level) {
} else if (is_id(KaxChapterTimeStart)) {
pt(level + 1, "<ChapterTimeStart>");
v = uint64(*static_cast<EbmlUInteger *>(e)) / 1000000;
mxprint(o, "%02llu:%02llu:%02llu.%03llu</ChapterTimeStart>\n",
v / 1000 / 60 / 60, (v / 1000 / 60) % 60, (v / 1000) % 60,
v % 1000);
v = uint64(*static_cast<EbmlUInteger *>(e));
mxprint(o, FMT_TIMECODEN "</ChapterTimeStart>\n",
ARG_TIMECODEN(v));
start_time_found = true;
} else if (is_id(KaxChapterTimeEnd)) {
pt(level + 1, "<ChapterTimeEnd>");
v = uint64(*static_cast<EbmlUInteger *>(e)) / 1000000;
mxprint(o, "%02llu:%02llu:%02llu.%03llu</ChapterTimeEnd>\n",
v / 1000 / 60 / 60, (v / 1000 / 60) % 60, (v / 1000) % 60,
v % 1000);
v = uint64(*static_cast<EbmlUInteger *>(e));
mxprint(o, FMT_TIMECODEN "</ChapterTimeEnd>\n",
ARG_TIMECODEN(v));
} else if (is_id(KaxChapterFlagHidden)) {
pt(level + 1, "<ChapterFlagHidden>");

View File

@ -1383,3 +1383,49 @@ void engage_hacks(const char *hacks) {
mxerror("'%s' is not a valid hack.\n", engage_args[aidx].c_str());
}
}
const char *timecode_parser_error = NULL;
static bool
set_tcp_error(const char *error) {
timecode_parser_error = error;
return false;
}
bool
parse_timecode(const char *src,
int64_t *timecode) {
// Recognized format:
// HH:MM:SS and HH:MM:SS.nnn with up to nine 'n' for ns precision
// 012345678901...
int h, m, s, n, i;
char format[10];
if ((strlen(src) < 8) || (strlen(src) > 18) || (src[2] != ':') ||
(src[5] != ':'))
return set_tcp_error("Invalid format");
if (sscanf(src, "%02d:%02d:%02d", &h, &m, &s) != 3)
return set_tcp_error("Invalid format (non-numbers encountered)");
if (h > 23)
return set_tcp_error("Invalid hour");
if (m > 59)
return set_tcp_error("Invalid minute");
if (s > 59)
return set_tcp_error("Invalid second");
if (strlen(src) > 9) {
if (src[8] != '.')
return set_tcp_error("Invalid format (expected a dot '.' after the "
"seconds)");
sprintf(format, "%%0%dd", strlen(src) - 8);
if (sscanf(&src[9], format, &n) != 1)
return set_tcp_error("Invalid format (non-numbers encountered)");
for (i = strlen(src); i < 18; i++)
n *= 10;
} else
n = 0;
if (timecode != NULL)
*timecode = ((int64_t)h * 60 * 60 + (int64_t)m * 60 + (int64_t)s) *
1000000000 + n;
timecode_parser_error = "no error";
return true;
}

View File

@ -78,11 +78,20 @@ using namespace std;
#define MXMSG_DEBUG 8
#define FMT_TIMECODE "%02d:%02d:%02d.%03d"
#define ARG_TIMECODE(t) (int32_t)(t) / 60 / 60 / 1000, \
(int32_t)((t) / 60 / 1000) % 60, \
(int32_t)((t) / 1000) % 60, \
(int32_t)(t) % 1000
#define ARG_TIMECODEINT(t) (int32_t)((t) / 60 / 60 / 1000), \
(int32_t)(((t) / 60 / 1000) % 60), \
(int32_t)(((t) / 1000) % 60), \
(int32_t)((t) % 1000)
#define ARG_TIMECODE(t) ARG_TIMECODEINT((int64_t)t)
#define ARG_TIMECODE_NS(t) ARG_TIMECODE(t / 1000000)
#define FMT_TIMECODEN "%02d:%02d:%02d.%09d"
#define ARG_TIMECODENINT(t) (int32_t)((t) / 60 / 60 / 1000000000), \
(int32_t)(((t) / 60 / 1000000000) % 60), \
(int32_t)(((t) / 1000000000) % 60), \
(int32_t)((t) % 1000000000)
#define ARG_TIMECODEN(t) ARG_TIMECODENINT((int64_t)t)
extern const char * MTX_DLL_API timecode_parser_error;
extern bool MTX_DLL_API parse_timecode(const char *src, int64_t *timecode);
extern bool MTX_DLL_API suppress_warnings;
void MTX_DLL_API fix_format(const char *fmt, string &new_fmt);

View File

@ -403,19 +403,15 @@ bool parse_chapter_atom(EbmlStream *es, EbmlElement *l0, int level) {
uint64_t s;
KaxChapterTimeStart &start =
*static_cast<KaxChapterTimeStart *>(l1);
s = uint64(start) / 1000000;
show_element(l1, level + 1, "Start: %02llu:%02llu:%02llu.%03llu",
(s / 1000 / 60 / 60), (s / 1000 / 60) % 60,
(s / 1000) % 60, s % 1000);
s = uint64(start);
show_element(l1, level + 1, "Start: " FMT_TIMECODEN, ARG_TIMECODEN(s));
} else if (is_id(l1, KaxChapterTimeEnd)) {
uint64_t e;
KaxChapterTimeEnd &end =
*static_cast<KaxChapterTimeEnd *>(l1);
e = uint64(end) / 1000000;
show_element(l1, level + 1, "End: %02llu:%02llu:%02llu.%03llu",
(e / 1000 / 60 / 60), (e / 1000 / 60) % 60,
(e / 1000) % 60, e % 1000);
e = uint64(end);
show_element(l1, level + 1, "End: " FMT_TIMECODEN, ARG_TIMECODEN(e));
} else if (is_id(l1, KaxChapterTrack)) {
show_element(l1, level + 1, "Track");

View File

@ -255,12 +255,12 @@ tab_chapters::tab_chapters(wxWindow *parent,
new wxStaticText(this, wxID_STATIC, _("Start:"), wxPoint(10, 235));
tc_start_time =
new wxTextCtrl(this, ID_TC_CHAPTERSTART, _(""),
wxPoint(60, 235 + YOFF), wxSize(110, -1));
wxPoint(50, 235 + YOFF), wxSize(125, -1));
new wxStaticText(this, wxID_STATIC, _("End:"), wxPoint(200, 235));
new wxStaticText(this, wxID_STATIC, _("End:"), wxPoint(190, 235));
tc_end_time =
new wxTextCtrl(this, ID_TC_CHAPTEREND, _(""),
wxPoint(250, 235 + YOFF), wxSize(110, -1));
wxPoint(235, 235 + YOFF), wxSize(125, -1));
new wxStaticBox(this, wxID_STATIC, _("Chapter names and languages"),
wxPoint(10, 270), wxSize(480, 180));
@ -397,14 +397,14 @@ tab_chapters::create_chapter_label(KaxChapterAtom &chapter) {
label += " [";
tstart = FindChild<KaxChapterTimeStart>(chapter);
if (tstart != NULL) {
timestamp = uint64(*static_cast<EbmlUInteger *>(tstart)) / 1000000;
s.Printf(FMT_TIMECODE, ARG_TIMECODE(timestamp));
timestamp = uint64(*static_cast<EbmlUInteger *>(tstart));
s.Printf(FMT_TIMECODEN, ARG_TIMECODEN(timestamp));
label += s;
tend = FindChild<KaxChapterTimeEnd>(chapter);
if (tend != NULL) {
timestamp = uint64(*static_cast<EbmlUInteger *>(tend)) / 1000000;
s.Printf(FMT_TIMECODE, ARG_TIMECODE(timestamp));
timestamp = uint64(*static_cast<EbmlUInteger *>(tend));
s.Printf(FMT_TIMECODEN, ARG_TIMECODEN(timestamp));
label += " - " + s;
}
@ -1604,16 +1604,16 @@ tab_chapters::set_timecode_values(KaxChapterAtom *atom) {
tstart = FINDFIRST(atom, KaxChapterTimeStart);
if (tstart != NULL) {
timestamp = uint64(*static_cast<EbmlUInteger *>(tstart)) / 1000000;
label.Printf(FMT_TIMECODE, ARG_TIMECODE(timestamp));
timestamp = uint64(*static_cast<EbmlUInteger *>(tstart));
label.Printf(FMT_TIMECODEN, ARG_TIMECODEN(timestamp));
tc_start_time->SetValue(label);
} else
tc_start_time->SetValue("");
tend = FINDFIRST(atom, KaxChapterTimeEnd);
if (tend != NULL) {
timestamp = uint64(*static_cast<EbmlUInteger *>(tend)) / 1000000;
label.Printf(FMT_TIMECODE, ARG_TIMECODE(timestamp));
timestamp = uint64(*static_cast<EbmlUInteger *>(tend));
label.Printf(FMT_TIMECODEN, ARG_TIMECODEN(timestamp));
tc_end_time->SetValue(label);
} else
tc_end_time->SetValue("");
@ -1662,48 +1662,27 @@ tab_chapters::set_display_values(KaxChapterDisplay *display) {
no_update = false;
}
#define iscolon(s) (*(s) == ':')
#define isdot(s) (*(s) == '.')
#define istwodigits(s) (isdigit(*(s)) && isdigit(*(s + 1)))
#define isthreedigits(s) (istwodigits(s) && isdigit(*(s + 2)))
#define istimestamp(s) ((strlen(s) == 12) && \
istwodigits(s) && iscolon(s + 2) && \
istwodigits(s + 3) && iscolon(s + 5) && \
istwodigits(s + 6) && isdot(s + 8) && \
isthreedigits(s + 9))
#define isshorttimestamp(s) ((strlen(s) == 8) && \
istwodigits(s) && iscolon(s + 2) && \
istwodigits(s + 3) && iscolon(s + 5) && \
istwodigits(s + 6))
int64_t
tab_chapters::parse_time(wxString s) {
int64_t nsecs;
const char *c;
int hour, minutes, seconds, msecs;
int64_t secs;
strip(s);
if (s.length() == 0)
return -2;
c = s.c_str();
if (istimestamp(c)) {
sscanf(c, FMT_TIMECODE, &hour, &minutes, &seconds, &msecs);
return ((int64_t)hour * 60 * 60 * 1000 +
(int64_t)minutes * 60 * 1000 +
(int64_t)seconds * 1000 + msecs) * 1000000;
} else if (isshorttimestamp(c)) {
sscanf(c, "%02d:%02d:%02d", &hour, &minutes, &seconds);
return ((int64_t)hour * 60 * 60 * 1000 +
(int64_t)minutes * 60 * 1000 +
(int64_t)seconds * 1000) * 1000000;
} else if (parse_int(c, secs))
return secs * 1000000000;
return -1;
while (*c != 0) {
if (!isdigit(*c)) {
if (parse_timecode(s.c_str(), &nsecs))
return nsecs;
return -1;
}
c++;
}
if (!parse_int(s.c_str(), nsecs))
return -1;
return nsecs * 1000000000;
}
IMPLEMENT_CLASS(chapter_values_dlg, wxDialog);