RenderIO
Guides

Convert Video Formats

Convert between video formats like MP4, WebM, MOV, AVI, and MKV.

Convert Video Formats

Video format conversion is one of the most common FFmpeg operations. Different platforms and devices require different container formats and codecs. RenderIO makes it easy to convert between any formats that FFmpeg supports.

MP4 to WebM (VP9)

WebM with VP9 is the preferred format for web playback. The -crf 30 flag controls quality (lower is better), and -b:v 0 enables constant quality mode.

ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}

MOV to MP4 (H.264)

Apple's MOV files are common from iPhone and Final Cut Pro exports. Converting to MP4 with H.264 gives you the widest compatibility across browsers and devices.

ffmpeg -i {{in_video}} -c:v libx264 -c:a aac {{out_video}}

AVI to MP4

Legacy AVI files can be modernized to MP4 with the same H.264/AAC encoding.

ffmpeg -i {{in_video}} -c:v libx264 -c:a aac {{out_video}}

MKV to MP4 (stream copy)

When both containers support the same codecs, you can copy streams directly without re-encoding. This is nearly instant and preserves the original quality.

ffmpeg -i {{in_video}} -c copy {{out_video}}

Stream copy only works when the codecs inside the MKV file are compatible with the MP4 container (e.g., H.264 video and AAC audio). If you get errors, switch to re-encoding with -c:v libx264 -c:a aac.

Full API example

Convert an MP4 file to WebM with VP9 video and Opus audio.

curl -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: ffsk_your_api_key_here" \
  -d '{
    "input_files": {
      "in_video": "https://example.com/source.mp4"
    },
    "output_files": {
      "out_video": "converted.webm"
    },
    "ffmpeg_command": "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}"
  }'
import requests

response = requests.post(
    "https://renderio.dev/api/v1/run-ffmpeg-command",
    headers={
        "Content-Type": "application/json",
        "X-API-KEY": "ffsk_your_api_key_here",
    },
    json={
        "input_files": {
            "in_video": "https://example.com/source.mp4",
        },
        "output_files": {
            "out_video": "converted.webm",
        },
        "ffmpeg_command": "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}",
    },
)

data = response.json()
print("Command ID:", data["command_id"])
interface RunCommandResponse {
  command_id: string;
}

const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-KEY": "ffsk_your_api_key_here",
  },
  body: JSON.stringify({
    input_files: {
      in_video: "https://example.com/source.mp4",
    },
    output_files: {
      out_video: "converted.webm",
    },
    ffmpeg_command:
      "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}",
  }),
});

const { command_id } = (await response.json()) as RunCommandResponse;
console.log("Command ID:", command_id);
const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-KEY": "ffsk_your_api_key_here",
  },
  body: JSON.stringify({
    input_files: {
      in_video: "https://example.com/source.mp4",
    },
    output_files: {
      out_video: "converted.webm",
    },
    ffmpeg_command:
      "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}",
  }),
});

const { command_id } = await response.json();
console.log("Command ID:", command_id);
<?php
$ch = curl_init("https://renderio.dev/api/v1/run-ffmpeg-command");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Content-Type: application/json",
    "X-API-KEY: ffsk_your_api_key_here",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "input_files" => [
        "in_video" => "https://example.com/source.mp4",
    ],
    "output_files" => [
        "out_video" => "converted.webm",
    ],
    "ffmpeg_command" => "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}",
]));

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
echo "Command ID: " . $data["command_id"] . "\n";

The API returns a command_id immediately. Poll GET /api/v1/commands/:commandId to check when the conversion is complete, then download the output from the storage_url in the response.

Tips and variations

  • Container vs. codec: A container (MP4, WebM, MKV) is the file wrapper. A codec (H.264, VP9, AAC) is the compression algorithm inside it. The same codec can live in different containers, and the same container can hold different codecs.
  • Codec compatibility: Not every codec works in every container. H.264 + AAC works in MP4 and MKV but not WebM. VP9 + Opus works in WebM and MKV but not MP4.
  • Stream copy when possible: If you only need to change the container and the codecs are compatible, use -c copy to avoid re-encoding. This is faster and lossless.
  • Quality control: For H.264, use -crf 23 (default) for good quality. Range is 0-51 where lower is better. For VP9, -crf 30 with -b:v 0 is a good starting point.
  • Audio codec pairing: Use AAC (-c:a aac) with MP4, Opus (-c:a libopus) with WebM, and Vorbis (-c:a libvorbis) as a WebM fallback.

Further reading

On this page