Skip to content
This repository was archived by the owner on Jan 7, 2023. It is now read-only.

Commit a22e2e6

Browse files
committed
Merge pull request #9 from SSE4/master
- add Matroska (MKV) format support
2 parents 591cd97 + 6e20d77 commit a22e2e6

File tree

13 files changed

+1082
-1
lines changed

13 files changed

+1082
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// =================================================================================================
2+
// ADOBE SYSTEMS INCORPORATED
3+
// Copyright 2016 Adobe Systems Incorporated
4+
// All Rights Reserved
5+
//
6+
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7+
// of the Adobe license agreement accompanying it.
8+
// =================================================================================================
9+
10+
#include "XMPFiles/source/FileHandlers/Matroska_Handler.hpp"
11+
#include "XMPFiles/source/FormatSupport/EBML_Support.hpp"
12+
#include "XMPFiles/source/FormatSupport/EBML/ids.h"
13+
#include "XMPFiles/source/FormatSupport/EBML/exceptions.h"
14+
#include "XMPFiles/source/FormatSupport/EBML/variant.h"
15+
#include "XMPFiles/source/FormatSupport/EBML/element.h"
16+
17+
using namespace ebml;
18+
19+
// =================================================================================================
20+
// Matroska_CheckFormat
21+
// ===============
22+
//
23+
// A Matroska file must begin with EBML [1A][45][DF][A3], a 4 byte length
24+
25+
class MatroskaDOM
26+
{
27+
public:
28+
MatroskaDOM(XMP_IO * fileRef)
29+
{
30+
assert(fileRef);
31+
fileRef->Rewind();
32+
33+
_root = std::make_shared<ebml_element_t>(ebml_type_t::kEBMLTypeMaster, fileRef->Length());
34+
35+
ebml_restrictions restrictions;
36+
restrictions.max_id_length = 4;
37+
restrictions.max_size_length = 8;
38+
39+
ReadEBMLMaster(fileRef, _root, restrictions, 0, std::bind(&MatroskaDOM::OnElement, this, std::placeholders::_1));
40+
41+
auto segments = _root->getElementsById(kSegment);
42+
if (segments.empty()) throw no_elements();
43+
}
44+
bool OnElement(ebml_element_t::ptr element)
45+
{
46+
return true;
47+
}
48+
ebml_element_t::ptr _root;
49+
};
50+
51+
bool Matroska_CheckFormat(XMP_FileFormat format,
52+
XMP_StringPtr filePath,
53+
XMP_IO* file,
54+
XMPFiles* parent)
55+
{
56+
IgnoreParam(format); IgnoreParam(parent);
57+
XMP_Assert(format == kXMP_MatroskaFile);
58+
59+
try
60+
{
61+
MatroskaDOM DOM(file);
62+
63+
return true;
64+
}
65+
catch (std::logic_error &)
66+
{
67+
return false;
68+
}
69+
} // Matroska_CheckFormat
70+
71+
extern XMPFileHandler * Matroska_MetaHandlerCTor(XMPFiles * parent)
72+
{
73+
return new Matroska_MetaHandler(parent);
74+
}
75+
76+
Matroska_MetaHandler::Matroska_MetaHandler(XMPFiles* parent)
77+
: XMPFileHandler(parent)
78+
{
79+
this->parent = parent;
80+
this->handlerFlags = kMatroska_HandlerFlags;
81+
this->stdCharForm = kXMP_Char8Bit;
82+
}
83+
84+
void Matroska_MetaHandler::CacheFileData()
85+
{
86+
XMP_Assert(parent && parent->ioRef);
87+
try
88+
{
89+
containsXMP = false;
90+
_dom = std::make_shared<MatroskaDOM>(parent->ioRef);
91+
92+
for (auto segment : _dom->_root->getElementsById(kSegment))
93+
{
94+
for (auto tags : segment->getElementsById(kTags))
95+
{
96+
for (auto tag : tags->getElementsById(kTag))
97+
{
98+
for (auto simple_tag : tag->getElementsById(kSimpleTag))
99+
{
100+
auto tag_name = simple_tag->getElementById(kTagName);
101+
if (tag_name->_value.StringValue == "XMP")
102+
{
103+
auto tag_string = simple_tag->getElementById(kTagString);
104+
105+
xmpPacket = tag_string->_value.StringValue;
106+
containsXMP = true;
107+
108+
break;
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}
115+
catch (std::logic_error & e)
116+
{
117+
e.what();
118+
XMP_Validate(false, e.what(), kXMPErr_BadFileFormat);
119+
}
120+
}
121+
122+
void Matroska_MetaHandler::UpdateFile(bool doSafeUpdate)
123+
{
124+
if (!needsUpdate) return; // If needsUpdate is set then at least the XMP changed.
125+
126+
needsUpdate = false; // Make sure only called once.
127+
XMP_Assert(!doSafeUpdate); // This should only be called for "unsafe" updates.
128+
XMP_Assert(parent && parent->ioRef);
129+
130+
XMP_AbortProc abortProc = parent->abortProc;
131+
void * abortArg = parent->abortArg;
132+
const bool checkAbort = (abortProc != 0);
133+
134+
bool localProgressTracking(false);
135+
XMP_ProgressTracker* progressTracker = parent->progressTracker;
136+
if (progressTracker)
137+
{
138+
float xmpSize = static_cast<float>(xmpPacket.size());
139+
if (progressTracker->WorkInProgress())
140+
{
141+
progressTracker->AddTotalWork(xmpSize);
142+
}
143+
else
144+
{
145+
localProgressTracking = true;
146+
progressTracker->BeginWork(xmpSize);
147+
}
148+
}
149+
UpdateXMP();
150+
151+
if (localProgressTracking) progressTracker->WorkComplete();
152+
}
153+
154+
void Matroska_MetaHandler::UpdateXMP()
155+
{
156+
XMP_IO* fileRef = parent->ioRef;
157+
158+
fileRef->Seek(fileRef->Length(), kXMP_SeekFromStart);
159+
160+
for (auto segment : _dom->_root->getElementsById(kSegment))
161+
{
162+
for (auto tags : segment->getElementsById(kTags))
163+
{
164+
for (auto tag : tags->getElementsById(kTag))
165+
{
166+
for (auto simple_tag : tag->getElementsById(kSimpleTag))
167+
{
168+
auto tag_name = simple_tag->getElementById(kTagName);
169+
if (tag_name->_value.StringValue == "XMP")
170+
{
171+
auto tag_string = simple_tag->getElementById(kTagString);
172+
173+
// we have found valid XMP, and if it's in the very end of file, we can truncate segment
174+
if (tag_string->_offset + tag_string->size() == fileRef->Length())
175+
{
176+
segment->_size._value -= tags->size();
177+
178+
fileRef->Truncate(tags->_offset);
179+
fileRef->Seek(tags->_offset, kXMP_SeekFromStart);
180+
}
181+
// otherwise, make old XMP tag Void and create new one from the scratch
182+
else
183+
{
184+
tags->wipe(fileRef);
185+
}
186+
}
187+
}
188+
}
189+
}
190+
}
191+
auto segments = _dom->_root->getElementsById(kSegment);
192+
auto segment = segments.back();
193+
194+
auto tag_name = std::make_shared<ebml_element_t>(kTagName, ebml_variant_t("XMP"));
195+
auto tag_string = std::make_shared<ebml_element_t>(kTagString, ebml_variant_t(xmpPacket));
196+
auto tag_language = std::make_shared<ebml_element_t>(kTagLanguage, ebml_variant_t("eng"));
197+
auto tag_default = std::make_shared<ebml_element_t>(kTagDefault, ebml_variant_t(1ULL));
198+
auto simple_tag = std::make_shared<ebml_element_t>(kSimpleTag, ebml_element_t::vec{ tag_language, tag_default, tag_name, tag_string });
199+
auto tag = std::make_shared<ebml_element_t>(kTag, simple_tag);
200+
auto tags = std::make_shared<ebml_element_t>(kTags, tag);
201+
202+
tags->write(fileRef);
203+
204+
segment->_size._value += tags->size();
205+
segment->update_header(fileRef);
206+
}
207+
208+
void Matroska_MetaHandler::WriteTempFile(XMP_IO* tempRef)
209+
{
210+
XMP_Assert(needsUpdate);
211+
212+
XMP_IO* originalRef = parent->ioRef;
213+
214+
bool localProgressTracking(false);
215+
XMP_ProgressTracker* progressTracker = parent->progressTracker;
216+
if (progressTracker)
217+
{
218+
float xmpSize = static_cast<float>(xmpPacket.size());
219+
if (progressTracker->WorkInProgress())
220+
{
221+
progressTracker->AddTotalWork(xmpSize);
222+
}
223+
else
224+
{
225+
localProgressTracking = true;
226+
progressTracker->BeginWork(xmpSize);
227+
}
228+
}
229+
230+
XMP_Assert(tempRef);
231+
XMP_Assert(originalRef);
232+
233+
tempRef->Rewind();
234+
originalRef->Rewind();
235+
XIO::Copy(originalRef, tempRef, originalRef->Length(), parent->abortProc, parent->abortArg);
236+
237+
try
238+
{
239+
parent->ioRef = tempRef; // ! Fool UpdateFile into using the temp file.
240+
UpdateFile(false);
241+
parent->ioRef = originalRef;
242+
}
243+
catch (...)
244+
{
245+
parent->ioRef = originalRef;
246+
throw;
247+
}
248+
if (localProgressTracking) progressTracker->WorkComplete();
249+
}
250+
251+
void Matroska_MetaHandler::ProcessXMP()
252+
{
253+
if (processedXMP) return;
254+
processedXMP = true; // Make sure we only come through here once.
255+
256+
// Process the XMP packet.
257+
258+
if (!xmpPacket.empty())
259+
{
260+
XMP_Assert(containsXMP);
261+
262+
xmpObj.ParseFromBuffer(xmpPacket.c_str(), static_cast<XMP_StringLen>(xmpPacket.size()));
263+
264+
containsXMP = true;
265+
}
266+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// =================================================================================================
2+
// ADOBE SYSTEMS INCORPORATED
3+
// Copyright 2016 Adobe Systems Incorporated
4+
// All Rights Reserved
5+
//
6+
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7+
// of the Adobe license agreement accompanying it.
8+
// =================================================================================================
9+
10+
#ifndef __Matroska_Handler_hpp__
11+
#define __Matroska_Handler_hpp__ 1
12+
13+
#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
14+
#include "public/include/XMP_Const.h"
15+
16+
#include "source/Endian.h"
17+
#include "source/XIO.hpp"
18+
19+
#include "XMPFiles/source/XMPFiles_Impl.hpp"
20+
21+
#include <memory>
22+
23+
#if !defined(_MSC_FULL_VER) || _MSC_FULL_VER < 190023026
24+
// MSVC before 2015 doesn't support 'noexcept'
25+
#define _ALLOW_KEYWORD_MACROS 1
26+
#define noexcept throw()
27+
#endif
28+
29+
extern XMPFileHandler * Matroska_MetaHandlerCTor(XMPFiles * parent);
30+
31+
extern bool Matroska_CheckFormat(XMP_FileFormat format,
32+
XMP_StringPtr filePath,
33+
XMP_IO* fileRef,
34+
XMPFiles * parent);
35+
36+
static const XMP_OptionBits kMatroska_HandlerFlags = (kXMPFiles_CanInjectXMP |
37+
kXMPFiles_CanExpand |
38+
kXMPFiles_CanRewrite |
39+
kXMPFiles_PrefersInPlace |
40+
kXMPFiles_CanReconcile |
41+
kXMPFiles_AllowsOnlyXMP |
42+
kXMPFiles_ReturnsRawPacket |
43+
kXMPFiles_AllowsSafeUpdate |
44+
kXMPFiles_CanNotifyProgress
45+
);
46+
47+
class MatroskaDOM;
48+
49+
class Matroska_MetaHandler : public XMPFileHandler
50+
{
51+
public:
52+
explicit Matroska_MetaHandler(XMPFiles* parent);
53+
~Matroska_MetaHandler() noexcept {}
54+
55+
void CacheFileData() override;
56+
void UpdateFile(bool doSafeUpdate) override;
57+
void WriteTempFile(XMP_IO* tempRef) override;
58+
void ProcessXMP() override;
59+
void UpdateXMP();
60+
std::shared_ptr<MatroskaDOM> _dom;
61+
};
62+
63+
#endif /* __Matroska_Handler_hpp__ */

0 commit comments

Comments
 (0)