Disclaimer: This is not an officially supported Google product. This project is not eligible for the Google Open Source Software Vulnerability Rewards Program.
libmotionphoto is a cross-platform C++ and Java library for parsing, building, validating, and extracting Google Motion Photos across JPEG and HEIF (HEIC/AVIF) image containers alongside MP4 video trailers.
- C++ Core Libraries:
motion_photo: Core metadata structures, XMP parser, and provider interfaces.motion_photo_jpeg: APP1 segment parsing and JPEG Motion Photo processing.motion_photo_heif: MPVD box handling and HEIF/HEIC/AVIF container support.motion_photo_mp4: Video trailer parsing and track optimization.
- Plugins & Format Extensibility (
lib/core/plugins): Modular Motion Photo Provider Interface (IMetadataProvider) plugins for identifying, parsing, and extracting proprietary or legacy motion photo formats (such as legacy PixelGCamera:MicroVideoformats). See Plugin Integration Guidance for details on authoring and registering plugins. - Executables:
motion_photo_builder: Builds a Motion Photo from primary image, video, and XMP metadata.motion_photo_checker: Validates Motion Photo compliance against specifications.motion_photo_extractor: Extracts primary image, video trailer, and metadata blocks.motion_photo_metadata_engine: Extracts semantic metadata and outputs structured JSON or Protobuf representations.
- Java & JNI Wrappers: High-level Java APIs (
MetadataEngine,MotionPhotoBuilder,MotionPhotoExtractor,MotionPhotoChecker) bridging native execution via JNI. - Android Sample App: Sample application demonstrating mobile integration under
samples/android.
libmotionphoto_api.h is the sole public C++ facade
header, providing clean C++17 interfaces (<cstdint>, <string>, <vector>)
with zero internal engine leakage.
View C++ Usage Example & Build Instructions
#include "libmotionphoto_api.h"
// 1. Inspect container & metadata
libmotionphoto::api::MotionPhotoInfo info =
libmotionphoto::api::ParseMotionPhotoFromFile("/path/to/IMG_MP.jpg");
if (info.is_motion_photo) {
int64_t video_offset = info.video_offset;
int64_t video_length = info.video_length;
}
// 2. Validate compliance
bool is_valid = libmotionphoto::api::CheckMotionPhotoFile("/path/to/IMG_MP.jpg");
// 3. Extract embedded MP4 video track
bool extracted = libmotionphoto::api::ExtractVideoTrackFromFile(
"/path/to/IMG_MP.jpg", "/path/to/extracted_video.mp4");
// 4. Extract SMPTE ST 2094-50 (AGTM HDR) dynamic tone mapping metadata
std::string agtm_json = libmotionphoto::api::ExtractAgtmFromFile("/path/to/IMG_MP.jpg");
// 5. Author a new Motion Photo container
libmotionphoto::api::MotionPhotoBuilderParams params;
params.primary_image_file_name = "/path/to/still.jpg";
params.primary_video_file_name = "/path/to/trailer.mp4";
params.output_file_name = "/path/to/IMG_MP.jpg";
params.presentation_timestamp_us = 500000;
bool built = libmotionphoto::api::BuildMotionPhotoFromFile(params);-
CMake:
target_link_libraries(your_app PRIVATE motion_photo_api)
-
Bazel / Google3:
deps = ["//path/to/libmotionphoto:libmotionphoto_api"]
High-level Java SDK classes are located in package
com.google.libmotionphoto.motionphoto.
View Java Usage Example & Build Instructions
import com.google.libmotionphoto.motionphoto.MetadataEngine;
import com.google.libmotionphoto.motionphoto.MotionPhotoExtractor;
import com.google.libmotionphoto.motionphoto.MotionPhotoBuilder;
import com.google.libmotionphoto.motionphoto.MotionPhotoChecker;
// 1. Parse metadata / Extract AGTM HDR metadata
byte[] metadataProto = MetadataEngine.parseMetadata(filePath);
String agtmJson = MetadataEngine.extractAgtm(filePath);
// 2. Extract media streams
boolean ok = MotionPhotoExtractor.extract(
inputPath, outputImagePath, outputVideoPath, outputXmpPath);
// 3. Build Motion Photo
MotionPhotoBuilder.BuilderParams params = new MotionPhotoBuilder.BuilderParams();
params.primaryImageFilePath = stillImagePath;
params.primaryVideoFilePath = videoFilePath;
params.outputFilePath = outputPath;
boolean success = MotionPhotoBuilder.build(params);
// 4. Validate Motion Photo
boolean valid = MotionPhotoChecker.check(filePath);For secure untrusted media handling, bind to MetadataParserService
(android:isolatedProcess="true") via AIDL using ParcelFileDescriptor
streaming:
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
String agtmJson = metadataParserService.extractAgtm(pfd);In your build.gradle:
dependencies {
implementation project(':libmotionphoto') // or AAR artifact
}libmotionphoto includes a multi-layered security architecture designed for
mobile environments. Because media containers (JPEG, HEIF, AVIF, MP4) often
originate from untrusted external sources, native C++ parsing and decoding are
strictly decoupled and sandboxed from the host application process.
View Process & IPC Isolation Architecture Diagram
╭─────────────────────────────────────────────────────────────────────────────────────────╮
│ Host Application Process │
│ │
│ ╭───────────────────────────────────────────────────────────────────────────────────╮ │
│ │ Application UI / Activities / ViewModels │ │
│ ╰─────────────────────────────────────────┬─────────────────────────────────────────╯ │
│ ▼ │
│ ╭───────────────────────────────────────────────────────────────────────────────────╮ │
│ │ High-Level Java SDK Layer (Public APIs) │ │
│ │ [MetadataEngine] [MotionPhotoExtractor] [MotionPhotoBuilder] [Checker] │ │
│ ╰─────────────────────────────────────────┬─────────────────────────────────────────╯ │
╰────────────────────────────────────────────┼────────────────────────────────────────────╯
▼
═══════════════════════════════ Android Binder IPC Boundary ══════════════════════════════
[IMetadataParserService] [IMotionPhotoExtractorService] [IMotionPhotoBuilderService]
│ (ParcelFileDescriptor Streaming) │ (Filesystem Path Isolation)
▼ ▼
╭─────────────────────────────────────╮ ╭───────────────────────────────────────────────╮
│ MetadataParserService │ │ Dedicated Worker Services │
│ [android:isolatedProcess="true"] │ │ (Extractor, Builder, Checker Services) │
│ │ │ [android:exported="false"] │
│ • Isolated Linux UID (u0_iXX) │ │ │
│ • Zero filesystem / network access │ │ • Asynchronous background worker execution │
│ • Strict SELinux isolated_app │ │ • Encapsulates native C++ file operations │
│ • Encapsulated JNI C++ bindings │ │ • Offloads heavy memory & I/O from UI thread │
╰──────────────────┬──────────────────╯ ╰───────────────────────┬───────────────────────╯
│ │
╰───────────────────────┬──────────────────────╯
▼
╭─────────────────────────────────────────────────────────────────────────────────────────╮
│ Native C++ Core Engines │
│ │
│ ╭────────────────────────────╮ ╭────────────────────────────╮ ╭─────────────────╮ │
│ │ libmotionphoto_api │ │ motion_photo_jpeg / │ │ motion_photo_mp4│ │
│ │ (High-level C++ facade) │ │ motion_photo_heif │ │ (MP4 box parser)│ │
│ ╰────────────────────────────╯ ╰────────────────────────────╯ ╰─────────────────╯ │
│ │
│ ╭─────────────────────────────────────────────────────────────────────────────────╮ │
│ │ ImageIO / Memory-Safe Provider Registry (IMetadataProvider Plugins) │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────╯ │
╰────────────────────────────────────────────┬────────────────────────────────────────────╯
▼
🛡️ [Crash Containment Boundary]
If malformed untrusted media triggers a fault/SIGSEGV, only the isolated service
process terminates. The host application catches DeadObjectException, auto-recovers,
and handles the error gracefully without crashing.
- Isolated Process Sandbox (
android:isolatedProcess="true"):MetadataParserServiceexecutes inside Android'sisolated_appSELinux domain under an ephemeral UID with zero ambient filesystem, IPC, or network permissions. - File Descriptor Handle Passing: File contents are shared exclusively via
Binder
ParcelFileDescriptorhandles (preadrandom-access read calls). - Crash Containment: Untrusted or corrupted media streams that might
trigger memory violations in native parsing code are contained inside the
isolated service process (
DeadObjectExceptionauto-recovery). - Thread & Memory Decoupling: Path-based builder, checker, and extractor
services run in dedicated internal background services
(
android:exported="false"), preventing UI thread stalls.
- C++17 compiler (
gcc,clang) cmake(VERSION 3.14+)protobufandabseil-cpp
./build.sh # Build C++ libraries and CLI executables
./test.sh # Run 54 unit testsView Repository Structure Tree
libmotionphoto/
├── libmotionphoto_api.h # Sole public C++ facade header
├── CMakeLists.txt # Top-level CMake configuration
├── build.sh # Top-level CMake build runner
├── test.sh # Unit test execution runner
├── LICENSE
├── README.md
├── CONTRIBUTING.md
├── .github/
│ └── workflows/ # GitHub Actions CI for Linux & macOS
├── lib/ # Decoupled core libraries
│ ├── core/ # Native C++ engine (internal headers, plugins, third_party, and tests)
│ │ ├── CMakeLists.txt
│ │ ├── motion_photo/ # Core C++ engine sources, private headers, and unit tests
│ │ ├── plugins/ # Format provider plugins (e.g. legacy_micro_video)
│ │ ├── third_party/ # Vendored C++ dependencies (image_io, mp4v2)
│ │ └── testdata/ # Test assets
│ ├── java/ # Pure JVM Java library module
│ │ ├── build.gradle
│ │ ├── java_api_description.md # Complete Java API documentation
│ │ └── src/main/java/com/google/libmotionphoto/motionphoto/
│ └── android/ # Android IPC & AIDL sandbox module
│ ├── build.gradle
│ └── src/main/
│ ├── aidl/com/google/libmotionphoto/ # Promoted AIDL service interfaces
│ └── java/com/google/libmotionphoto/ # Promoted Android sandboxed services
├── javatests/ # Java unit test suite
├── samples/
│ └── android/ # Android sample playground app (:libmotionphoto)
└── tools/ # Developer tools and CI scripts
- General Contributions: See CONTRIBUTING.md for guidelines on contributing to this project and signing the Contributor License Agreement (CLA).
- Format Plugin Contributions: If you would like to contribute support for additional legacy or proprietary motion photo formats, please follow our Plugin Contribution Guide for step-by-step instructions on implementing provider interfaces, defining metadata schemas, and adding unit tests.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.