Vue d’ensemble
AIR ne fournit actuellement aucune implémentation à jour du streaming multimédia en temps réel. KlugMedia l’ajoute sous forme d’extension native. Caméra, microphone, encodeur et annulation d’écho s’exécutent ensemble dans une seule session native, et ActionScript se contente d’envoyer des commandes et de recevoir des états.
La vidéo est publiée et reçue en H.264, la voix en Opus, le tout via RTMP standard. La vidéo reçue est dessinée nativement au-dessus de la scène AIR : elle reste donc nette et fluide, même sur des appareils plus anciens.
KlugMedia est utilisé chaque jour dans nos applications, notre preuve de concept, sur l’App Store, Google Play et le Mac.
Fonctionnalités
- Autonome : une seule ANE, sans bibliothèque centrale ni ANE de dépendance à ajouter
- Capture caméra et publication H.264 dans une seule session native
- Réception et affichage H.264 natifs, au bon rapport d’aspect, avec recadrage-remplissage et rotation
- Voix Opus bidirectionnelle avec annulation d’écho matérielle, les deux côtés pouvant parler en même temps
- Aperçu caméra natif, changement de caméra, lampe torche et zoom
- Instantanés pris directement depuis l’image de la caméra
- Enregistrement MP4 des flux en direct sur le Klug Streaming Server, image et son
- Adaptation automatique du débit sur les réseaux faibles
- Tampon vocal adaptatif qui encaisse les micro-coupures du Wi-Fi et revient seul à un faible délai
- Sur Mac : passage de la caméra intégrée aux caméras USB en environ une seconde
- RTMP standard, donc compatible avec les serveurs multimédias ordinaires
Serveur de streaming
KlugMedia est fourni avec un logiciel serveur adapté, développé en même temps que l’extension et optimisé pour les applications AIR : gestion des flux, reconnexions fiables, relais de la voix bidirectionnelle, enregistrement MP4 et visionnage en direct dans un navigateur web.
Plateformes prises en charge
Applications iPad sur Mac
Sur les Mac Apple silicon, le build iOS d’une application AIR peut s’exécuter comme application iPad. KlugMedia prend en charge ce mode avec caméra, vidéo en direct et voix bidirectionnelle, y compris la caméra FaceTime intégrée et les caméras USB externes. Un seul build pour iPhone, iPad et Mac.
Prêt pour iOS 27 et macOS 27
KlugMedia est testé sur iOS 27, iPadOS 27 et macOS 27, sur iPhone, iPad, MacBook et Mac mini, ainsi que toujours sur iOS 17. Points forts de cette version :
- Chemin de réception renforcé : arrêter et relancer un flux en direct est sûr à tout moment, y compris avec le minutage d’iOS 27
- Aperçu caméra stable sur macOS 27 pour les applications iPad sur Mac, pendant que la caméra démarre, s’arrête ou change
- Orientation correcte sur chaque Mac : les caméras intégrées et USB s’affichent à l’endroit dans l’aperçu et chez le récepteur, sur macOS 26 et 27
- Changement de caméra rapide sur Mac entre la caméra FaceTime et les caméras USB, en environ une seconde
- Voix claire sur le Wi-Fi des portables : un tampon adaptatif absorbe les brèves rafales Wi-Fi typiques des MacBook et se réduit à nouveau quand le réseau est calme
Prise en main
Ajoutez l’extension au descripteur de votre application :
<extensions>
<extensionID>com.klug.media</extensionID>
</extensions>
Demandez l’autorisation d’accès à la caméra et au microphone avant le premier démarrage. L’extension prend alors en charge la capture : n’ouvrez pas en même temps une Camera ou un Microphone AIR.
Exemples de code
Cinq flux complets, tirés de l’API qui tourne chaque jour dans nos applications. Chaque appel retourne immédiatement ; les résultats arrivent sous forme de StatusEvents. Documentation complète →
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(); }
// 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 ); });
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 ); }
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
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" );
Aperçu de l’API
| isSupported | Vrai là où la session multimédia native est disponible. |
| startPublish(…) | Démarre la caméra et publie en H.264 via RTMP : taille, fps, débit, intervalle d’images clés, caméra, identifiants RTMP facultatifs. |
| stopPublish() | Arrête la publication ; la caméra est libérée, sauf si l’aperçu est encore affiché. |
| startReceive(…) | Lit nativement un flux distant dans un rectangle de la scène : ajustement au rapport d’aspect ou recadrage-remplissage avec rotation (étirement sur Android). |
| setReceiveFrame(x, y, w, h) | Déplace et redimensionne la vidéo native, pour la rotation, les téléphones pliables et le plein écran. |
| stopReceive() | Arrête la lecture et supprime la vue native. |
| showPreview(…) / hidePreview() | Aperçu caméra natif au-dessus de la scène, seul ou à côté d’une publication en direct. |
| switchCamera() | Passe à la caméra suivante pendant la diffusion : avant et arrière sur iPhone et iPad, caméras intégrée et USB sur Mac. |
| setTorch(on) / hasTorch | Lampe de la caméra pendant la diffusion, et indication de sa présence sur l’appareil. |
| setCameraZoom(step) | Véritable zoom caméra sur iOS et Android, de 0 à 8 pour 1,0x à 2,0x ; le flux est lui aussi zoomé. |
| setVideoBitrate(kbps) | Modifie le débit de l’encodeur en direct. |
| captureSnapshot(w, h) | Enregistre un JPEG à partir de l’image de la caméra en direct. |
| isiOSAppOnMac | Vrai lorsque le build iOS s’exécute comme application iPad sur un Mac Apple silicon. |
| OpusVoiceBridge | Voix bidirectionnelle : démarrage, arrêt, annulation d’écho, haut-parleur, gain, tampon de lecture. |
| Red5OpusTransport | Transporte les paquets Opus sur votre connexion RTMP, via deux flux dédiés. |
Événements d’état
| cameraStatus | connecting · connected · disconnected · failed:<reason> · authError:<reason> · newBitrate:<rate> · authSuccess (Android) |
| receiveStatus | playing · firstFrame · disconnected · streamEnded et failed:<reason> (iOS, macOS) |
| snapshotStatus | saved:<path> · failed:<reason> |
Acheter KlugMedia
Une licence annuelle pour une application AIR, avec toutes les mises à jour pendant 12 mois. Après le paiement, vous recevez l’extension, la bibliothèque ActionScript et la documentation par e-mail, généralement sous un jour ouvré. Licence et conditions
- Licence pour une application AIR
- Toutes les mises à jour pendant 12 mois
- Sans support
- Licence pour une application AIR
- Toutes les mises à jour pendant 12 mois
- Réponse par e-mail sous 7 jours
- Licence pour une application AIR
- Toutes les mises à jour pendant 12 mois
- Réponse par e-mail sous 48 heures
Prérequis
| SDK AIR | 51.x (HARMAN) |
| iOS | iOS 17 ou version ultérieure |
| Android | Android 6.0 (API 23) ou version ultérieure |
| macOS | macOS 11 ou version ultérieure |
| Applications iPad sur Mac | Apple silicon, macOS 26 ou version ultérieure |
| Autorisations | Caméra et microphone |
| Serveur | Tout serveur RTMP ; le serveur de streaming Klug correspondant est recommandé |