Publicação e assinatura com o SDK de Transmissão na Web do IVS | Streaming em tempo real - Amazon IVS

Publicação e assinatura com o SDK de Transmissão na Web do IVS | Streaming em tempo real

Este documento descreve as etapas envolvidas na publicação e assinatura de um estágio usando o SDK de Transmissão na Web para streaming em tempo real do IVS.

Conceitos

Existem três conceitos principais que fundamentam a funcionalidade em tempo real: palco, estratégia e eventos. O objetivo do projeto é minimizar a quantidade de lógica do lado do cliente que é necessária para desenvolver um produto funcional.

Estágio

A classe Stage corresponde ao principal ponto de interação entre a aplicação de host e o SDK. Ela representa o próprio palco e é usada para entrar e sair do palco. Criar e entrar em um palco requer uma string de token válida e não expirada do ambiente de gerenciamento (representada como token). Entrar e sair de um palco é simples:

const stage = new Stage(token, strategy) try { await stage.join(); } catch (error) { // handle join exception } stage.leave();

Strategy

A interface StageStrategy fornece uma maneira para a aplicação de host comunicar o estado desejado do palco ao SDK. Três funções precisam ser implementadas: shouldSubscribeToParticipant, shouldPublishParticipant e stageStreamsToPublish. Todas serão discutidas abaixo.

Para usar uma estratégia definida, passe-a para o Stage de criação. Veja a seguir um exemplo completo de uma aplicação que usa uma estratégia para publicar a webcam de um participante no palco e realizar a inscrição para todos os participantes. A finalidade de cada função de estratégia necessária é explicada em detalhes nas seções subsequentes.

const devices = await navigator.mediaDevices.getUserMedia({ audio: true, video: { width: { max: 1280 }, height: { max: 720 }, } }); const myAudioTrack = new LocalStageStream(devices.getAudioTracks()[0]); const myVideoTrack = new LocalStageStream(devices.getVideoTracks()[0]); // Define the stage strategy, implementing required functions const strategy = { audioTrack: myAudioTrack, videoTrack: myVideoTrack, // optional updateTracks(newAudioTrack, newVideoTrack) { this.audioTrack = newAudioTrack; this.videoTrack = newVideoTrack; }, // required stageStreamsToPublish() { return [this.audioTrack, this.videoTrack]; }, // required shouldPublishParticipant(participant) { return true; }, // required shouldSubscribeToParticipant(participant) { return SubscribeType.AUDIO_VIDEO; } }; // Initialize the stage and start publishing const stage = new Stage(token, strategy); await stage.join(); // To update later (e.g. in an onClick event handler) strategy.updateTracks(myNewAudioTrack, myNewVideoTrack); stage.refreshStrategy();

Como se inscrever como participante

shouldSubscribeToParticipant(participant: StageParticipantInfo): SubscribeType

Quando um participante remoto entra no palco, o SDK consulta a aplicação de host sobre o estado de inscrição desejado para esse participante. As opções são NONE, AUDIO_ONLY e AUDIO_VIDEO. Ao retornar um valor para essa função, a aplicação de host não precisa se preocupar com o estado de publicação, o estado atual da inscrição ou o estado da conexão do palco. Se AUDIO_VIDEO for retornado, o SDK aguardará até que o participante remoto esteja publicando antes de inscrever e atualizará a aplicação de host ao emitir eventos durante todo o processo.

Veja a seguir uma amostra de implementação:

const strategy = { shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } // ... other strategy functions }

Esta é a implementação completa desta função para uma aplicação de host que sempre deseja que todos os participantes se vejam, por exemplo, uma aplicação de bate-papo por vídeo.

Implementações mais avançadas também são possíveis. Por exemplo, suponha que o aplicativo forneça um atributo role ao criar o token com CreateParticipantToken. A aplicação pode usar a propriedade attributes em StageParticipantInfo para se inscrever, de forma seletiva, como participante com base nos recursos fornecidos pelo servidor:

const strategy = { shouldSubscribeToParticipant(participant) { switch (participant.attributes.role) { case 'moderator': return SubscribeType.NONE; case 'guest': return SubscribeType.AUDIO_VIDEO; default: return SubscribeType.NONE; } } // . . . other strategies properties }

Isso pode ser usado para criar um palco no qual os moderadores podem monitorar todos os convidados sem serem vistos ou ouvidos. A aplicação de host pode usar uma lógica de negócios adicional para permitir que os moderadores se vejam, mas permaneçam invisíveis para os convidados.

Configuração da assinatura de participantes

subscribeConfiguration(participant: StageParticipantInfo): SubscribeConfiguration

Se um participante remoto estiver fazendo uma assinatura (consulte Assinatura de participantes), o SDK consultará a aplicação host sobre uma configuração de assinatura personalizada para esse participante. Essa configuração é opcional e permite que a aplicação host controle certos aspectos do comportamento do assinante. Para obter informações sobre o que pode ser configurado, consulte SubscribeConfiguration na documentação de referência do SDK.

Veja a seguir uma amostra de implementação:

const strategy = { subscribeConfiguration: (participant) => { return { jitterBuffer: { minDelay: JitterBufferMinDelay.MEDIUM } } // ... other strategy functions }

Essa implementação atualiza o atraso mínimo do buffer de instabilidade para todos os participantes assinantes para uma predefinição de MEDIUM.

Como com shouldSubscribeToParticipant, implementações mais avançadas são possíveis. As ParticipantInfo fornecidas podem ser usadas para atualizar seletivamente a configuração de assinatura para participantes específicos.

Recomendamos usar os valores padrão. Especifique a configuração personalizada somente se houver um comportamento específico que você queira alterar.

Publicação

shouldPublishParticipant(participant: StageParticipantInfo): boolean

Uma vez conectado ao palco, o SDK consulta a aplicação de host para visualizar se um determinado participante deve realizar uma publicação. Isso é invocado somente para participantes locais que têm permissão para realizar publicações com base no token fornecido.

Veja a seguir uma amostra de implementação:

const strategy = { shouldPublishParticipant: (participant) => { return true; } // . . . other strategies properties }

Isso é para uma aplicação de bate-papo por vídeo padrão na qual os usuários sempre desejam realizar publicações. Eles podem ativar e desativar o áudio e o vídeo para serem ocultados ou vistos/ouvidos instantaneamente. (Também é possível usar publicar/cancelar a publicação, mas isso é muito mais lento. Ativar/Desativar o áudio é preferível para casos de uso em que é desejável alterar a visibilidade com frequência.)

Como escolher streams para realizar publicações

stageStreamsToPublish(): LocalStageStream[];

Ao realizar publicações, isso é usado para determinar quais streams de áudio e de vídeo devem ser publicados. Isso será abordado com mais detalhes posteriormente em Publish a Media Stream.

Como atualizar a estratégia

A estratégia pretende ser dinâmica, ou seja, os valores retornados de qualquer uma das funções acima podem ser alterados a qualquer momento. Por exemplo, se a aplicação de host não desejar realizar publicações até que o usuário final toque em um botão, você poderá retornar uma variável de shouldPublishParticipant (algo como hasUserTappedPublishButton). Quando essa variável for alterada com base em uma interação do usuário final, chame stage.refreshStrategy() para sinalizar ao SDK que ele deve consultar a estratégia para obter os valores mais recentes, aplicando somente o que sofreu alterações. Se o SDK observa que o valor shouldPublishParticipant foi alterado, ele inicia o processo de publicação. Se as consultas do SDK e todas as funções retornarem o mesmo valor anterior, a chamada refreshStrategy não modificará o palco.

Se o valor de retorno de shouldSubscribeToParticipant for alterado de AUDIO_VIDEO para AUDIO_ONLY, a transmissão de vídeo será removida para todos os participantes com os valores retornados alterados, caso uma transmissão de vídeo tenha existido anteriormente.

Geralmente, o palco usa a estratégia para aplicar com mais eficiência a diferença entre as estratégias anteriores e atuais, sem que a aplicação de host precise se preocupar com todo o estado necessário para realizar o gerenciamento adequado. Por causa disso, pense na chamada stage.refreshStrategy() como uma operação barata, porque ela não faz nada a menos que a estratégia seja alterada.

Eventos

Uma instância Stage é um emissor de eventos. Ao usar stage.on(), o estado do palco é comunicado à aplicação de host. Geralmente, as atualizações na interface do usuário da aplicação de host podem ser totalmente apoiadas pelos eventos. Os eventos são os seguintes:

stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_SUBSCRIBE_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_REMOVED, (participant, streams) => {}) stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => {})

Para a maioria desses eventos, o correspondente ParticipantInfo é fornecido.

Não é esperado que as informações fornecidas pelos eventos impactem os valores de retorno da estratégia. Por exemplo, não se espera que o valor de retorno de shouldSubscribeToParticipant seja alterado quando STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED for chamado. Se a aplicação de host desejar inscrever um determinado participante, ele deverá retornar o tipo de inscrição desejado, independentemente do estado de publicação desse participante. O SDK é responsável por garantir que o estado desejado da estratégia seja acionado no momento correto com base no estado do palco.

Publicação de uma transmissão de mídia

Dispositivos locais, como microfones e câmeras, são recuperados usando as mesmas etapas descritas acima em Recuperar um MediaStream de um dispositivo. No exemplo, usamos MediaStream para criar uma lista de objetos LocalStageStream usados ​​para publicação pelo SDK:

try { // Get stream using steps outlined in document above const stream = await getMediaStreamFromDevice(); let streamsToPublish = stream.getTracks().map(track => { new LocalStageStream(track) }); // Create stage with strategy, or update existing strategy const strategy = { stageStreamsToPublish: () => streamsToPublish } }

Publicação de um compartilhamento de tela

Geralmente, as aplicações precisam publicar um compartilhamento de tela além da câmera da Web do usuário. A publicação de um compartilhamento de tela exige a criação de um token adicional para o palco, especificamente para a publicação da mídia do compartilhamento de tela. Use getDisplayMedia e restrinja a resolução a um máximo de 720p. Depois disso, as etapas são semelhantes à publicação de uma câmera no palco.

// Invoke the following lines to get the screenshare's tracks const media = await navigator.mediaDevices.getDisplayMedia({ video: { width: { max: 1280, }, height: { max: 720, } } }); const screenshare = { videoStream: new LocalStageStream(media.getVideoTracks()[0]) }; const screenshareStrategy = { stageStreamsToPublish: () => { return [screenshare.videoStream]; }, shouldPublishParticipant: (participant) => { return true; }, shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } } const screenshareStage = new Stage(screenshareToken, screenshareStrategy); await screenshareStage.join();

Exibição e remoção de participantes

Após a conclusão da inscrição, você recebe uma matriz de objetos StageStream por meio do evento STAGE_PARTICIPANT_STREAMS_ADDED. O evento também fornece informações do participante para ajudar na exibição de transmissões de mídia:

stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { const streamsToDisplay = streams; if (participant.isLocal) { // Ensure to exclude local audio streams, otherwise echo will occur streamsToDisplay = streams.filter(stream => stream.streamType === StreamType.VIDEO) } // Create or find video element already available in your application const videoEl = getParticipantVideoElement(participant.id); // Attach the participants streams videoEl.srcObject = new MediaStream(); streamsToDisplay.forEach(stream => videoEl.srcObject.addTrack(stream.mediaStreamTrack)); })

Quando um participante interrompe as publicações ou cancela a inscrição de uma transmissão, a função STAGE_PARTICIPANT_STREAMS_REMOVED é chamada com as transmissões que foram removidas. As aplicações de host devem usar isso como um sinal para remover a transmissão de vídeo do participante do DOM.

STAGE_PARTICIPANT_STREAMS_REMOVED é invocada para todos os cenários em que uma transmissão pode ser removida, incluindo:

  • Um participante remoto que interrompe as publicações.

  • Um dispositivo local que cancela a inscrição ou altera a inscrição de AUDIO_VIDEO para AUDIO_ONLY.

  • Um participante remoto que sai do palco.

  • Um participante local que sai do palco.

Como STAGE_PARTICIPANT_STREAMS_REMOVED é invocada para todos os cenários, nenhuma lógica de negócios personalizada é necessária para remover participantes da IU durante operações de saída remotas ou locais.

Ativação ou desativação do áudio para transmissões de mídia

Os objetos LocalStageStream têm uma função setMuted que controla se a transmissão é silenciada. Essa função pode ser chamada na transmissão antes ou depois de ser retornada da função de estratégia stageStreamsToPublish.

Importante: se uma nova instância de objeto LocalStageStream for retornada por stageStreamsToPublish após uma chamada para refreshStrategy, o estado mudo do novo objeto de transmissão será aplicado ao palco. Tenha cuidado ao criar novas instâncias LocalStageStream para garantir que o estado mudo esperado seja mantido.

Monitoramento do estado mudo da mídia do participante remoto

Quando os participantes alteram o estado mudo do vídeo ou do áudio, o evento STAGE_STREAM_MUTE_CHANGED é acionado com uma lista de transmissões que foram alteradas. Use a propriedade isMuted na StageStream para atualizar a IU adequadamente:

stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (stream.streamType === 'video' && stream.isMuted) { // handle UI changes for video track getting muted } })

Além disso, você pode consultar StageParticipantInfo para saber se o áudio ou o vídeo estão silenciados:

stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (participant.videoStopped || participant.audioMuted) { // handle UI changes for either video or audio } })

Obtenção de estatísticas WebRTC

Para obter as estatísticas WebRTC mais recentes para uma transmissão de publicação ou de inscrição, use getStats em StageStream. Este é um método assíncrono com o qual você pode recuperar estatísticas utilizando await ou encadeando uma promessa. O resultado é um RTCStatsReport, que corresponde a um dicionário que contém todas as estatísticas padrão.

try { const stats = await stream.getStats(); } catch (error) { // Unable to retrieve stats }

Otimização de mídia

É recomendável limitar as chamadas getUserMedia e getDisplayMedia para as seguintes restrições com a finalidade de obter a melhor performance:

const CONSTRAINTS = { video: { width: { ideal: 1280 }, // Note: flip width and height values if portrait is desired height: { ideal: 720 }, framerate: { ideal: 30 }, }, };

É possível restringir ainda mais a mídia por meio de opções adicionais passadas para o construtor LocalStageStream:

const localStreamOptions = { minBitrate?: number; maxBitrate?: number; maxFramerate?: number; simulcast: { enabled: boolean } } const localStream = new LocalStageStream(track, localStreamOptions)

No código acima:

  • minBitrate define uma taxa de bits mínima que o navegador deve usar. No entanto, um stream de vídeo de baixa complexidade pode impulsionar o codificador a diminuir a taxa de bits.

  • maxBitrate define uma taxa de bits máxima que o navegador não deve exceder para este stream.

  • maxFramerate define uma taxa de quadros máxima que o navegador não deve exceder para este stream.

  • A opção simulcast pode ser usada somente em navegadores baseados no Chromium. Ela possibilita o envio de três camadas de representação do stream.

    • Isso permite que o servidor escolha qual representação enviar aos outros participantes, com base em suas limitações de rede.

    • Quando simulcast é especificada junto com um valor maxBitrate e/ou maxFramerate, espera-se que a camada de representação mais alta seja configurada considerando esses valores, desde que maxBitrate não seja inferior ao valor de maxBitrate padrão para a segunda camada mais alta do SDK interno de 900 kbps.

    • Se maxBitrate for especificada com um valor muito inferior em comparação com o valor padrão da segunda camada mais alta, a simulcast será desabilitada.

    • A simulcast não pode ser ativada e desativada sem republicar a mídia por meio de uma combinação de shouldPublishParticipant retornar false, chamar refreshStrategy, fazer shouldPublishParticipant retornar true e chamar refreshStrategy novamente.

Obtenção de atributos do participante

Se você especificar atributos na solicitação de endpoint CreateParticipantToken, poderá visualizar os atributos nas propriedades StageParticipantInfo:

stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => { console.log(`Participant ${participant.id} info:`, participant.attributes); })

Tratamento de problemas de rede

Quando a conexão de rede do dispositivo local é perdida, o SDK tenta se reconectar internamente sem nenhuma ação do usuário. Em alguns casos, o SDK não obtém êxito e a ação do usuário é necessária.

De maneira geral, o estado do palco pode ser tratado por meio do evento STAGE_CONNECTION_STATE_CHANGED:

stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => { switch (state) { case StageConnectionState.DISCONNECTED: // handle disconnected UI break; case StageConnectionState.CONNECTING: // handle establishing connection UI break; case StageConnectionState.CONNECTED: // SDK is connected to the Stage break; case StageConnectionState.ERRORED: // SDK encountered an error and lost its connection to the stage. Wait for CONNECTED. break; })

Em geral, você pode ignorar um estado de erro encontrado após ingressar com êxito em um palco, pois o SDK tentará se recuperar internamente. Se o SDK reportar um estado de ERRORED e o estágio permanecer no estado CONNECTING por um longo período de tempo (por exemplo, 30 segundos ou mais), você provavelmente está desconectado da rede.

Transmissão do palco para um canal do IVS

Para transmitir um palco, crie uma sessão IVSBroadcastClient separada e, em seguida, siga as instruções usuais para transmissão com o SDK, descritas acima. A lista de StageStream expostas por meio de STAGE_PARTICIPANT_STREAMS_ADDED pode ser usada para recuperar as transmissões de mídia participantes que podem ser aplicadas à composição do fluxo de transmissão da seguinte forma:

// Setup client with preferred settings const broadcastClient = getIvsBroadcastClient(); stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { streams.forEach(stream => { const inputStream = new MediaStream([stream.mediaStreamTrack]); switch (stream.streamType) { case StreamType.VIDEO: broadcastClient.addVideoInputDevice(inputStream, `video-${participant.id}`, { index: DESIRED_LAYER, width: MAX_WIDTH, height: MAX_HEIGHT }); break; case StreamType.AUDIO: broadcastClient.addAudioInputDevice(inputStream, `audio-${participant.id}`); break; } }) })

Você também pode compor um palco e transmiti-lo para um canal de baixa latência do IVS para alcançar um público maior. Consulte Enabling Multiple Hosts on an Amazon IVS Stream no Guia do usuário do streaming de baixa latência do IVS.