Mobile Development 25 min read

How Android Dynamic Photos Work: XMP Metadata, Formats, and Kotlin Extraction

This article explores the technical architecture of Android dynamic photos, detailing the three‑layer file structure, XMP metadata specifications, and differences among Xiaomi Micro Video, Google Motion Photo, and OPPO O Live Photo, and provides a unified Kotlin solution for detection, parsing, and playback.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
How Android Dynamic Photos Work: XMP Metadata, Formats, and Kotlin Extraction

Overview

Dynamic photos combine a static image, an auxiliary video, and metadata using a "main static file + additional video + metadata" model, providing richer visual experience while maintaining compatibility.

Different manufacturers implement different formats: Xiaomi uses Micro Video with custom EXIF fields, Google uses standard Motion Photo with XMP, and OPPO uses O Live Photo with additional features.

Core Structure

Three‑layer architecture

Static image file (JPEG/HEIC/AVIF), optional video file (1‑3 seconds), and metadata system.

Main static image file

Purpose: visual main image.

Supported formats: JPEG, HEIC, AVIF.

Characteristics: may include gain map for HDR.

Secondary video file

Purpose: dynamic effect.

Content: short video clip.

Storage: appended to static file.

Metadata system

Camera XMP: defines display rules, e.g., Camera:MotionPhoto (0 = static, 1 = dynamic).

Container XMP: points to video location, e.g., Length field.

Official XMP Metadata Specification

Namespace URI: http://ns.google.com/photos/1.0/camera/ (prefix Camera). Camera:MotionPhoto (Integer) – 0 or 1. Camera:MotionPhotoVersion (Integer) – current version "1". Camera:MotionPhotoPresentationTimestampUs (Long) – timestamp of video frame, –1 if unset.

File Naming Convention

Official regex:

^([^\s\/\\][^\/\\]*MP)\.(JPG|jpg|JPEG|jpeg|HEIC|heic|AVIF|avif)

. The "MP" suffix marks a dynamic photo, but manufacturers often ignore it.

Case Analyses

Xiaomi Micro Video

Uses custom EXIF fields under the Google Camera namespace. Example metadata extracted with exiftool shows Micro Video flag, offset, and presentation timestamp. Video size is derived from file size minus offset.

exiftool /Users/allenzhang/Downloads/1752549853110.jpg
File Name                           : 1752549853110.jpg
File Size                           : 5.5 MB
Make                                : (小米手机型号)
Micro Video Version                 : 1
Micro Video                         : 1
Micro Video Offset                  : 1735850
Micro Video Presentation Timestamp Us: 761955
Image Width                         : 3072
Image Height                        : 4096

Google Motion Photo

Standard XMP container with Directory array distinguishing Primary image and MotionPhoto video. Video size is given by Item:Length. Supports extended XMP for large metadata.

exiftool /Users/allenzhang/Downloads/PXL_20250722_065151464.MP.jpg
File Name                           : PXL_20250722_065151464.MP.jpg
File Size                           : 4.2 MB
Make                                : Google
Camera Model Name                    : Pixel 4
Motion Photo                         : 1
Motion Photo Version                 : 1
Motion Photo Presentation Timestamp Us: 411003
Directory Item Mime                  : image/jpeg, video/mp4
Directory Item Semantic              : Primary, MotionPhoto
Directory Item Length                : 0, 870399

OPPO O Live Photo

Combines Primary, GainMap (HDR), and MotionPhoto items in a three‑layer container. Uses proprietary namespace http://ns.oplus.com/photos/1.0/camera/ for additional fields such as MotionPhotoOwner and VideoLength. Supports HDR gain map and dual timestamps.

exiftool /Users/allenzhang/Downloads/IMG20250722163505.jpg
File Name                           : IMG20250722163505.jpg
File Size                           : 7.0 MB
Make                                : OPPO
Camera Model Name                    : OPPO Find X8
Motion Photo                         : 1
Motion Photo Version                 : 1
Motion Photo Presentation Timestamp Us: 266704
Motion Photo Owner                  : oplus
O Live Photo Version                 : 2
Video Length                         : 3334498
Directory Item Mime                  : image/jpeg, image/jpeg, video/mp4
Directory Item Semantic              : Primary, GainMap, MotionPhoto
Directory Item Length                : 0, 474937, 3334834

Comparison (summarized):

Format identifier: Xiaomi Micro Video = 1, Google Motion Photo = 1, OPPO O Live Photo = 1 + O Live Photo Version 2.

Storage: Xiaomi custom EXIF, Google standard XMP, OPPO XMP + proprietary.

Video size proportion: Xiaomi ≈ 70 %, Google ≈ 20 %, OPPO ≈ 47 %.

HDR support: Xiaomi none, Google yes, OPPO yes.

Audio: Xiaomi none, Google partial, OPPO full.

Kotlin Unified Implementation

The article provides a Kotlin class UnifiedMotionPhotoExtractor that:

Extracts XMP from JPEG APP1 segment.

Detects vendor‑specific tags (MicroVideo, MotionPhoto, O Live Photo).

Finds MP4 header offsets by scanning the file tail.

Extracts video data to a separate file with progress callbacks.

Validates MP4 files.

Key functions include extractXMPFromJPEG, isXMPSegment, parseXMPFromSegment, checkOppoLivePhotoInXMP, findMp4HeaderOffset, and extractVideoData.

class UnifiedMotionPhotoExtractor {
    companion object {
        const val TAG = "MotionPhotoExtractor"
        private const val BUFFER_SIZE = 8192
        private val FTYP_SIGNATURE = byteArrayOf('f'.code.toByte(), 't'.code.toByte(), 'y'.code.toByte(), 'p'.code.toByte())
        // ... other signatures omitted for brevity ...
    }

    private fun extractXMPFromJPEG(filePath: String): XMPMeta? {
        return try {
            RandomAccessFile(filePath, "r").use { raf ->
                if (raf.readUnsignedShort() != JPEG_SOI) return null
                while (true) {
                    val marker = raf.readUnsignedShort()
                    if (marker == JPEG_SOS) break
                    if (marker == JPEG_APP1) {
                        val segmentLength = raf.readUnsignedShort()
                        val segmentData = ByteArray(segmentLength - 2)
                        raf.readFully(segmentData)
                        if (isXMPSegment(segmentData)) {
                            return parseXMPFromSegment(segmentData)
                        }
                    } else {
                        val segmentLength = raf.readUnsignedShort()
                        raf.skipBytes(segmentLength - 2)
                    }
                }
                null
            }
        } catch (e: Exception) {
            null
        }
    }
    // ... other methods omitted for brevity ...
}

Playback Component

A custom MotionPhotoView view combines an ImageView for the static picture and a VideoView for the video, handling start, pause, stop, and looping based on the presentation timestamp.

class MotionPhotoView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
    private val imageView: ImageView
    private val videoView: VideoView
    private var motionPhotoResult: MotionPhotoResult? = null

    fun setMotionPhoto(imagePath: String, result: MotionPhotoResult) {
        this.originalImagePath = imagePath
        this.motionPhotoResult = result
        loadStaticImage(imagePath)
    }

    fun playVideo() {
        try {
            videoView.setVideoPath(videoPath)
            videoView.isVisible = true
            imageView.isVisible = false
            videoView.start()
        } catch (e: Exception) {
            showStaticImage()
        }
    }
    // ... other methods omitted for brevity ...
}

Conclusion and Outlook

Android dynamic photos rely on carefully designed file structures and XMP metadata, but vendor differences cause compatibility challenges. Understanding the specifications and using a unified extraction approach enables developers to build robust cross‑vendor support and improve user experience.

mobile developmentAndroidmetadataKotlinDynamic PhotoXMP
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.