mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
Increased resolution for chapters to ns.
This commit is contained in:
parent
61581b676e
commit
a519d8b84b
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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>");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user