MP4 describes a container, not a single video codec. When an MP4 file fails in a player or processing service, inspect the encoded streams and the destination's requirements before changing its filename or downloading the source again. The container, video codec, audio codec and stream properties all matter.
This guide helps you separate those checks in a media API workflow. It includes a small Python rule tested against synthetic inspection results. That rule can flag a codec mismatch; it cannot certify playback on every browser, device or editor.
Separate the file wrapper from its streams
A container holds media streams and associated information. The codec describes how a stream is encoded. An MP4 extension therefore does not tell you everything about its contents. MDN's media container reference explains this distinction and why container and codec support must be considered together.
Ask the receiving system for a concrete input contract. For a particular workflow, it might require one H.264 video stream and one AAC audio stream, plus restrictions on profile, pixel format, dimensions or duration. Treat that as the chosen consumer's contract, not a universal rule for all MP4 files.
Keep these questions separate:
| Check | What it answers |
|---|---|
| Container | How are streams packaged? |
| Video and audio codecs | Can the destination decode the selected streams? |
| Profile, pixel format and dimensions | Does this encoding fit the consumer's detailed limits? |
| Playback or processing test | Does the exact delivered artifact work in the target workflow? |
Changing .webm to .mp4 does not rewrite a file's container or encode its streams differently. Likewise, a successful transfer only establishes that bytes arrived; it does not establish decoder compatibility.
Inspect the delivered artifact
Use a completed local copy of the exact artifact you intend to pass downstream. Preserve its job ID and output identity so you do not accidentally inspect one revision and publish another. The checksum guide covers byte identity across copies.
FFprobe can report format and stream information as JSON. The following command selects a compact set of fields described by the official FFprobe documentation source:
ffprobe -v error -show_entries format=format_name:stream=index,codec_type,codec_name,profile,pix_fmt,width,height,sample_rate,channels -of json ./delivered.mp4
Inspect every relevant stream, rather than assuming the first array element is the intended video. A file may include additional audio tracks or other streams. A shared format-family name in the report also needs interpretation; it is not a complete destination compatibility verdict.
The command is a documented inspection step; it was not successfully run on media during this review. The executable validation below covers only the Python rule with synthetic reports, not probing or playback.
Turn codec observations into a narrow check
Suppose your application has deliberately chosen a simple policy: exactly one H.264 video stream and one AAC audio stream. This helper checks only that pair. It returns a reason when the report is missing, malformed or outside that policy.
def check_codec_pair(report):
if not isinstance(report, dict):
return "invalid_report"
streams = report.get("streams")
if not isinstance(streams, list) or any(
not isinstance(stream, dict) for stream in streams
):
return "invalid_streams"
videos = [s for s in streams if s.get("codec_type") == "video"]
audios = [s for s in streams if s.get("codec_type") == "audio"]
if len(videos) != 1 or len(audios) != 1:
return "review_track_selection"
if videos[0].get("codec_name") != "h264":
return "video_codec_mismatch"
if audios[0].get("codec_name") != "aac":
return "audio_codec_mismatch"
return "codec_pair_matches"
video = {"codec_type": "video", "codec_name": "h264"}
audio = {"codec_type": "audio", "codec_name": "aac"}
assert check_codec_pair({"streams": [video, audio]}) == "codec_pair_matches"
assert check_codec_pair({"streams": [audio, video]}) == "codec_pair_matches"
assert check_codec_pair({"streams": [video]}) == "review_track_selection"
assert check_codec_pair({"streams": [
{"codec_type": "video", "codec_name": "hevc"}, audio
]}) == "video_codec_mismatch"
These dictionaries are test inputs, not real Tornado responses or observed probe output. The four assertions were executed locally, together with additional malformed-report and track-selection cases. The helper neither invokes FFprobe nor processes media.
A matching pair is only one gate. This example does not inspect container identity, profile, level, pixel format, corruption, encryption, subtitle compatibility or the content of the streams. It also deliberately sends multiple audio or video tracks for review instead of guessing which one the consumer should use. Adapt the policy to your consumer and retain these separate checks.
Diagnose the mismatch before converting
If the destination accepts the encoded streams but rejects their packaging, investigate whether a supported remux is appropriate. If the stream encoding itself is unsupported, changing only the packaging cannot resolve that decoder mismatch. Conversion choices should follow the destination's exact requirements and a representative test.
Retain the original delivered artifact while evaluating a conversion. Track the derived file as a new revision and compare the parts your workflow needs: picture, audio, duration and synchronization. Do not treat an encoder's zero exit status as editorial approval.
For Tornado requests, the create-job reference lists separate format, video_codec and audio_codec options. Consult their current supported values and behavior when preparing a request. Record what you requested separately from what you observed in the delivered file; a stored request is not an inspection result.
Verify in the actual destination
Test the file in the player, editor or downstream API that will consume it. Include the device or runtime variants your application supports. If the file opens but looks wrong, follow the resolution inspection guide. If the next step only needs speech, the audio output guide covers a different consumer contract.
When a job has delivered an artifact, investigate that artifact and the downstream error before submitting the same source again. Preserve the existing job ID, inspection result and consumer failure stage so a retry does not erase the evidence.
Start with one complete media workflow. Write down the destination's accepted container and stream properties, inspect the delivery, then run it through the real consumer. That gives you an acceptance test you can reuse when source formats or downstream requirements change.