diff --git a/ChangeLog b/ChangeLog index 6306117c6..8f27a9722 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-09-29 Moritz Bunkus + + * mkvmerge: new feature: Attachments are kept when reading + Matroska files. + 2003-09-28 Moritz Bunkus * mmg: new feature: Added a (nearly) full-featured chapter editor. diff --git a/debian/rules b/debian/rules index 6104b1ad2..d33c0d3f2 100755 --- a/debian/rules +++ b/debian/rules @@ -76,7 +76,7 @@ binary-arch: build install dh_testdir dh_testroot # 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_installmenu # dh_installlogrotate diff --git a/doc/mkvmerge.1 b/doc/mkvmerge.1 index 281b16a2b..40f711d44 100644 --- a/doc/mkvmerge.1 +++ b/doc/mkvmerge.1 @@ -179,6 +179,9 @@ Don't copy any subtitle track from this file. \fB\-\-no\-chapters\fR If the source is a Matroska file then don't copy chapters from it. .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]]> 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 diff --git a/src/mkvmerge.cpp b/src/mkvmerge.cpp index 1078c07a2..7da0ce4f4 100644 --- a/src/mkvmerge.cpp +++ b/src/mkvmerge.cpp @@ -300,6 +300,7 @@ static void usage() { " -D, --novideo Don't copy any video 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-attachments Don't keep attachments from a Matroska file.\n" " -y, --sync \n" " Synchronize, delay the audio track with the\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) { + KaxAttachments *other_as; KaxAttached *kax_a; KaxFileData *fdata; attachment_t *attch; @@ -894,9 +896,21 @@ static void render_attachments(IOCallback *out) { int64_t size; mm_io_c *io; - if (!(((file_num == 1) && (attachment_sizes_first > 0)) || - (attachment_sizes_others > 0))) + other_as = new KaxAttachments; + 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((*other_as)[i]); + fdata = FindChild(*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; + } if (kax_as != NULL) delete kax_as; @@ -936,10 +950,7 @@ static void render_attachments(IOCallback *out) { try { io = new mm_io_c(attch->name, MODE_READ); - io->setFilePointer(0, seek_end); - size = io->getFilePointer(); - io->setFilePointer(0, seek_beginning); - + size = io->get_size(); buffer = new binary[size]; io->read(buffer, size); 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); } @@ -1417,6 +1434,9 @@ static void parse_args(int argc, char **argv) { } else if (!strcmp(this_arg, "--no-chapters")) { ti.no_chapters = true; + } else if (!strcmp(this_arg, "--no-attachments")) { + ti.no_attachments = true; + } else if (!strcmp(this_arg, "--dump-packets")) { if (next_arg == NULL) mxerror("'--dump-packets' lacks the output path.\n"); diff --git a/src/mmg/mmg.cpp b/src/mmg/mmg.cpp index 00164cc6f..39dd751e9 100644 --- a/src/mmg/mmg.cpp +++ b/src/mmg/mmg.cpp @@ -657,6 +657,10 @@ void mmg_dialog::update_command_line() { cmdline += "--no-chapters "; clargs.Add("--no-chapters"); } + if (f->no_attachments) { + cmdline += "--no-attachments "; + clargs.Add("--no-attachments"); + } if (no_video) { cmdline += "-D "; clargs.Add("-D"); diff --git a/src/mmg/mmg.h b/src/mmg/mmg.h index 60d9c9b08..07886ccaa 100644 --- a/src/mmg/mmg.h +++ b/src/mmg/mmg.h @@ -116,6 +116,7 @@ using namespace libmatroska; #define ID_CB_CHAPTERSELECTLANGUAGECODE 10069 #define ID_CB_CHAPTERSELECTCOUNTRYCODE 10070 #define ID_B_ADDSUBCHAPTER 10071 +#define ID_CB_NOATTACHMENTS 10072 #define ID_M_FILE_LOAD 20000 #define ID_M_FILE_SAVE 20001 @@ -164,7 +165,7 @@ typedef struct { typedef struct { wxString *file_name; vector *tracks; - bool no_chapters; + bool no_chapters, no_attachments; } mmg_file_t; typedef struct { @@ -191,7 +192,7 @@ class tab_input: public wxPanel { protected: wxListBox *lb_input_files; 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; wxComboBox *cob_language, *cob_cues, *cob_sub_charset; wxComboBox *cob_aspect_ratio, *cob_fourcc; @@ -210,6 +211,7 @@ public: void on_track_selected(wxCommandEvent &evt); void on_track_enabled(wxCommandEvent &evt); void on_nochapters_clicked(wxCommandEvent &evt); + void on_noattachments_clicked(wxCommandEvent &evt); void on_default_track_clicked(wxCommandEvent &evt); void on_aac_is_sbr_clicked(wxCommandEvent &evt); void on_language_selected(wxCommandEvent &evt); diff --git a/src/mmg/tab_input.cpp b/src/mmg/tab_input.cpp index a61692daa..eab7d43dd 100644 --- a/src/mmg/tab_input.cpp +++ b/src/mmg/tab_input.cpp @@ -61,11 +61,18 @@ tab_input::tab_input(wxWindow *parent): wxDefaultSize, 0); cb_no_chapters = 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->SetToolTip(_("Do not copy chapters from this file. Only " "applies to Matroska files.")); 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), wxDefaultSize, 0); clb_tracks = @@ -424,6 +431,7 @@ void tab_input::on_remove_file(wxCommandEvent &evt) { lb_input_files->Delete(selected_file); selected_file = -1; cb_no_chapters->Enable(false); + cb_no_attachments->Enable(false); b_remove_file->Enable(false); clb_tracks->Enable(false); no_track_mode(); @@ -438,10 +446,12 @@ void tab_input::on_file_selected(wxCommandEvent &evt) { b_remove_file->Enable(true); cb_no_chapters->Enable(true); + cb_no_attachments->Enable(true); selected_file = -1; new_sel = lb_input_files->GetSelection(); f = &files[new_sel]; cb_no_chapters->SetValue(f->no_chapters); + cb_no_attachments->SetValue(f->no_attachments); clb_tracks->Clear(); 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(); } +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) { mmg_file_t *f; mmg_track_t *t; @@ -631,6 +646,7 @@ void tab_input::save(wxConfigBase *cfg) { cfg->SetPath(s); cfg->Write("file_name", *f->file_name); cfg->Write("no_chapters", f->no_chapters); + cfg->Write("no_attachments", f->no_attachments); cfg->Write("number_of_tracks", (int)f->tracks->size()); for (tidx = 0; tidx < f->tracks->size(); tidx++) { @@ -718,6 +734,7 @@ void tab_input::load(wxConfigBase *cfg) { } fi.file_name = new wxString(s); cfg->Read("no_chapters", &fi.no_chapters); + cfg->Read("no_attachments", &fi.no_attachments); fi.tracks = new vector; 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_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_AACISSBR, tab_input::on_aac_is_sbr_clicked) diff --git a/src/pr_generic.h b/src/pr_generic.h index 4e244b9ec..a1ead5af6 100644 --- a/src/pr_generic.h +++ b/src/pr_generic.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -118,7 +119,7 @@ typedef struct { vector *track_names; // As given on the command line char *track_name; // For this very track - bool no_chapters; + bool no_chapters, no_attachments, no_tags; } track_info_t; class generic_reader_c; @@ -234,6 +235,8 @@ public: virtual void set_headers() = 0; virtual void identify() = 0; + virtual void add_attachments(KaxAttachments *a) { + }; // virtual void set_tag_track_uids() = 0; protected: diff --git a/src/r_matroska.cpp b/src/r_matroska.cpp index 7c5952a61..5f3f78cd1 100644 --- a/src/r_matroska.cpp +++ b/src/r_matroska.cpp @@ -143,6 +143,9 @@ kax_reader_c::~kax_reader_c() { safefree(tracks[i]); } + for (i = 0; i < attachments.size(); i++) + safefree(attachments[i].data); + if (es != NULL) delete es; if (saved_l1 != NULL) @@ -464,10 +467,11 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es, KaxAttachments *atts; KaxAttached *att; EbmlElement *l1, *l2; + UTFstring description, name; int upper_lvl_el, i, k; - string name, type; + string mime_type; int64_t size, id; - char *str; + unsigned char *data; bool found; 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++) { att = (KaxAttached *)(*atts)[i]; if (EbmlId(*att) == KaxAttached::ClassInfos.GlobalId) { - name = ""; - type = ""; + name = L""; + mime_type = ""; + description = L""; size = -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) { KaxFileName &fname = *static_cast(l2); - str = UTFstring_to_cstr(UTFstring(fname)); - name = str; - safefree(str); + name = UTFstring(fname); + + } else if (EbmlId(*l2) == KaxFileDescription::ClassInfos.GlobalId) { + KaxFileDescription &fdesc = *static_cast(l2); + description = UTFstring(fdesc); } else if (EbmlId(*l2) == KaxMimeType::ClassInfos.GlobalId) { KaxMimeType &mtype = *static_cast(l2); - type = string(mtype); + mime_type = string(mtype); } else if (EbmlId(*l2) == KaxFileUID::ClassInfos.GlobalId) { KaxFileUID &fuid = *static_cast(l2); @@ -509,11 +516,12 @@ void kax_reader_c::handle_attachments(mm_io_c *io, EbmlStream *es, } else if (EbmlId(*l2) == KaxFileData::ClassInfos.GlobalId) { KaxFileData &fdata = *static_cast(l2); 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; 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) { matt.name = name; - matt.type = type; + matt.mime_type = mime_type; + matt.description = description; matt.size = size; matt.id = id; + matt.data = (unsigned char *)safememdup(data, size); attachments.push_back(matt); } } @@ -1487,6 +1497,7 @@ void kax_reader_c::set_headers() { void kax_reader_c::identify() { int i; string info; + char *str; mxinfo("File '%s': container: Matroska\n", ti->fname); for (i = 0; i < tracks.size(); i++) @@ -1511,12 +1522,20 @@ void kax_reader_c::identify() { for (i = 0; i < attachments.size(); i++) { 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); + 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) mxinfo("no file name given\n"); - else - mxinfo("file name '%s'\n", attachments[i].name.c_str()); + else { + 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; } + +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 + (&GetChild(*attached)) = + attachments[i].description; + + *static_cast(&GetChild(*attached)) = + attachments[i].mime_type; + + *static_cast(&GetChild(*attached)) = + attachments[i].name; + + *static_cast(&GetChild(*attached)) = + attachments[i].id; + + fdata = &GetChild(*attached); + buffer = new binary[attachments[i].size]; + memcpy(buffer, attachments[i].data, attachments[i].size); + fdata->SetBuffer(buffer, attachments[i].size); + + a->PushElement(*attached); + } +} diff --git a/src/r_matroska.h b/src/r_matroska.h index 96a98fa6f..d9d1c01fd 100644 --- a/src/r_matroska.h +++ b/src/r_matroska.h @@ -32,14 +32,19 @@ #include "pr_generic.h" #include "error.h" +#include + #include #include +using namespace libebml; using namespace libmatroska; using namespace std; typedef struct { - string name, type; + string mime_type; + UTFstring name, description; + unsigned char *data; int64 size, id; } kax_attachment_t; @@ -118,6 +123,7 @@ public: virtual void display_progress(bool final = false); virtual void set_headers(); virtual void identify(); + virtual void add_attachments(KaxAttachments *a); static int probe_file(mm_io_c *mm_io, int64_t size);