I am producing Dwell Photographs programmatically to be used as wallpapers on iOS. I am utilizing a recognized metadata.mov file bundled with the app (doubtless extracted from a working Dwell Picture with decision 1080×1920). This setup works effective once I use a video of the identical decision.
I am utilizing this open-source library to deal with the video-to-LivePhoto conversion:
https://github.com/TouSC/Video2LivePhoto
Nevertheless, once I attempt utilizing a higher-resolution video (e.g. 2560×1440) to keep away from black bars on high-resolution units (like iPhone 14 Professional Max), the Photographs app exhibits the Dwell Picture, however the movement element does not work—it simply says “Movement Not Accessible.”
I imagine the difficulty is that the static metadata.mov comprises resolution-specific metadata, which prevents it from working accurately with different video sizes.
-
Tried altering the decision of the video (e.g. 1440×2560, 1284×2778) – movement breaks.
-
Tried producing a brand new .mov file utilizing FFmpeg with a silent video observe, matching the brand new decision – Dwell Picture not acknowledged or exhibits errors.
-
Tried modifying the present metadata.mov with instruments like FFmpeg, AtomicParsley, Bento4, and mp4box – ensuing recordsdata typically break the Dwell Picture fully.
-
I anticipated to generate a sound metadata.mov (or related observe) that might assist the customized decision and restore Dwell Picture movement assist.
static func convertVideo(videoURL: URL, full: @escaping (_ success: Bool, _ errorMessage: String?) -> Void) {
print("begin changing")
guard let metaURL = Bundle.fundamental.url(forResource: "metadata", withExtension: "mov") else {
full(false, "metadata.mov not discovered")
return
}
let livePhotoSize = CGSize(width: 1440, top: 2560) // <-- up to date decision
let livePhotoDuration = CMTime(worth: 550, timescale: 600)
let assetIdentifier = UUID().uuidString
guard let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
full(false, "Doc path not discovered")
return
}
let durationPath = documentPath + "/period.mp4"
let acceleratePath = documentPath + "/speed up.mp4"
let resizePath = documentPath + "/resize.mp4"
let finalPath = resizePath
removeFileIfExists(at: durationPath)
removeFileIfExists(at: acceleratePath)
removeFileIfExists(at: resizePath)
let converter = Converter4Video(path: finalPath)
Job {
do {
attempt await converter.durationVideo(at: videoURL, outputPath: durationPath, targetDuration: 3)
attempt await converter.accelerateVideo(at: durationPath, to: livePhotoDuration, outputPath: acceleratePath)
attempt await converter.resizeVideo(at: acceleratePath, outputPath: resizePath, outputSize: livePhotoSize)
print("### resize Success")
let picture = attempt await generateCGImage(finalPath: finalPath)
await generateLivePhoto(
picture: picture,
documentPath: documentPath,
assetIdentifier: assetIdentifier,
metaURL: metaURL,
converter: converter,
full: full
)
} catch {
print("Video conversion error: (error)")
full(false, error.localizedDescription)
return
}
}
}