mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-25 12:27:21 +00:00
Added support for attaching files to the output file(s).
This commit is contained in:
parent
bf83058489
commit
666a004d22
@ -1,5 +1,8 @@
|
|||||||
2003-07-14 Moritz Bunkus <moritz@bunkus.org>
|
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
|
* mkvinfo: Support for the elements dealing with attachments
|
||||||
(KaxAttachments, KaxAttached, KaxFileDescription, KaxFileName,
|
(KaxAttachments, KaxAttached, KaxFileDescription, KaxFileName,
|
||||||
KaxMimeType, KaxFileData).
|
KaxMimeType, KaxFileData).
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
.TH MKVMERGE "1" "June 2003" "mkvmerge v0.5.0" "User Commands"
|
.TH MKVMERGE "1" "June 2003" "mkvmerge v0.5.0" "User Commands"
|
||||||
|
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
mkvmerge \- Merge multimedia streams into a Matroska file
|
mkvmerge \- Merge multimedia streams into a Matroska file
|
||||||
|
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B mkvmerge
|
.B mkvmerge
|
||||||
[\fIglobal options\fR] \-o \fIout\fR [\fIoptions\fR] <file1> [[\fIoptions\fR] <file2> ...] [@optionsfile]
|
[\fIglobal options\fR] \-o \fIout\fR [\fIoptions\fR] <file1> [[\fIoptions\fR] <file2> ...] [@optionsfile]
|
||||||
|
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.LP
|
.LP
|
||||||
This program takes the input from several media files and joins
|
This program takes the input from several media files and joins
|
||||||
their streams (all of them or just a selection) into a Matroska file.
|
their streams (all of them or just a selection) into a Matroska file.
|
||||||
.UR http://www.matroska.org/
|
.UR http://www.matroska.org/
|
||||||
.UE
|
.UE
|
||||||
|
|
||||||
|
|
||||||
.LP
|
.LP
|
||||||
Global options:
|
Global options:
|
||||||
.TP
|
.TP
|
||||||
@ -87,6 +92,35 @@ section \fBFILE LINKING\fR below for details.
|
|||||||
\fB\-\-link\-to\-next\fR <\fIUID\fR>
|
\fB\-\-link\-to\-next\fR <\fIUID\fR>
|
||||||
Links the last output file to the segment with the given \fIUID\fR. See the
|
Links the last output file to the segment with the given \fIUID\fR. See the
|
||||||
section \fBFILE LINKING\fR below for details.
|
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
|
.LP
|
||||||
Options that can be used for each input file:
|
Options that can be used for each input file:
|
||||||
.TP
|
.TP
|
||||||
@ -167,6 +201,8 @@ listed with the \fB\-\-list\-languages\fR option.
|
|||||||
.br
|
.br
|
||||||
This option can be used multiple times for an input file applying to several
|
This option can be used multiple times for an input file applying to several
|
||||||
tracks by selecting different track IDs each time.
|
tracks by selecting different track IDs each time.
|
||||||
|
|
||||||
|
|
||||||
.LP
|
.LP
|
||||||
Options that only apply to video tracks:
|
Options that only apply to video tracks:
|
||||||
.TP
|
.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
|
in \fBmkvinfo\fR's output then please read the section about \fBDEFAULT
|
||||||
VALUES\fR.
|
VALUES\fR.
|
||||||
|
|
||||||
|
|
||||||
.SH SUBTITLES
|
.SH SUBTITLES
|
||||||
.LP
|
.LP
|
||||||
There are several text subtitle formats that can be embedded into Matroska.
|
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
|
all UTF-16 formats) are not supported, UTF-8/ASCII only; 2) The \fB\\fe\fR
|
||||||
markup is not supported correctly.
|
markup is not supported correctly.
|
||||||
|
|
||||||
|
|
||||||
.SH FILE LINKING
|
.SH FILE LINKING
|
||||||
.LP
|
.LP
|
||||||
Matroska supports file linking which simply says that a specific file is the
|
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
|
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.
|
output file will be linked to both of the two UIDs.
|
||||||
|
|
||||||
|
|
||||||
.SH TRACK IDS
|
.SH TRACK IDS
|
||||||
.LP
|
.LP
|
||||||
Some of the options for \fBmkvmerge\fR need a track ID to specify which track
|
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
|
\fB\-\-stracks\fR, \fB\-\-sync\fR, \fB\-\-default-track\fR, \fB\-\-cues\fR
|
||||||
and \fB\-\-language\fR.
|
and \fB\-\-language\fR.
|
||||||
|
|
||||||
|
|
||||||
.SH DEFAULT VALUES
|
.SH DEFAULT VALUES
|
||||||
.LP
|
.LP
|
||||||
The Matroska specs say that some elements have a default value. Usually an
|
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
|
if you used \fB--language 0:eng\fR for a track then it will not show up
|
||||||
in \fBmkvinfo\fR's output.
|
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
|
.SH NOTES
|
||||||
.LP
|
.LP
|
||||||
What works:
|
What works:
|
||||||
@ -487,6 +548,9 @@ DTS audio files
|
|||||||
MP3 audio files
|
MP3 audio files
|
||||||
.TP
|
.TP
|
||||||
*
|
*
|
||||||
|
RealVideo and RealAudio from RealMedia files
|
||||||
|
.TP
|
||||||
|
*
|
||||||
Track selection
|
Track selection
|
||||||
.TP
|
.TP
|
||||||
*
|
*
|
||||||
|
159
src/mkvmerge.cpp
159
src/mkvmerge.cpp
@ -45,6 +45,8 @@
|
|||||||
#include <ebml/EbmlVoid.h>
|
#include <ebml/EbmlVoid.h>
|
||||||
|
|
||||||
#include <matroska/FileKax.h>
|
#include <matroska/FileKax.h>
|
||||||
|
#include <matroska/KaxAttached.h>
|
||||||
|
#include <matroska/KaxAttachements.h>
|
||||||
#include <matroska/KaxBlock.h>
|
#include <matroska/KaxBlock.h>
|
||||||
#include <matroska/KaxCluster.h>
|
#include <matroska/KaxCluster.h>
|
||||||
#include <matroska/KaxClusterData.h>
|
#include <matroska/KaxClusterData.h>
|
||||||
@ -110,8 +112,16 @@ typedef struct {
|
|||||||
generic_packetizer_c *packetizer;
|
generic_packetizer_c *packetizer;
|
||||||
} packetizer_t;
|
} packetizer_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *name, *mime_type, *description;
|
||||||
|
int64_t size;
|
||||||
|
bool to_all_files;
|
||||||
|
} attachment_t;
|
||||||
|
|
||||||
vector<packetizer_t *> packetizers;
|
vector<packetizer_t *> packetizers;
|
||||||
vector<filelist_t *> files;
|
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.
|
// Variables set by the command line parser.
|
||||||
char *outfile = NULL;
|
char *outfile = NULL;
|
||||||
@ -207,6 +217,15 @@ static void usage(void) {
|
|||||||
" --dont-link Don't link splitted files.\n"
|
" --dont-link Don't link splitted files.\n"
|
||||||
" --link-to-previous <UID> Link the first file to the given UID.\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"
|
" --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"
|
"\n Options for each input file:\n"
|
||||||
" -a, --atracks <n,m,...> Copy audio tracks n,m etc. Default: copy all\n"
|
" -a, --atracks <n,m,...> Copy audio tracks n,m etc. Default: copy all\n"
|
||||||
" audio tracks.\n"
|
" audio tracks.\n"
|
||||||
@ -426,6 +445,8 @@ static float parse_aspect_ratio(char *s) {
|
|||||||
float w, h;
|
float w, h;
|
||||||
|
|
||||||
div = strchr(s, '/');
|
div = strchr(s, '/');
|
||||||
|
if (div == NULL)
|
||||||
|
div = strchr(s, ':');
|
||||||
if (div == NULL)
|
if (div == NULL)
|
||||||
return strtod(s, 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() {
|
static void create_readers() {
|
||||||
filelist_t *file;
|
filelist_t *file;
|
||||||
int i;
|
int i;
|
||||||
@ -851,6 +942,8 @@ static void parse_args(int argc, char **argv) {
|
|||||||
cue_creation_t cues;
|
cue_creation_t cues;
|
||||||
int64_t id;
|
int64_t id;
|
||||||
language_t lang;
|
language_t lang;
|
||||||
|
attachment_t *attachment;
|
||||||
|
mm_io_c *io;
|
||||||
|
|
||||||
memset(&ti, 0, sizeof(track_info_t));
|
memset(&ti, 0, sizeof(track_info_t));
|
||||||
ti.audio_syncs = new vector<audio_sync_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.atracks = new vector<int64_t>;
|
||||||
ti.vtracks = new vector<int64_t>;
|
ti.vtracks = new vector<int64_t>;
|
||||||
ti.stracks = 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
|
// Check if only information about the file is wanted. In this mode only
|
||||||
// two parameters are allowed: the --identify switch and the file.
|
// 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"))
|
else if (!strcmp(argv[i], "--no-lacing"))
|
||||||
no_lacing = true;
|
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.
|
// Options that apply to the next input file only.
|
||||||
else if (!strcmp(argv[i], "-A") || !strcmp(argv[i], "--noaudio"))
|
else if (!strcmp(argv[i], "-A") || !strcmp(argv[i], "--noaudio"))
|
||||||
ti.no_audio = true;
|
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 "
|
mxprint(stderr, "Warning: '--dont-link' is only useful in combination "
|
||||||
"with '--split'.\n");
|
"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) {
|
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);
|
cluster_helper->set_output(out);
|
||||||
render_headers(out, last_file, first_file);
|
render_headers(out, last_file, first_file);
|
||||||
|
render_attachments(out);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1464,6 +1622,7 @@ void create_next_output_file(bool last_file, bool first_file) {
|
|||||||
|
|
||||||
cluster_helper->set_output(out);
|
cluster_helper->set_output(out);
|
||||||
render_headers(out, last_file, first_file);
|
render_headers(out, last_file, first_file);
|
||||||
|
render_attachments(out);
|
||||||
|
|
||||||
file_num++;
|
file_num++;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user