A user picks a video from Photos. The app turns that video into a file the server can accept. Then it uploads the file.
On the surface, this sounds like a simple upload flow.
In practice, iOS makes you answer a much more specific question:
How do you reliably turn a video from the user's Photo Library into an uploadable MP4 file?
At first, I used AVAssetExportPresetPassthrough. It looked like the right default. It preserves the original media as much as possible, avoids unnecessary re-encoding, and is fast when it works. No quality loss, less CPU usage, less battery cost.
But some videos failed during export. The error was usually in the AVFoundationErrorDomain Code=-11838 family.
Apple describes this error as operationNotSupportedForAsset: an operation was attempted that is not supported for the asset.
At first, I suspected an iCloud download issue, a Photos permission edge case, or something retryable inside PHImageManager. That was not the real problem.
The actual problem was more fundamental:
I was trying to write an iPhone MOV asset into an MP4 file using a passthrough export.
The Original Flow
The original code was roughly shaped like this:
PHImageManager.default().requestExportSession(
forVideo: asset,
options: options,
exportPreset: AVAssetExportPresetPassthrough
) { exportSession, info in
exportSession?.outputURL = outputURL
exportSession?.outputFileType = .mp4
exportSession?.exportAsynchronously {
// upload
}
}
The intention was reasonable:
- Ask Photos for an export session for the
PHAsset. - Use
AVAssetExportPresetPassthroughto preserve the original quality. - Write the result as an
.mp4file for upload.
Many videos exported successfully this way. That is what made the bug confusing. Some videos worked. Some did not.
The hidden assumption was:
Any iPhone video can become an MP4 file through a passthrough export.
That assumption is not always true.
iPhone Videos Are Usually MOV Files
To a user, it is just "a video." Internally, an iPhone camera video is commonly stored as a QuickTime Movie container, or .mov.
Apple documents the QuickTime Movie type as com.apple.quicktime-movie, commonly using the .mov or .qt extension. MP4, on the other hand, is an MPEG-4 movie type. UTType.mpeg4Movie uses the identifier public.mpeg-4.
Both are video containers, but they are not the same file type.
MOV
- QuickTime Movie container
- Common in Apple's media ecosystem
- Usually uses the .mov extension
- Can contain QuickTime-specific metadata and track structures
MP4
- MPEG-4 based container
- Common across web, servers, Android, browsers, and CDNs
- Usually uses the .mp4 extension
- Broadly compatible, but still has its own container rules
MOV and MP4 are structurally related. Some MOV files can be moved into MP4 without re-encoding.
But "similar" does not mean "always interchangeable."
Container and Codec Are Different Things
The easiest way to get confused here is to mix up container and codec.
A container is the file wrapper. MOV, MP4, and MKV are containers.
A codec is how the actual video or audio data is encoded. H.264, HEVC, ProRes, and AAC are codecs.
An iPhone video can look like any of these:
MOV + H.264 + AAC
MOV + HEVC(H.265) + AAC
MOV + HEVC 10-bit HDR + AAC
MOV + Dolby Vision HDR metadata
MOV + Apple ProRes
MOV + Cinematic mode metadata
So it is not enough to ask whether the file is MOV or MP4. You also need to care about the video codec, audio codec, HDR metadata, edit information, QuickTime metadata, and the actual track layout inside the asset.
Apple explains that High Efficiency media on iPhone and iPad uses HEVC, also known as H.265, for video. If the camera is set to Most Compatible, new videos use H.264 instead.
High Efficiency
- video: HEVC / H.265
Most Compatible
- video: H.264
That means "an iPhone video" is not one single format.
One video might be H.264 SDR. Another might be HEVC. Another might be HEVC 10-bit HDR or Dolby Vision. On Pro devices, it might even be ProRes.
Passthrough Is Not a Converter
The name AVAssetExportPresetPassthrough is accurate.
It tries to export the asset close to its current format. It is not a general-purpose "make this compatible with MP4" converter.
Passthrough tries to preserve the original tracks when possible.
passthrough
- Does not re-encode the video track
- Tries to preserve the audio track
- Fast
- Minimal or no quality loss
- Requires the original track structure to be compatible with the output container
That makes this combination stronger than it first appears:
exportPreset: AVAssetExportPresetPassthrough
outputFileType = .mp4
This is effectively asking AVFoundation:
Take the video, audio, and metadata from this original MOV asset, preserve them as much as possible, and write them into an MP4 file.
Some MOV files can satisfy that request.
Some cannot.
Cases That Can Cause Trouble
The following cases are not guaranteed to fail. They are just the kinds of assets that make passthrough + .mp4 a risky assumption.
HEVC / H.265
HEVC itself is not the problem. HEVC can exist inside MP4.
But real compatibility depends on more than the codec name. Profile, level, bit depth, codec tag, color information, OS version, and the export session's supported file types all matter.
Copying an original HEVC track into MP4 is not the same thing as re-encoding that video into an MP4-friendly output.
HDR / Dolby Vision
Supported iPhone models can record Dolby Vision HDR video.
HDR video carries more information than a normal SDR video: HEVC 10-bit data, HLG or Dolby Vision metadata, color space information, and related signaling.
Those details make sense in Apple's Photos and MOV ecosystem. But if you ask AVFoundation to preserve them and write an MP4 file through passthrough, the specific combination still has to be supported.
If it is not supported, export fails.
Apple ProRes
ProRes is a high-quality codec intended for editing workflows.
A ProRes source is very different from the small, widely compatible MP4 file an upload backend usually expects. Passthrough does not turn ProRes into H.264 or HEVC.
If the server needs a normal MP4 file, ProRes may need to be transcoded.
Cinematic Mode
Cinematic mode is not just a visual effect baked into a video.
It allows focus points and depth-of-field effects to be changed after capture. Apple also exposes QuickTime metadata related to Cinematic Video intent in AVFoundation.
That metadata is meaningful in Apple's MOV and Photos ecosystem. It does not mean the same information can always be preserved into an upload-oriented MP4 file through passthrough.
Slo-mo, Time-lapse, and Edited Videos
Slow-motion, time-lapse, and edited videos can be more complex than a single plain video track.
Time mapping, current versus original versions, audio mixes, and edit metadata can all be involved.
The video may play perfectly in Photos, but still fail for a specific export preset and output file type combination.
MOV Files Saved by Other Apps
Videos in the Photo Library are not always captured by Apple's Camera app.
They may be saved by other apps, downloaded from the web, or produced by editing tools. These files can have more varied codecs, audio formats, metadata, and track layouts.
Assuming every one of them can be passed through into MP4 is risky.
So It Was Not Just a Codec Problem
It is tempting to ask:
- Is this failing because it is HEVC?
- Is it failing because it is MOV?
- Is iCloud failing to download the original?
The more accurate explanation is:
The original MOV asset contained a combination of tracks and metadata that could not be written into an MP4 container without re-encoding.
So the problem was not one codec by itself.
Possible contributors
- MOV container
- HEVC, HDR, ProRes, or codec profile
- QuickTime metadata
- Cinematic/depth/focus metadata
- Edit information
- Photos current asset representation
- MP4 outputFileType
- passthrough export preset
Actual failure
- This combination could not be written as MP4 through passthrough
That is why AVFoundationErrorDomain Code=-11838 was an important clue.
It did not mean "try again later." It was closer to "this operation is not supported for this asset."
Repeating the same request was not a real fix.
Remux vs Transcode
This issue becomes easier to reason about if you separate remuxing from transcoding.
remux
- Keeps the original video/audio data
- Changes only the container
- Can turn MOV into MP4 quickly
- No quality loss
- Works only if the original tracks are compatible with the target container
transcode
- Re-encodes video and/or audio
- Creates new streams suitable for the target container
- Slower and more CPU intensive
- May change quality or file size
- More compatible
AVAssetExportPresetPassthrough is closer to remuxing.
An export preset such as AVAssetExportPreset1920x1080 is closer to transcoding. It can create a new output that is more suitable for MP4 upload.
If your goal is to preserve the original media, passthrough is a good first attempt.
If your goal is to produce an uploadable MP4 file for a backend and other clients, passthrough alone is not enough.
The Fix
The fix was not to blindly retry the same export.
The fix was to use a fallback pipeline.
1. passthrough + mp4
- Fastest path when it works.
- Minimal quality loss.
- Use it when the original tracks are compatible with MP4.
2. 1920x1080 + mp4
- If passthrough is unsupported, switch to re-encoding.
- Do not try to preserve the original MOV structure.
- Produce a more upload-friendly MP4 file.
3. requestAVAsset + export
- If the Photos requestExportSession path fails, request the AVAsset directly.
- Build another export path from the requested asset.
The important part is that passthrough was not removed.
It is still the best first path when it works. The mistake was treating it as the only path.
What I Learned
The main lesson was simple:
"An iPhone video" is not one format.
Every selected video may look like PHAsset.mediaType == .video, but internally it can involve MOV, H.264, HEVC, HDR, ProRes, Cinematic metadata, edits, and iCloud state.
AVAssetExportPresetPassthrough does not normalize all of that into a standard MP4 file. It preserves the original when possible.
The right question for the upload pipeline was:
Are we trying to preserve the original, or are we trying to produce a reliably uploadable file?
In this case, the answer was both.
Try to preserve the original first. If that is not supported, fall back to a transcoding path.
That turned the issue from "retry a failed export" into "switch export strategies when the current strategy is not supported."
References
- Apple Developer Documentation: AVAssetExportPresetPassthrough
- Apple Developer Documentation: PHImageManager.requestExportSession
- Apple Developer Documentation: AVError.operationNotSupportedForAsset
- Apple Developer Documentation: AVFileTypeQuickTimeMovie
- Apple Developer Documentation: UTType.mpeg4Movie
- Apple Support: Using HEIF or HEVC media on Apple devices
- Apple Support: Adjust HDR camera settings on iPhone
- Apple Support: About Apple ProRes on iPhone
- Apple Support: Record video in Cinematic mode with your iPhone camera
Top comments (0)