diff --git a/Project/GNU/CLI/Makefile.am b/Project/GNU/CLI/Makefile.am index 3a0b576..bba02fc 100644 --- a/Project/GNU/CLI/Makefile.am +++ b/Project/GNU/CLI/Makefile.am @@ -27,6 +27,7 @@ movmetaedit_SOURCES = \ ../../../Source/Common/mp4/mp4_moov_trak_mdia_minf_stbl_stsd_xxxx_chan.cpp \ ../../../Source/Common/mp4/mp4_moov_trak_mdia_minf_vmhd.cpp \ ../../../Source/Common/mp4/mp4_moov_trak_mdia_minf_smhd.cpp \ + ../../../Source/Common/mp4/mp4_moov_trak_mdia_mdhd.cpp \ ../../../Source/Common/mp4/mp4_moov_trak_tkhd.cpp \ ../../../Source/Common/mp4/mp4_moov_meta.cpp \ ../../../Source/Common/mp4/mp4_moov_meta_hdlr.cpp \ diff --git a/Project/MSVC2017/CLI/MOVMetaEdit_CLI.vcxproj b/Project/MSVC2017/CLI/MOVMetaEdit_CLI.vcxproj index 140f2db..7b4b1ba 100644 --- a/Project/MSVC2017/CLI/MOVMetaEdit_CLI.vcxproj +++ b/Project/MSVC2017/CLI/MOVMetaEdit_CLI.vcxproj @@ -47,6 +47,7 @@ + diff --git a/Project/Qt/movmetaedit-gui.pro b/Project/Qt/movmetaedit-gui.pro index 3be03a0..bf038e3 100644 --- a/Project/Qt/movmetaedit-gui.pro +++ b/Project/Qt/movmetaedit-gui.pro @@ -85,6 +85,7 @@ SOURCES += \ ../../Source/Common/mp4/mp4_moov_trak_mdia_minf_stbl_stsd_xxxx_clli.cpp \ ../../Source/Common/mp4/mp4_moov_trak_mdia_minf_stbl_stsd_xxxxSound.cpp \ ../../Source/Common/mp4/mp4_moov_trak_mdia_minf_stbl_stsd_xxxx_chan.cpp \ + ../../Source/Common/mp4/mp4_moov_trak_mdia_mdhd.cpp \ ../../Source/Common/mp4/mp4_moov_trak_mdia_minf_vmhd.cpp \ ../../Source/Common/mp4/mp4_moov_trak_mdia_minf_smhd.cpp \ ../../Source/Common/mp4/mp4_moov_meta.cpp \ diff --git a/Source/CLI/Help.cpp b/Source/CLI/Help.cpp index c4f50ee..fbdf423 100644 --- a/Source/CLI/Help.cpp +++ b/Source/CLI/Help.cpp @@ -146,9 +146,18 @@ ReturnValue Help_Tech(bool WithExamples) TEXTOUT(" Read HDR values from DolbyLabsMDF XML"); TEXTOUT(" --from-id VALUE"); TEXTOUT(" Read HDR values from the MasteringDisplay with the the specified ID in the XML"); + TEXTOUT("Options related to the media header atom:"); + TEXTOUT(" --language, --languages VALUE"); + TEXTOUT(" Modify the media header language to VALUE for the specified audio track, \"[trackIndex=]lang[,[trackIndex=]lang...]\" format, e.g en,fr or 0=eng,1=fra"); + TEXTOUT(" MOV style: Lang can be numeric or any of these predefined keywords(ISO 639-1) : en, fr, de, it, nl, sv, es, da, pt, no, he, ja, ar, fi, el, is,"); + TEXTOUT(" mt, tr, hr, zh-tw, ur, hi, th, ko, lt, pl, hu, et, lv, smi, fo, fa, ru, zh-cn, nl-be, ga, sq, ro, cs, sk, sl, yi, sr, mk, bg, uk, be, uz, kk,"); + TEXTOUT(" hy-az, hy, ka, mo, ky, tg, tk, mn-cn, mn, ps, ku, ks, sd, bo, ne, sa, mr, bn, as, gu, pa, or, ml, kn, ta, te, si, my, km, lo, vi, id,"); + TEXTOUT(" az, tl, ms, ms-bn, am, om, so, sw, rw, rn, ny, mg, eo, cy, eu, ca, la, qu, gn, ay, tt, ug, dz, jv"); + TEXTOUT(" MP4 style: Lang is a 3-letter code which should be a ISO 639-2 language code"); + TEXTOUT(""); TEXTOUT("Options related to the audio channels description labels:"); TEXTOUT(" --channels VALUE"); - TEXTOUT(" Modify the channels description to VALUE for the specified track, \"[track=]code[,[track=]code...]\" format, e.g L,R or 0=L,1=R,3=Delete"); + TEXTOUT(" Modify the channels description to VALUE for the specified track, \"[trackIndex=]code[,[trackIndex=]code...]\" format, e.g L,R or 0=L,1=R,3=Delete"); TEXTOUT(" Code can be numeric or any of these predefined keywords: Delete(delete the channel information atom), L, R, C, LFE, Ls, Rs, Lc, Rc, Cs,"); TEXTOUT(" Lsd, Rsd, Tcs, Vhl, Vhc, Vhr, Trs, Trs2, Trs3, Lrs, Rrs, Lw, Rw, LFE2, Lt, Rt, HearingImpaired, Narration, M, DialogCentricMix,"); TEXTOUT(" CenterSurroundDirect, Haptic, W, X, Y,Z, M2, S, X2, Y2, HeadphonesLeft, HeadphonesRight, ClickTrack, ForeignLanguage,"); diff --git a/Source/Common/mp4/mp4_.h b/Source/Common/mp4/mp4_.h index 5a72af9..129890e 100644 --- a/Source/Common/mp4/mp4_.h +++ b/Source/Common/mp4/mp4_.h @@ -60,6 +60,7 @@ namespace Elements const uint32_t moov_trak_mdia_minf_stbl_stsd_xxxx_chan = 0x6368616E; const uint32_t moov_trak_mdia_minf_vmhd = 0x766D6864; const uint32_t moov_trak_mdia_minf_smhd = 0x736D6864; + const uint32_t moov_trak_mdia_mdhd = 0x6D646864; const uint32_t moov_trak_tkhd = 0x746B6864; const uint32_t moov_meta = 0x6D657461; const uint32_t moov_meta_hdlr = 0x68646C72; @@ -128,6 +129,7 @@ CHUNK_I(7, moov_trak_mdia_minf_stbl_stsd_xxxxSound); CHUNK_W(8, moov_trak_mdia_minf_stbl_stsd_xxxx_chan); CHUNK__(5, moov_trak_mdia_minf_vmhd); CHUNK__(5, moov_trak_mdia_minf_smhd); +CHUNK_W(4, moov_trak_mdia_mdhd); CHUNK_W(3, moov_trak_tkhd); CHUNK_I(2, moov_meta); CHUNK_W(3, moov_meta_hdlr); diff --git a/Source/Common/mp4/mp4_moov_trak_mdia.cpp b/Source/Common/mp4/mp4_moov_trak_mdia.cpp index 2d1cb49..155fded 100644 --- a/Source/Common/mp4/mp4_moov_trak_mdia.cpp +++ b/Source/Common/mp4/mp4_moov_trak_mdia.cpp @@ -17,6 +17,7 @@ void mp4_moov_trak_mdia::Read_Internal () { SUBS_BEGIN(); SUB_ELEMENT(moov_trak_mdia_minf); + SUB_ELEMENT(moov_trak_mdia_mdhd); // SUB_ELEMENT(moov_meta_keys); // SUB_ELEMENT(moov_meta_ilst); SUBS_END(); diff --git a/Source/Common/mp4/mp4_moov_trak_mdia_mdhd.cpp b/Source/Common/mp4/mp4_moov_trak_mdia_mdhd.cpp new file mode 100644 index 0000000..753ca76 --- /dev/null +++ b/Source/Common/mp4/mp4_moov_trak_mdia_mdhd.cpp @@ -0,0 +1,109 @@ +/* Copyright (c) MediaArea.net SARL. All Rights Reserved. + * + * Use of this source code is governed by a MIT-style license that can + * be found in the License.html file in the root of the source tree. + */ + +//--------------------------------------------------------------------------- +#include "Common/mp4/mp4_.h" +//--------------------------------------------------------------------------- + +#include + +//*************************************************************************** +// Read +//*************************************************************************** + +//--------------------------------------------------------------------------- +void mp4_moov_trak_mdia_mdhd::Read_Internal() +{ + Chunk.trak_Index=Global->moov_trak.size(); + Global->moov_trak_mdia_mdhd[Chunk.trak_Index]=new global::block_moov_trak_mdia_mdhd(); + + Read_Internal_ReadAllInBuffer(); + + int32u Temp32; + + Get_B1(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version); + + if (Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version>1) + throw exception_read_block("moov trak mdia mdhd version unsupported"); + + if (Chunk.Content.Size!=(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version==0?24:36)) + throw exception_read_block("moov trak mdia mdhd invalid size"); + + Get_B3(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Flags); + if (Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version==0) + { + Get_B4(Temp32); + Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->CreationTime=Temp32; + Get_B4(Temp32); + Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->ModificationTime=Temp32; + } + else + { + Get_B8(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->CreationTime); + Get_B8(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->ModificationTime); + } + Get_B4(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->TimeScale); + if (Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version==0) + { + Get_B4(Temp32); + Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Duration=Temp32; + } + else + { + Get_B8(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Duration); + } + Get_B2(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Language); + Get_B2(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Quality); +} + +//*************************************************************************** +// Modify +//*************************************************************************** + +//--------------------------------------------------------------------------- +void mp4_moov_trak_mdia_mdhd::Modify_Internal() +{ + if (Chunk.Content.IsModified) + return; + + if (Global->moov_trak_mdia_mdhd.find(Chunk.trak_Index)==Global->moov_trak_mdia_mdhd.end()) + return; + + if (Chunk.Content.Buffer) + delete[] Chunk.Content.Buffer; + Chunk.Content.Buffer_Offset=0; + Chunk.Content.Size=Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version==0?24:36; + Chunk.Content.Buffer=new int8u[Chunk.Content.Size]; + + Put_B1(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version); + Put_B3(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Flags); + if (Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version==0) + { + Put_B4(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->CreationTime); + Put_B4(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->ModificationTime); + } + else + { + Put_B8(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->CreationTime); + Put_B8(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->ModificationTime); + } + Put_B4(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->TimeScale); + if (Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Version==0) + Put_B4(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Duration); + else + Put_B8(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Duration); + Put_B2(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Language); + Put_B2(Global->moov_trak_mdia_mdhd[Chunk.trak_Index]->Quality); + + Chunk.Content.IsModified=true; + Chunk.Content.Size_IsModified=true; +} + +//--------------------------------------------------------------------------- +void mp4_moov_trak_mdia_mdhd::Write_Internal() +{ + mp4_Base::Write_Internal(Chunk.Content.Buffer, (size_t)Chunk.Content.Size); +} diff --git a/Source/Common/mp4_Base.h b/Source/Common/mp4_Base.h index 9832e0f..1cd6e1f 100644 --- a/Source/Common/mp4_Base.h +++ b/Source/Common/mp4_Base.h @@ -344,6 +344,29 @@ class mp4_Base NumberChannelDescriptions=0; } }; + struct block_moov_trak_mdia_mdhd + { + int8u Version; + int32u Flags; + int64u CreationTime; + int64u ModificationTime; + int32u TimeScale; + int64u Duration; + int16u Language; + int16u Quality; + + block_moov_trak_mdia_mdhd() + { + Version=0; + Flags=0; + CreationTime=0; + ModificationTime=0; + TimeScale=0; + Duration=0; + Language=0; + Quality=0; + } + }; struct block_moov_trak_tkhd { double Width_Scale; @@ -430,6 +453,8 @@ class mp4_Base bool moov_trak_mdia_minf_stbl_stsd_xxxx_clli_Modified; map moov_trak_mdia_minf_stbl_stsd_xxxx_chan; bool moov_trak_mdia_minf_stbl_stsd_xxxx_chan_Modified; + map moov_trak_mdia_mdhd; + bool moov_trak_mdia_mdhd_Modified; block_moov_trak_tkhd* moov_trak_tkhd; bool moov_trak_tkhd_Modified; block_moov_meta_hdlr* moov_meta_hdlr; @@ -476,6 +501,7 @@ class mp4_Base moov_trak_mdia_minf_stbl_stsd_xxxx_clli=NULL; moov_trak_mdia_minf_stbl_stsd_xxxx_clli_Modified=false; moov_trak_mdia_minf_stbl_stsd_xxxx_chan_Modified=false; + moov_trak_mdia_mdhd_Modified=false; moov_trak_tkhd=NULL; moov_trak_tkhd_Modified=false; moov_meta_hdlr=NULL; @@ -519,6 +545,9 @@ class mp4_Base for(map::iterator It=moov_trak_mdia_minf_stbl_stsd_xxxx_chan.begin(); It!=moov_trak_mdia_minf_stbl_stsd_xxxx_chan.end(); It++) delete It->second; + + for(map::iterator It=moov_trak_mdia_mdhd.begin(); It!=moov_trak_mdia_mdhd.end(); It++) + delete It->second; } }; diff --git a/Source/Common/mp4_Handler.cpp b/Source/Common/mp4_Handler.cpp index e7f6264..56dd3cd 100644 --- a/Source/Common/mp4_Handler.cpp +++ b/Source/Common/mp4_Handler.cpp @@ -18,6 +18,7 @@ #ifdef MACSTORE #include "Common/Mac_Helpers.h" +#include "mp4_Handler.h" #endif using namespace std; @@ -28,6 +29,118 @@ using namespace ZenLib; const string mp4_Handler_EmptyZtring_Const; //Use it when we can't return a reference to a true Ztring, const version //--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +static const vector mp4_mdhd_Languages= +{ + "en", // English + "fr", // French + "de", // German + "it", // Italian + "nl", // Dutch + "sv", // Swedish + "es", // Spanish + "da", // Danish + "pt", // Portuguese + "no", // Norwegian + "he", // Hebrew + "ja", // Japanese + "ar", // Arabic + "fi", // Finnish + "el", // Greek + "is", // Icelandic + "mt", // Maltese + "tr", // Turkish + "hr", // Croatian + "zh-tw", // Chinese (Taiwan) + "ur", // Urdu + "hi", // Hindi + "th", // Thai + "ko", // Korean + "lt", // Lithuanian + "pl", // Polish + "hu", // Hungarian + "et", // Estonian + "lv", // Latvian + "smi", // Sami + "fo", // Faroese + "fa", // Persian + "ru", // Russian + "zh-cn", // Chinese (China) + "nl-be", // Flemish + "ga", // Irish + "sq", // Albanian + "ro", // Romanian + "cs", // Czech + "sk", // Slovak + "sl", // Slovenian + "yi", // Yiddish + "sr", // Serbian + "mk", // Macedonian + "bg", // Bulgarian + "uk", // Ukrainian + "be", // Belarusian + "uz", // Uzbek + "kk", // Kazakh + "az", // Azerbaijani + "hy-az", // Armenian (Azerbaijani) + "hy", // Armenian + "ka", // Georgian + "mo", // Moldavian + "ky", // Kirghiz + "tg", // Tajik + "tk", // Turkmen + "mn-cn", // Mongolian (China) + "mn", // Mongolian + "ps", // Pushto + "ku", // Kurdish + "ks", // Kashmiri + "sd", // Sindhi + "bo", // Tibetan + "ne", // Nepali + "sa", // Sanskrit + "mr", // Marathi + "bn", // Bengali + "as", // Assamese + "gu", // Gujarati + "pa", // Panjabi + "or", // Oriya + "ml", // Malayalam + "kn", // Kannada + "ta", // Tamil + "te", // Telugu + "si", // Sinhala + "my", // Burmese + "km", // Khmer + "lo", // Lao + "vi", // Vietnamese + "id", // Indonesian + "tl", // Tagalog + "ms", // Malay + "ms-bn", // Malay (Brunei) + "am", // Amharic + "86", // Empty + "om", // Oromo + "so", // Somali + "sw", // Swahili + "rw", // Kinyarwanda + "rn", // Rundi + "ny", // Nyanja + "mg", // Malagasy + "eo", // Esperanto + // Gap 95-128 + "cy", // Welsh + "eu", // Basque + "ca", // Catalan + "la", // Latin + "qu", // Quechua + "gn", // Guarani + "ay", // Aymara + "tt", // Tatar + "ug", // Uighur + "dz", // Dzongkha + "jv", // Javanese +}; + //*************************************************************************** // Helpers //*************************************************************************** @@ -119,6 +232,7 @@ string mp4_chan_ChannelDescription (uint32_t ChannelLabel) bool mp4_chan_ChannelCode (string ChannelLabel, uint32_t &Code, bool& Ignore, bool& Delete) { Delete=false; + Ignore=false; Code=0; if(ChannelLabel.empty()) @@ -243,6 +357,65 @@ bool mp4_chan_ChannelCode (string ChannelLabel, uint32_t &Code, bool& Ignore, bo return true; } + +//--------------------------------------------------------------------------- +string mp4_mdhd_LanguageLabel(uint16_t LanguageCode) +{ + if (LanguageCode>=0x400 && LanguageCode!=0x7FFF && LanguageCode!=0xFFFF) + { + string Temp; + Temp.append(1, (char)((LanguageCode>>10&0x1F)+0x60)); + Temp.append(1, (char)((LanguageCode>> 5&0x1F)+0x60)); + Temp.append(1, (char)((LanguageCode>> 0&0x1F)+0x60)); + return Temp; + } + + if (LanguageCode>94) + LanguageCode-=(128-94); // Gap 95-128 + + if (LanguageCode> Code; + if (!iss.fail() && iss.eof()) + return true; + + transform( LanguageLabel.begin(), LanguageLabel.end(), LanguageLabel.begin(), ::tolower); + vector::const_iterator It=find(mp4_mdhd_Languages.begin(), mp4_mdhd_Languages.end(), LanguageLabel); + if (It!=mp4_mdhd_Languages.end()) + { + Code=(uint16_t)(It-mp4_mdhd_Languages.begin()); + return true; + } + + if (LanguageLabel.size()==3 && + LanguageLabel[0] > 0x60 && LanguageLabel[0] < 0x7B && + LanguageLabel[1] > 0x60 && LanguageLabel[1] < 0x7B && + LanguageLabel[2] > 0x60 && LanguageLabel[2] < 0x7B) + { + Code=(LanguageLabel[0]-0x60)<<10; + Code|=(LanguageLabel[1]-0x60)<<5; + Code|=(LanguageLabel[2]-0x60)<<0; + return true; + } + + return false; +} + //*************************************************************************** // Constructor/Destructor //*************************************************************************** @@ -460,6 +633,34 @@ bool mp4_Handler::Save() } if (Chunks->Global->moov_trak_tkhd_Modified) Chunks->Modify(Elements::moov, Elements::moov_trak, Elements::moov_trak_tkhd); + // Modify mdhd in all audio tracks + if (Chunks->Global->moov_trak_mdia_mdhd_Modified) + { + size_t trak_Index=0; + for (size_t Pos=0; PosSubs.size(); Pos++) + { + if (Chunks->Subs[Pos]->Chunk.Header.Name==Elements::moov) + { + for (size_t Pos2=0; Pos2Subs[Pos]->Subs.size(); Pos2++) + { + if (Chunks->Subs[Pos]->Subs[Pos2]->Chunk.Header.Name==Elements::moov_trak) + { + if(trak_IndexGlobal->moov_trak.size() && Chunks->Global->moov_trak[trak_Index]->IsSound) + { + Chunks->Chunk.Content.IsModified=true; + Chunks->Chunk.Content.Size_IsModified=true; + Chunks->Subs[Pos]->Chunk.Content.IsModified=true; + Chunks->Subs[Pos]->Chunk.Content.Size_IsModified=true; + Chunks->Subs[Pos]->Subs[Pos2]->Chunk.Content.IsModified=true; + Chunks->Subs[Pos]->Subs[Pos2]->Chunk.Content.Size_IsModified=true; + Chunks->Subs[Pos]->Subs[Pos2]->Modify(Elements::moov_trak_mdia, Elements::moov_trak_mdia_mdhd); + } + trak_Index++; + } + } + } + } + } // Modify chan in all audio tracks if (Chunks->Global->moov_trak_mdia_minf_stbl_stsd_xxxx_chan_Modified) { @@ -711,6 +912,30 @@ string mp4_Handler::Get(const string &Field) return ss.str(); } + else if (Field=="lang") + { + stringstream ss; + size_t Index=0; + + for (size_t Pos=0; PosGlobal->moov_trak.size(); Pos++) + { + if (!Chunks->Global->moov_trak[Pos]->IsSound) + continue; + + if (!ss.str().empty()) + ss << ","; + + ss << Index << "="; + + map::iterator It=Chunks->Global->moov_trak_mdia_mdhd.find(Pos+1); + if (It!=Chunks->Global->moov_trak_mdia_mdhd.end()) + ss << It->second->Language; + else + ss << "ABSENT"; + Index++; + } + return ss.str(); + } else if (Field=="chan") { stringstream ss; @@ -1333,6 +1558,84 @@ bool mp4_Handler::Set(const string &Field, const string &Value, bool Simulate) return true; } + else if (Field=="lang") + { + bool Ok=true; + + size_t Start=0, End=0; + do + { + End=Value.find(',', Start); + string Current=Value.substr(Start, End!=string::npos?End-Start:string::npos); + + size_t Equal_Pos=Current.find('='); + if (!Equal_Pos || Equal_Pos==string::npos || Equal_Pos+1==Current.size()) + { + Ok=false; + break; + } + + uint32_t Track; + uint16_t Code; + bool Delete=false; + bool Ignore=false; + + istringstream iss(Current.substr(0, Equal_Pos)); + iss >> Track; + if (iss.fail() || !iss.eof()) + { + Ok=false; + break; + } + + if (!mp4_mdhd_LanguageCode(Current.substr(Equal_Pos+1), Code, Ignore, Delete)) + { + Ok=false; + break; + } + + size_t Index=0; + size_t trak_Index=0; + for (size_t Pos=0; PosGlobal->moov_trak.size(); Pos++) + { + if (!Chunks->Global->moov_trak[Pos]->IsSound) + continue; + + if (Index++==Track) + { + trak_Index=Pos+1; //Track indexes are 1 based + break; + } + } + + if (!trak_Index) + { + Ok=false; + continue; + } + + if (!Chunks->Global->moov_trak[trak_Index-1]->IsSound) + { + Ok=false; + continue; + } + + if (!Simulate && !Ignore) + { + if (Chunks->Global->moov_trak_mdia_mdhd.find(trak_Index)!=Chunks->Global->moov_trak_mdia_mdhd.end()) + Chunks->Global->moov_trak_mdia_mdhd[trak_Index]->Language=Code; + else + Ok=false; + } + } + while((Start=Value.find_first_not_of(',', End))!=string::npos); + + if (!Simulate && Ok) + Chunks->Global->moov_trak_mdia_mdhd_Modified=true; + + return Ok; + } + else if (Field=="chan") { bool Ok=true; diff --git a/Source/Common/mp4_Handler.h b/Source/Common/mp4_Handler.h index 15028c8..8fe7dbd 100644 --- a/Source/Common/mp4_Handler.h +++ b/Source/Common/mp4_Handler.h @@ -109,6 +109,8 @@ class mp4_Handler string mp4_chan_ChannelDescription (uint32_t ChannelLabel); bool mp4_chan_ChannelCode (string ChannelLabel, uint32_t &Code, bool& Ignore, bool& Delete); +string mp4_mdhd_LanguageLabel (uint16_t LanguageCode); +bool mp4_mdhd_LanguageCode (string LanguageLabel, uint16_t &Code, bool& Ignore, bool& Delete); int tfsxml_next_named(tfsxml_string* tfsxml_priv, tfsxml_string* result, const char* name); int tfsxml_last_named(tfsxml_string* tfsxml_priv, tfsxml_string* result, const char* name); diff --git a/Source/movedit_main.cpp b/Source/movedit_main.cpp index 0e400bf..474a81a 100644 --- a/Source/movedit_main.cpp +++ b/Source/movedit_main.cpp @@ -55,6 +55,8 @@ int main(int argc, char* argv[]) std::string clap_New=string(); bool clap_Delete=false; bool clap_OK=true; + std::map lang_New; + std::map lang_OK; std::map chan_New; std::map chan_OK; bool chan_Delete=false; @@ -433,6 +435,66 @@ int main(int argc, char* argv[]) maximum_frame_average_light_level_New.clear(); clli_Delete=true; } + else if (Ztring(argv[argp]) == __T("-languages") + || Ztring(argv[argp]) == __T("-language") + || Ztring(argv[argp]) == __T("--languages") + || Ztring(argv[argp]) == __T("--language")) + { + bool Ok=true; + string lang(argv[argp + 1]); + + size_t start=0, end=0, index=0; + uint16_t code; + bool _ignore; + bool _delete; + do + { + end = lang.find(',', start); + string current=lang.substr(start, end!=string::npos?end-start:string::npos); + + if (size_t equal_pos=current.find('=')!=string::npos) + { + if (!equal_pos || equal_pos+1==current.size()) + { + Ok=false; + break; + } + istringstream iss(current.substr(0, equal_pos)); + iss >> index; + if (iss.fail()) + { + Ok=false; + break; + } + + if (!mp4_mdhd_LanguageCode(current.substr(equal_pos+1, current.size()-equal_pos-1), code, _ignore, _delete)) + { + Ok=false; + break; + } + + lang_New[index++]=current.substr(equal_pos+1, current.size()-equal_pos-1); + } + else + { + if (!mp4_mdhd_LanguageCode(current, code, _ignore, _delete)) + { + Ok=false; + break; + } + + lang_New[index++]=current; + } + } + while((start=lang.find_first_not_of(',', end))!=string::npos); + + if (!Ok) + { + cout << "Can not understand languages label value " << argv[argp] << ", it must be in [trackIndex=]code[,[trackIndex=]code...] format" << endl; + return ReturnValue_ERROR; + } + argp++; + } else if ((Ztring(argv[argp]) == __T("-channels") || Ztring(argv[argp]) == __T("--channels"))) { @@ -486,7 +548,7 @@ int main(int argc, char* argv[]) if (!Ok) { - cout << "Can not understand channels label value " << argv[argp] << ", it must be in [track=]code[,[track=]code...] format" << endl; + cout << "Can not understand channels label value " << argv[argp] << ", it must be in [trackIndex=]code[,[trackIndex=]code...] format" << endl; return ReturnValue_ERROR; } @@ -533,6 +595,7 @@ int main(int argc, char* argv[]) !colr_New.empty() || colr_Delete || !clap_New.empty() || clap_Delete || !chan_New.empty() || chan_Delete || + !lang_New.empty() || mdcv_Delete || clli_Delete || !luminance_New.empty() || @@ -695,7 +758,7 @@ int main(int argc, char* argv[]) cout << " it (empty)" << endl; cout << "M = The field will be modified ('Y') or should be modified but it is not possible" << endl; cout << " due to feature not implemented ('N')" << endl; - cout << FileNameFake << "|OK?|Clean Ap.|M| Prod Ap.|M| Enc. Ap.|M| PAR|M| Display Primaries|M| Luminance|M| Max content light lev.|M| Max frame avg. light lev.|M|w-scale|M|Field|M| Color|M|Gamma|M| Aperture|M| Channels|M|" << endl; + cout << FileNameFake << "|OK?|Clean Ap.|M| Prod Ap.|M| Enc. Ap.|M| PAR|M| Display Primaries|M| Luminance|M| Max content light lev.|M| Max frame avg. light lev.|M|w-scale|M| Field|M| Color|M|Gamma|M| Aperture|M| Languages|M| Channels|M|" << endl; } else cout << FileNameFake << "|OK?| Registry|UniversalAdId value" << endl; @@ -944,6 +1007,16 @@ int main(int argc, char* argv[]) } else if (clap_Delete) H->Remove("clap"); + if (!lang_New.empty()) + { + for (map::iterator It=lang_New.begin(); It!=lang_New.end(); It++) + { + stringstream ss; ss << It->first << "=" << It->second; + lang_OK[It->first]=H->Set("lang", ss.str()); + if (!lang_OK[It->first]) + ToReturn=ReturnValue_ERROR; + } + } if (!chan_New.empty()) { for (map::iterator It=chan_New.begin(); It!=chan_New.end(); It++) @@ -1012,8 +1085,8 @@ int main(int argc, char* argv[]) cout << wscale << "|" << (!wscale_New.empty() ? ((OK && wscale_OK) ? "Y" : "N") : " ") << "|"; string fiel = H->Get("fiel"); - if (fiel.size() < 5) - fiel.insert(0, 5 - fiel.size(), ' '); + if (fiel.size() < 8) + fiel.insert(0, 8 - fiel.size(), ' '); cout << fiel << "|" << ((!fiel_New.empty() || fiel_Delete) ? ((OK && fiel_OK) ? "Y" : "N") : " ") << "|"; string colr = H->Get("colr"); @@ -1031,6 +1104,37 @@ int main(int argc, char* argv[]) clap.insert(0, 25 - clap.size(), ' '); cout << clap << "|" << ((!clap_New.empty() || clap_Delete) ? ((OK && clap_OK) ? "Y" : "N") : " ") << "|"; + vectorlangs; + string lang = H->Get("lang"); + { + size_t start=0, sep=0, end=0; + do + { + sep = lang.find('=', start); + end = lang.find(',', start); + + string number=lang.substr(start, sep - start); + string value=lang.substr(sep+1, end-sep-1); + + if (value!="ABSENT") + { + string current = number + ") "; + + size_t code=0; + istringstream(value) >> code; + current+=(mp4_mdhd_LanguageLabel(code)); + + size_t track=0; + istringstream(number) >> track; + if (current.size() < 10) + current.insert(0, 10 - current.size(), ' '); + + langs.push_back(current + "|" + (lang_New.find(track)!=lang_New.end() ? ((OK && lang_OK.find(track)!=lang_OK.end() && lang_OK[track]) ? "Y" : "N") : " ") + "|"); + } + } + while((start=lang.find_first_not_of(',', end))!=std::string::npos); + } + vectorchans; string chan = H->Get("chan"); { size_t start=0, sep=0, end=0; @@ -1061,14 +1165,23 @@ int main(int argc, char* argv[]) istringstream(number) >> track; if (current.size() < 25) current.insert(0, 25 - current.size(), ' '); - if (start!=0) - cout << endl << string(FileNameFake.size() + 247, ' ') << '|'; - cout << current << "|" << ((chan_New.find(track)!=chan_New.end() || chan_Delete) ? ((OK && chan_OK.find(track)!=chan_OK.end() && chan_OK[track]) ? "Y" : "N") : " ") << "|"; + + chans.push_back(current + "|" + ((chan_New.find(track)!=chan_New.end() || chan_Delete) ? ((OK && chan_OK.find(track)!=chan_OK.end() && chan_OK[track]) ? "Y" : "N") : " ") + "|"); } } while((start=chan.find_first_not_of(',', end))!=std::string::npos); } + for (size_t Pos=0; Pos