Visão geral
Atualmente, o AIR não oferece uma implementação atualizada para streaming de mídia em tempo real. O KlugMedia a adiciona como extensão nativa. Câmera, microfone, encoder e cancelamento de eco rodam juntos em uma única sessão nativa, e o ActionScript apenas envia comandos e recebe status.
O vídeo é publicado e recebido em H.264, a voz em Opus, ambos sobre RTMP padrão. O vídeo recebido é desenhado nativamente sobre o palco do AIR, por isso continua nítido e fluido mesmo em dispositivos mais antigos.
O KlugMedia é usado diariamente em nossos apps, nossa prova de conceito, na App Store, no Google Play e no Mac.
Recursos
- Autossuficiente: uma ANE, sem biblioteca principal nem ANEs de dependência para adicionar
- Captura de câmera e publicação em H.264 em uma única sessão nativa
- Recepção e exibição nativas de H.264, com proporção correta, preenchimento com corte e rotação
- Voz Opus bidirecional com cancelamento de eco por hardware, com os dois lados falando ao mesmo tempo
- Prévia nativa da câmera, troca de câmera, lanterna e zoom
- Fotos capturadas direto da imagem da câmera
- Gravação em MP4 de streams ao vivo no servidor de streaming da Klug, imagem e som
- Adaptação automática de bitrate em redes fracas
- Buffer de voz adaptativo que supera as oscilações do Wi-Fi e volta sozinho a um atraso baixo
- No Mac: alterne entre a câmera integrada e câmeras USB em cerca de um segundo
- RTMP padrão, por isso funciona com servidores de mídia comuns
Servidor de streaming
O KlugMedia acompanha software de servidor compatível, desenvolvido junto com a extensão e ajustado para apps AIR: gerenciamento de streams, reconexões confiáveis, retransmissão de voz bidirecional, gravação em MP4 e visualização ao vivo em um navegador web.
Plataformas compatíveis
Apps iPad no Mac
Em Macs com Apple silicon, o build iOS de um app AIR pode rodar como app iPad. O KlugMedia oferece suporte a esse modo com câmera, vídeo ao vivo e voz bidirecional, incluindo a câmera FaceTime integrada e câmeras USB externas. Um único build atende iPhone, iPad e Mac.
Pronto para iOS 27 e macOS 27
O KlugMedia é testado no iOS 27, iPadOS 27 e macOS 27, em iPhone, iPad, MacBook e Mac mini, e também ainda no iOS 17. Destaques desta versão:
- Caminho de recepção reforçado: parar e reiniciar um stream ao vivo é seguro a qualquer momento, inclusive com o timing do iOS 27
- Prévia de câmera estável no macOS 27 para apps iPad no Mac, enquanto a câmera inicia, para ou troca
- Orientação correta em todo Mac: câmeras integradas e USB aparecem na posição correta na prévia e no receptor, no macOS 26 e 27
- Troca rápida de câmera no Mac entre a câmera FaceTime e câmeras USB, em cerca de um segundo
- Voz nítida no Wi-Fi de notebooks: um buffer adaptativo absorve as rajadas curtas de Wi-Fi típicas de MacBooks e volta a diminuir quando a rede está calma
Primeiros passos
Adicione a extensão ao descritor do seu aplicativo:
<extensions>
<extensionID>com.klug.media</extensionID>
</extensions>
Solicite a permissão de câmera e microfone antes da primeira inicialização. A extensão passa então a controlar a captura: não abra uma Camera ou um Microphone do AIR ao mesmo tempo.
Exemplos de código
Cinco fluxos completos, extraídos da API que roda em nossos apps todos os dias. Cada chamada retorna imediatamente; os resultados chegam como StatusEvents. Documentação completa →
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" );
Visão geral da API
| isSupported | Verdadeiro onde a sessão de mídia nativa está disponível. |
| startPublish(…) | Inicia a câmera e publica H.264 sobre RTMP: tamanho, fps, bitrate, intervalo de keyframes, câmera, login RTMP opcional. |
| stopPublish() | Interrompe a publicação; a câmera é liberada, a menos que a prévia ainda esteja sendo exibida. |
| startReceive(…) | Reproduz um stream remoto nativamente dentro de um retângulo do palco: ajuste proporcional ou preenchimento com corte e rotação (esticado no Android). |
| setReceiveFrame(x, y, w, h) | Move e redimensiona o vídeo nativo, para rotação, celulares dobráveis e tela cheia. |
| stopReceive() | Interrompe a reprodução e remove a view nativa. |
| showPreview(…) / hidePreview() | Prévia nativa da câmera sobre o palco, isolada ou junto de uma publicação ao vivo. |
| switchCamera() | Alterna para a próxima câmera durante o streaming: frontal e traseira no iPhone e iPad, câmeras integradas e USB no Mac. |
| setTorch(on) / hasTorch | Luz da câmera durante o streaming, e se o dispositivo tem uma. |
| setCameraZoom(step) | Zoom real da câmera no iOS e Android, 0..8 para 1.0x a 2.0x; o stream também recebe o zoom. |
| setVideoBitrate(kbps) | Altera o bitrate do encoder durante a transmissão ao vivo. |
| captureSnapshot(w, h) | Salva um JPEG da imagem ao vivo da câmera. |
| isiOSAppOnMac | Verdadeiro quando o build iOS roda como app iPad em um Mac com Apple silicon. |
| OpusVoiceBridge | Voz bidirecional: iniciar, parar, cancelamento de eco, alto-falante, ganho, buffer de reprodução. |
| Red5OpusTransport | Transporta os pacotes Opus pela sua conexão RTMP em dois streams dedicados. |
Eventos de status
| cameraStatus | connecting · connected · disconnected · failed:<reason> · authError:<reason> · newBitrate:<rate> · authSuccess (Android) |
| receiveStatus | playing · firstFrame · disconnected · streamEnded e failed:<reason> (iOS, macOS) |
| snapshotStatus | saved:<path> · failed:<reason> |
Compre o KlugMedia
Uma licença anual para um app AIR, com todas as atualizações por 12 meses. Após o pagamento, você recebe a extensão, a biblioteca ActionScript e a documentação por e-mail, normalmente em até um dia útil. Licença e termos
- Licença para um app AIR
- Todas as atualizações por 12 meses
- Sem suporte
- Licença para um app AIR
- Todas as atualizações por 12 meses
- Resposta por e-mail em até 7 dias
- Licença para um app AIR
- Todas as atualizações por 12 meses
- Resposta por e-mail em até 48 horas
Requisitos
| AIR SDK | 51.x (HARMAN) |
| iOS | iOS 17 ou posterior |
| Android | Android 6.0 (API 23) ou posterior |
| macOS | macOS 11 ou posterior |
| Apps iPad no Mac | Apple silicon, macOS 26 ou posterior |
| Permissões | Câmera e microfone |
| Servidor | Qualquer servidor RTMP; recomendamos o servidor de streaming da Klug compatível |