Android Support

MediaX includes a native H.264 backend for Android built on the NDK's AMediaCodec API, allowing Android devices to transmit and receive RFC 6184 H.264 RTP streams using the same RtpPayloader/RtpDepayloader interfaces used everywhere else in MediaX.

Overview

  • Hardware-accelerated H.264 encode/decode via AMediaCodec (video/avc)
  • Byte-buffer input/output mode — no ANativeWindow dependency, since Qt does not expose a public API to obtain one from a QVideoWidget
  • Implements RFC 6184 packetization/depacketization (Single NAL Unit Mode and FU-A fragmentation)
  • Decoded/encoded YUV is converted via mediax::video::ColourSpaceFactory, matching the Callback()/Receive() convention of every other MediaX H.264 backend
  • No GStreamer dependency
  • Ported from the Android-only gva-app-bms streaming implementation (AndroidH264Encoder/AndroidH264Decoder and RtpH264Packetizer/RtpH264Receiver)

Building for Android

Android support is enabled automatically when configuring with the Android NDK's CMake toolchain file — there is no separate BUILD_ANDROID option:

cmake -B build \
  -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
  -DANDROID_ABI=arm64-v8a \
  -DANDROID_PLATFORM=android-26 \
  -DBUILD_QT6=ON ..
make -C build -j$(nproc)

When CMake detects ANDROID, MediaX:

  • Defines MEDIAX_HAS_ANDROID=1
  • Compiles h264/android/rtp_h264_android_payloader.cc, rtp_h264_android_depayloader.cc and android_probe.cc
  • Links against the NDK mediandk (AMediaCodec) library
  • Links Bionic's combined libc (no separate libpthread since Android 5.0)

Transmitting H.264 Video

#include "h264/android/rtp_h264_android_payloader.h"

mediax::rtp::h264::android::RtpH264AndroidPayloader payloader;

mediax::rtp::StreamInformation stream_info;
stream_info.session_name = "android-h264";
stream_info.hostname = "239.192.1.1";
stream_info.port = 5004;
stream_info.width = 1280;
stream_info.height = 720;
stream_info.framerate = 30;
// Frames passed to Transmit() must be tightly-packed NV12
stream_info.encoding = mediax::rtp::ColourspaceType::kColourspaceNv12;

payloader.SetStreamInfo(stream_info);

mediax::rtp::h264::android::AndroidEncoderConfig config;
config.bitrate_bps = 2000000;
config.framerate = 30;
config.i_frame_interval_secs = 2;
payloader.SetEncoderConfig(config);
payloader.SetMtu(1400);

payloader.Open();

// Send NV12 frames captured from the device camera
std::vector<uint8_t> nv12_buffer(1280 * 720 * 3 / 2);
payloader.Transmit(nv12_buffer.data(), true);

payloader.Close();

Receiving H.264 Video

#include "h264/android/rtp_h264_android_depayloader.h"

mediax::rtp::h264::android::RtpH264AndroidDepayloader depayloader;

mediax::rtp::StreamInformation stream_info;
stream_info.hostname = "239.192.1.1";
stream_info.port = 5004;
stream_info.width = 1280;
stream_info.height = 720;
stream_info.encoding = mediax::rtp::ColourspaceType::kColourspaceH264Part10;

depayloader.SetStreamInfo(stream_info);
depayloader.Open();
depayloader.Start();

mediax::rtp::RtpFrameData frame_data{};
if (depayloader.Receive(&frame_data, 1000)) {
  // frame_data.cpu_buffer contains decoded RGB24 data
}

depayloader.Stop();
depayloader.Close();

Capability Probing

Use AndroidH264Probe to check whether the device exposes a usable hardware encoder/decoder before opening a stream:

#include "h264/android/android_probe.h"

if (mediax::rtp::h264::android::AndroidH264Probe::HasH264Encoder()) {
  // Safe to construct RtpH264AndroidPayloader
}

if (mediax::rtp::h264::android::AndroidH264Probe::HasH264Decoder()) {
  // Safe to construct RtpH264AndroidDepayloader
}

Qt6 Integration

The Qt6 layer provides thin QObject wrappers for Android streaming:

  • mediax::qt6::QtRtpAndroidPayloader — transmit wrapper around RtpH264AndroidPayloader
  • mediax::qt6::QtRtpAndroidDepayloader — receive wrapper around RtpH264AndroidDepayloader

Multicast Reception on Android

Android's Wi-Fi chipset/driver power-saving filter silently drops incoming multicast frames at the driver level, even though a socket's IP_ADD_MEMBERSHIP join reports success. mediax::qt6::QtAndroidMulticastLock is an RAII wrapper around WifiManager.MulticastLock that must be held for as long as the app is receiving multicast traffic (SAP discovery, RTP video reception). Senders (QtSapAnnouncer, QtRtpAndroidPayloader) do not need it since only inbound multicast is affected.

#include "qt6/QtAndroidMulticastLock.h"

// Acquire once before starting any multicast receiver
mediax::qt6::QtAndroidMulticastLock multicast_lock("MediaXReceiver");
multicast_lock.acquire();

// ... start QtSapListener / QtRtpAndroidDepayloader ...

// Released automatically when multicast_lock goes out of scope,
// or explicitly via multicast_lock.release()

This is the only Android/JNI-specific code in MediaX's Qt6 layer — the core (non-Qt) library stays JNI-free.

Limitations

Note

  • No ANativeWindow surface rendering path — decoded frames are always returned as CPU-side RGB24 buffers via byte-buffer mode.
  • Requires Android API level with NDK AMediaCodec support (API 21+; API 26+ recommended for the most reliable encoder behaviour).

See Also