mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
Added a tool for encoding/decoding Base64.
This commit is contained in:
parent
1396beb59f
commit
480640f014
@ -686,7 +686,7 @@ Matroska element.
|
||||
Otherwise the data is expected to be \fIBase64\fR encoded. This is an
|
||||
encoding that transforms binary data into a limited set of ASCII
|
||||
characters and is used e.g. in email programs. \fBmkvtoolnix\fR comes
|
||||
with a utility, \fBbase64util\fR, that can be used to encode to and
|
||||
with a utility, \fBbase64tool\fR, that can be used to encode to and
|
||||
decode from Base64. \fBmkvextract\fR will output Base64 encoded data
|
||||
for binary elements.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
AUTOMAKE_OPTIONS = dist-zip foreign
|
||||
|
||||
bin_PROGRAMS = mkvmerge mkvinfo mkvextract
|
||||
bin_PROGRAMS = mkvmerge mkvinfo mkvextract base64tool
|
||||
|
||||
mkvmerge_SOURCES = mkvmerge.cpp mkvmerge.h \
|
||||
aac_common.cpp aac_common.h \
|
||||
@ -53,6 +53,11 @@ mkvextract_SOURCES = mkvextract.cpp \
|
||||
mm_io.cpp mm_io.h \
|
||||
tagwriter.cpp tagwriter.h
|
||||
|
||||
base64tool_SOURCES = base64tool.cpp \
|
||||
base64.cpp base64.h \
|
||||
common.cpp common.h \
|
||||
mm_io.cpp mm_io.h
|
||||
|
||||
mkvmerge_LDADD = @AVILIB_LIBS@ @PROFILING_LIBS@ \
|
||||
@MATROSKA_LIBS@ @EBML_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ \
|
||||
@ICONV_LIBS@ @MINGW_LIBS@ @EXPAT_LIBS@ -lexpat
|
||||
@ -63,3 +68,5 @@ mkvinfo_LDADD = @PROFILING_LIBS@ @MATROSKA_LIBS@ @EBML_LIBS@ @ICONV_LIBS@ \
|
||||
mkvextract_LDADD = @AVILIB_LIBS@ @PROFILING_LIBS@ @MATROSKA_LIBS@ @EBML_LIBS@ \
|
||||
@VORBIS_LIBS@ @OGG_LIBS@ @ICONV_LIBS@
|
||||
|
||||
base64tool_LDADD = @EBML_LIBS@ @ICONV_LIBS@
|
||||
|
||||
|
139
src/base64tool.cpp
Normal file
139
src/base64tool.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
base64util - Utility for encoding and decoding Base64 files.
|
||||
|
||||
base64util.cpp
|
||||
|
||||
Written by Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
Distributed under the GPL
|
||||
see the file COPYING for details
|
||||
or visit http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
\file
|
||||
\version $Id$
|
||||
\brief command line parameter parsing, looping, output handling
|
||||
\author Moritz Bunkus <moritz@bunkus.org>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "os.h"
|
||||
|
||||
#include "base64.h"
|
||||
#include "common.h"
|
||||
#include "mm_io.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void usage(int retval) {
|
||||
mxprint(stdout,
|
||||
"base64util <encode|decode> <input> <output> [maxlen]\n"
|
||||
"\n"
|
||||
" encode - Read from <input>, encode to Base64 and write to <output>.\n"
|
||||
" Max line length can be specified and is 72 if left out.\n"
|
||||
" decode - Read from <input>, decode to binary and write to <output>.\n");
|
||||
|
||||
exit(retval);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int maxlen;
|
||||
int64_t size;
|
||||
unsigned char *buffer;
|
||||
char mode;
|
||||
string s, line;
|
||||
mm_io_c *in, *out;
|
||||
mm_text_io_c *intext;
|
||||
|
||||
if (argc < 4)
|
||||
usage(0);
|
||||
|
||||
if (!strcmp(argv[1], "encode"))
|
||||
mode = 'e';
|
||||
else if (!strcmp(argv[1], "decode"))
|
||||
mode = 'd';
|
||||
else {
|
||||
mxprint(stdout, "Error: invalid mode '%s'.\n\n", argv[1]);
|
||||
usage(1);
|
||||
}
|
||||
|
||||
maxlen = 72;
|
||||
if ((argc == 5) && (mode == 'e')) {
|
||||
if (!parse_int(argv[4], maxlen) || (maxlen < 4)) {
|
||||
mxprint(stdout, "Error: Max line length must be >= 4.\n\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if ((argc > 5) || ((argc > 4) && (mode == 'd')))
|
||||
usage(1);
|
||||
|
||||
maxlen = ((maxlen + 3) / 4) * 4;
|
||||
|
||||
try {
|
||||
if (mode == 'e')
|
||||
in = new mm_io_c(argv[2], MODE_READ);
|
||||
else {
|
||||
intext = new mm_text_io_c(argv[2]);
|
||||
in = intext;
|
||||
}
|
||||
} catch(...) {
|
||||
mxprint(stdout, "Error: Could not open '%s' for reading (%d, %s).\n",
|
||||
argv[2], errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
out = new mm_io_c(argv[3], MODE_WRITE);
|
||||
} catch(...) {
|
||||
mxprint(stdout, "Error: Could not open '%s' for writing (%d, %s).\n",
|
||||
argv[3], errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
in->save_pos();
|
||||
in->setFilePointer(0, seek_end);
|
||||
size = in->getFilePointer();
|
||||
in->restore_pos();
|
||||
|
||||
if (mode == 'e') {
|
||||
buffer = (unsigned char *)safemalloc(size);
|
||||
size = in->read(buffer, size);
|
||||
delete in;
|
||||
|
||||
s = base64_encode(buffer, size, true, maxlen);
|
||||
safefree(buffer);
|
||||
|
||||
out->write(s.c_str(), s.length());
|
||||
delete out;
|
||||
|
||||
} else {
|
||||
|
||||
while (intext->getline2(line)) {
|
||||
strip(line);
|
||||
s += line;
|
||||
}
|
||||
delete intext;
|
||||
|
||||
buffer = (unsigned char *)safemalloc(s.length() / 4 * 3 + 100);
|
||||
try {
|
||||
size = base64_decode(s, buffer);
|
||||
} catch(...) {
|
||||
mxprint(stdout, "Error decoding data.\n");
|
||||
delete in;
|
||||
delete out;
|
||||
exit(1);
|
||||
}
|
||||
out->write(buffer, size);
|
||||
|
||||
safefree(buffer);
|
||||
delete out;
|
||||
}
|
||||
|
||||
mxprint(stdout, "Done.\n");
|
||||
|
||||
return 0;
|
||||
}
|
154
src/chapters.cpp
154
src/chapters.cpp
@ -339,3 +339,157 @@ void write_chapters_xml(KaxChapters *chapters, FILE *out) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class chapter_entry_c {
|
||||
public:
|
||||
string name;
|
||||
int64_t start, level;
|
||||
|
||||
chapter_entry_c(string n, int64_t s, int64_t l);
|
||||
|
||||
bool operator < (const chapter_entry_c &cmp) const;
|
||||
};
|
||||
|
||||
chapter_entry_c::chapter_entry_c(string n, int64_t s, int64_t l) {
|
||||
name = n;
|
||||
start = s;
|
||||
level = l;
|
||||
}
|
||||
|
||||
bool chapter_entry_c::operator <(const chapter_entry_c &cmp) const {
|
||||
return start < cmp.start;
|
||||
}
|
||||
|
||||
static vector<chapter_entry_c> chapter_start_times, chapter_names,
|
||||
chapter_entries;
|
||||
|
||||
static void handle_name(int level, const char *name) {
|
||||
int i, j;
|
||||
vector<chapter_entry_c>::iterator itr;
|
||||
|
||||
for (i = 0; i < chapter_start_times.size(); i++) {
|
||||
chapter_entry_c &e = chapter_start_times[i];
|
||||
if (e.level == level) {
|
||||
chapter_entries.push_back(chapter_entry_c(string(name), e.start, level));
|
||||
itr = chapter_start_times.begin();
|
||||
for (j = 0; j < i; j++)
|
||||
itr++;
|
||||
chapter_start_times.erase(itr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
chapter_names.push_back(chapter_entry_c(name, 0, level));
|
||||
}
|
||||
|
||||
static void handle_start_time(int level, int64_t start_time) {
|
||||
int i, j;
|
||||
vector<chapter_entry_c>::iterator itr;
|
||||
|
||||
for (i = 0; i < chapter_names.size(); i++) {
|
||||
chapter_entry_c &e = chapter_names[i];
|
||||
if (e.level == level) {
|
||||
chapter_entries.push_back(chapter_entry_c(e.name, start_time, level));
|
||||
itr = chapter_names.begin();
|
||||
for (j = 0; j < i; j++)
|
||||
itr++;
|
||||
chapter_names.erase(itr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
chapter_start_times.push_back(chapter_entry_c("", start_time, level));
|
||||
}
|
||||
|
||||
static void write_chapter_atom_simple(KaxChapterAtom *atom, int level);
|
||||
|
||||
static void write_chapter_display_simple(KaxChapterDisplay *display,
|
||||
int level) {
|
||||
int i;
|
||||
EbmlElement *e;
|
||||
char *s;
|
||||
|
||||
for (i = 0; i < display->ListSize(); i++) {
|
||||
e = (*display)[i];
|
||||
if (is_id(KaxChapterString)) {
|
||||
s = UTFstring_to_cstrutf8(UTFstring(*static_cast
|
||||
<EbmlUnicodeString *>(e)).c_str());
|
||||
handle_name(level - 1, s);
|
||||
safefree(s);
|
||||
|
||||
} else if (is_id(KaxChapterAtom))
|
||||
write_chapter_atom_simple((KaxChapterAtom *)e, level + 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void write_chapter_track_simple(KaxChapterTrack *track, int level) {
|
||||
int i;
|
||||
EbmlElement *e;
|
||||
|
||||
for (i = 0; i < track->ListSize(); i++) {
|
||||
e = (*track)[i];
|
||||
|
||||
if (is_id(KaxChapterAtom))
|
||||
write_chapter_atom_simple((KaxChapterAtom *)e, level + 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void write_chapter_atom_simple(KaxChapterAtom *atom, int level) {
|
||||
int i;
|
||||
EbmlElement *e;
|
||||
uint64_t v;
|
||||
|
||||
for (i = 0; i < atom->ListSize(); i++) {
|
||||
e = (*atom)[i];
|
||||
|
||||
if (is_id(KaxChapterTimeStart)) {
|
||||
v = uint64(*static_cast<EbmlUInteger *>(e)) / 1000000;
|
||||
handle_start_time(level, level);
|
||||
|
||||
} else if (is_id(KaxChapterTrack))
|
||||
write_chapter_track_simple((KaxChapterTrack *)e, level + 1);
|
||||
|
||||
else if (is_id(KaxChapterDisplay))
|
||||
write_chapter_display_simple((KaxChapterDisplay *)e, level + 1);
|
||||
|
||||
else if (is_id(KaxChapterAtom))
|
||||
write_chapter_atom_simple((KaxChapterAtom *)e, level + 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void write_chapters_simple(int &chapter_num, KaxChapters *chapters,
|
||||
FILE *out) {
|
||||
int i, j;
|
||||
int64_t v;
|
||||
KaxEditionEntry *edition;
|
||||
|
||||
chapter_start_times.clear();
|
||||
chapter_names.clear();
|
||||
chapter_entries.clear();
|
||||
|
||||
for (i = 0; i < chapters->ListSize(); i++) {
|
||||
if (is_id2((*chapters)[i], KaxEditionEntry)) {
|
||||
edition = (KaxEditionEntry *)(*chapters)[i];
|
||||
for (j = 0; j < edition->ListSize(); j++)
|
||||
if (is_id2((*edition)[j], KaxChapterAtom))
|
||||
write_chapter_atom_simple((KaxChapterAtom *)(*edition)[j], 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < chapter_entries.size(); i++) {
|
||||
v = chapter_entries[i].start;
|
||||
mxprint(out, "CHAPTER%02d=%02lld:%02lld:%02lld.%03lld\n", chapter_num,
|
||||
(v / 1000 / 60 / 60), (v / 1000 / 60) % 60,
|
||||
(v / 1000) % 60, v % 1000);
|
||||
mxprint(out, "CHAPTER%02dNAME=%s\n", chapter_num,
|
||||
chapter_entries[i].name.c_str());
|
||||
chapter_num++;
|
||||
}
|
||||
|
||||
chapter_start_times.clear();
|
||||
chapter_names.clear();
|
||||
chapter_entries.clear();
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ KaxChapters *parse_chapters(const char *file_name, int64_t min_tc = 0,
|
||||
int64_t max_tc = -1, int64_t offset = 0);
|
||||
|
||||
void write_chapters_xml(KaxChapters *chapters, FILE *out);
|
||||
void write_chapters_simple(int &chapter_num, KaxChapters *chapters, FILE *out);
|
||||
|
||||
#endif // __CHAPTERS_H
|
||||
|
||||
|
@ -137,6 +137,7 @@ typedef struct {
|
||||
} kax_track_t;
|
||||
|
||||
vector<kax_track_t> tracks;
|
||||
bool chapter_format_simple = false;
|
||||
|
||||
kax_track_t *find_track(int tid) {
|
||||
int i;
|
||||
@ -176,9 +177,11 @@ void usage() {
|
||||
" Third mode extracts attachments from inname.\n"
|
||||
" AID:outname Write the attachment with the ID AID to 'outname'.\n"
|
||||
"\n"
|
||||
" Second mode extracts the chapters and converts them to XML. The output is\n"
|
||||
" Fourth mode extracts the chapters and converts them to XML. The output is\n"
|
||||
" written to the standard output. The output can be used as a source\n"
|
||||
" for mkvmerge.\n"
|
||||
" -s, --simple Exports the chapter infomartion in the simple format\n"
|
||||
" used in OGM tools (CHAPTER01=... CHAPTER01NAME=...).\n"
|
||||
"\n"
|
||||
" These options can be used instead of the mode keyword to obtain\n"
|
||||
" further information:\n"
|
||||
@ -251,11 +254,19 @@ void parse_args(int argc, char **argv, char *&file_name, int &mode) {
|
||||
conv_handle = utf8_init(argv[i + 1]);
|
||||
i++;
|
||||
|
||||
} else if ((mode == MODE_TAGS) || (mode == MODE_CHAPTERS)) {
|
||||
} else if (mode == MODE_TAGS) {
|
||||
mxprint(stderr, "Error: No further options allowed when extracting "
|
||||
"%s.\n", argv[1]);
|
||||
exit(1);
|
||||
|
||||
} else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--simple")) {
|
||||
if (mode != MODE_CHAPTERS) {
|
||||
mxprint(stderr, "Error: %s is only allowed for chapter extraction.\n",
|
||||
argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
chapter_format_simple = true;
|
||||
|
||||
} else {
|
||||
copy = safestrdup(argv[i]);
|
||||
colon = strchr(copy, ':');
|
||||
@ -1580,6 +1591,7 @@ void extract_chapters(const char *file_name) {
|
||||
EbmlStream *es;
|
||||
mm_io_c *in;
|
||||
bool chapters_extracted = false;
|
||||
int next_chapter = 1;
|
||||
|
||||
// open input file
|
||||
try {
|
||||
@ -1640,12 +1652,15 @@ void extract_chapters(const char *file_name) {
|
||||
chapters.Read(*es, KaxChapters::ClassInfos.Context, upper_lvl_el, l2,
|
||||
true);
|
||||
|
||||
if (!chapters_extracted) {
|
||||
if (!chapters_extracted && !chapter_format_simple) {
|
||||
mxprint(stdout, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n"
|
||||
"<Chapters>\n");
|
||||
chapters_extracted = true;
|
||||
}
|
||||
|
||||
if (chapter_format_simple)
|
||||
write_chapters_simple(next_chapter, &chapters, stdout);
|
||||
else
|
||||
write_chapters_xml(&chapters, stdout);
|
||||
|
||||
} else
|
||||
@ -1683,7 +1698,7 @@ void extract_chapters(const char *file_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chapters_extracted)
|
||||
if (chapters_extracted && !chapter_format_simple)
|
||||
mxprint(stdout, "</Chapters>\n");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user