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