mkvmerge will copy the attachments from Matroska source files. Can be disabled with the --no-attachments option.

This commit is contained in:
Moritz Bunkus 2003-09-29 19:26:26 +00:00
parent b7c1b707c7
commit 0b02af818d
10 changed files with 140 additions and 26 deletions

View File

@ -1,3 +1,8 @@
2003-09-29 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: new feature: Attachments are kept when reading
Matroska files.
2003-09-28 Moritz Bunkus <moritz@bunkus.org> 2003-09-28 Moritz Bunkus <moritz@bunkus.org>
* mmg: new feature: Added a (nearly) full-featured chapter editor. * mmg: new feature: Added a (nearly) full-featured chapter editor.

2
debian/rules vendored
View File

@ -76,7 +76,7 @@ binary-arch: build install
dh_testdir dh_testdir
dh_testroot dh_testroot
# dh_installdebconf # dh_installdebconf
# dh_installdocs doc/matroskatags.dtd doc/matroskachapters.dtd doc/example-tags-1.xml doc/example-chapters-1.xml doc/example-chapters-2.xml dh_installdocs doc/mkvmerge-gui.html
dh_installexamples examples/*xml examples/*dtd dh_installexamples examples/*xml examples/*dtd
# dh_installmenu # dh_installmenu
# dh_installlogrotate # dh_installlogrotate

View File

@ -179,6 +179,9 @@ Don't copy any subtitle track from this file.
\fB\-\-no\-chapters\fR \fB\-\-no\-chapters\fR
If the source is a Matroska file then don't copy chapters from it. If the source is a Matroska file then don't copy chapters from it.
.TP .TP
\fB\-\-no\-attachments\fR
If the source is a Matroska file then don't copy attachments from it.
.TP
\fB\-y\fR, \fB\-\-sync\fR <\fITID\fR:\fId\fR[,\fIo\fR[/\fIp\fR]]> \fB\-y\fR, \fB\-\-sync\fR <\fITID\fR:\fId\fR[,\fIo\fR[/\fIp\fR]]>
Synchronize manually, delay the audio track with the id \fITID\fR by \fId\fR Synchronize manually, delay the audio track with the id \fITID\fR by \fId\fR
ms. The track IDs are the same as the ones given with \fB\-\-identify\fR (see ms. The track IDs are the same as the ones given with \fB\-\-identify\fR (see

View File

@ -300,6 +300,7 @@ static void usage() {
" -D, --novideo Don't copy any video track from this file.\n" " -D, --novideo Don't copy any video track from this file.\n"
" -S, --nosubs Don't copy any text track from this file.\n" " -S, --nosubs Don't copy any text track from this file.\n"
" --no-chapters Don't keep chapters from a Matroska file.\n" " --no-chapters Don't keep chapters from a Matroska file.\n"
" --no-attachments Don't keep attachments from a Matroska file.\n"
" -y, --sync <TID:d[,o[/p]]>\n" " -y, --sync <TID:d[,o[/p]]>\n"
" Synchronize, delay the audio track with the\n" " Synchronize, delay the audio track with the\n"
" id TID by d ms. \n" " id TID by d ms. \n"
@ -885,6 +886,7 @@ static void render_headers(mm_io_c *out, bool last_file, bool first_file) {
} }
static void render_attachments(IOCallback *out) { static void render_attachments(IOCallback *out) {
KaxAttachments *other_as;
KaxAttached *kax_a; KaxAttached *kax_a;
KaxFileData *fdata; KaxFileData *fdata;
attachment_t *attch; attachment_t *attch;
@ -894,9 +896,21 @@ static void render_attachments(IOCallback *out) {
int64_t size; int64_t size;
mm_io_c *io; mm_io_c *io;
if (!(((file_num == 1) && (attachment_sizes_first > 0)) || other_as = new KaxAttachments;
(attachment_sizes_others > 0))) for (i = 0; i < files.size(); i++)
files[i]->reader->add_attachments(other_as);
for (i = 0, size = 0; i < other_as->ListSize(); i++) {
kax_a = static_cast<KaxAttached *>((*other_as)[i]);
fdata = FindChild<KaxFileData>(*kax_a);
if (fdata != NULL)
size += fdata->GetSize();
}
if (!(((file_num == 1) && ((attachment_sizes_first + size) > 0)) ||
((attachment_sizes_others + size) > 0))) {
delete other_as;
return; return;
}
if (kax_as != NULL) if (kax_as != NULL)
delete kax_as; delete kax_as;
@ -936,10 +950,7 @@ static void render_attachments(IOCallback *out) {
try { try {
io = new mm_io_c(attch->name, MODE_READ); io = new mm_io_c(attch->name, MODE_READ);
io->setFilePointer(0, seek_end); size = io->get_size();
size = io->getFilePointer();
io->setFilePointer(0, seek_beginning);
buffer = new binary[size]; buffer = new binary[size];
io->read(buffer, size); io->read(buffer, size);
delete io; delete io;
@ -952,6 +963,12 @@ static void render_attachments(IOCallback *out) {
} }
} }
while (other_as->ListSize() > 0) {
kax_as->PushElement(*(*other_as)[0]);
other_as->Remove(0);
}
delete other_as;
kax_as->Render(*out); kax_as->Render(*out);
} }
@ -1417,6 +1434,9 @@ static void parse_args(int argc, char **argv) {
} else if (!strcmp(this_arg, "--no-chapters")) { } else if (!strcmp(this_arg, "--no-chapters")) {
ti.no_chapters = true; ti.no_chapters = true;
} else if (!strcmp(this_arg, "--no-attachments")) {
ti.no_attachments = true;
} else if (!strcmp(this_arg, "--dump-packets")) { } else if (!strcmp(this_arg, "--dump-packets")) {
if (next_arg == NULL) if (next_arg == NULL)
mxerror("'--dump-packets' lacks the output path.\n"); mxerror("'--dump-packets' lacks the output path.\n");

View File

@ -657,6 +657,10 @@ void mmg_dialog::update_command_line() {
cmdline += "--no-chapters "; cmdline += "--no-chapters ";
clargs.Add("--no-chapters"); clargs.Add("--no-chapters");
} }
if (f->no_attachments) {
cmdline += "--no-attachments ";
clargs.Add("--no-attachments");
}
if (no_video) { if (no_video) {
cmdline += "-D "; cmdline += "-D ";
clargs.Add("-D"); clargs.Add("-D");

View File

@ -116,6 +116,7 @@ using namespace libmatroska;
#define ID_CB_CHAPTERSELECTLANGUAGECODE 10069 #define ID_CB_CHAPTERSELECTLANGUAGECODE 10069
#define ID_CB_CHAPTERSELECTCOUNTRYCODE 10070 #define ID_CB_CHAPTERSELECTCOUNTRYCODE 10070
#define ID_B_ADDSUBCHAPTER 10071 #define ID_B_ADDSUBCHAPTER 10071
#define ID_CB_NOATTACHMENTS 10072
#define ID_M_FILE_LOAD 20000 #define ID_M_FILE_LOAD 20000
#define ID_M_FILE_SAVE 20001 #define ID_M_FILE_SAVE 20001
@ -164,7 +165,7 @@ typedef struct {
typedef struct { typedef struct {
wxString *file_name; wxString *file_name;
vector<mmg_track_t> *tracks; vector<mmg_track_t> *tracks;
bool no_chapters; bool no_chapters, no_attachments;
} mmg_file_t; } mmg_file_t;
typedef struct { typedef struct {
@ -191,7 +192,7 @@ class tab_input: public wxPanel {
protected: protected:
wxListBox *lb_input_files; wxListBox *lb_input_files;
wxButton *b_add_file, *b_remove_file, *b_browse_tags; wxButton *b_add_file, *b_remove_file, *b_browse_tags;
wxCheckBox *cb_no_chapters, *cb_default, *cb_aac_is_sbr; wxCheckBox *cb_no_chapters, *cb_no_attachments, *cb_default, *cb_aac_is_sbr;
wxCheckListBox *clb_tracks; wxCheckListBox *clb_tracks;
wxComboBox *cob_language, *cob_cues, *cob_sub_charset; wxComboBox *cob_language, *cob_cues, *cob_sub_charset;
wxComboBox *cob_aspect_ratio, *cob_fourcc; wxComboBox *cob_aspect_ratio, *cob_fourcc;
@ -210,6 +211,7 @@ public:
void on_track_selected(wxCommandEvent &evt); void on_track_selected(wxCommandEvent &evt);
void on_track_enabled(wxCommandEvent &evt); void on_track_enabled(wxCommandEvent &evt);
void on_nochapters_clicked(wxCommandEvent &evt); void on_nochapters_clicked(wxCommandEvent &evt);
void on_noattachments_clicked(wxCommandEvent &evt);
void on_default_track_clicked(wxCommandEvent &evt); void on_default_track_clicked(wxCommandEvent &evt);
void on_aac_is_sbr_clicked(wxCommandEvent &evt); void on_aac_is_sbr_clicked(wxCommandEvent &evt);
void on_language_selected(wxCommandEvent &evt); void on_language_selected(wxCommandEvent &evt);

View File

@ -61,11 +61,18 @@ tab_input::tab_input(wxWindow *parent):
wxDefaultSize, 0); wxDefaultSize, 0);
cb_no_chapters = cb_no_chapters =
new wxCheckBox(this, ID_CB_NOCHAPTERS, _("No chapters"), wxPoint(5, 110), new wxCheckBox(this, ID_CB_NOCHAPTERS, _("No chapters"), wxPoint(5, 110),
wxSize(100, -1), 0); wxDefaultSize, 0);
cb_no_chapters->SetValue(false); cb_no_chapters->SetValue(false);
cb_no_chapters->SetToolTip(_("Do not copy chapters from this file. Only " cb_no_chapters->SetToolTip(_("Do not copy chapters from this file. Only "
"applies to Matroska files.")); "applies to Matroska files."));
cb_no_chapters->Enable(false); cb_no_chapters->Enable(false);
cb_no_attachments =
new wxCheckBox(this, ID_CB_NOATTACHMENTS, _("No attachments"),
wxPoint(110, 110), wxDefaultSize, 0);
cb_no_attachments->SetValue(false);
cb_no_attachments->SetToolTip(_("Do not copy attachments from this file. "
"Only applies to Matroska files."));
cb_no_attachments->Enable(false);
new wxStaticText(this, wxID_STATIC, _("Tracks:"), wxPoint(5, 140), new wxStaticText(this, wxID_STATIC, _("Tracks:"), wxPoint(5, 140),
wxDefaultSize, 0); wxDefaultSize, 0);
clb_tracks = clb_tracks =
@ -424,6 +431,7 @@ void tab_input::on_remove_file(wxCommandEvent &evt) {
lb_input_files->Delete(selected_file); lb_input_files->Delete(selected_file);
selected_file = -1; selected_file = -1;
cb_no_chapters->Enable(false); cb_no_chapters->Enable(false);
cb_no_attachments->Enable(false);
b_remove_file->Enable(false); b_remove_file->Enable(false);
clb_tracks->Enable(false); clb_tracks->Enable(false);
no_track_mode(); no_track_mode();
@ -438,10 +446,12 @@ void tab_input::on_file_selected(wxCommandEvent &evt) {
b_remove_file->Enable(true); b_remove_file->Enable(true);
cb_no_chapters->Enable(true); cb_no_chapters->Enable(true);
cb_no_attachments->Enable(true);
selected_file = -1; selected_file = -1;
new_sel = lb_input_files->GetSelection(); new_sel = lb_input_files->GetSelection();
f = &files[new_sel]; f = &files[new_sel];
cb_no_chapters->SetValue(f->no_chapters); cb_no_chapters->SetValue(f->no_chapters);
cb_no_attachments->SetValue(f->no_attachments);
clb_tracks->Clear(); clb_tracks->Clear();
for (i = 0; i < f->tracks->size(); i++) { for (i = 0; i < f->tracks->size(); i++) {
@ -468,6 +478,11 @@ void tab_input::on_nochapters_clicked(wxCommandEvent &evt) {
files[selected_file].no_chapters = cb_no_chapters->GetValue(); files[selected_file].no_chapters = cb_no_chapters->GetValue();
} }
void tab_input::on_noattachments_clicked(wxCommandEvent &evt) {
if (selected_file -1)
files[selected_file].no_attachments = cb_no_attachments->GetValue();
}
void tab_input::on_track_selected(wxCommandEvent &evt) { void tab_input::on_track_selected(wxCommandEvent &evt) {
mmg_file_t *f; mmg_file_t *f;
mmg_track_t *t; mmg_track_t *t;
@ -631,6 +646,7 @@ void tab_input::save(wxConfigBase *cfg) {
cfg->SetPath(s); cfg->SetPath(s);
cfg->Write("file_name", *f->file_name); cfg->Write("file_name", *f->file_name);
cfg->Write("no_chapters", f->no_chapters); cfg->Write("no_chapters", f->no_chapters);
cfg->Write("no_attachments", f->no_attachments);
cfg->Write("number_of_tracks", (int)f->tracks->size()); cfg->Write("number_of_tracks", (int)f->tracks->size());
for (tidx = 0; tidx < f->tracks->size(); tidx++) { for (tidx = 0; tidx < f->tracks->size(); tidx++) {
@ -718,6 +734,7 @@ void tab_input::load(wxConfigBase *cfg) {
} }
fi.file_name = new wxString(s); fi.file_name = new wxString(s);
cfg->Read("no_chapters", &fi.no_chapters); cfg->Read("no_chapters", &fi.no_chapters);
cfg->Read("no_attachments", &fi.no_attachments);
fi.tracks = new vector<mmg_track_t>; fi.tracks = new vector<mmg_track_t>;
for (tidx = 0; tidx < (uint32_t)num_tracks; tidx++) { for (tidx = 0; tidx < (uint32_t)num_tracks; tidx++) {
@ -905,6 +922,7 @@ BEGIN_EVENT_TABLE(tab_input, wxPanel)
EVT_CHECKLISTBOX(ID_CLB_TRACKS, tab_input::on_track_enabled) EVT_CHECKLISTBOX(ID_CLB_TRACKS, tab_input::on_track_enabled)
EVT_CHECKBOX(ID_CB_NOCHAPTERS, tab_input::on_nochapters_clicked) EVT_CHECKBOX(ID_CB_NOCHAPTERS, tab_input::on_nochapters_clicked)
EVT_CHECKBOX(ID_CB_NOATTACHMENTS, tab_input::on_noattachments_clicked)
EVT_CHECKBOX(ID_CB_MAKEDEFAULT, tab_input::on_default_track_clicked) EVT_CHECKBOX(ID_CB_MAKEDEFAULT, tab_input::on_default_track_clicked)
EVT_CHECKBOX(ID_CB_AACISSBR, tab_input::on_aac_is_sbr_clicked) EVT_CHECKBOX(ID_CB_AACISSBR, tab_input::on_aac_is_sbr_clicked)

View File

@ -26,6 +26,7 @@
#include <deque> #include <deque>
#include <vector> #include <vector>
#include <matroska/KaxAttachments.h>
#include <matroska/KaxBlock.h> #include <matroska/KaxBlock.h>
#include <matroska/KaxCluster.h> #include <matroska/KaxCluster.h>
#include <matroska/KaxTracks.h> #include <matroska/KaxTracks.h>
@ -118,7 +119,7 @@ typedef struct {
vector<language_t> *track_names; // As given on the command line vector<language_t> *track_names; // As given on the command line
char *track_name; // For this very track char *track_name; // For this very track
bool no_chapters; bool no_chapters, no_attachments, no_tags;
} track_info_t; } track_info_t;
class generic_reader_c; class generic_reader_c;
@ -234,6 +235,8 @@ public:
virtual void set_headers() = 0; virtual void set_headers() = 0;
virtual void identify() = 0; virtual void identify() = 0;
virtual void add_attachments(KaxAttachments *a) {
};
// virtual void set_tag_track_uids() = 0; // virtual void set_tag_track_uids() = 0;
protected: protected:

View File

@ -143,6 +143,9 @@ kax_reader_c::~kax_reader_c() {
safefree(tracks[i]); safefree(tracks[i]);
} }
for (i = 0; i < attachments.size(); i++)
safefree(attachments[i].data);
if (es != NULL) if (es != NULL)
delete es; delete es;
if (saved_l1 != NULL) if (saved_l1 != NULL)
@ -464,10 +467,11 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es,
KaxAttachments *atts; KaxAttachments *atts;
KaxAttached *att; KaxAttached *att;
EbmlElement *l1, *l2; EbmlElement *l1, *l2;
UTFstring description, name;
int upper_lvl_el, i, k; int upper_lvl_el, i, k;
string name, type; string mime_type;
int64_t size, id; int64_t size, id;
char *str; unsigned char *data;
bool found; bool found;
kax_attachment_t matt; kax_attachment_t matt;
@ -484,8 +488,9 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es,
for (i = 0; i < atts->ListSize(); i++) { for (i = 0; i < atts->ListSize(); i++) {
att = (KaxAttached *)(*atts)[i]; att = (KaxAttached *)(*atts)[i];
if (EbmlId(*att) == KaxAttached::ClassInfos.GlobalId) { if (EbmlId(*att) == KaxAttached::ClassInfos.GlobalId) {
name = ""; name = L"";
type = ""; mime_type = "";
description = L"";
size = -1; size = -1;
id = -1; id = -1;
@ -494,13 +499,15 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es,
if (EbmlId(*l2) == KaxFileName::ClassInfos.GlobalId) { if (EbmlId(*l2) == KaxFileName::ClassInfos.GlobalId) {
KaxFileName &fname = *static_cast<KaxFileName *>(l2); KaxFileName &fname = *static_cast<KaxFileName *>(l2);
str = UTFstring_to_cstr(UTFstring(fname)); name = UTFstring(fname);
name = str;
safefree(str); } else if (EbmlId(*l2) == KaxFileDescription::ClassInfos.GlobalId) {
KaxFileDescription &fdesc = *static_cast<KaxFileDescription *>(l2);
description = UTFstring(fdesc);
} else if (EbmlId(*l2) == KaxMimeType::ClassInfos.GlobalId) { } else if (EbmlId(*l2) == KaxMimeType::ClassInfos.GlobalId) {
KaxMimeType &mtype = *static_cast<KaxMimeType *>(l2); KaxMimeType &mtype = *static_cast<KaxMimeType *>(l2);
type = string(mtype); mime_type = string(mtype);
} else if (EbmlId(*l2) == KaxFileUID::ClassInfos.GlobalId) { } else if (EbmlId(*l2) == KaxFileUID::ClassInfos.GlobalId) {
KaxFileUID &fuid = *static_cast<KaxFileUID *>(l2); KaxFileUID &fuid = *static_cast<KaxFileUID *>(l2);
@ -509,11 +516,12 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es,
} else if (EbmlId(*l2) == KaxFileData::ClassInfos.GlobalId) { } else if (EbmlId(*l2) == KaxFileData::ClassInfos.GlobalId) {
KaxFileData &fdata = *static_cast<KaxFileData *>(l2); KaxFileData &fdata = *static_cast<KaxFileData *>(l2);
size = fdata.GetSize(); size = fdata.GetSize();
data = (unsigned char *)fdata.GetBuffer();
} }
} }
if ((id != -1) && (size != -1) && (type.length() != 0)) { if ((id != -1) && (size != -1) && (mime_type.length() > 0) &&
(name.length() > 0)) {
found = false; found = false;
for (k = 0; k < attachments.size(); k++) for (k = 0; k < attachments.size(); k++)
@ -524,9 +532,11 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es,
if (!found) { if (!found) {
matt.name = name; matt.name = name;
matt.type = type; matt.mime_type = mime_type;
matt.description = description;
matt.size = size; matt.size = size;
matt.id = id; matt.id = id;
matt.data = (unsigned char *)safememdup(data, size);
attachments.push_back(matt); attachments.push_back(matt);
} }
} }
@ -1487,6 +1497,7 @@ void kax_reader_c::set_headers() {
void kax_reader_c::identify() { void kax_reader_c::identify() {
int i; int i;
string info; string info;
char *str;
mxinfo("File '%s': container: Matroska\n", ti->fname); mxinfo("File '%s': container: Matroska\n", ti->fname);
for (i = 0; i < tracks.size(); i++) for (i = 0; i < tracks.size(); i++)
@ -1511,12 +1522,20 @@ void kax_reader_c::identify() {
for (i = 0; i < attachments.size(); i++) { for (i = 0; i < attachments.size(); i++) {
mxinfo("Attachment ID %lld: type '%s', size %lld bytes, ", mxinfo("Attachment ID %lld: type '%s', size %lld bytes, ",
attachments[i].id, attachments[i].type.c_str(), attachments[i].id, attachments[i].mime_type.c_str(),
attachments[i].size); attachments[i].size);
if (attachments[i].description.length() > 0) {
str = UTFstring_to_cstr(attachments[i].description.c_str());
mxinfo("description '%s', ", str);
safefree(str);
}
if (attachments[i].name.length() == 0) if (attachments[i].name.length() == 0)
mxinfo("no file name given\n"); mxinfo("no file name given\n");
else else {
mxinfo("file name '%s'\n", attachments[i].name.c_str()); str = UTFstring_to_cstr(attachments[i].name.c_str());
mxinfo("file name '%s'\n", str);
safefree(str);
}
} }
} }
@ -1533,3 +1552,37 @@ int64_t kax_reader_c::get_queued_bytes() {
return bytes; return bytes;
} }
void kax_reader_c::add_attachments(KaxAttachments *a) {
uint32_t i;
KaxAttached *attached;
KaxFileData *fdata;
binary *buffer;
if (ti->no_attachments)
return;
for (i = 0; i < attachments.size(); i++) {
attached = new KaxAttached;
if (attachments[i].description.length() > 0)
*static_cast<EbmlUnicodeString *>
(&GetChild<KaxFileDescription>(*attached)) =
attachments[i].description;
*static_cast<EbmlString *>(&GetChild<KaxMimeType>(*attached)) =
attachments[i].mime_type;
*static_cast<EbmlUnicodeString *>(&GetChild<KaxFileName>(*attached)) =
attachments[i].name;
*static_cast<EbmlUInteger *>(&GetChild<KaxFileUID>(*attached)) =
attachments[i].id;
fdata = &GetChild<KaxFileData>(*attached);
buffer = new binary[attachments[i].size];
memcpy(buffer, attachments[i].data, attachments[i].size);
fdata->SetBuffer(buffer, attachments[i].size);
a->PushElement(*attached);
}
}

View File

@ -32,14 +32,19 @@
#include "pr_generic.h" #include "pr_generic.h"
#include "error.h" #include "error.h"
#include <ebml/EbmlUnicodeString.h>
#include <matroska/KaxBlock.h> #include <matroska/KaxBlock.h>
#include <matroska/KaxCluster.h> #include <matroska/KaxCluster.h>
using namespace libebml;
using namespace libmatroska; using namespace libmatroska;
using namespace std; using namespace std;
typedef struct { typedef struct {
string name, type; string mime_type;
UTFstring name, description;
unsigned char *data;
int64 size, id; int64 size, id;
} kax_attachment_t; } kax_attachment_t;
@ -118,6 +123,7 @@ public:
virtual void display_progress(bool final = false); virtual void display_progress(bool final = false);
virtual void set_headers(); virtual void set_headers();
virtual void identify(); virtual void identify();
virtual void add_attachments(KaxAttachments *a);
static int probe_file(mm_io_c *mm_io, int64_t size); static int probe_file(mm_io_c *mm_io, int64_t size);