fix(video): fail iOS export instead of silently returning an audio-only MP4 (#400)#403
Merged
Merged
Conversation
…ly MP4 Completes the remaining half of numandev1#400. The root-cause configuration fix already landed in numandev1#402, but the exporter could still resolve successfully while dropping the video track: - setupVideoOutput() only printed "Unsupported output configuration" when AVAssetWriter rejected the video output settings, then exported audio only and completed with .success. It now fails the export with a new unsupportedVideoOutputConfiguration error. - Even when canApply(...) returns true, the iOS encoder can drop the video track at write time and the writer still ends as .completed (the exact failure mode of numandev1#400). complete() now verifies that an output produced from a video source actually contains a video track, deletes the bad file, and fails with missingVideoTrackInOutput otherwise.
Contributor
|
Could you please replace the function Before you merge this.. @numandev1's previous review on my PR was not addressed // Function to prepare the video decoder
private fun prepareDecoder(
inputFormat: MediaFormat,
outputSurface: OutputSurface,
): MediaCodec {
// Some inputs (e.g. iPhone .MOV files) report a "video/dolby-vision" MIME
// type that many devices cannot decode. Remap to a decodable base-layer
// codec, or fail with a clear error, before creating the decoder (#398).
ensureDecodableVideoFormat(inputFormat)
// Clear Dolby Vision specific profile and level to prevent configuration failures
// when the MIME type has been remapped to AVC/HEVC.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
inputFormat.removeKey(MediaFormat.KEY_PROFILE)
inputFormat.removeKey(MediaFormat.KEY_LEVEL)
}
// ---------------------------------
val decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)!!)
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0)
return decoder
}Changes look good otherwise. |
Contributor
Author
Got it. Thank u for the review bro😀 |
numandev1
approved these changes
Jun 11, 2026
Owner
|
@XChikuX will add prepareDecoder in next PR |
Owner
|
@XChikuX added |
Owner
|
relased in |
Contributor
|
@numandev1 Could you please add the fix to v1 version. I don't feel like v2 is really tested properly. I'd be happier sticking to v1 for now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the remaining half of #400 (iOS:
Video.compresssilently drops the video track and still resolves as success).The configuration root cause (the undocumented frame-rate keys added in #392) was already removed in #402 —
VideoMain.swiftnow carries a NOTE about it. However, the exporter itself can still produce a silent audio-only success, which is the failure mode that made #400 so damaging (callers upload corrupted files without any error):setupVideoOutput()swallowed writer rejections. WhenAVAssetWriter.canApply(outputSettings:forMediaType:)returnedfalse, it only printed"Unsupported output configuration", exported audio only, and completed with.success. It now aborts the export with a newunsupportedVideoOutputConfigurationerror.complete()trusted.completedblindly. As analyzed in iOS: Video.compress silently drops the video track (audio-only output) since 1.17.0 (#392) #400, the iOS encoder can accept a configuration viacanApplyand still drop the video track at write time, ending as.completed.complete()now verifies that an output produced from a video source actually contains a video track; if not, it deletes the bad file and fails withmissingVideoTrackInOutput.With both guards, any future regression of this class surfaces as an explicit error on the JS side instead of a corrupted upload.
Changes
ios/Video/NextLevelSessionExporter.swiftonlyNextLevelSessionExporterErrorcases (existing cases untouched)setupVideoOutput(withAsset:)returnsBool(private API; audio-only assets still returntrueand behave as before)complete()Testing
swiftc -typecheckagainst the iOS simulator SDK passes (only pre-existing warnings remain)Refs #400, #392, #402