Skip to content

MediaCodec

currentMigrated from docs/encoders_hw/mediacodec.mdx at 37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0

MediaCodec is Android’s framework for the codec components that ship with a device, which on a phone are mostly hardware blocks in the SoC. FFmpeg wraps its encoders as h264_mediacodec, hevc_mediacodec, vp8_mediacodec, vp9_mediacodec, mpeg4_mediacodec and av1_mediacodec, so you need an FFmpeg build for Android configured with MediaCodec support. The H.264 and HEVC encoders arrived in FFmpeg 6.0, the others in 6.1. It is the Android counterpart of VideoToolbox.

Which codecs you get depends on the phone. H.264 and HEVC encoders are nearly universal, VP9 and AV1 encoders are rarer, and quality, rate-control accuracy and bugs vary between vendors much more than with desktop GPUs.

Hardware encoders in phones are designed for camera recording and video calls: real time, low power, forgiving of bitrate. They lose to x264, x265 and SVT-AV1 at slow presets in quality per bit, and many components accept only bitrate modes. Use MediaCodec for quick on-device transcodes. For anything you keep, encode on a computer. More in NVENC: hardware or software.

Apps such as Codec Info list every encoder component with its supported codecs, bitrate modes and quality range. Names are vendor-specific, for example c2.exynos.h264.encoder or c2.google.av1.encoder, and you pass the exact name to FFmpeg with -codec_name when a device has several encoders for a codec. Without -codec_name Android picks one for you.

Bitrate modes work on virtually every encoder:

Terminal window
ffmpeg -i input.mkv -c:v hevc_mediacodec -bitrate_mode vbr -b:v 4M -g 60 output.mp4
Terminal window
ffmpeg -i input.mkv -c:v av1_mediacodec -codec_name c2.google.av1.encoder -bitrate_mode vbr -b:v 4M -g 60 output.mkv

Constant quality only works if the encoder reports BITRATE_MODE_CQ and a quality range:

Terminal window
ffmpeg -i input.mkv -c:v hevc_mediacodec -bitrate_mode cq -global_quality 70 output.mp4

FFmpeg passes -global_quality straight to Android’s quality key, so the value must be inside the range the device reports. It has no relation to CRF.

OptionWhat it does
-bitrate_modecq, vbr, cbr or cbr_fd (CBR that may drop frames). Unset leaves it to the component. Check what the device supports first.
-b:vTarget bitrate. Required for every mode but cq.
-gKeyframe interval. Android takes whole seconds, so FFmpeg divides -g by the integer frame rate and drops the remainder: -g 60 at 30 fps is a 2-second interval, -g 89 is still 2 seconds. Anything under one second, including FFmpeg’s default of 12 frames, becomes one second.
-codec_namePick a specific encoder component.
-operating_rateHint for how fast the encoder will be fed; not a frame-rate conversion.
-levelAV1 level. FFmpeg’s values are Android bit flags, so use the named values (4.1), never numbers copied from NVENC or Vulkan.
-qp_i_min … -qp_b_maxQuantizer bounds per frame type, when the component honours them.
  • Input must be 8-bit yuv420p or nv12; the wrapper does not take 10-bit pixel formats.
  • Frame-dropping CBR (cbr_fd) changes frame cadence and is wrong for anything that must stay frame-exact.
  • An encoder that opens is not necessarily good. Check the output for distortion, missing headers (AV1 and VP9 in particular) and correct timestamps before trusting a device.

Every private option of av1_mediacodec in FFmpeg 9.0.2; the H.264, HEVC, VP8, VP9 and MPEG-4 MediaCodec encoders share all of them except -level, which each defines with its own codec’s levels. FFmpeg passes these to the Android component, which may ignore a key it does not support. INT_MAX is 2147483647. -profile:v also accepts main, high and professional for AV1.

ParameterInitial defaultAccepted values / meaning
-ndk_codec-1 (auto)Use the NDK MediaCodec API (1) or the Java one (0); auto uses NDK when no Java VM is available
-ndk_async0Use the NDK API in asynchronous mode
-codec_nameunsetPick a specific component, e.g. c2.google.av1.encoder; unset uses the default for the MIME type
-bitrate_mode-1 (component default)cq=0, vbr=1, cbr=2, cbr_fd=3 (CBR with frame dropping). cq takes its quality from -global_quality
-pts_as_dts-1 (auto)Copy PTS to DTS; auto enables it when -bf is 0 or unset
-operating_rate0 (unspecified)Frames per second the component should be able to process, a speed hint; it does not change the output frame rate
-qp_i_min-1 (unset)Lowest QP for I-frames
-qp_p_min-1 (unset)Lowest QP for P-frames
-qp_b_min-1 (unset)Lowest QP for B-frames
-qp_i_max-1 (unset)Highest QP for I-frames
-qp_p_max-1 (unset)Highest QP for P-frames
-qp_b_max-1 (unset)Highest QP for B-frames
-level0 (unspecified)AV1 level, e.g. 5.1; named 2, 2.1, 2.2, 2.3, 3, 3.1, 3.2, 3.3, 4, 4.1, 4.2, 4.3, 5, 5.1, 5.2, 5.3, 6, 6.1, 6.2, 6.3, 7, 7.1, 7.2, 7.3 map to Android’s AV1Level* bit constants