← All extensions

Native extension · Flagship

KlugMedia

com.klug.media

NO APM NEEDED. Download and enjoy. It can’t get any simpler. Just update the Adobe AIR SDK. If you need help, we are here.

Camera, two-way voice and hardware echo cancellation in one native session, publishing and receiving H.264 and Opus over standard RTMP.

iOSAndroidmacOS In production From €72 / year · Buy

Overview

AIR does not currently provide an up-to-date implementation for real-time media streaming. KlugMedia adds it as a native extension. Camera, microphone, encoder and echo cancellation run together in one native session, and ActionScript only sends commands and receives status.

Video is published and received as H.264, voice as Opus, both over standard RTMP. Received video is drawn natively on top of the AIR stage, so it stays sharp and smooth even on older devices.

KlugMedia is in daily use in our apps, our proof of concept, on the App Store, Google Play and the Mac.

Features

  • Self-contained: one ANE, no core library or dependency ANEs to add
  • Camera capture and H.264 publishing in one native session
  • Native H.264 receive and display, aspect-correct, with crop-to-fill and rotation
  • Two-way Opus voice with hardware echo cancellation, both sides talking at once
  • Native camera preview, camera switch, torch and zoom
  • Snapshots taken straight from the camera image
  • MP4 recording of live streams on the Klug streaming server, picture and sound
  • Automatic bitrate adaptation on weak networks
  • Adaptive voice buffer that rides out Wi-Fi hiccups and returns to low delay on its own
  • On the Mac: switch between the built-in camera and USB cameras in about a second
  • Standard RTMP, so it works with ordinary media servers

Streaming server

KlugMedia comes with matching server software, developed together with the extension and tuned for AIR apps: stream handling, reliable reconnects, the two-way voice relay, MP4 recording and live viewing in a web browser.

The server is described on the overview page: Streaming server.

Supported platforms

iOS arm64
Android arm64 · armv7
macOS Apple silicon · Intel
iPad apps on the Mac Apple silicon · macOS 26 and 27

iPad apps on the Mac

On Macs with Apple silicon, the iOS build of an AIR app can run as an iPad app. KlugMedia supports this mode with camera, live video and two-way voice, including the built-in FaceTime camera and external USB cameras. One build serves iPhone, iPad and Mac.

Ready for iOS 27 and macOS 27

KlugMedia is tested on iOS 27, iPadOS 27 and macOS 27, on iPhone, iPad, MacBook and Mac mini, and still on iOS 17. Highlights of this release:

  • Hardened receive path: stopping and restarting a live stream is safe at any moment, also under the timing of iOS 27
  • Stable camera preview on macOS 27 for iPad apps on the Mac, while the camera starts, stops or switches
  • Correct orientation on every Mac: built-in and USB cameras stand upright in the preview and at the receiver, on macOS 26 and 27
  • Fast camera switch on the Mac between the FaceTime camera and USB cameras, in about a second
  • Clear voice on laptop Wi-Fi: an adaptive buffer absorbs the short Wi-Fi bursts typical of MacBooks and shrinks back when the network is calm

Getting started

Add the extension to your application descriptor:

app.xml
<extensions>
    <extensionID>com.klug.media</extensionID>
</extensions>

Request camera and microphone permission before the first start. The extension then owns the capture: do not open an AIR Camera or Microphone at the same time.

Code examples

Five complete flows, taken from the API that runs in our apps every day. Every call returns at once; results arrive as StatusEvents. Full documentation →

1 · Publish a camera stream
Station.as
import com.klug.petcam.camera.CameraStreamANE;
import flash.events.StatusEvent;

var media:CameraStreamANE = CameraStreamANE.instance;
media.addEventListener( StatusEvent.STATUS, onMedia );

if ( media.isSupported )
{
    // camera, H.264 encoder and RTMP in one native session
    media.startPublish( "rtmp://your.server:1935/live", "station-1",
                        640, 480,     // size
                        12, 800,      // fps, kbps
                        2,            // keyframe every 2 s: fast start at the viewer
                        CameraStreamANE.CAMERA_BACK );
}

function onMedia( e:StatusEvent ):void
{
    if ( e.code != CameraStreamANE.STATUS_EVENT ) return;
    if ( e.level == "connected" )             showLive();
    else if ( e.level.indexOf( "failed:" ) == 0 )  retryLater( e.level.substr( 7 ) );
    else if ( e.level.indexOf( "authError:" ) == 0 ) askForLogin();
}
2 · Receive and follow the layout
Viewer.as
// the video is decoded natively and drawn in a view above the stage (pixels)
media.startReceive( "rtmp://your.server:1935/live", "station-1",
                    0, 80, stage.stageWidth, stage.stageHeight - 160,
                    0, CameraStreamANE.RECEIVE_FIT );   // or RECEIVE_CROPFILL

media.addEventListener( StatusEvent.STATUS, function( e:StatusEvent ):void {
    if ( e.code == "receiveStatus" && e.level == "firstFrame" ) hideSpinner();
});

// rotation, a folding phone, fullscreen: move the native view with the stage
stage.addEventListener( Event.RESIZE, function( e:Event ):void {
    media.setReceiveFrame( 0, 80, stage.stageWidth, stage.stageHeight - 160 );
});
3 · Two-way voice with echo cancellation
Voice.as
import com.klug.petcam.audioecho.*;

var voice:OpusVoiceBridge = new OpusVoiceBridge( new NativeOpusCodec(), 48000, 1, 960 );
var link:Red5OpusTransport;

var nc:NetConnection = new NetConnection();
nc.addEventListener( NetStatusEvent.NET_STATUS, function( e:NetStatusEvent ):void {
    if ( e.info.code == "NetConnection.Connect.Success" ) startVoice();
});
nc.connect( "rtmp://your.server:1935/live" );

function startVoice():void
{
    link = new Red5OpusTransport( nc );
    link.addEventListener( OpusPacketEvent.PACKET, function( e:OpusPacketEvent ):void {
        voice.playRemoteOpus( e.packet );            // decode and play the other side
    });
    link.start( "voice_me", "voice_other" );     // my stream, the other side's stream

    voice.setVoiceProcessing( true );             // hardware echo cancellation
    EchoCancellationANE.instance.setDuck( false ); // both sides may talk at once
    voice.start();
    addEventListener( Event.ENTER_FRAME, sendVoice );
}

function sendVoice( e:Event ):void
{
    var packet:ByteArray;
    while ( ( packet = voice.readLocalOpus() ) != null )   // every finished 20 ms frame
        link.sendPacket( packet );
}
The voice packets travel as data messages on their own pair of streams. The Klug streaming server relays them out of the box; other RTMP servers need to forward NetStream.send to the subscribers.
4 · Preview, camera switch, torch, zoom, snapshot
Controls.as
media.showPreview( 20, 100, 320, 240, CameraStreamANE.CAMERA_FRONT );   // aiming preview

media.switchCamera();               // front / back, or built-in / USB camera on the Mac
if ( media.hasTorch ) media.setTorch( true );
media.setCameraZoom( 4 );           // 0..8 = 1.0x..2.0x, the stream is zoomed too
media.setVideoBitrate( 500 );       // change the bitrate while live

media.addEventListener( StatusEvent.STATUS, function( e:StatusEvent ):void {
    if ( e.code == "snapshotStatus" && e.level.indexOf( "saved:" ) == 0 )
        showPhoto( e.level.substr( 6 ) );                // absolute path of the JPEG
});
media.captureSnapshot( 640, 480 );

media.hidePreview();   // native views sit above the stage: hide them before dialogs
5 · Clean stop and restart
Session.as
function stopAll():void
{
    removeEventListener( Event.ENTER_FRAME, sendVoice );
    voice.stop();
    link.stop();
    media.stopReceive();
    media.stopPublish();    // always stop before publishing the same name again
}

// safe at any moment, also while a stream is still connecting
stopAll();
media.startPublish( "rtmp://your.server:1935/live", "station-1" );

API overview

isSupportedTrue where the native media session is available.
startPublish(…)Starts the camera and publishes H.264 over RTMP: size, fps, bitrate, keyframe interval, camera, optional RTMP login.
stopPublish()Stops publishing; the camera is released unless the preview is still shown.
startReceive(…)Plays a remote stream natively inside a rectangle of the stage: aspect-fit or crop-to-fill with rotation (stretch on Android).
setReceiveFrame(x, y, w, h)Moves and resizes the native video, for rotation, folding phones and fullscreen.
stopReceive()Stops playback and removes the native view.
showPreview(…) / hidePreview()Native camera preview over the stage, alone or beside a live publish.
switchCamera()Switches to the next camera while streaming: front and back on iPhone and iPad, built-in and USB cameras on the Mac.
setTorch(on) / hasTorchCamera light while streaming, and whether the device has one.
setCameraZoom(step)Real camera zoom on iOS and Android, 0..8 for 1.0x to 2.0x; the stream is zoomed too.
setVideoBitrate(kbps)Changes the encoder bitrate while live.
captureSnapshot(w, h)Saves a JPEG from the live camera image.
isiOSAppOnMacTrue when the iOS build runs as an iPad app on an Apple silicon Mac.
OpusVoiceBridgeTwo-way voice: start, stop, echo cancellation, speaker, gain, playback buffer.
Red5OpusTransportCarries the Opus packets over your RTMP connection on two dedicated streams.

Status events

cameraStatusconnecting · connected · disconnected · failed:<reason> · authError:<reason> · newBitrate:<rate> · authSuccess (Android)
receiveStatusplaying · firstFrame · disconnected · streamEnded and failed:<reason> (iOS, macOS)
snapshotStatussaved:<path> · failed:<reason>

Buy KlugMedia

A yearly licence for one AIR application, with all updates for 12 months. After payment you receive the extension, the ActionScript library and the documentation by e-mail, usually within one business day. Licence and terms

Simple
€72 / year
  • Licence for one AIR application
  • All updates for 12 months
  • No support
Buy Simple
Standard
€159 / year
  • Licence for one AIR application
  • All updates for 12 months
  • Answer by e-mail within 7 days
Buy Standard
Priority
€299 / year
  • Licence for one AIR application
  • All updates for 12 months
  • Answer by e-mail within 48 hours
Buy Priority
Taxes are calculated at checkout by Link (Stripe). You enter the application ID of your AIR app during checkout.

Requirements

AIR SDK51.x (HARMAN)
iOSiOS 17 or later
AndroidAndroid 6.0 (API 23) or later
macOSmacOS 11 or later
iPad apps on the MacApple silicon, macOS 26 or later
PermissionsCamera and microphone
ServerAny RTMP server; the matching Klug streaming server is recommended