Added support for attaching files to the output file(s).

This commit is contained in:
Moritz Bunkus 2003-07-14 19:10:36 +00:00
parent bf83058489
commit 666a004d22
3 changed files with 226 additions and 0 deletions

View File

@ -1,5 +1,8 @@
2003-07-14 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: Added support for attaching files to the output
file(s).
* mkvinfo: Support for the elements dealing with attachments
(KaxAttachments, KaxAttached, KaxFileDescription, KaxFileName,
KaxMimeType, KaxFileData).

View File

@ -1,18 +1,23 @@
.TH MKVMERGE "1" "June 2003" "mkvmerge v0.5.0" "User Commands"
.SH NAME
mkvmerge \- Merge multimedia streams into a Matroska file
.SH SYNOPSIS
.B mkvmerge
[\fIglobal options\fR] \-o \fIout\fR [\fIoptions\fR] <file1> [[\fIoptions\fR] <file2> ...] [@optionsfile]
.SH DESCRIPTION
.LP
This program takes the input from several media files and joins
their streams (all of them or just a selection) into a Matroska file.
.UR http://www.matroska.org/
.UE
.LP
Global options:
.TP
@ -87,6 +92,35 @@ section \fBFILE LINKING\fR below for details.
\fB\-\-link\-to\-next\fR <\fIUID\fR>
Links the last output file to the segment with the given \fIUID\fR. See the
section \fBFILE LINKING\fR below for details.
.TP
\fB\-\-attachment\-description\fR <\fIdescription\fR>
Plain text description of the following attachment. Applies to the next
\fB\-\-attach\-file\fR or \fB\-\-attach\-file\-once\fR command.
.TP
\fB\-\-attachment\-mime\-type\fR <\fIMIME type\fR>
MIME type of the following attachment. Applies to the next
\fB\-\-attach\-file\fR or \fB\-\-attach\-file\-once\fR command.
A list of officially recognized MIME types can be found e.g. at
.URL
ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/media-types
The MIME type is mandatory for an attachment.
.TP
\fB\-\-attach\-file\fR <\fIfile name\fR>
.TP
\fB\-\-attach\-file\-once\fR <\fIfile name\fR>
Creates a file attachment inside the Matroska file. The MIME type must have
been set before this option can used. The difference between the two forms
is that during splitting the files attached with \fB\-\-attach\-file\fR are
attached to all output files while the ones attached with
\fB\-\-attach\-file\-once\fR are only attached to the first file created.
If splitting is not used then both do the same.
.br
\fBmkvextract\fR can be used to extract attached files from a Matroska file.
.br
\fBNote:\fR If an input file is a Matroska file then the attached files will
not be copied to the output file(s). This may change in the future.
.LP
Options that can be used for each input file:
.TP
@ -167,6 +201,8 @@ listed with the \fB\-\-list\-languages\fR option.
.br
This option can be used multiple times for an input file applying to several
tracks by selecting different track IDs each time.
.LP
Options that only apply to video tracks:
.TP
@ -358,6 +394,7 @@ If you do not see the language or default track flags that you've specified
in \fBmkvinfo\fR's output then please read the section about \fBDEFAULT
VALUES\fR.
.SH SUBTITLES
.LP
There are several text subtitle formats that can be embedded into Matroska.
@ -382,6 +419,7 @@ limitations: 1) Files saved with more than one byte per character (e.g.
all UTF-16 formats) are not supported, UTF-8/ASCII only; 2) The \fB\\fe\fR
markup is not supported correctly.
.SH FILE LINKING
.LP
Matroska supports file linking which simply says that a specific file is the
@ -419,6 +457,7 @@ If splitting is used then the first file is linked to the UID given with
with \'\fB\-\-link\-to\-next\fR\'. If splitting is not used then the one
output file will be linked to both of the two UIDs.
.SH TRACK IDS
.LP
Some of the options for \fBmkvmerge\fR need a track ID to specify which track
@ -448,6 +487,7 @@ The options that use the track IDs are: \fB\-\-atracks\fR, \fB\-\-vtracks\fR,
\fB\-\-stracks\fR, \fB\-\-sync\fR, \fB\-\-default-track\fR, \fB\-\-cues\fR
and \fB\-\-language\fR.
.SH DEFAULT VALUES
.LP
The Matroska specs say that some elements have a default value. Usually an
@ -459,6 +499,27 @@ and the default value for the \fIdefault track flag\fR is \fItrue\fR. Therefore
if you used \fB--language 0:eng\fR for a track then it will not show up
in \fBmkvinfo\fR's output.
.SH ATTACHMENTS
.LP
Maybe you also want to keep some photos along with your Matroska file, or
you're using SSA subtitles and need a special TrueType font that's really
rare. In these cases you can attach those files to the Matroska file. They
will not be just appended to the file but embedded in it. A player can then
show those files (the 'photos' case) or use them to render the subtitles
(the 'TrueType fonts' case).
.LP
Here's an example how to attach a photo and a TrueType font to the output
file:
.br
$ \fBmkvmerge -o output.mkv -A video.avi sound.ogg \-\-attachment\-description
"Me and the band behind the stage in a small get-together"
\-\-attachment\-mime\-type image/jpeg \-\-attach\-file me_and_the_band.jpg
\-\-attachment\-description "The real rare and unbelievably good looking font"
\-\-attachment\-type application/octet\-stream
\-\-attach\-file really_cool_font.ttf
.SH NOTES
.LP
What works:
@ -487,6 +548,9 @@ DTS audio files
MP3 audio files
.TP
*
RealVideo and RealAudio from RealMedia files
.TP
*
Track selection
.TP
*

View File

@ -45,6 +45,8 @@
#include <ebml/EbmlVoid.h>
#include <matroska/FileKax.h>
#include <matroska/KaxAttached.h>
#include <matroska/KaxAttachements.h>
#include <matroska/KaxBlock.h>
#include <matroska/KaxCluster.h>
#include <matroska/KaxClusterData.h>
@ -110,8 +112,16 @@ typedef struct {
generic_packetizer_c *packetizer;
} packetizer_t;
typedef struct {
char *name, *mime_type, *description;
int64_t size;
bool to_all_files;
} attachment_t;
vector<packetizer_t *> packetizers;
vector<filelist_t *> files;
vector<attachment_t *> attachments;
int64_t attachment_sizes_first = 0, attachment_sizes_others = 0;
// Variables set by the command line parser.
char *outfile = NULL;
@ -207,6 +217,15 @@ static void usage(void) {
" --dont-link Don't link splitted files.\n"
" --link-to-previous <UID> Link the first file to the given UID.\n"
" --link-to-next <UID> Link the last file to the given UID.\n"
" --attachment-description <desc>\n"
" Description for the following attachment.\n"
" --attachment-mime-type <mime type>\n"
" Mime type for the following attachment.\n"
" --attach-file <file> Creates a file attachment inside the\n"
" Matroska file.\n"
" --attach-file-once <file>\n"
" Creates a file attachment inside the\n"
" firsts Matroska file written.\n"
"\n Options for each input file:\n"
" -a, --atracks <n,m,...> Copy audio tracks n,m etc. Default: copy all\n"
" audio tracks.\n"
@ -426,6 +445,8 @@ static float parse_aspect_ratio(char *s) {
float w, h;
div = strchr(s, '/');
if (div == NULL)
div = strchr(s, ':');
if (div == NULL)
return strtod(s, NULL);
@ -703,6 +724,76 @@ static void render_headers(mm_io_c *out, bool last_file, bool first_file) {
}
}
static void render_attachments(IOCallback *out) {
KaxAttachements *kax_as;
KaxAttached *kax_a;
KaxFileData *fdata;
attachment_t *attch;
int i;
char *name;
binary *buffer;
int64_t size;
mm_io_c *io;
if (!(((file_num == 1) && (attachment_sizes_first > 0)) ||
(attachment_sizes_others > 0)))
return;
kax_as = new KaxAttachements();
kax_a = NULL;
for (i = 0; i < attachments.size(); i++) {
attch = attachments[i];
if ((file_num == 1) || attch->to_all_files) {
if (kax_a == NULL)
kax_a = &GetChild<KaxAttached>(*kax_as);
else
kax_a = &GetNextChild<KaxAttached>(*kax_as, *kax_a);
if (attch->description != NULL)
*static_cast<EbmlUnicodeString *>
(&GetChild<KaxFileDescription>(*kax_a)) =
cstr_to_UTFstring(attch->mime_type);;
if (attch->mime_type != NULL)
*static_cast<EbmlString *>(&GetChild<KaxMimeType>(*kax_a)) =
attch->mime_type;
name = &attch->name[strlen(attch->name) - 1];
while ((name != attch->name) && (*name != '/'))
name--;
if (*name == '/')
name++;
if (*name == 0)
die("Internal error: *name == 0 on %d.", __LINE__);
*static_cast<EbmlUnicodeString *>
(&GetChild<KaxFileName>(*kax_a)) =
cstr_to_UTFstring(name);
try {
io = new mm_io_c(attch->name, MODE_READ);
io->setFilePointer(0, seek_end);
size = io->getFilePointer();
io->setFilePointer(0, seek_beginning);
buffer = new binary[size];
io->read(buffer, size);
delete io;
fdata = &GetChild<KaxFileData>(*kax_a);
fdata->SetBuffer(buffer, size);
} catch (...) {
mxprint(stderr, "Error: Could not open the attachment '%s'.\n",
attch->name);
}
}
}
kax_as->Render(*out);
delete kax_as;
}
static void create_readers() {
filelist_t *file;
int i;
@ -851,6 +942,8 @@ static void parse_args(int argc, char **argv) {
cue_creation_t cues;
int64_t id;
language_t lang;
attachment_t *attachment;
mm_io_c *io;
memset(&ti, 0, sizeof(track_info_t));
ti.audio_syncs = new vector<audio_sync_t>;
@ -862,6 +955,8 @@ static void parse_args(int argc, char **argv) {
ti.atracks = new vector<int64_t>;
ti.vtracks = new vector<int64_t>;
ti.stracks = new vector<int64_t>;
attachment = (attachment_t *)safemalloc(sizeof(attachment_t));
memset(attachment, 0, sizeof(attachment_t));
// Check if only information about the file is wanted. In this mode only
// two parameters are allowed: the --identify switch and the file.
@ -1034,6 +1129,62 @@ static void parse_args(int argc, char **argv) {
else if (!strcmp(argv[i], "--no-lacing"))
no_lacing = true;
else if (!strcmp(argv[i], "--attachment-description")) {
if ((i + 1) >= argc) {
mxprint(stderr, "Error: --attachment-description lacks the "
"description.\n");
exit(1);
}
safefree(attachment->description);
attachment->description = safestrdup(argv[i + 1]);
i++;
} else if (!strcmp(argv[i], "--attachment-mime-type")) {
if ((i + 1) >= argc) {
mxprint(stderr, "Error: --attachment-mime-type lacks the "
"MIME type.\n");
exit(1);
}
safefree(attachment->mime_type);
attachment->mime_type = safestrdup(argv[i + 1]);
i++;
} else if (!strcmp(argv[i], "--attach-file") ||
!strcmp(argv[i], "--attach-file-once")) {
if ((i + 1) >= argc) {
mxprint(stderr, "Error: %s lacks the file name.\n", argv[i]);
exit(1);
}
if (attachment->mime_type == NULL) {
mxprint(stderr, "Error: No MIME type was set for the attachment '%s'."
"\n", argv[i + 1]);
exit(1);
}
attachment->name = safestrdup(argv[i + 1]);
if (!strcmp(argv[i], "--attach-file"))
attachment->to_all_files = true;
try {
io = new mm_io_c(attachment->name, MODE_READ);
io->setFilePointer(0, seek_end);
attachment->size = io->getFilePointer();
delete io;
if (attachment->size == 0)
throw exception();
} catch (...) {
mxprint(stderr, "Error: Could not open the attachment '%s', or its "
"size is 0.\n", attachment->name);
exit(1);
}
attachments.push_back(attachment);
attachment = (attachment_t *)safemalloc(sizeof(attachment_t));
memset(attachment, 0, sizeof(attachment_t));
i++;
}
// Options that apply to the next input file only.
else if (!strcmp(argv[i], "-A") || !strcmp(argv[i], "--noaudio"))
ti.no_audio = true;
@ -1213,6 +1364,12 @@ static void parse_args(int argc, char **argv) {
mxprint(stderr, "Warning: '--dont-link' is only useful in combination "
"with '--split'.\n");
}
for (i = 0; i < attachments.size(); i++) {
attachment_sizes_first += attachments[i]->size;
if (attachments[i]->to_all_files)
attachment_sizes_others += attachments[i]->size;
}
}
static char **add_string(int &num, char **values, char *new_string) {
@ -1442,6 +1599,7 @@ void create_next_output_file(bool last_file, bool first_file) {
cluster_helper->set_output(out);
render_headers(out, last_file, first_file);
render_attachments(out);
return;
}
@ -1464,6 +1622,7 @@ void create_next_output_file(bool last_file, bool first_file) {
cluster_helper->set_output(out);
render_headers(out, last_file, first_file);
render_attachments(out);
file_num++;
}