summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorEino-Ville Talvala <etalvala@google.com>2018-11-15 15:49:02 -0800
committerEino-Ville Talvala <etalvala@google.com>2018-11-15 16:07:24 -0800
commit09f199a694ef5b956cabc368e40ab5ca11c64044 (patch)
tree456d184a3817c8b6524a90fc2da60893a2fc1895 /includes
parent2d25fc4f6a7e7453f877958a2f3f59b6cc588ca4 (diff)
downloaddynamic_depth-09f199a694ef5b956cabc368e40ab5ca11c64044.tar.gz
Initial commit of libdynamic_depth
Dynamic depth is a standard for embedding depth maps and other similar extensions into standard image files like JPEG. Test: m libdynamic_depth Bug: 109735087 Bug: 119211681 Change-Id: I0103b7d47e60dc8e3a3b277456903d76f727926f
Diffstat (limited to 'includes')
-rw-r--r--includes/dynamic_depth/app_info.h81
-rw-r--r--includes/dynamic_depth/camera.h126
-rw-r--r--includes/dynamic_depth/cameras.h53
-rw-r--r--includes/dynamic_depth/container.h52
-rw-r--r--includes/dynamic_depth/depth_map.h128
-rw-r--r--includes/dynamic_depth/device.h131
-rw-r--r--includes/dynamic_depth/dynamic_depth.h34
-rw-r--r--includes/dynamic_depth/earth_pose.h94
-rw-r--r--includes/dynamic_depth/image.h89
-rw-r--r--includes/dynamic_depth/imaging_model.h90
-rw-r--r--includes/dynamic_depth/item.h89
-rw-r--r--includes/dynamic_depth/light_estimate.h68
-rw-r--r--includes/dynamic_depth/plane.h93
-rw-r--r--includes/dynamic_depth/planes.h61
-rw-r--r--includes/dynamic_depth/point_cloud.h61
-rw-r--r--includes/dynamic_depth/pose.h98
-rw-r--r--includes/dynamic_depth/profile.h56
-rw-r--r--includes/dynamic_depth/profiles.h56
-rw-r--r--includes/dynamic_depth/vendor_info.h74
-rw-r--r--includes/dynamic_depth/version.h20
-rw-r--r--includes/xmpmeta/jpeg_io.h53
-rw-r--r--includes/xmpmeta/md5.h16
-rw-r--r--includes/xmpmeta/version.h26
-rw-r--r--includes/xmpmeta/xmp_const.h30
-rw-r--r--includes/xmpmeta/xmp_data.h34
-rw-r--r--includes/xmpmeta/xmp_parser.h27
-rw-r--r--includes/xmpmeta/xmp_writer.h43
-rw-r--r--includes/xmpmeta/xmpmeta.h11
28 files changed, 1794 insertions, 0 deletions
diff --git a/includes/dynamic_depth/app_info.h b/includes/dynamic_depth/app_info.h
new file mode 100644
index 0000000..4a519b4
--- /dev/null
+++ b/includes/dynamic_depth/app_info.h
@@ -0,0 +1,81 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_APP_INFO_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_APP_INFO_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/item.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+/**
+ * A AppInfo element for a Dynamic Depth device.
+ */
+class AppInfo : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("AppInfo", "http://ns.google.com/photos/dd/1.0/appinfo/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates an AppInfo from the given fields. Returns null if the version
+ // field is empty and [item_uri is empty and items is null].
+ // Params' descriptions:
+ // application is the name of the application that created the content.
+ // version is the application's version for the content.
+ // data is the optional payload associated with the given app. If this field
+ // is not empty but item_uri is empty or items is null, then the data will
+ // not be serialized.
+ // item_uri is the Container URI of the file that contains the content.
+ // application, and at least one of version or item_uri, must not be
+ // empty.
+ // items is the list of items where the serialized data is stored. It is the
+ // caller's responsibility to use items to construct a Container, and
+ // ensure that it is serialized along with this Image element. Data will
+ // not be serialized if this field is null.
+ static std::unique_ptr<AppInfo> FromData(
+ const string& application, const string& version, const string& data,
+ const string& item_uri, std::vector<std::unique_ptr<Item>>* items);
+
+ // Returns the deserialized AppInfo; null if parsing fails.
+ static std::unique_ptr<AppInfo> FromDeserializer(
+ const xml::Deserializer& parent_deserializer,
+ const string& namespace_str);
+
+ // Getters.
+ const string& GetApplication() const;
+ const string& GetVersion() const;
+ const string& GetItemUri() const;
+
+ // Disallow copying.
+ AppInfo(const AppInfo&) = delete;
+ void operator=(const AppInfo&) = delete;
+
+ private:
+ AppInfo();
+
+ bool ParseFields(const xml::Deserializer& deserializer);
+
+ // Required.
+ string application_;
+
+ // At least one of version or item_uri must be present.
+ string version_;
+ string item_uri_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_APP_INFO_H_ // NOLINT
diff --git a/includes/dynamic_depth/camera.h b/includes/dynamic_depth/camera.h
new file mode 100644
index 0000000..9c9c001
--- /dev/null
+++ b/includes/dynamic_depth/camera.h
@@ -0,0 +1,126 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERA_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERA_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "dynamic_depth/app_info.h"
+#include "dynamic_depth/depth_map.h"
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/image.h"
+#include "dynamic_depth/imaging_model.h"
+#include "dynamic_depth/light_estimate.h"
+#include "dynamic_depth/point_cloud.h"
+#include "dynamic_depth/pose.h"
+#include "dynamic_depth/vendor_info.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// The camera trait is serialized only if it is one of PHYSICAL or LOGICAL.
+// NONE signifies an undefined trait.
+enum CameraTrait { NONE = 0, PHYSICAL = 1, LOGICAL = 2 };
+
+struct CameraParams {
+ // The Image must be present.
+ std::unique_ptr<Image> image;
+
+ // Optional elements.
+ std::unique_ptr<DepthMap> depth_map;
+ std::unique_ptr<LightEstimate> light_estimate;
+ std::unique_ptr<Pose> pose;
+ std::unique_ptr<ImagingModel> imaging_model;
+ std::unique_ptr<PointCloud> point_cloud;
+ std::unique_ptr<VendorInfo> vendor_info;
+ std::unique_ptr<AppInfo> app_info;
+ CameraTrait trait;
+
+ explicit CameraParams(std::unique_ptr<Image> img)
+ : depth_map(nullptr),
+ light_estimate(nullptr),
+ pose(nullptr),
+ imaging_model(nullptr),
+ point_cloud(nullptr),
+ vendor_info(nullptr),
+ app_info(nullptr),
+ trait(CameraTrait::PHYSICAL) {
+ image = std::move(img);
+ }
+
+ inline bool operator==(const CameraParams& other) const {
+ return image.get() == other.image.get() &&
+ light_estimate.get() == other.light_estimate.get() &&
+ pose.get() == other.pose.get() &&
+ depth_map.get() == other.depth_map.get() &&
+ imaging_model.get() == other.imaging_model.get() &&
+ point_cloud.get() == other.point_cloud.get() &&
+ vendor_info.get() == other.vendor_info.get() &&
+ app_info.get() == other.app_info.get();
+ }
+
+ inline bool operator!=(const CameraParams& other) const {
+ return !(*this == other);
+ }
+};
+
+// Implements the Camera element from the Dynamic Depth specification, with
+// serialization and deserialization.
+class Camera : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates a Camera from the given objects in params.
+ // Aside from the Image element, all other elements are optional and can be
+ // null.
+ static std::unique_ptr<Camera> FromData(std::unique_ptr<CameraParams> params);
+
+ // Same as above, but allows the Image element to be null. This should be used
+ // only for Camera 0, since the lack of an Image element indicates that it
+ // refers to the JPEG container image. An Item element is generated for the
+ // container image, and populated into items.
+ // Currently supports only image/jpeg.
+ // The items parameter may be null if params->image is not null.
+ // TODO(miraleung): Add other mime types as args when needed.
+ static std::unique_ptr<Camera> FromDataForCamera0(
+ std::unique_ptr<CameraParams> params,
+ std::vector<std::unique_ptr<Item>>* items);
+
+ // Returns the deserialized Camera object, null if parsing fails.
+ // Not sensitive to case when parsing a camera's trait.
+ static std::unique_ptr<Camera> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Getters. Except for Imaeg (which should never be null), these will return
+ // null if the corresponding fields are not present.
+ // which should never be null.
+ const Image* GetImage() const;
+ const LightEstimate* GetLightEstimate() const;
+ const Pose* GetPose() const;
+ const DepthMap* GetDepthMap() const;
+ const ImagingModel* GetImagingModel() const;
+ const PointCloud* GetPointCloud() const;
+ const VendorInfo* GetVendorInfo() const;
+ const AppInfo* GetAppInfo() const;
+ CameraTrait GetTrait() const;
+
+ // Disallow copying.
+ Camera(const Camera&) = delete;
+ void operator=(const Camera&) = delete;
+
+ private:
+ explicit Camera(std::unique_ptr<CameraParams> params);
+
+ std::unique_ptr<CameraParams> params_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERA_H_ // NOLINT
diff --git a/includes/dynamic_depth/cameras.h b/includes/dynamic_depth/cameras.h
new file mode 100644
index 0000000..5537e03
--- /dev/null
+++ b/includes/dynamic_depth/cameras.h
@@ -0,0 +1,53 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERAS_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERAS_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/camera.h"
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// Implements the Device:Cameras field from the Dynamic Depth specification,
+// with serialization and deserialization for its child Camera elements.
+class Cameras : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates this object from the given cameras. Returns null if the list is
+ // empty.
+ // If creation succeeds, ownership of the Camera objects are transferred to
+ // the resulting Cameras object. The vector of Camera objects will be cleared.
+ static std::unique_ptr<Cameras> FromCameraArray(
+ std::vector<std::unique_ptr<Camera>>* camera_list);
+
+ // Returns the deserialized cameras in a Cameras object, null if parsing
+ // failed for all the cameras.
+ static std::unique_ptr<Cameras> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Returns the list of cameras.
+ const std::vector<const Camera*> GetCameras() const;
+
+ // Disallow copying.
+ Cameras(const Cameras&) = delete;
+ void operator=(const Cameras&) = delete;
+
+ private:
+ Cameras();
+
+ std::vector<std::unique_ptr<Camera>> camera_list_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERAS_H_ // NOLINT
diff --git a/includes/dynamic_depth/container.h b/includes/dynamic_depth/container.h
new file mode 100644
index 0000000..a8a437d
--- /dev/null
+++ b/includes/dynamic_depth/container.h
@@ -0,0 +1,52 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CONTAINER_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CONTAINER_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/item.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// A Container that holds a directory / array of file Item elementss. Files
+// at the end of the image appear in the same sequential order as the Item
+// elements held in an instance of this object.
+class Container : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates this object from the given items. Returns null if the list is
+ // empty. If creation succeeds, ownership of the Item objects are transferred
+ // to the resulting Container object. The vector of Item objects will be
+ // cleared.
+ static std::unique_ptr<Container> FromItems(
+ std::vector<std::unique_ptr<Item>>* items);
+
+ // Returns the deserialized item elements, null if parsing failed for all
+ // items.
+ static std::unique_ptr<Container> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Returns the list of cameras.
+ const std::vector<const Item*> GetItems() const;
+
+ // Disallow copying.
+ Container(const Container&) = delete;
+ void operator=(const Container&) = delete;
+
+ private:
+ Container();
+
+ std::vector<std::unique_ptr<Item>> items_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CONTAINER_H_ // NOLINT
diff --git a/includes/dynamic_depth/depth_map.h b/includes/dynamic_depth/depth_map.h
new file mode 100644
index 0000000..836ff23
--- /dev/null
+++ b/includes/dynamic_depth/depth_map.h
@@ -0,0 +1,128 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEPTH_MAP_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEPTH_MAP_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/item.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// The depth conversion format. Please see the Depth Map element in the
+// Dynamnic Depth specification for more details.
+enum class DepthFormat { kFormatNone = 0, kRangeInverse = 1, kRangeLinear = 2 };
+
+// The Units of the depth map. Please see the Depth Map element in the Dynamic
+// Depth specification.
+enum class DepthUnits { kUnitsNone = 0, kMeters = 1, kDiopters = 2 };
+
+// The type of depth measurement. Please see the Depth Map element in the
+// Dynamic Depth specification.
+enum class DepthMeasureType { kOpticalAxis = 1, kOpticRay = 2 };
+
+// The semantics of this depth map.
+enum class DepthItemSemantic { kDepth = 1, kSegmentation = 2 };
+
+struct DepthMapParams {
+ // Mandatory values.
+ DepthFormat format;
+ float near;
+ float far;
+ DepthUnits units;
+ string depth_uri;
+ string mime;
+ DepthItemSemantic item_semantic = DepthItemSemantic::kDepth;
+
+ // The bytes of the depth image. Must be non-empty at write-time (i.e.
+ // programmatic construction).
+ string depth_image_data = "";
+
+ // Optional values.
+ DepthMeasureType measure_type = DepthMeasureType::kOpticalAxis;
+ string confidence_uri = "";
+ // The bytes of the confidence map. If confidence_uri is not empty, the
+ // confidence data must be non-empty at write-time (i.e. programmatic
+ // construction).
+ string confidence_data = "";
+ string software = "";
+
+ // A list of (distance, radius) pairs. This should generally have a short
+ // length, so copying is expected to be inexpensive.
+ std::vector<float> focal_table;
+
+ explicit DepthMapParams(DepthFormat in_format, float in_near, float in_far,
+ DepthUnits in_units, string in_depth_uri)
+ : format(in_format),
+ near(in_near),
+ far(in_far),
+ units(in_units),
+ depth_uri(in_depth_uri) {}
+
+ inline bool operator==(const DepthMapParams& other) const {
+ return format == other.format && near == other.near && far == other.far &&
+ units == other.units && depth_uri == other.depth_uri &&
+ depth_image_data == other.depth_image_data &&
+ measure_type == other.measure_type &&
+ confidence_uri == other.confidence_uri &&
+ confidence_data == other.confidence_data &&
+ software == other.software && focal_table == other.focal_table;
+ }
+
+ inline bool operator!=(const DepthMapParams& other) const {
+ return !(*this == other);
+ }
+};
+
+// Implements the Depth Map element from the Dynamic Depth specification, with
+// serialization and deserialization.
+class DepthMap : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates a DepthMap from the given objects in params.
+ static std::unique_ptr<DepthMap> FromData(
+ const DepthMapParams& params, std::vector<std::unique_ptr<Item>>* items);
+
+ // Returns the deserialized DepthMap object, null if parsing fails.
+ // Not sensitive to case when parsing the Format, Units, or MeasureType
+ // fields.
+ static std::unique_ptr<DepthMap> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ DepthFormat GetFormat() const;
+ float GetNear() const;
+ float GetFar() const;
+ DepthUnits GetUnits() const;
+ const string GetDepthUri() const;
+ DepthItemSemantic GetItemSemantic() const;
+ const string GetConfidenceUri() const;
+ DepthMeasureType GetMeasureType() const;
+ const string GetSoftware() const;
+ const std::vector<float>& GetFocalTable() const;
+ size_t GetFocalTableEntryCount() const;
+
+ // Disallow copying
+ DepthMap(const DepthMap&) = delete;
+ void operator=(const DepthMap&) = delete;
+
+ private:
+ explicit DepthMap(const DepthMapParams& params);
+ static std::unique_ptr<DepthMap> ParseFields(
+ const xml::Deserializer& deserializer);
+
+ DepthMapParams params_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEPTH_MAP_H_ // NOLINT
diff --git a/includes/dynamic_depth/device.h b/includes/dynamic_depth/device.h
new file mode 100644
index 0000000..77f1665
--- /dev/null
+++ b/includes/dynamic_depth/device.h
@@ -0,0 +1,131 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEVICE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEVICE_H_ // NOLINT
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xpath.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "dynamic_depth/app_info.h"
+#include "dynamic_depth/cameras.h"
+#include "dynamic_depth/container.h"
+#include "dynamic_depth/earth_pose.h"
+#include "dynamic_depth/planes.h"
+#include "dynamic_depth/pose.h"
+#include "dynamic_depth/profiles.h"
+#include "dynamic_depth/vendor_info.h"
+#include "xmpmeta/xmp_data.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+struct DeviceParams {
+ // Cameras must be present (i.e. contain at least one camera).
+ std::unique_ptr<Cameras> cameras;
+
+ // GContainer. Optional, depending on element presence or user choice.
+ std::unique_ptr<Container> container;
+
+ // Optional elements.
+ std::unique_ptr<Profiles> profiles;
+ std::unique_ptr<Planes> planes;
+ std::unique_ptr<EarthPose> earth_pose;
+ std::unique_ptr<Pose> pose;
+ std::unique_ptr<VendorInfo> vendor_info;
+ std::unique_ptr<AppInfo> app_info;
+
+ explicit DeviceParams(std::unique_ptr<Cameras> new_cameras)
+ : container(nullptr),
+ profiles(nullptr),
+ planes(nullptr),
+ earth_pose(nullptr),
+ pose(nullptr),
+ vendor_info(nullptr),
+ app_info(nullptr) {
+ cameras = std::move(new_cameras);
+ }
+
+ inline bool operator==(const DeviceParams& other) const {
+ return cameras.get() == other.cameras.get() &&
+ container.get() == other.container.get() &&
+ profiles.get() == other.profiles.get() &&
+ planes.get() == other.planes.get() &&
+ earth_pose.get() == other.earth_pose.get() &&
+ pose.get() == other.pose.get() &&
+ vendor_info.get() == other.vendor_info.get() &&
+ app_info.get() == other.app_info.get();
+ }
+
+ inline bool operator!=(const DeviceParams& other) const {
+ return !(*this == other);
+ }
+};
+
+// Implements a Device from the Dynamic Depth specification, with serialization
+// and deserialization.
+// Does not implement the Element interface because Device is at the top level
+// in the tree.
+class Device {
+ public:
+ // Creates a Device from the given elements.
+ static std::unique_ptr<Device> FromData(std::unique_ptr<DeviceParams> params);
+
+ // Creates a Device from pre-extracted XMP metadata. Returns null if
+ // parsing fails. Both the standard and extended XMP sections are required.
+ static std::unique_ptr<Device> FromXmp(const XmpData& xmp);
+
+ // Creates a Device by extracting XMP metadata from a JPEG and parsing it.
+ // If using XMP for other things as well, FromXmp() should be used instead to
+ // prevent redundant extraction of XMP from the JPEG.
+ static std::unique_ptr<Device> FromJpegFile(const string& filename);
+
+ // Creates a Device by parsing XML file containing the metadata.
+ static std::unique_ptr<Device> FromXmlFile(const string& filename);
+
+ // Getters.
+ // May return null values for optional fields.
+ const Cameras* GetCameras() const;
+ const Container* GetContainer() const;
+ const EarthPose* GetEarthPose() const;
+ const Pose* GetPose() const;
+ const Planes* GetPlanes() const;
+ const Profiles* GetProfiles() const;
+ const VendorInfo* GetVendorInfo() const;
+ const AppInfo* GetAppInfo() const;
+
+ // Not const for XML memory management reasons. More info in source comments.
+ bool SerializeToXmp(XmpData* xmp);
+
+ // Saves Device metadata to a .xml file.
+ bool SerializeToXmlFile(const char* filename);
+
+ // Disallow copying.
+ Device(const Device&) = delete;
+ void operator=(const Device&) = delete;
+
+ private:
+ explicit Device(std::unique_ptr<DeviceParams> params);
+
+ // Retrieves the namespaces of all child elements.
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) const;
+ // Gathers all the XML namespaces of child elements.
+ void PopulateNamespaces();
+ bool Serialize(xmlDocPtr* xmlDoc);
+
+ // Keep a reference to the XML namespaces, so that they are created only once
+ // when Device is constructed.
+ std::unordered_map<string, xmlNsPtr> namespaces_;
+
+ std::unique_ptr<DeviceParams> params_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEVICE_H_ // NOLINT
diff --git a/includes/dynamic_depth/dynamic_depth.h b/includes/dynamic_depth/dynamic_depth.h
new file mode 100644
index 0000000..40a0db7
--- /dev/null
+++ b/includes/dynamic_depth/dynamic_depth.h
@@ -0,0 +1,34 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DYNAMIC_DEPTH_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DYNAMIC_DEPTH_H_ // NOLINT
+
+#include "dynamic_depth/device.h"
+#include "xmpmeta/xmp_writer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// Serialize a JPEG image, its Dynamic Depth metadata, and GContainer files
+// if applicable.
+bool WriteImageAndMetadataAndContainer(const string& out_filename,
+ const uint8_t* primary_image_bytes,
+ size_t primary_image_bytes_count,
+ Device* device);
+
+// Retrieves the contents of a Container:Item's associated file. The contents
+// are populated into out_payload.
+// As per the Dynamic Depth spec, file contents are base64-encoded if they're
+// of an image/ mime type, and not if they're of a text/ mime type. Dynamic
+// Depth elements that use Container:Item will haev handled this appropriately
+// on item construction.
+bool GetItemPayload(const string& input_image_filename,
+ const Container* container, const string& item_uri,
+ string* out_payload);
+
+// Convenience method for the aboove.
+bool GetItemPayload(const string& input_image_filename, const Device* device,
+ const string& item_uri, string* out_payload);
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DYNAMIC_DEPTH_H_ // NOLINT
diff --git a/includes/dynamic_depth/earth_pose.h b/includes/dynamic_depth/earth_pose.h
new file mode 100644
index 0000000..0487cdb
--- /dev/null
+++ b/includes/dynamic_depth/earth_pose.h
@@ -0,0 +1,94 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_EARTH_POSE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_EARTH_POSE_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// Implements the EarthPose element in the Dynamic Depth specification, with
+// serialization and deserialization.
+class EarthPose : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("EarthPose", "http://ns.google.com/photos/dd/1.0/earthpose/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object. Returns true on success.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates a EarthPose from the given data.
+ // The order of values in position is latitude, longitude, altitude..
+ // The order of values in orientation is the quaternion x, y, z, w fields.
+ // rotation angle in quaternion format.
+ // Position and orientation are in raw coordinates, and will be stored as
+ // normalied values. Please refer to the Dynamic Depth specification for the
+ // Realm coordinate system.
+ // At least one valid position or orientation must be provided. These
+ // arguments will be ignored if the vector is of the wrong size.
+ static std::unique_ptr<EarthPose> FromData(
+ const std::vector<double>& position,
+ const std::vector<float>& orientation, const int64 timestamp = -1);
+
+ // Returns the deserialized XdmAudio; null if parsing fails.
+ // The returned pointer is owned by the caller.
+ static std::unique_ptr<EarthPose> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Returns true if the device's position is provided.
+ bool HasPosition() const;
+
+ // Returns true if the device's orientation is provided.
+ bool HasOrientation() const;
+
+ // Returns the device's position fields, or an empty vector if they are
+ // not present.
+ const std::vector<double>& GetPosition() const;
+
+ // Returns the device's orientation fields, or an empty vector if they are
+ // not present.
+ const std::vector<float>& GetOrientation() const;
+
+ // Timestamp.
+ int64 GetTimestamp() const;
+
+ // Disallow copying.
+ EarthPose(const EarthPose&) = delete;
+ void operator=(const EarthPose&) = delete;
+
+ private:
+ EarthPose();
+
+ // Extracts device pose fields.
+ bool ParseEarthPoseFields(const xml::Deserializer& deserializer);
+
+ // Position variables, in meters relative to camera 0.
+ // If providing position data, all three fields must be set.
+ // Stored in normalized form.
+ // TODO(miraleung): Cleanup: consider std::optional for this and orientation_.
+ std::vector<double> position_;
+
+ // Orientation variables.
+ // If providing orientation data, all four fields must be set.
+ // Stored in normalized form.
+ std::vector<float> orientation_;
+
+ // Timestamp is Epoch time in milliseconds.
+ int64 timestamp_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_EARTH_POSE_H_ // NOLINT
diff --git a/includes/dynamic_depth/image.h b/includes/dynamic_depth/image.h
new file mode 100644
index 0000000..b944337
--- /dev/null
+++ b/includes/dynamic_depth/image.h
@@ -0,0 +1,89 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGE_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/item.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// The ItemSemantic of this Image.
+enum class ImageItemSemantic { kPrimary = 1, kOriginal = 2 };
+
+/**
+ * An Image element for a Dynamic Depth device.
+ */
+class Image : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("Image", "http://ns.google.com/photos/dd/1.0/image/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates an original (non-primary) Image from the given fields. Returns null
+ // if one of the following is true:
+ // - Mime field is empty.
+ // - Data field is null or empty.
+ // - Uri field is empty.
+ // - The items field is null.
+ // Param description:
+ // - Data is NOT base64-encoded. This method takes care of encoding when it
+ // is passed to the generated Item element.
+ // - Mime is the mimetype of the image data.
+ // - Uri is the case-sensitive Item:DataUri of this Image, and must be
+ // unique amongst all Dynamic Depth elements. This will be the only way
+ // that metadata parsers will be able to retrieve the image.
+ // Both data and mime are used to construct a new Container:Item, which will
+ // be the last one appended to items.
+ // It is the caller's responsibility to use items to construct a Container,
+ // and ensure that it is serialized along with this Image element.
+ static std::unique_ptr<Image> FromData(
+ const string& data, const string& mime, const string& item_uri,
+ std::vector<std::unique_ptr<Item>>* items);
+
+ // Same as above, but more performant because it avoids an extra string copy.
+ static std::unique_ptr<Image> FromData(
+ const uint8_t* data, size_t data_size, const string& mime,
+ const string& item_uri, std::vector<std::unique_ptr<Item>>* items);
+
+ // Image instantiator for the primary (container) image.
+ static std::unique_ptr<Image> FromDataForPrimaryImage(
+ const string& mime, std::vector<std::unique_ptr<Item>>* items);
+
+ // Returns the deserialized Image; null if parsing fails.
+ static std::unique_ptr<Image> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ const string& GetItemUri() const;
+ ImageItemSemantic GetItemSemantic() const;
+
+ // Disallow copying.
+ Image(const Image&) = delete;
+ void operator=(const Image&) = delete;
+
+ private:
+ Image();
+
+ // Extracts image fields.
+ bool ParseImageFields(const xml::Deserializer& deserializer);
+
+ string item_uri_;
+ ImageItemSemantic item_semantic_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGE_H_ // NOLINT
diff --git a/includes/dynamic_depth/imaging_model.h b/includes/dynamic_depth/imaging_model.h
new file mode 100644
index 0000000..1175a66
--- /dev/null
+++ b/includes/dynamic_depth/imaging_model.h
@@ -0,0 +1,90 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGING_MODEL_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGING_MODEL_H_ // NOLINT
+
+#include <memory>
+#include <unordered_map>
+
+#include "dynamic_depth/dimension.h"
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/point.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+struct ImagingModelParams {
+ // Required. The order of numbers is (x, y), in pixels.
+ Point<double> focal_length;
+
+ // Required. The order of numbers is (width, height), in pixels.
+ Dimension image_size;
+
+ // Optional. Set to (0.5, 0.5) if not present.
+ // The order of numbers is (x, y).
+ Point<double> principal_point;
+
+ // Optional.
+ std::vector<float> distortion; // Distortion parameters.
+ double skew;
+ double pixel_aspect_ratio;
+
+ ImagingModelParams(const Point<double>& focal_len,
+ const Dimension& image_size)
+ : focal_length(focal_len),
+ image_size(image_size),
+ principal_point(Point<double>(0.5, 0.5)),
+ distortion(std::vector<float>()),
+ skew(0),
+ pixel_aspect_ratio(1) {}
+
+ inline bool operator==(const ImagingModelParams& other) const {
+ return focal_length == other.focal_length &&
+ image_size == other.image_size &&
+ principal_point == other.principal_point &&
+ distortion == other.distortion && skew == other.skew &&
+ pixel_aspect_ratio == other.pixel_aspect_ratio;
+ }
+
+ inline bool operator!=(const ImagingModelParams& other) const {
+ return !(*this == other);
+ }
+};
+
+class ImagingModel : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates an ImagingModel from the given params.
+ static std::unique_ptr<ImagingModel> FromData(
+ const ImagingModelParams& params);
+
+ // Returns the deserialized equirect model, null if parsing fails.
+ static std::unique_ptr<ImagingModel> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Getters.
+ Point<double> GetFocalLength() const;
+ Point<double> GetPrincipalPoint() const;
+ Dimension GetImageSize() const;
+ double GetSkew() const;
+ double GetPixelAspectRatio() const;
+ const std::vector<float>& GetDistortion() const;
+ int GetDistortionCount() const;
+
+ // Disallow copying.
+ ImagingModel(const ImagingModel&) = delete;
+ void operator=(const ImagingModel&) = delete;
+
+ private:
+ ImagingModel(const ImagingModelParams& params);
+
+ ImagingModelParams params_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGING_MODEL_H_ // NOLINT
diff --git a/includes/dynamic_depth/item.h b/includes/dynamic_depth/item.h
new file mode 100644
index 0000000..d1fcd36
--- /dev/null
+++ b/includes/dynamic_depth/item.h
@@ -0,0 +1,89 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_ITEM_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_ITEM_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/dimension.h"
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/point.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+struct ItemParams {
+ // Required fields.
+ string mime; // Must not be empty.
+ unsigned int length; // Must not be zero.
+
+ // Optional.
+ unsigned int padding;
+ string data_uri;
+
+ // Only for final file serialization - not used in XMP metadata I/O.
+ // IMPORTANT: Callers should enforce that this file exists.
+ // TODO(miraleung): This could be stored as char* to optimize performance.
+ string payload_to_serialize;
+
+ ItemParams(const string& in_mime, unsigned int len)
+ : mime(in_mime),
+ length(len),
+ padding(0),
+ data_uri(""),
+ payload_to_serialize("") {}
+ ItemParams(const string& in_mime, unsigned int len, const string& uri)
+ : mime(in_mime),
+ length(len),
+ padding(0),
+ data_uri(uri),
+ payload_to_serialize("") {}
+
+ inline bool operator==(const ItemParams& other) const {
+ return mime == other.mime && length == other.length &&
+ padding == other.padding && data_uri == other.data_uri &&
+ payload_to_serialize == other.payload_to_serialize;
+ }
+
+ inline bool operator!=(const ItemParams& other) const {
+ return !(*this == other);
+ }
+};
+
+class Item : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ static std::unique_ptr<Item> FromData(const ItemParams& params);
+
+ // Returns the deserialized item elements, null if parsing failed for all
+ // items.
+ static std::unique_ptr<Item> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ const string& GetMime() const;
+ unsigned int GetLength() const;
+ const string& GetDataUri() const;
+ unsigned int GetPadding() const;
+ const string& GetPayloadToSerialize() const;
+
+ // Disallow copying.
+ Item(const Item&) = delete;
+ void operator=(const Item&) = delete;
+
+ private:
+ Item(const ItemParams& params);
+ static std::unique_ptr<Item> FromDataInternal(const ItemParams& params,
+ bool check_filepath);
+
+ ItemParams params_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_ITEM_H_ // NOLINT
diff --git a/includes/dynamic_depth/light_estimate.h b/includes/dynamic_depth/light_estimate.h
new file mode 100644
index 0000000..51cbde2
--- /dev/null
+++ b/includes/dynamic_depth/light_estimate.h
@@ -0,0 +1,68 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_LIGHT_ESTIMATE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_LIGHT_ESTIMATE_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// Light estimation parameters for a camera.
+// This is stored as a sibling element to LightEstimate because it may apply to
+// the container image, so the decoupling is required.
+class LightEstimate : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("LightEstimate", "http://ns.google.com/photos/dd/1.0/image/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates an LightEstimate from the given field.
+ static std::unique_ptr<LightEstimate> FromData(float pixel_intensity);
+
+ // Takes the first three values from color_correction if the vector length is
+ // greater than 3.
+ // Color correction values should be between 0 and 1 (plus or minus 0.2).
+ static std::unique_ptr<LightEstimate> FromData(
+ float pixel_intensity, const std::vector<float>& color_correction);
+
+ // Returns the deserialized LightEstimate; null if parsing fails.
+ static std::unique_ptr<LightEstimate> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Returns the average pixel internsity.
+ float GetPixelIntensity() const;
+ const std::vector<float>& GetColorCorrection() const;
+
+ // Disallow copying.
+ LightEstimate(const LightEstimate&) = delete;
+ void operator=(const LightEstimate&) = delete;
+
+ private:
+ LightEstimate();
+
+ float pixel_intensity_ = 1.0f;
+
+ // Optional, either all three together or none at all.
+ // A size-3 vector of color correction values, in the order R, G, B.
+ // Values can be approximately between 0 and 1 (plus or minus 0.2).
+ // On reading back image metadata, if only one or two of the values are
+ // present, then the defaults below are used.
+ std::vector<float> color_correction_ = {1.0f, 1.0f, 1.0f};
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_LIGHT_ESTIMATE_H_ // NOLINT
diff --git a/includes/dynamic_depth/plane.h b/includes/dynamic_depth/plane.h
new file mode 100644
index 0000000..bec2289
--- /dev/null
+++ b/includes/dynamic_depth/plane.h
@@ -0,0 +1,93 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANE_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/pose.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// A Plane element for a Dynamic Depth device.
+// Only horizontal planes are currently supported.
+class Plane : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("Plane", "http://ns.google.com/photos/dd/1.0/plane/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Returns the deserialized Plane; null if parsing fails.
+ static std::unique_ptr<Plane> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Creates a Plane from the given fields.
+ // The Pose must be present.
+ // boundary contains the bounding vertices: [x0, z0, x1, z1, ..., xn, zn].
+ // extent_x is the length of the plane on the X axis.
+ // extent_z is the length of the plane on the Z axis.
+ static std::unique_ptr<Plane> FromData(std::unique_ptr<Pose> pose,
+ const std::vector<float>& boundary,
+ const double extent_x,
+ const double extent_z);
+
+ // Getters.
+ const Pose* GetPose() const;
+
+ // Returns the plane's bounding vertices on the XZ plane.
+ const std::vector<float>& GetBoundary() const;
+
+ // Returns the number of vertices in the boundary polygon.
+ int GetBoundaryVertexCount() const;
+
+ // Returns the plane's length along the X axis.
+ const double GetExtentX() const;
+
+ // Returns the plane's length along the Z axis.
+ const double GetExtentZ() const;
+
+ // Disallow copying.
+ Plane(const Plane&) = delete;
+ void operator=(const Plane&) = delete;
+
+ private:
+ Plane();
+
+ // Extracts plane fields.
+ bool ParsePlaneFields(const xml::Deserializer& deserializer);
+
+ // The pose of the center of this plane.
+ std::unique_ptr<Pose> pose_;
+
+ // Contains the plane's bounding vertices: [x0, z0, x1, z1, ..., xn, zn].
+ // Optional field, set to an empty vector if not present.
+ std::vector<float> boundary_;
+
+ // Required field if "Boundary" is present. Optional otherwise and
+ // expected to be zero; set to 0 if not present.
+ int boundary_vertex_count_;
+
+ // The length of the plane on the X axis. A value of -1 represents infinity.
+ // Optional field, set to -1 (infinity) if not present.
+ double extent_x_;
+
+ // The length of the plane on the Z axis. A value of -1 represents infinity.
+ // Optional field, set to -1 (infinity) if not present.
+ double extent_z_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANE_H_ // NOLINT
diff --git a/includes/dynamic_depth/planes.h b/includes/dynamic_depth/planes.h
new file mode 100644
index 0000000..c38aad4
--- /dev/null
+++ b/includes/dynamic_depth/planes.h
@@ -0,0 +1,61 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANES_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANES_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/plane.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// THe list of planes in a Dynamic Depth Device t ype.
+class Planes : public Element {
+ public:
+ // Creates this object from the given planes. Returns null if the list is
+ // empty, null, or contains null elements.
+ // If creation succeeds, ownership of the Plane objects are transferred to
+ // the resulting Planes object. The pointer to the vector of Plane objects
+ // will be replaced with the plane_list_, whose contents are not necessarily
+ // defined.
+ static std::unique_ptr<Planes> FromPlaneArray(
+ std::vector<std::unique_ptr<Plane>>* plane_list);
+
+ // Returns the deserialized cameras in a Planes object, null if parsing
+ // failed for all the planes, one of the planes is null, or the list of
+ // planes was empty.
+ static std::unique_ptr<Planes> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Disallow copying.
+ Planes(const Planes&) = delete;
+ Plane& operator=(const Planes&) = delete;
+
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Returns false if the list of planes is empty, or serialization fails.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Returns the number of plane elements in this Plane object.
+ int GetPlaneCount() const;
+
+ // Returns the ith plane, which is guaranteed not to be null because
+ // FromPlaneArray or FromDeserializer would have failed to construct a valid
+ // Planes object.
+ const Plane* GetPlaneAt(int i) const;
+
+ private:
+ Planes();
+
+ std::vector<std::unique_ptr<Plane>> plane_list_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANES_H_ // NOLINT
diff --git a/includes/dynamic_depth/point_cloud.h b/includes/dynamic_depth/point_cloud.h
new file mode 100644
index 0000000..3e53744
--- /dev/null
+++ b/includes/dynamic_depth/point_cloud.h
@@ -0,0 +1,61 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POINT_CLOUD_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POINT_CLOUD_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+// Implements the Point Cloud element from the Dynamic Depth specification, with
+// serialization and deserialization.
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+class PointCloud : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates a Point Cloud from the given fields. Returns null if position is
+ // empty or points.size() is not divisible by 3.
+ // The first two arguments are required fields, the rest are optional.
+ // points is a list of (x, y, z) tuples, so it must have a size that is
+ // evenly divisible by 3.
+ static std::unique_ptr<PointCloud> FromData(const std::vector<float>& points,
+ bool metric);
+
+ // Returns the deserialized PointCloud; null if parsing fails.
+ // The returned pointer is owned by the caller.
+ static std::unique_ptr<PointCloud> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Getters.
+ // Returns the number of (x, y, z) tuples, *not* the length of points_.
+ int GetPointCount() const;
+ const std::vector<float>& GetPoints() const;
+ bool GetMetric() const;
+
+ PointCloud(const PointCloud&) = delete;
+ void operator=(const PointCloud&) = delete;
+
+ private:
+ PointCloud();
+
+ bool ParseFields(const xml::Deserializer& deserializer);
+
+ // Required fields.
+ std::vector<float> points_;
+
+ // Optional fields.
+ bool metric_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POINT_CLOUD_H_ // NOLINT
diff --git a/includes/dynamic_depth/pose.h b/includes/dynamic_depth/pose.h
new file mode 100644
index 0000000..9bee030
--- /dev/null
+++ b/includes/dynamic_depth/pose.h
@@ -0,0 +1,98 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POSE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POSE_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+/**
+ * Implements the Pose element in the Dynamic Depth specification, with
+ * serialization and deserialization.
+ * See xdm.org.
+ */
+class Pose : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("Pose", "http://ns.google.com/photos/dd/1.0/pose/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object. Returns true on success.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates a Pose from the given data.
+ // The order of values in position is x, y, z.
+ // The order of values in orientation is the quaternion x, y, z, w fields.
+ // This is assusmed to be un-normalized.
+ // Position coordinates are relative to Camera 0, and must be 0 for Camera 0.
+ // Position and orientation are in raw coordinates, and will be stored as
+ // normalied values.
+ // At least one valid position or orientation must be provided. These
+ // arguments will be ignored if the vector is of the wrong size.
+ static std::unique_ptr<Pose> FromData(const std::vector<float>& position,
+ const std::vector<float>& orientation,
+ const int64 timestamp = -1);
+
+ // Returns the deserialized XdmAudio; null if parsing fails.
+ // The returned pointer is owned by the caller.
+ static std::unique_ptr<Pose> FromDeserializer(
+ const xml::Deserializer& parent_deserializer,
+ const char* parent_namespace);
+
+ // Returns true if the device's position is provided.
+ bool HasPosition() const;
+
+ // Returns true if the device's orientation is provided.
+ bool HasOrientation() const;
+
+ // Returns the device's position fields, or an empty vector if they are
+ // not present.
+ const std::vector<float>& GetPosition() const;
+
+ // Returns the device's orientation fields, or an empty vector if they are
+ // not present.
+ const std::vector<float>& GetOrientation() const;
+
+ // Timestamp.
+ int64 GetTimestamp() const;
+
+ // Disallow copying.
+ Pose(const Pose&) = delete;
+ void operator=(const Pose&) = delete;
+
+ private:
+ Pose();
+
+ // Extracts camera pose fields.
+ bool ParsePoseFields(const xml::Deserializer& deserializer);
+
+ // Position variables, in meters relative to camera 0.
+ // If providing position data, all three fields must be set.
+ // Stored in normalized form.
+ // TODO(miraleung): Cleanup: consider std::optional for this and orientation_.
+ std::vector<float> position_;
+
+ // Orientation variables.
+ // If providing orientation data, all four fields must be set.
+ // Stored in normalized form.
+ std::vector<float> orientation_;
+
+ // Timestamp is Epoch time in milliseconds.
+ int64 timestamp_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POSE_H_ // NOLINT
diff --git a/includes/dynamic_depth/profile.h b/includes/dynamic_depth/profile.h
new file mode 100644
index 0000000..b1a311c
--- /dev/null
+++ b/includes/dynamic_depth/profile.h
@@ -0,0 +1,56 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILE_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILE_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// Implements the Profile element in the Dynamic Depth specification, with
+// serialization and deserialization.
+class Profile : public Element {
+ public:
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates a Profile element from the given fields. Returns null if
+ // the type is empty, or if the camera_indices are shorter than the
+ // specified minimum length for types supported in the Dynamic Depth
+ // specification. Type is case-sensitive. If wrong, this will be treated as an
+ // unsupported type.
+ static std::unique_ptr<Profile> FromData(
+ const string& type, const std::vector<int>& camera_indices);
+
+ // Returns the deserialized Profile, null if parsing fails.
+ static std::unique_ptr<Profile> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Returns the Profile type.
+ const string& GetType() const;
+
+ // Returns the camera indices.
+ const std::vector<int>& GetCameraIndices() const;
+
+ // Disallow copying.
+ Profile(const Profile&) = delete;
+ void operator=(const Profile&) = delete;
+
+ private:
+ Profile(const string& type, const std::vector<int>& camera_indices);
+
+ string type_;
+ std::vector<int> camera_indices_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILE_H_ // NOLINT
diff --git a/includes/dynamic_depth/profiles.h b/includes/dynamic_depth/profiles.h
new file mode 100644
index 0000000..abcf6c1
--- /dev/null
+++ b/includes/dynamic_depth/profiles.h
@@ -0,0 +1,56 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILES_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILES_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "dynamic_depth/profile.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+// Implements the Device:Profiles field from the Dynamic Depth specification,
+// with serialization and deserialization for its child Profile elements.
+class Profiles : public Element {
+ public:
+ // Interface methods.
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Static methods.
+
+ // Creates this object from the given profiles. If the list is empty, returns
+ // a unique_ptr owning nothing.
+ static std::unique_ptr<Profiles> FromProfileArray(
+ std::vector<std::unique_ptr<Profile>>* profile_list);
+
+ // Returns the deserialized profiles in a Profiles object, a unique_ptr owning
+ // nothing if parsing failed for all the profiles.
+ static std::unique_ptr<Profiles> FromDeserializer(
+ const xml::Deserializer& parent_deserializer);
+
+ // Non-static methods.
+
+ // Returns the list of cameras.
+ const std::vector<const Profile*> GetProfiles() const;
+
+ // Disallow copying
+ Profiles(const Profiles&) = delete;
+ void operator=(const Profiles&) = delete;
+
+ private:
+ Profiles() = default;
+
+ std::vector<std::unique_ptr<Profile>> profile_list_;
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILES_H_ // NOLINT
diff --git a/includes/dynamic_depth/vendor_info.h b/includes/dynamic_depth/vendor_info.h
new file mode 100644
index 0000000..ba8ca48
--- /dev/null
+++ b/includes/dynamic_depth/vendor_info.h
@@ -0,0 +1,74 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VENDOR_INFO_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VENDOR_INFO_H_ // NOLINT
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "dynamic_depth/element.h"
+#include "xmpmeta/xml/deserializer.h"
+#include "xmpmeta/xml/serializer.h"
+
+namespace photos_editing_formats {
+namespace dynamic_depth {
+
+/**
+ * A VendorInfo element for a Dynamic Depth device.
+ */
+class VendorInfo : public Element {
+ public:
+ // Appends child elements' namespaces' and their respective hrefs to the
+ // given collection, and any parent nodes' names to prefix_names.
+ // Key: Name of the namespace.
+ // Value: Full namespace URL.
+ // Example: ("VendorInfo", "http://ns.google.com/photos/dd/1.0/vendorinfo/")
+ void GetNamespaces(
+ std::unordered_map<string, string>* ns_name_href_map) override;
+
+ // Serializes this object.
+ bool Serialize(xml::Serializer* serializer) const override;
+
+ // Creates an VendorInfo from the given fields. Returns null if
+ // any of the required fields is empty (see fields below).
+ // Manufacturer is the manufacturer of the element that created the content.
+ // Model is the model of the element that created the content.
+ // Notes is general comments.
+ static std::unique_ptr<VendorInfo> FromData(const string& manufacturer,
+ const string& model,
+ const string& notes);
+
+ // Returns the deserialized VendorInfo; null if parsing fails.
+ static std::unique_ptr<VendorInfo> FromDeserializer(
+ const xml::Deserializer& parent_deserializer,
+ const string& namespace_str);
+
+ // Returns the Manufacturer.
+ const string& GetManufacturer() const;
+
+ // Returns the Model.
+ const string& GetModel() const;
+
+ // Returns the Notes.
+ const string& GetNotes() const;
+
+ // Disallow copying.
+ VendorInfo(const VendorInfo&) = delete;
+ void operator=(const VendorInfo&) = delete;
+
+ private:
+ VendorInfo();
+
+ bool ParseFields(const xml::Deserializer& deserializer);
+
+ // Required field.
+ string manufacturer_; // The manufacturer.
+
+ // Optional fields.
+ string model_; // The model.
+ string notes_; // The notes.
+};
+
+} // namespace dynamic_depth
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VENDOR_INFO_H_ // NOLINT
diff --git a/includes/dynamic_depth/version.h b/includes/dynamic_depth/version.h
new file mode 100644
index 0000000..6193b1a
--- /dev/null
+++ b/includes/dynamic_depth/version.h
@@ -0,0 +1,20 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VERSION_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VERSION_H_ // NOLINT
+
+#define DYNAMIC_DEPTH_VERSION_MAJOR 1
+#define DYNAMIC_DEPTH_VERSION_MINOR 0
+#define DYNAMIC_DEPTH_VERSION_REVISION 0
+
+// Classic CPP stringifcation; the extra level of indirection allows the
+// preprocessor to expand the macro before being converted to a string.
+#define DYNAMIC_DEPTH_TO_STRING_HELPER(x) #x
+#define DYNAMIC_DEPTH_TO_STRING(x) DYNAMIC_DEPTH_TO_STRING_HELPER(x)
+
+// The xdmlib version as a string; for example "1.9.0".
+#define DYNAMIC_DEPTH_VERSION_STRING \
+ DYNAMIC_DEPTH_TO_STRING(DYNAMIC_DEPTH_VERSION_MAJOR) \
+ "." DYNAMIC_DEPTH_TO_STRING( \
+ DYNAMIC_DEPTH_VERSION_MINOR) "." \
+ DYNAMIC_DEPTH_TO_STRING(DYNAMIC_DEPTH_VERSION_REVISION)
+
+#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VERSION_H_ // NOLINT
diff --git a/includes/xmpmeta/jpeg_io.h b/includes/xmpmeta/jpeg_io.h
new file mode 100644
index 0000000..aba2b5a
--- /dev/null
+++ b/includes/xmpmeta/jpeg_io.h
@@ -0,0 +1,53 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_JPEG_IO_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_JPEG_IO_H_ // NOLINT
+
+#include <string>
+#include <vector>
+
+#include "base/port.h"
+
+namespace photos_editing_formats {
+
+// Contains the data for a section in a JPEG file.
+// A JPEG file contains many sections in addition to image data.
+struct Section {
+ // Constructors.
+ Section() = default;
+ explicit Section(const string& buffer);
+
+ // Returns true if the section's marker matches an APP1 marker.
+ bool IsMarkerApp1();
+
+ int marker;
+ bool is_image_section;
+ string data;
+};
+
+struct ParseOptions {
+ // If set to true, keeps only the EXIF and XMP sections (with
+ // marker kApp1) and ignores others. Otherwise, keeps everything including
+ // image data.
+ bool read_meta_only = false;
+
+ // If section_header is set, this boolean controls whether only the 1st
+ // section matching the section_header will be returned. If not set
+ // (the default), all the sections that math the section header will be
+ // returned.
+ bool section_header_return_first = false;
+
+ // A filter that keeps all the sections whose data starts with the
+ // given string. Ignored if empty.
+ string section_header;
+};
+
+// Parses the JPEG image file.
+std::vector<Section> Parse(const ParseOptions& options,
+ std::istream* input_stream);
+
+// Writes JPEG data sections to a file.
+void WriteSections(const std::vector<Section>& sections,
+ std::ostream* output_stream);
+
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_JPEG_IO_H_ // NOLINT
diff --git a/includes/xmpmeta/md5.h b/includes/xmpmeta/md5.h
new file mode 100644
index 0000000..a3e8a7f
--- /dev/null
+++ b/includes/xmpmeta/md5.h
@@ -0,0 +1,16 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_MD5_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_MD5_H_ // NOLINT
+
+#include <string>
+
+#include "base/port.h"
+
+namespace photos_editing_formats {
+
+// Returns the MD5 hash of to_hash as a 32-character hex string.
+// Wrapper around OpenSSL to avoid Gyp dependency problems.
+string MD5Hash(const string& to_hash);
+
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_MD5_H_ // NOLINT
diff --git a/includes/xmpmeta/version.h b/includes/xmpmeta/version.h
new file mode 100644
index 0000000..91198c4
--- /dev/null
+++ b/includes/xmpmeta/version.h
@@ -0,0 +1,26 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_H_ // NOLINT
+
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MAJOR 1 // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MINOR 0 // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_REVISION 0 // NOLINT
+
+// Classic CPP stringifcation; the extra level of indirection allows the
+// preprocessor to expand the macro before being converted to a string.
+#define \
+ PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING_HELPER( \
+ x) \
+#x
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING(x) \ // NOLINT
+ PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING_HELPER(x)
+
+// The Dynamic Depth version as a string; for example "1.9.0".
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_STRING \ // NOLINT
+ PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING( \
+ PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MAJOR) \
+ "." PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING( \
+ PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MINOR) \
+ "." PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING( \
+ PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_REVISION)
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_H_ // NOLINT
diff --git a/includes/xmpmeta/xmp_const.h b/includes/xmpmeta/xmp_const.h
new file mode 100644
index 0000000..c32ecd9
--- /dev/null
+++ b/includes/xmpmeta/xmp_const.h
@@ -0,0 +1,30 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_CONST_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_CONST_H_ // NOLINT
+
+namespace photos_editing_formats {
+
+// Constants used in writing XMP metadata.
+struct XmpConst {
+ // XMP namespaces.
+ static const char* Namespace();
+ static const char* NamespacePrefix();
+ static const char* NodeName();
+ static const char* AdobePropName();
+ static const char* AdobePropValue();
+ static const char* NoteNamespace();
+
+ // XMP headers.
+ static const char* Header();
+ static const char* ExtensionHeader();
+ static const char* HasExtensionPrefix();
+ static const char* HasExtension();
+
+ // Sizes.
+ static const int ExtensionHeaderOffset();
+ static const int MaxBufferSize();
+ static const int ExtendedMaxBufferSize();
+};
+
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_CONST_H_ // NOLINT
diff --git a/includes/xmpmeta/xmp_data.h b/includes/xmpmeta/xmp_data.h
new file mode 100644
index 0000000..1e59b1d
--- /dev/null
+++ b/includes/xmpmeta/xmp_data.h
@@ -0,0 +1,34 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_DATA_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_DATA_H_ // NOLINT
+
+#include <libxml/tree.h>
+
+namespace photos_editing_formats {
+
+// XmpData contains the standard, and optionally extended, XMP metadata from a
+// JPEG file. See xmp_parser for reading XmpData from a JPEG or reading
+// attributes from XmpData.
+class XmpData {
+ public:
+ XmpData();
+ ~XmpData();
+
+ // Frees any allocated resources and resets the xmlDocPtrs to null.
+ void Reset();
+
+ // The standard XMP section.
+ const xmlDocPtr StandardSection() const;
+ xmlDocPtr* MutableStandardSection();
+
+ // The extended XMP section.
+ const xmlDocPtr ExtendedSection() const;
+ xmlDocPtr* MutableExtendedSection();
+
+ private:
+ xmlDocPtr xmp_;
+ xmlDocPtr xmp_extended_;
+};
+
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_DATA_H_ // NOLINT
diff --git a/includes/xmpmeta/xmp_parser.h b/includes/xmpmeta/xmp_parser.h
new file mode 100644
index 0000000..e2781ec
--- /dev/null
+++ b/includes/xmpmeta/xmp_parser.h
@@ -0,0 +1,27 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_PARSER_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_PARSER_H_ // NOLINT
+
+#include <fstream>
+#include <string>
+
+#include "base/port.h"
+#include "xmpmeta/xmp_data.h"
+
+namespace photos_editing_formats {
+
+// Populates a XmpData from the header of the JPEG file.
+bool ReadXmpHeader(const string& filename, bool skip_extended,
+ XmpData* xmp_data);
+
+// Populates a XmpData from the header of JPEG file that has already been read
+// into memory.
+bool ReadXmpFromMemory(const string& jpeg_contents, bool skip_extended,
+ XmpData* xmp_data);
+
+// Populates a XmpData from the header of the given stream (stream data is
+// in JPEG format).
+bool ReadXmpHeader(std::istream* input_stream, bool skip_extended,
+ XmpData* xmp_data);
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_PARSER_H_ // NOLINT
diff --git a/includes/xmpmeta/xmp_writer.h b/includes/xmpmeta/xmp_writer.h
new file mode 100644
index 0000000..e2ea8f7
--- /dev/null
+++ b/includes/xmpmeta/xmp_writer.h
@@ -0,0 +1,43 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_WRITER_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_WRITER_H_ // NOLINT
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include "base/port.h"
+#include "xmpmeta/xmp_data.h"
+
+namespace photos_editing_formats {
+
+// Creates a new XmpData object and initializes the boilerplate for the
+// standard XMP section.
+// The extended section is initialized only if create_extended is true.
+std::unique_ptr<XmpData> CreateXmpData(bool create_extended);
+
+// Writes XMP data to an existing JPEG image file.
+// This is equivalent to writeXMPMeta in geo/lightfield/metadata/XmpUtil.java.
+// If the extended section is not null, this will modify the given XmpData by
+// setting a property in the standard section that links it with the
+// extended section.
+bool WriteLeftEyeAndXmpMeta(const string& left_data, const string& filename,
+ const XmpData& xmp_data);
+
+// Same as above, but allows the caller to manage their own istream and ostream.
+// filename is written to only if metadata serialization is successful.
+// Assumes the caller will take care of opening and closing the
+// output_jpeg_stream, as well as initialization of the input_jpeg_stream.
+bool WriteLeftEyeAndXmpMeta(const string& filename, const XmpData& xmp_data,
+ std::istringstream* input_jpeg_stream,
+ std::ofstream* output_jpeg_stream);
+
+// Updates a JPEG input stream with new XMP data and writes it to an
+// output stream.
+// This is equivalent to writeXMPMeta in geo/lightfield/metadata/XmpUtil.java.
+bool AddXmpMetaToJpegStream(std::istream* input_jpeg_stream,
+ const XmpData& xmp_data,
+ std::ostream* output_jpeg_stream);
+
+} // namespace photos_editing_formats
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_WRITER_H_ // NOLINT
diff --git a/includes/xmpmeta/xmpmeta.h b/includes/xmpmeta/xmpmeta.h
new file mode 100644
index 0000000..d27a854
--- /dev/null
+++ b/includes/xmpmeta/xmpmeta.h
@@ -0,0 +1,11 @@
+#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMPMETA_H_ // NOLINT
+#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMPMETA_H_ // NOLINT
+
+#include "xmpmeta/base64.h"
+#include "xmpmeta/md5.h"
+#include "xmpmeta/xmp_const.h"
+#include "xmpmeta/xmp_data.h"
+#include "xmpmeta/xmp_parser.h"
+#include "xmpmeta/xmp_writer.h"
+
+#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMPMETA_H_ // NOLINT