Transmissões de publicação — Web

Depois de conectado a uma sessão, você pode publicar um stream que outros clientes conectados à sessão possam visualizar.

Este tópico inclui as seguintes seções:

Verificar se um cliente possui recursos de publicação

Depois de se conectar a uma sessão, você pode verificar se o cliente consegue publicar. Verifique o valor do capabilities.publish propriedade do Session objeto. Se estiver definido como 1, o cliente pode publicar:

if (session.capabilities.publish == 1) {
    // The client can publish. See the next section.
} else {
    // The client cannot publish.
    // You may want to notify the user.
}

Para publicar, o cliente deve se conectar à sessão com um token ao qual tenha sido atribuída uma função que permita a publicação. É necessário que haja uma câmera e um microfone conectados. Além disso, o ambiente do cliente deve permitir a publicação (consulte Compatibilidade com navegadores).

Além disso, a publicação só é compatível com páginas HTTPS.

Inicializando um Publisher

O OT.initPublisher() O método inicializa e retorna um objeto Publisher. O objeto Publisher representa a visualização de um vídeo que você publica:

var publisher;
var targetElement = 'publisherContainer';

publisher = OT.initPublisher(targetElement, null, function(error) {
  if (error) {
    // The client cannot publish.
    // You may want to notify the user.
  } else {
    console.log('Publisher initialized.');
  }
});

O OT.initPublisher() O método recebe três parâmetros:

  • targetElement— (Opcional) Define o elemento DOM que o vídeo do Publisher substitui.

  • properties— (Opcional) Um conjunto de propriedades que personalizam o Publisher. O properties O parâmetro também inclui opções para especificar um dispositivo de entrada de áudio e vídeo utilizado pelo editor (consulte Configurar a câmera e o microfone utilizados pelo editor). O properties Esse parâmetro também inclui opções para personalizar a aparência da visualização na página HTML (consulte Personalização da interface do usuário) e selecione se deseja publicar áudio e vídeo (consulte Publicação apenas de áudio ou vídeo). Para mais opções de editor, consulte a documentação do properties parâmetro do OT.initPublisher() método.

  • completionHandler— (Opcional) Um manipulador de conclusão que especifica se o editor foi instanciado com sucesso ou se ocorreu um erro.

Você pode passar esse objeto Publisher para o Session.publish() método para publicar um fluxo em uma sessão. Consulte Publicação de um stream.

Antes de ligar Session.publish(), você pode usar esse objeto Publisher para testar o microfone e a câmera conectados ao Publisher.

O insertMode propriedade do properties parâmetro do OT.initPublisher() O método especifica como o objeto Publisher será inserido no DOM HTML, em relação ao targetElement parâmetro. É possível definir esse parâmetro com um dos seguintes valores:

  • "replace" — O objeto Publisher substitui o conteúdo do targetElement. Essa é a configuração padrão.
  • "after" — O objeto Publisher é um novo elemento inserido após o targetElement no DOM HTML. (Tanto o Publisher quanto o targetElement têm o mesmo elemento pai.)
  • "before" — O objeto Publisher é um novo elemento inserido antes do targetElement no DOM HTML. (Tanto o Publisher quanto o targetElement têm o mesmo elemento pai.)
  • "append" — O objeto Publisher é um novo elemento adicionado como filho do targetElement. Se houver outros elementos filhos, o Publisher é anexado como o último elemento filho do targetElement.

Por exemplo, o código a seguir adiciona um novo objeto Publisher como filho de um publisherContainer Elemento DOM:

// Try setting insertMode to other values: "replace", "after", or "before":
var publisherProperties = {insertMode: "append"};
var publisher = OT.initPublisher('publisherContainer', publisherProperties, function (error) {
  if (error) {
    console.log(error);
  } else {
    console.log("Publisher initialized.");
  }
});

Detectar quando um cliente concedeu acesso à câmera e ao microfone

Antes que um objeto Publisher possa acessar a câmera e o microfone do cliente, o usuário deve conceder acesso a esses recursos. O objeto Publisher dispara eventos quando o usuário concede ou nega acesso à câmera e ao microfone:

publisher.on({
  accessAllowed: function (event) {
    // The user has granted access to the camera and mic.
  },
  accessDenied: function accessDeniedHandler(event) {
    // The user has denied access to the camera and mic.
  }
});

Além disso, um objeto Publisher dispara eventos quando é apresentada ao usuário a opção de permitir ou negar o acesso à câmera e ao microfone:

publisher.on({
  accessDialogOpened: function (event) {
    // The Allow/Deny dialog box is opened.
  },
  accessDialogClosed, function (event) {
    // The Allow/Deny dialog box is closed.
  }
});

A editora tem um accessAllowed propriedade que indica se um cliente possui (true) ou não (false) concedeu acesso à câmera e ao microfone.

Configurar a câmera e o microfone utilizados pelo editor

Você pode (opcionalmente) especificar um dispositivo de entrada de áudio e vídeo para o publisher utilizar. Ao chamar o OT.initPublisher() método, você pode (opcionalmente) definir o audioSource e videoSource propriedades do properties objeto passado para o OT.initPublisher() método.

Primeiro, use o OT.getDevices() método para enumerar os dispositivos disponíveis. A matriz de dispositivos é passada como o devices parâmetro do callback função passada para a OT.getDevices() método. Por exemplo, o código a seguir obtém uma lista de dispositivos de entrada de áudio e vídeo:

var audioInputDevices;
var videoInputDevices;
OT.getDevices(function(error, devices) {
  audioInputDevices = devices.filter(function(element) {
    return element.kind == "audioInput";
  });
  videoInputDevices = devices.filter(function(element) {
    return element.kind == "videoInput";
  });
  for (var i = 0; i < audioInputDevices.length; i++) {
    console.log("audio input device: ", audioInputDevices[i].deviceId);
  }
  for (i = 0; i < videoInputDevices.length; i++) {
    console.log("video input device: ", videoInputDevices[i].deviceId);
  }
});

Cada dispositivo listado por OT.getDevices() possui um ID exclusivo do dispositivo, definido como o deviceId propriedade. Você pode usar esses valores de ID do dispositivo como o audioSource e videoSource propriedades do properties objeto passado para OT.initPublisher():

var pubOptions =
  {
    audioSource: audioInputDevices[0].deviceId,
    videoSource: videoInputDevices[0].deviceId
  };
var publisher = OT.initPublisher(null, pubOptions, function(error) {
  console.log("OT.initPublisher error: ", error);
});

Defina o videoSource propriedade para null ou false em uma sessão apenas de voz (consulte Publicação em uma sessão de voz).

O Componente de configuração de hardware do OpenTok oferece uma interface de usuário para que os clientes selecionem a câmera e o microfone a serem utilizados. Ela foi desenvolvida utilizando o OT.getDevices() método.

Observe que você também pode publicar uma transmissão de compartilhamento de tela — na qual a fonte é a tela do cliente, e não uma câmera. Para mais detalhes, consulte Compartilhamento de tela.

Você também pode alterar a câmera utilizada pelo editor, ou configure-o para usar o câmera frontal ou traseira (quando essa opção estiver disponível).

Você também pode alterar a fonte de áudio utilizada pelo editor.

Usando a câmera frontal ou traseira

Ao inicializar um editor, é possível definir o facingMode propriedade do objeto de opções que você passa para o OT.initPublisher(). Por exemplo, você pode definir a propriedade como "user" (câmera frontal) ou "environment" (câmera traseira), quando essa opção estiver disponível no sistema do cliente. (Geralmente, essas opções estão disponíveis apenas em dispositivos móveis.)

Se você definir o facingMode opção, faça não definir o videoSource propriedade.

Lembrando-se da seleção da câmera e do microfone

Por motivos de segurança nas páginas carregadas via HTTP, todos os navegadores sempre solicitam que o usuário selecione a câmera e o microfone a serem usados para transmitir um vídeo.

Em páginas carregadas por HTTPS no Chrome, a seleção da câmera e do microfone do usuário é lembrada e reutilizada em visitas subsequentes a uma página carregada a partir do mesmo domínio HTTPS.

Em páginas carregadas via HTTPS no Firefox, o usuário tem a opção de salvar as configurações da câmera e do microfone (em visitas subsequentes a uma página carregada do mesmo domínio HTTPS) ao selecionar os dispositivos.

Em páginas carregadas via HTTPS no IE, é possível usar a seleção anterior de câmera e microfone do usuário, feita em acessos anteriores ao mesmo domínio HTTPS (se houver), definindo o usePreviousDeviceSelection propriedade para true nas opções que você passa para o OT.initPublisher() método:

var pubOptions = {usePreviousDeviceSelection: true};
var publisher = OT.initPublisher(null, pubOptions, function(error) {
  console.log("OT.initPublisher error: ", error);
});

Para solicitar que o usuário selecione a câmera e o microfone a serem usados no IE (e ignorar as seleções anteriores de dispositivos), faça o seguinte: não definir o usePreviousDevices propriedade nas opções que você passa para o OT.initPublisher() método (ou defina-o como false, o padrão).

Desativando o gerenciamento do dispositivo de entrada de áudio padrão

Por padrão, o SDK lida automaticamente com a troca do dispositivo de entrada de áudio caso um novo seja conectado. Esse comportamento pode não ser o desejado por alguns usuários finais que gostariam de manter a seleção do microfone atual.

Como usuário avançado do SDK, você pode desativar o gerenciamento automático dos dispositivos de entrada de áudio. Para isso, basta definir o disableAudioInputDeviceManagement propriedade das opções passadas para o OT.initPublisher() método:

var pubOptions = {disableAudioInputDeviceManagement: true};
var publisher = OT.initPublisher(null, pubOptions, function(error) {
  console.log("Publishing a stream");
});

Observação: Este é um recurso avançado. Se você ativar essa opção, o dispositivo de entrada de áudio utilizado pelo SDK não vai ser atualizado quando o usuário final trocar o microfone.

Publicação de um stream

Depois de criar um objeto Publisher (Veja Inicializando um editor), você pode passá-lo para o publish() método de um objeto Session para publicar um fluxo na sessão:

    publisher = OT.initPublisher('replacementElementId');
    session.publish(publisher, function(error) {
      if (error) {
        console.log(error);
      } else {
        console.log('Publishing a stream.');
      }
    });

O segundo parâmetro é uma função de manipulador de conclusão à qual é passado um objeto de erro caso a publicação falhe. Caso contrário, a função de manipulador de conclusão é chamada sem que nenhum erro seja passado a ela.

Este código pressupõe que session é um objeto Session e que o cliente se conectou à sessão. Para obter mais informações, consulte Participar de uma sessão.

O objeto Publish dispara um streamCreated evento quando a transmissão para a sessão é iniciada:

var publisher = OT.initPublisher();
session.publish(publisher, function(error) {
  if (error) {
    console.log(error);
  } else {
    console.log('Publishing a stream.');
  }
});
publisher.on('streamCreated', function (event) {
    console.log('The publisher started streaming.');
});

O objeto Publisher possui um element propriedade, que é definida como o elemento DOM HTML que a contém.

Impedir que um editor transmita para uma sessão

Você pode impedir que o editor transmita para a sessão chamando a função unpublish() método do objeto Session:

    session.unpublish(publisher);

Observe que é possível interromper individualmente o envio de vídeo ou áudio (mesmo continuando a transmissão). Para obter mais informações, consulte Ajustando o áudio e o vídeo.

Detectar quando um stream publicado sai de uma sessão

O objeto Publisher dispacha um streamDestroyed evento quando a transmissão para a sessão é interrompida:

var publisher = OT.initPublisher();
session.publish(publisher);
publisher.on("streamDestroyed", function (event) {
  console.log("The publisher stopped streaming. Reason: "
    + event.reason);
});

O streamDestroyed O evento é definido pela classe StreamEvent. O evento inclui um reason propriedade, que explica por que a transmissão foi encerrada. Esses motivos incluem "clientDisconnected", "forceDisconnected", "forceUnpublished", ou "networkDisconnected". Para mais detalhes, consulte StreamEvent.

Por padrão, quando um Publisher despacha o streamDestroyed evento, o Publisher é destruído e removido do DOM HTML. É possível impedir esse comportamento padrão chamando o preventDefault() método do objeto StreamEvent:

publisher.on("streamDestroyed", function (event) {
    event.preventDefault();
    console.log("The publisher stopped streaming.");
});

Talvez você queira impedir o comportamento padrão e manter o Publisher, caso deseje reutilizar o objeto Publisher para publicar novamente na sessão.

A editora também envia um destroyed evento que ocorre quando o objeto é removido do DOM HTML. Em resposta a esse evento, você pode optar por ajustar (ou remover) os elementos do DOM relacionados ao editor que foi removido.

Definindo a resolução de vídeo de uma transmissão

Para definir uma resolução de vídeo recomendada para uma transmissão publicada, defina o resolution propriedade do properties parâmetro que você passa para o OT.initPublisher() método:

var publisherProperties = {resolution: '1280x720'};
var publisher = OT.initPublisher(targetElement,
                                 publisherProperties);
publisher.on('streamCreated', function(event) {
   console.log('Stream resolution: ' +
     event.stream.videoDimensions.width +
     'x' + event.stream.videoDimensions.height);
});

Isso resolution A propriedade é uma string que define a resolução desejada do vídeo. O formato da string é "_width_x_height_", em que a largura e a altura são representadas em pixels. Os valores válidos são "1920x1080", "1280x720", "640x480", e "320x240".

A resolução solicitada para uma transmissão de vídeo é definida como a videoDimensions.width e videoDimensions.height propriedades do objeto Stream.

A resolução padrão para uma transmissão (caso você não especifique uma resolução) é de 640x480 pixels. Se o sistema do cliente não for compatível com a resolução solicitada, a transmissão utilizará a próxima configuração maior compatível.

O videoHeight() e videoWidth() Esses métodos retornam a resolução configurada do objeto Publisher. A resolução real de um fluxo de vídeo do Subscriber é retornada pelo videoWidth() e videoHeight() métodos do objeto Subscriber. Esses valores podem diferir dos valores do resolution propriedade passada como o properties propriedade do OT.initPublisher() método, caso o navegador de publicação não seja compatível com a resolução solicitada.

Observação: Veja o Guia para desenvolvedores sobre 1080p para considerações sobre o uso da resolução 1080p.

Configurando a taxa de quadros de uma transmissão

Para definir uma taxa de quadros recomendada para uma transmissão publicada, defina o frameRate propriedade do properties parâmetro que você passa para o OT.initPublisher() método:

var publisherProperties = {frameRate: 7};
var publisher = OT.initPublisher(targetElement,
                                 publisherProperties);
publisher.on('streamCreated', function(event) {
   console.log('Frame rate: ' + event.stream.frameRate);
});

Defina o valor para a taxa de quadros desejada, em quadros por segundo, do vídeo. Os valores válidos são 30, 15, 7 e 1.

Se o editor especificar uma taxa de quadros, a taxa de quadros real do fluxo de vídeo é definida como a frameRate propriedade do objeto Stream, embora a taxa de quadros real varie de acordo com as condições variáveis da rede e do sistema. Se você não especificar uma taxa de quadros ao chamar OT.initPublisher, essa propriedade não está definida.

Para sessões que utilizam o OpenTok Media Router (sessões com o modo de mídia (configurado como “routed”), reduzir a taxa de quadros diminui proporcionalmente a largura de banda máxima que o stream pode utilizar. No entanto, durante a sessão com o modo de mídia Quando configurado para retransmissão, a redução da taxa de quadros não diminui a largura de banda da transmissão.

Você também pode restringir a taxa de quadros do stream de vídeo de um assinante. Para obter mais informações, consulte Restrição da taxa de quadros de uma transmissão assinada.

Definindo a taxa de bits máxima para uma transmissão

É possível definir a taxa de bits máxima para uma transmissão publicada. Definir a taxa de bits máxima pode ajudar a reduzir o consumo de largura de banda quando um usuário se conecta por meio de uma conexão com limite de tráfego. Consulte esta documentação.

Exclusão de um editor

É possível excluir um Publisher chamando seu destroy() método:

    publisher.destroy();

Chamando o destroy() O método exclui o objeto Publisher e o remove do DOM HTML.

Como obter estatísticas sobre o stream de um editor

O Publisher.getStats() O método fornece uma matriz de objetos que definem as estatísticas atuais de áudio e vídeo do editor. Para um editor em uma sessão roteada (aquela que utiliza o Roteador de mídia OpenTok), esse array inclui um objeto, que define as estatísticas do único fluxo de áudio e vídeo enviado ao OpenTok Media Router. Em uma sessão retransmitida, o array inclui um objeto para cada assinante do fluxo publicado.

Para obter estatísticas detalhadas de baixo nível sobre as conexões entre pares, use o Publisher.getRtcStatsReport() método. Ele retorna uma promessa que, em caso de sucesso, é resolvida com um array de RtcStatsReport objetos.

Consulte Guia do desenvolvedor sobre observabilidade do cliente para obter informações detalhadas.

Testando a transmissão de um emissor

Você pode publicar uma transmissão de teste e verificar suas estatísticas de áudio e vídeo para determinar o tipo de transmissão (como alta resolução ou somente áudio) compatível com a sua conexão.

Para obter estatísticas de um stream publicado pelo cliente local, é necessário usar uma sessão que utilize o OpenTok Media Router (sessões com o modo de mídia definido como “roteado”), e você deve definir o testNetwork propriedade para true no options objeto que você passa para o Session.subscribe() método. Em seguida, você pode usar o getStats() método do objeto Subscriber para obter estatísticas de áudio e vídeo da transmissão que você publica. Consulte esse assunto para mais informações.

O teste-da-rede-opentok O repositório inclui um código de exemplo que mostra como utilizar as estatísticas de um fluxo de teste antes de publicá-lo em uma sessão.

Publicação de vídeo a partir de uma fonte de vídeo que não seja uma câmera ou uma tela

Você pode definir a fonte de vídeo de um Publisher como um vídeo MediaStreamTrack objeto. Isso permite que você faça o seguinte:

  • Publique o vídeo usando um elemento HTML Canvas como o vídeo. Você pode ligar para o captureStream() método do HTMLCanvasElement objeto e chamar o getVideoTracks() método do resultado CanvasCaptureMediaStream objeto para obter um objeto MediaStreamTrack de vídeo. Para um exemplo básico, consulte o exemplo Publish-Canvas repositório opentok-web-samples no GitHub.

  • Publicar vídeo a partir de um elemento “Vídeo”. Ligue para o captureStream() método de um HTMLVideoElement objeto para obter um objeto MediaStream. O getVideoTracks() O método do objeto MediaStream retorna uma matriz de objetos MediaStreamTrack de áudio (geralmente, apenas um). Você pode então usar o objeto MediaStreamTrack como o audioSource propriedade do options objeto que você passa para o OT.initPublisher() método. Para um exemplo básico, consulte o exemplo “Publish-Video” repositório opentok-web-samples no GitHub.

Você pode usar um objeto MediaStreamTrack de vídeo como o videoSource propriedade do options objeto que você passa para o OT.initPublisher() método. Isso faz com que o vídeo representado pelo objeto MediaStreamTrack seja a fonte de vídeo para a transmissão publicada.

Transmissão de áudio a partir de uma fonte de áudio que não seja um microfone

É possível definir a fonte de áudio de um Publisher como um arquivo de áudio MediaStreamTrack objeto. Isso permite que você faça o seguinte:

  • Publicar áudio a partir de um elemento de áudio ou vídeo. Ligue para o captureStream() método de um HTMLAudioElement objeto ou um HTMLVideoElement objeto para obter um objeto MediaStream. O getAudioTracks() O método do objeto MediaStream é uma matriz de objetos MediaStreamTrack de áudio (geralmente, apenas um). Você pode então usar o objeto MediaStreamTrack como o audioSource propriedade do options objeto que você passa para o OT.initPublisher() método.
  • Publicar áudio a partir de um objeto MediaStreamTrack de áudio. Por exemplo, você pode usar o AudioContext objeto e o API de áudio da Web para gerar áudio dinamicamente. Em seguida, você pode chamar createMediaStreamDestination().stream.getAudioTracks()[0] no objeto AudioContext para obter o objeto MediaStreamTrack de áudio a ser usado como o audioSource propriedade do options objeto que você passa para o OT.initPublisher() método. Para um exemplo básico, consulte o exemplo “Stereo-Audio” repositório opentok-web-samples no GitHub.

Aplicação de filtros e efeitos a arquivos de áudio e vídeo publicados

É possível aplicar filtros e efeitos, como substituição ou desfoque do fundo, ao áudio ou vídeo obtido de um microfone ou câmera utilizada como fonte de áudio ou vídeo para uma transmissão publicada — consulte esse assunto.

Definição de dicas de conteúdo de vídeo para melhorar o desempenho do vídeo em determinadas situações

É possível definir uma dica de conteúdo de vídeo para melhorar a qualidade e o desempenho de um vídeo publicado. Isso pode ser útil em determinadas situações:

  • Ao publicar um vídeo de compartilhamento de tela que conterá principalmente texto ou conteúdo de vídeo.
  • Ao usar uma fonte de vídeo de câmera, se você preferir reduzir a taxa de quadros e manter a resolução, pode definir a sugestão de conteúdo como “texto” ou “detalhe”. Em uma sessão roteada, se o editor for compatível com o uso de vídeo escalável, ele enviará um fluxo em resolução total com baixa taxa de quadros e — se as condições da rede permitirem — um fluxo em resolução total com taxa de quadros normal. O OpenTok Media Router encaminhará um desses fluxos aos assinantes.

Isso indica ao navegador que utilize métodos de codificação ou processamento mais adequados ao tipo de conteúdo que você especificar.

Defina a dica de conteúdo de vídeo inicial para uma transmissão configurando o videoContentHint propriedade das opções que você passa para o OT.initPublisher() método:

var publisherOptions = {
  videoContentHint: "text",
  // other options, such as videoSource: "screen"
};
var publisher = OT.initPublisher(targetElement, publisherOptions, callbackFunction);

É possível alterar a dica de conteúdo do vídeo dinamicamente chamando a função setVideoContentHint() método de um objeto Publisher:

publisher.setVideoContentHint("motion");

Você pode definir a dica de conteúdo de vídeo como um dos seguintes valores:

  • "" — Não é fornecida nenhuma indicação (configuração padrão). O cliente de publicação fará a melhor estimativa possível sobre como o conteúdo de vídeo deve ser tratado.
  • "motion" — A trilha deve ser tratada como se contivesse vídeo em que o movimento seja importante. Por exemplo, você pode usar essa configuração para uma transmissão de vídeo com compartilhamento de tela que contenha vídeo.
  • "detail" — A trilha deve ser tratada como se os detalhes do vídeo fossem extremamente importantes. Por exemplo, você pode usar essa configuração para uma transmissão de vídeo com compartilhamento de tela que contenha texto, pinturas ou desenhos a linha.
  • "text" — A faixa deve ser tratada como se os detalhes do texto fossem extremamente importantes. Por exemplo, você pode usar essa configuração para uma transmissão de vídeo com compartilhamento de tela que contenha conteúdo de texto.

Com as dicas de conteúdo “text” e “detailed”, o navegador tenta manter uma alta resolução, mesmo que precise reduzir a taxa de quadros do vídeo. No caso da dica de conteúdo “motion”, o navegador reduz a resolução para evitar que a taxa de quadros fique lenta.

Você pode ler mais sobre essas opções no Rascunho de Trabalho do W3C.

O Chrome 60+, o Safari 12.1+, o Edge 79+, o Opera 47+, as versões mais recentes do Samsung Internet, o WebView no Android 70+ e o WebView no iOS 12.2+ oferecem suporte a dicas de conteúdo de vídeo. Essa configuração é ignorada em outros navegadores.

Se você não se importar com uma taxa de quadros baixa, também pode considerar restringir a taxa de quadros das transmissões assinadas para melhorar a qualidade.

Opção alternativa de áudio do editor

Consulte o guia do desenvolvedor para opção alternativa de áudio . O recurso de fallback de áudio do editor oferece monitoramento aprimorado de largura de banda e qualidade para melhorar as comunicações.

Outras opções de áudio e vídeo

Consulte o guia do desenvolvedor para Ajustando o áudio e o vídeo.

Melhores práticas na publicação

Esta seção traz dicas para publicar transmissões com sucesso.

Permitir o acesso ao dispositivo

É recomendável informar aos usuários que será solicitado que eles permitam o acesso à câmera e ao microfone. Constatamos que, de longe, a maior parte das falhas na publicação ocorre porque os usuários clicam no botão “recusar” ou simplesmente não clicam no botão “permitir”. Fornecemos a você todos os eventos necessários para orientar seus usuários nesse processo:

publisher.on({
  accessDialogOpened: function (event) {
    // Show allow camera message
    pleaseAllowCamera.style.display = 'block';
  },
  accessDialogClosed: function (event) {
    // Hide allow camera message
    pleaseAllowCamera.style.display = 'none';
  }
});

Também é uma boa ideia hospedar seu site por SSL. Isso porque o Chrome exige que os usuários cliquem para permitir o acesso aos dispositivos apenas uma vez por domínio, desde que esse domínio seja hospedado por SSL. Isso significa que seus usuários (se estiverem no Chrome) não precisam lidar com aquela caixa de diálogo inconveniente de permissão/recusa toda vez que carregarem a página.

Separar OT.initPublisher() e Session.publish()

Outra coisa que recomendamos é dividir o OT.initPublisher() e Session.publish() etapas. Isso agiliza o tempo de conexão inicial, pois você se conecta à sessão enquanto aguarda que o usuário clique no botão “Permitir”. Portanto, em vez de:

session.connect(token, function (err) {
{... your error handling code ...}
if (!err) {
    var publisher = OT.initPublisher();
    session.publish(publisher);
  }
});

Mova o OT.initPublisher() siga este passo antes de se conectar, conforme mostrado a seguir:

var publisher = OT.initPublisher();
session.connect(token, function (err) {
{... your error handling code ...}
  if (!err) {
    session.publish(publisher);
  }
});

Resolução e taxa de quadros

É possível definir a resolução e a taxa de quadros do Publisher ao inicializá-lo:

OT.initPublisher(divId, {
  resolution: '320x240',
  frameRate: 15
});

Por padrão, a resolução de um Publisher é 640x480, mas você também pode defini-la como 1920x1080, 1280x720 ou 320x240. É recomendável tentar ajustar a resolução ao tamanho em que o vídeo será exibido. Se você estiver exibindo o vídeo apenas em 320x240 pixels, não faz sentido transmitir em 1280x720 ou 1920x1080. Reduzir a resolução pode economizar largura de banda e diminuir o congestionamento e as quedas de conexão.

Por padrão, a taxa de quadros do vídeo é de 30 quadros por segundo, mas você também pode defini-la para 15, 7 ou 1. Reduzir a taxa de quadros pode diminuir a largura de banda necessária. Vídeos com resolução menor podem ter uma taxa de quadros mais baixa sem que haja uma diferença perceptível para o usuário. Portanto, se você estiver usando uma resolução baixa, talvez seja interessante considerar o uso de uma taxa de quadros baixa também.

Para obter mais informações, consulte a documentação sobre OT.initPublisher().

Solução de problemas

Siga as dicas desta seção para evitar problemas de conectividade ao publicar. Para obter informações gerais sobre solução de problemas, consulte Depuração — Web.

Tratamento de erros

Existem métodos de retorno de chamada para ambos Session.publish() e OT.initPublisher(). Recomendamos tratar as respostas de erro desses dois métodos. Conforme mencionado anteriormente, é melhor dividir essas etapas e chamar OT.initPublisher() antes de iniciar a conexão com sua sessão. Além disso, o tratamento de erros fica mais fácil se você não chamar esses dois métodos ao mesmo tempo. Isso porque ambos os manipuladores de erros serão acionados caso ocorra algum erro na publicação. É melhor aguardar até que OT.initPublisher() para concluir e Session.connect() preencher e, em seguida, ligar Session.publish(). Dessa forma, você pode lidar com todos os problemas relacionados ao hardware no OT.initPublisher() callback e todos os problemas relacionados à rede no Session.publish() chamada de retorno.

var connected = false,
  publisherInitialized = false;

var publisher = OT.initPublisher(function(err) {
  if (err) {
    // handle error
  } else {
    publisherInitialized = true;
    publish();
  }
});

var publish = function() {
  if (connected && publisherInitialized) {
    session.publish(publisher);
  }
};

session.connect(token, function(err) {
  if (err) {
    // handle error
  } else {
    connected = true;
    publish();
  }
});

Acesso negado

O maior número de casos em que não se conseguiu OT.initPublisher() são resultado da recusa do usuário final em conceder acesso à câmera e ao microfone. Isso pode ser resolvido monitorando o accessDenied evento ou ao detectar uma resposta de erro no método OT.initPublisher() com um code propriedade definida como 1500 e um message propriedade definida como “Acesso do editor negado:”. Recomendamos que você trate dessa situação e exiba uma mensagem ao usuário indicando que ele deve tentar publicar novamente e permitir o acesso à câmera.

publisher.on({
  'accessDenied': function() {
    showMessage('Please allow access to the Camera and Microphone and try publishing again.');
  }
});

Acesso ao dispositivo

Outra razão para OT.initPublisher() A falha ocorre quando o OpenTok não consegue acessar uma câmera ou um microfone. Isso pode acontecer se não houver uma câmera ou um microfone conectado ao computador, se houver algum problema com o driver da câmera ou do microfone, ou se algum outro aplicativo estiver usando a câmera ou o microfone (isso só ocorre no Windows). Você pode tentar minimizar a ocorrência desses problemas usando nosso Componente de Configuração de Hardware ou ligando para o OT.getDevices() método diretamente. No entanto, você também deve tratar qualquer erro ao chamar OT.initPublisher() porque ainda pode dar algo errado. Por exemplo, o usuário pode ter negado o acesso à câmera ou ao microfone. Nesse caso, o error.name a propriedade está definida como "OT_USER_MEDIA_ACCESS_DENIED":

publisher = OT.initPublisher('publisher', {}, function (err) {
  if (err) {
    if (err.name === 'OT_USER_MEDIA_ACCESS_DENIED') {
      // Access denied can also be handled by the accessDenied event
      showMessage('Please allow access to the Camera and Microphone and try publishing again.');
    } else {
      showMessage('Failed to get access to your camera or microphone. Please check that your webcam'
        + ' is connected and not being used by another application and try again.');
    }
    publisher.destroy();
    publisher = null;
  }
});

Erros de rede

As outras causas de falhas na publicação geralmente se devem a algum tipo de falha na rede. Lidamos com elas na função de retorno de chamada para Session.publish(). Se o usuário não estiver conectado à rede, é passado à função de retorno de chamada um objeto de erro com o name propriedade definida como "OT_NOT_CONNECTED". Se o usuário estiver em uma conexão de rede muito restritiva que não permita conexões WebRTC, o Publisher não conseguirá se conectar, e o elemento Publisher exibirá apenas uma roda giratória. Esse erro tem um name propriedade definida como "OT_CREATE_PEER_CONNECTION_FAILED". Nesse caso, recomendamos que você exiba uma mensagem ao usuário informando que a publicação não foi bem-sucedida e que ele deve verificar sua conexão de rede. O tratamento desses erros é feito da seguinte forma:

session.publish(publisher, function(err) {
  if (err) {
    switch (err.name) {
      case "OT_NOT_CONNECTED":
        showMessage("Publishing your video failed. You are not connected to the internet.");
        break;
      case "OT_CREATE_PEER_CONNECTION_FAILED":
        showMessage("Publishing your video failed. This could be due to a restrictive firewall.");
        break;
      default:
        showMessage("An unknown error occurred while trying to publish your video. Please try again later.");
    }
    publisher.destroy();
    publisher = null;
  }
});

Perda de conectividade

Seu Publisher também pode perder a conexão depois de já ter conseguido se conectar. Na maioria das vezes, isso também fará com que a sessão perca a conexão, mas nem sempre é assim. Você pode lidar com a desconexão do Publisher monitorando o streamDestroyed evento com um reason propriedade definida como “networkDisconnected” desta forma:

publisher.on({
  streamDestroyed: function (event) {
    if (event.reason === 'networkDisconnected') {
      showMessage('Your publisher lost its connection. Please check your internet connection and try publishing again.');
    }
  }
});

Juntando tudo isso

O código a seguir cria um publisher e se conecta a uma sessão (consulte Noções básicas sobre sessões), publica um fluxo na sessão quando o cliente se conecta a ela e detecta quando o emissor inicia e interrompe a transmissão:

var session;
var publisher;

// Replace with the replacement element ID:
publisher = OT.initPublisher(replacementElementId);
publisher.on({
  streamCreated: function (event) {
    console.log("Publisher started streaming.");
  },
  streamDestroyed: function (event) {
    console.log("Publisher stopped streaming. Reason: "
      + event.reason);
  }
});

// Replace apiKey and sessionID with your own values:
session = OT.initSession(apiKey, sessionID);
// Replace token with your own value:
session.connect(token, function (error) {
  if (session.capabilities.publish == 1) {
    session.publish(publisher);
  } else {
    console.log("You cannot publish an audio-video stream.");
  }
});

Implementação de tentativas de reenvio de publicação de sessão

Falhas temporárias na publicação são um padrão conhecido e recorrente no SDK JS da Video API, especialmente em navegadores móveis. Quando session.publish() Se falhar, o SDK retorna um erro por meio do callback do manipulador de conclusão. A abordagem recomendada é implementar uma lógica de repetição de tentativa no nível do aplicativo, com um intervalo entre as tentativas.

Observação: Suporte integrado para repetição de tentativas para session.publish() está no roteiro do SDK. Até que isso seja lançado, você precisará implementar isso por conta própria.

Como session.publish() Obras

session.publish() pode ser chamado de duas maneiras:

  • Com um publisher pré-inicializado: session.publish(publisher, callback) — você liga OT.initPublisher() primeiro, depois passe a instância do publisher resultante para session.publish(). Este é o abordagem recomendada uma vez que separa a aquisição de mídia da criação do fluxo, tornando o tratamento de erros mais claro.
  • Sem uma instância de editor: session.publish(targetElement, options, callback) — o SDK chama internamente OT.initPublisher() para você. Nesse caso, tanto os erros de aquisição de mídia quanto os erros de criação de fluxo são exibidos por meio de um único session.publish() chamada de retorno.

Melhores práticas: Divisão OT.initPublisher() e session.publish() em etapas separadas. Isso permite que você lide com erros de hardware/mídia no OT.initPublisher() erros de callback e de rede/sinalização no session.publish() callback — tornando a lógica de repetição de tentativas significativamente mais simples e direcionada.

// Recommended: split initialization from publishing
let publisherReady = false;
let sessionConnected = false;

const publisher = OT.initPublisher('publisher-container', publisherOptions, (err) => {
  if (err) {
    handleInitPublisherError(err); // hardware/media errors — see OT.initPublisher() errors below
    return;
  }
  publisherReady = true;
  maybePublish();
});

session.connect(token, (err) => {
  if (err) { /* handle connection error */ return; }
  sessionConnected = true;
  maybePublish();
});

function maybePublish() {
  if (sessionConnected && publisherReady) {
    publishWithRetry(session, publisher);
  }
}

Por que ocorrem falhas na publicação

As causas mais comuns para falhas transitórias na publicação são:

  • Tempo limite do StreamCreateRequest (erro 1500): O editor não conseguiu concluir a criação do fluxo em um prazo razoável — o que geralmente é causado por atrasos na rede durante a negociação do ICE/SDP.
  • mediaStopped eventos durante o fluxo de publicação, onde o acesso ao dispositivo de mídia pode ser interrompido.
  • Reutilização do objeto Publisher sem limpeza adequada — reutilizar uma instância de publisher inicializada com restrições diferentes sem chamar unpublish e reinicializando.
  • OT_NOT_CONNECTED — tentativa de publicação antes que a sessão esteja totalmente conectada.
  • OT_PERMISSION_DENIED — o token não possui a função de publicação (não pode ser repetido).

Erros provenientes de OT.initPublisher()

Ao pré-inicializar um publisher com OT.initPublisher(), todos os erros de hardware e de aquisição de mídia são encaminhados ao seu manipulador de conclusão — antes session.publish() seja chamada. Trate-os nessa função de retorno usando as ações específicas para cada tipo de erro abaixo: alguns exigem uma ação do usuário ou uma correção no código, enquanto erros transitórios de mídia podem ser resolvidos reinicializando o publisher.

Observação: Se você ligar session.publish() sem um publisher pré-inicializado, esses mesmos erros aparecerão por meio do session.publish() em vez disso, use uma função de retorno de chamada.

error.name Descrição Ação recomendada
OT_HARDWARE_UNAVAILABLE O hardware existe, mas não foi possível acessá-lo (por exemplo, porque está sendo usado por outro aplicativo). Solicite ao usuário que feche as outras aplicações em execução no dispositivo e, em seguida, ligue OT.initPublisher() novamente.
OT_INVALID_PARAMETER Um ou mais parâmetros passados para OT.initPublisher() eram inválidas. Corrija o objeto de opções passado para OT.initPublisher().
OT_MEDIA_ENDED O ended evento no elemento de vídeo disparado durante a inicialização. Reinicialize o editor.
OT_MEDIA_ERR_ABORTED A recuperação do fluxo para o elemento de vídeo foi interrompida. Reinicialize o editor após um breve intervalo.
OT_MEDIA_ERR_DECODE Ocorreu um erro de decodificação ao tentar reproduzir o stream no elemento de vídeo. Reinicialize o editor após um breve intervalo.
OT_MEDIA_ERR_NETWORK Um erro de rede fez com que a transmissão parasse de ser carregada. Reinicialize o editor após um breve intervalo.
OT_MEDIA_ERR_SRC_NOT_SUPPORTED Foi detectado que o stream não é adequado para reprodução. Verifique a configuração da fonte de vídeo/áudio do transmissor e reinicialize-o.
OT_NOT_SUPPORTED Algum elemento da solicitação de mídia do usuário não é compatível com o navegador. Informe o usuário e não tente novamente.
OT_NO_DEVICES_FOUND Não foram encontrados dispositivos de entrada de áudio ou vídeo. Solicite ao usuário que conecte um dispositivo antes de tentar novamente.
OT_NO_VALID_CONSTRAINTS Tanto o vídeo quanto o áudio estavam desativados — pelo menos um deles precisa estar ativado. Certifique-se de que publishAudio ou publishVideo é true nas opções do editor.
OT_PROXY_URL_ALREADY_SET_ERROR O proxyUrl já foi definida. Definir novamente não terá nenhum efeito. Defina a URL do proxy apenas uma vez, antes de inicializar qualquer objeto Session ou Publisher.
OT_REQUESTED_DEVICE_PERMISSION_DENIED O dispositivo de áudio solicitado não tem permissão para ser utilizado. Solicite ao usuário que conceda permissões para o dispositivo.
OT_USER_MEDIA_ACCESS_DENIED O usuário negou acesso à câmera, ao microfone ou à tela. Solicite ao usuário que permita o acesso nas configurações do navegador; não tente novamente automaticamente.
OT_SCREEN_SHARING_NOT_SUPPORTED O compartilhamento de tela não é compatível com o navegador atual. Informe o usuário e não tente novamente.
OT_UNABLE_TO_CAPTURE_SCREEN Foi solicitada o compartilhamento de tela, mas esse recurso não é compatível (por exemplo, videoSource definir como "screen", "application", ou "window"). Ligar OT.checkScreenSharingCapability() antes de inicializar um emissor de compartilhamento de tela.
OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED O compartilhamento de tela requer uma extensão de navegador, mas nenhuma foi registrada. Registre a extensão antes de ligar OT.initPublisher().
OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED O compartilhamento de tela requer uma extensão do navegador, mas ela não está instalada. Instrua o usuário a instalar a extensão necessária.
const publisher = OT.initPublisher('publisher-container', publisherOptions, (err) => {
  if (!err) {
    publisherReady = true;
    maybePublish();
    return;
  }

  // Hardware/media errors — handle before session.publish() is called
  switch (err.name) {
    case 'OT_REQUESTED_DEVICE_PERMISSION_DENIED':
      showMessage('Please allow access to your camera and microphone and try again.');
      break;
    case 'OT_HARDWARE_UNAVAILABLE':
    case 'OT_NO_DEVICES_FOUND':
      showMessage('Could not access your camera or microphone. Please check your devices.');
      break;
    case 'OT_SCREEN_SHARING_NOT_SUPPORTED':
    case 'OT_UNABLE_TO_CAPTURE_SCREEN':
    case 'OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED':
    case 'OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED':
      showMessage('Screen sharing is not available. Please check your browser settings.');
      break;
    default:
      showMessage('Could not initialize the publisher. Please try again.');
  }

  publisher.destroy();
});

Erros recuperáveis x erros não recuperáveis de session.publish()

Nem todos session.publish() nem todos os erros são iguais. Antes de implementar uma lógica de repetição de tentativa, é essencial classificar os erros corretamente — repetir a tentativa em caso de um erro irrecuperável desperdiça tempo, prejudica a experiência do usuário e pode mascarar falhas reais que exigem uma resposta diferente.

Observação: Código de erro 1500 está obsoleto como mecanismo de classificação. Sempre use o error.name propriedade para identificar erros programaticamente, uma vez que ela corresponde ao cenário específico de falha.

Observação: Quando session.publish() chama-se sem um publisher pré-inicializado, erros de aquisição de mídia provenientes de OT.initPublisher() (listados acima) também podem surgir por meio do session.publish() callback. Nesse caso, trate-os como não repetíveis e aplique o mesmo procedimento descrito acima.

Erros irrecuperáveis — Não tente novamente

Esses erros representam erros do programador, restrições rígidas de permissão ou contexto de chamada inválido. Repetir a tentativa não resolverá esses problemas. Em vez disso, exiba uma mensagem clara para o usuário ou corrija a lógica do aplicativo.

error.name Descrição Ação recomendada
OT_NOT_CONNECTED session.publish() foi chamada antes que a sessão fosse estabelecida. Certifique-se de que session.connect() tenha sido concluída com sucesso antes da publicação.
OT_PERMISSION_DENIED A função do token não permite a publicação (deve ser publisher ou moderator). Informe ao usuário que ele não possui permissões de publicação. Não tente novamente — gere um token com a função correta.
OT_INVALID_PARAMETER O editor fornecido é inválido, já foi publicado ou já está vinculado a outra sessão. Corrija a lógica do aplicativo: chame session.unpublish(publisher) antes de republicar ou criar um novo editor.
OT_USER_MEDIA_ACCESS_DENIED O usuário negou acesso à câmera ou ao microfone (ou à tela, no caso de transmissões com compartilhamento de tela). Solicite ao usuário que permita o acesso ao dispositivo nas configurações do navegador e tente novamente. Não tente novamente automaticamente.
OT_CHROME_MICROPHONE_ACQUISITION_ERROR O navegador não conseguiu acessar o microfone devido a um bug conhecido do navegador. O usuário final deve reiniciar o navegador e atualizar a página para resolver esse problema. Informe o usuário e não tente novamente.
OT_SCREEN_SHARING_NOT_SUPPORTED O compartilhamento de tela não é compatível com o navegador atual. Informe o usuário e não tente novamente.
OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED O compartilhamento de tela requer uma extensão de navegador, mas nenhuma foi registrada. Registre a extensão antes de tentar publicar uma transmissão de compartilhamento de tela.
OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED O compartilhamento de tela requer uma extensão do navegador, mas ela não está instalada. Instrua o usuário a instalar a extensão necessária.
OT_CONSTRAINTS_NOT_SATISFIED O navegador não conseguiu atender aos requisitos de mídia solicitados (resolução, taxa de quadros, dispositivo). Ajuste as restrições do editor e reinicialize.
OT_NO_VALID_CONSTRAINTS Tanto o vídeo quanto o áudio estavam desativados — pelo menos um deles precisa estar ativado. Certifique-se de que publishAudio ou publishVideo é true antes de ligar session.publish().
OT_NOT_SUPPORTED Algum elemento da solicitação de mídia do usuário não é compatível com o navegador. Informe o usuário e não tente novamente.
OT_STREAM_CREATE_FAILED O usuário tentou publicar em uma sessão com criptografia de ponta a ponta (E2EE) ativada sem especificar uma chave de criptografia; ou não foi possível criar o fluxo no modelo de servidor. Para sessões com criptografia de ponta a ponta (E2EE), certifique-se de que um segredo de criptografia esteja definido por meio de session.setEncryptionSecret() antes da publicação.
OT_INVALID_AUDIO_OUTPUT_SOURCE Foi fornecido um ID inválido do dispositivo de saída de áudio. Verify se o ID do dispositivo corresponde a um dispositivo de saída de áudio válido antes de tentar novamente.
OT_UNABLE_TO_CAPTURE_MEDIA Não foi possível capturar a mídia — ocorreu um erro desconhecido. Informe o usuário e solicite que ele verifique a disponibilidade do dispositivo.

Erros recuperáveis — É seguro tentar novamente

Esses erros são normalmente causados por condições transitórias da rede, tempo limite de sinalização ou indisponibilidade temporária da plataforma. Eles são o principal alvo da lógica de nova tentativa.

error.name Descrição Ação recomendada
OT_TIMEOUT (código 1500) session.publish() tempo limite esgotado — o StreamCreateRequest não foi concluída a tempo. Geralmente causada por atrasos na negociação entre ICE e SDP ou mediaStopped eventos. Tentar novamente com recuo exponencial (até 3 tentativas). Reutilizar a mesma instância do publisher, caso ela não tenha sido destruída.
OT_ICE_WORKFLOW_FAILED A negociação ICE falhou — não foi possível estabelecer a conexão entre pares. Isso costuma ocorrer temporariamente em redes restritivas. Tente novamente. Se o problema persistir após todas as tentativas, informe o usuário sobre um possível problema de rede ou firewall.
OT_CREATE_PEER_CONNECTION_FAILED Não foi possível estabelecer a conexão entre pares via WebRTC. Isso pode indicar um firewall restritivo ou um problema temporário na plataforma. Tente novamente. Se o problema persistir, exiba uma mensagem sugerindo que o usuário verifique sua conexão de rede.
OT_MEDIA_ERR_ABORTED / OT_MEDIA_ERR_NETWORK A aquisição da mídia foi cancelada ou interrompida devido a um erro de rede. Tente novamente após um breve intervalo.
OT_MEDIA_ERR_DECODE Ocorreu um erro de decodificação ao tentar reproduzir o stream no elemento de vídeo. Tente novamente após um breve intervalo. Se o problema persistir, o formato da mídia pode ser incompatível.
OT_MEDIA_ERR_SRC_NOT_SUPPORTED Foi detectado que o stream não é adequado para reprodução. Tente novamente uma vez. Se o problema persistir, verifique a configuração da fonte de vídeo/áudio do editor.
OT_SET_REMOTE_DESCRIPTION_FAILED A conexão WebRTC falhou durante setRemoteDescription. Normalmente, trata-se de um problema temporário de sinalização. Tente novamente com intervalo de espera. Se o problema persistir após todas as tentativas, informe o usuário sobre um possível problema de rede.
OT_UNEXPECTED_SERVER_RESPONSE O servidor retornou um erro inesperado. Tente novamente após um breve intervalo. Se o problema persistir, registre o erro e informe o usuário.

Erros que exigem uma ação diferente (não uma simples repetição da tentativa)

Alguns erros não podem ser resolvidos nem com uma simples nova tentativa nem com uma interrupção definitiva — eles exigem uma ação corretiva específica antes de se tentar novamente.

error.name Descrição Ação recomendada
OT_HARDWARE_UNAVAILABLE A câmera ou o microfone não estão disponíveis (por exemplo, estão sendo usados por outra aplicação ou estão desconectados). Solicite ao usuário que feche outras applications em execução no dispositivo e, em seguida, reinicialize o editor com OT.initPublisher() antes de tentar novamente.
OT_NO_DEVICES_FOUND Não foram encontrados dispositivos de entrada de áudio ou vídeo. Solicite ao usuário que conecte um dispositivo. Não tente novamente até que o usuário confirme que há um dispositivo disponível.

Resumindo: Padrão recomendado de nova tentativa com classificação de erros

async function publishWithRetry(session, publisher, attempt = 1) {
  const MAX_RETRIES = 3;
  const RETRY_DELAY_MS = 2000;

  const error = await new Promise((resolve) => {
    session.publish(publisher, resolve);
  });

  if (!error) {
    console.log('Publishing started successfully.');
    return;
  }

  // Non-recoverable: programmer error or hard permission constraint
  const nonRetryable = [
    'OT_NOT_CONNECTED',
    'OT_PERMISSION_DENIED',
    'OT_INVALID_PARAMETER',
    'OT_USER_MEDIA_ACCESS_DENIED',
    'OT_CHROME_MICROPHONE_ACQUISITION_ERROR',
    'OT_SCREEN_SHARING_NOT_SUPPORTED',
    'OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED',
    'OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED',
    'OT_CONSTRAINTS_NOT_SATISFIED',
    'OT_NO_VALID_CONSTRAINTS',
    'OT_NOT_SUPPORTED',
    'OT_STREAM_CREATE_FAILED',
    'OT_INVALID_AUDIO_OUTPUT_SOURCE',
    'OT_UNABLE_TO_CAPTURE_MEDIA',
  ];

  // Requires corrective action before retrying
  const requiresAction = [
    'OT_HARDWARE_UNAVAILABLE',
    'OT_NO_DEVICES_FOUND',
  ];

  if (nonRetryable.includes(error.name)) {
    console.error('Non-retryable error — user action or code fix required:', error.name);
    handleNonRecoverableError(error);
    return;
  }

  if (requiresAction.includes(error.name)) {
    console.warn('Device error — prompting user before retrying:', error.name);
    handleDeviceError(error);
    return;
  }

  // Recoverable: retry with backoff
  if (attempt < MAX_RETRIES) {
    console.warn(`Publish attempt ${attempt} failed (${error.name}), retrying...`);
    await delay(RETRY_DELAY_MS * attempt);
    await publishWithRetry(session, publisher, attempt + 1);
  } else {
    console.error('All publish attempts failed. Disconnecting user.');
    handlePublishFailure(session);
  }
}

function handleNonRecoverableError(error) {
  // Surface a meaningful message to the user based on error.name
  // e.g. for OT_USER_MEDIA_ACCESS_DENIED: "Please allow camera/mic access"
}

function handleDeviceError(error) {
  // Prompt the user to check their device, then allow them to retry manually
}

function handlePublishFailure(session) {
  session.disconnect();
}

Modo de uso:

const publisher = OT.initPublisher('publisher-container', publisherOptions);

// Wait for session to be connected before publishing
session.connect(token, (err) => {
  if (err) { /* handle connection error */ return; }
  publishWithRetry(session, publisher);
});

Importante: Limpeza do Publisher antes de tentar novamente

Na maioria dos cenários de falha — incluindo OT_TIMEOUT / OT_ICE_WORKFLOW_FAILED — a instância da editora pode ser reutilizado diretamente para o próximo session.publish() chamada. Não é necessário reinicializá-la.

Quando uma tentativa de publicação falha, o SDK encerra o fluxo que estava tentando criar e emite um streamDestroyed evento na editora com reason: "reset". Trata-se de uma limpeza esperada e isso não exigir que você reinicialize o editor — você pode tentar novamente com a mesma instância. Observe que "reset" é um motivo genérico do tipo “o stream do editor foi encerrado” (também é exibido quando você chama publisher.destroy()), portanto, considere-o como um sinal de limpeza, e não como um indicador específico de falha na publicação; use o error.name do session.publish() função de retorno para decidir se deve tentar novamente.

O SDK faz não tentar novamente automaticamente session.publish() em seu nome — a lógica de nova tentativa deve ser implementada no nível do aplicativo, conforme mostrado acima.

O único caso em que é necessário reinicializar o editor com OT.initPublisher() antes de tentar novamente é quando o próprio editor destroyed O evento é disparado. Esse evento é definitivo e indica que o próprio objeto editor não está mais disponível para uso.

publisher.on('destroyed', () => {
  // Publisher object is no longer usable — reinitialize before retrying
  publisher = OT.initPublisher('publisher-container', publisherOptions);
});

// A streamDestroyed event with reason 'reset' is emitted by the SDK when it tears
// down the stream (during a failed publish attempt, or when you call publisher.destroy()).
// A 'reset' during a failed publish does NOT require reinitializing the publisher.
publisher.on('streamDestroyed', (event) => {
  if (event.reason === 'reset') {
    // Expected cleanup — reuse the same publisher instance
    return;
  }
  // Handle other streamDestroyed reasons as appropriate for your application
});

O que NÃO fazer

  • Faça não chamada OT.initPublisher() duas vezes no mesmo objeto “publisher” com restrições diferentes, sem limpar primeiro (session.unpublish() → esperar por streamDestroyed → em seguida, reinicialize).
  • Faça não tentar novamente em OT_PERMISSION_DENIED (o usuário negou acesso à câmera/microfone) — isso requer uma ação do usuário, não uma nova tentativa.
  • Faça não tentar novamente em OT_NOT_CONNECTED — verifique se a sessão está conectada antes de publicar.
  • Faça não tentar novamente indefinidamente — limitar a 3 tentativas e lidar com a falha de maneira adequada.

Lidando com o mediaStopped Evento

A reprodução da mídia pode ser interrompida no meio da publicação. Fique atento a esse evento e trate-o como um gatilho para tentar novamente:

publisher.on('mediaStopped', async () => {
  console.warn('Media stopped during publish — retrying...');
  // Unpublish if already publishing, then retry
  try { session.unpublish(publisher); } catch (e) { /* ignore */ }
  await delay(2000);
  publishWithRetry(session, publisher);
});

Resumo dos parâmetros recomendados

Parâmetro Valor recomendado Notas
Número máximo de tentativas 3 Equilibra a resiliência e o tempo de espera do usuário
Atraso entre tentativas 2 s × tentativa (2 s, 4 s, 6 s) Dá tempo para a plataforma se recuperar
Em caso de falha em todas as tentativas de repetição Desconectar usuário Evita a situação de “participante fantasma”
Erros que não podem ser repetidos OT_NOT_CONNECTED, OT_PERMISSION_DENIED Falhe rápido nessas

Como lidar com problemas de captura de áudio: audioAcquisitionProblem e audioAcquisitionProblemResolved

Além das tentativas de repetição no nível da publicação, há uma categoria distinta de problemas de áudio que podem afetar um publicador ativo: o dispositivo de áudio do cliente pode não conseguir transmitir dados de áudio mesmo após uma publicação bem-sucedida. O SDK JS da Video API disponibiliza dois eventos específicos para esse cenário.

Causas comuns

O audioAcquisitionProblem O evento é acionado quando o SDK detecta — por meio das estatísticas do publisher — que a trilha de áudio parou de enviar bytes para a conexão entre pares, mesmo que getUserMedia foi bem-sucedida e a editora parece estar ativa. As causas principais mais comuns são:

  • Dispositivo de áudio Bluetooth conectado ou desconectado no meio da sessão: Quando um usuário conecta ou desconecta fones de ouvido, ou conecta fones de ouvido Bluetooth (por exemplo, AirPods) durante uma sessão ativa, o sistema operacional pode alterar o dispositivo de áudio padrão. O pipeline de áudio do navegador pode não conseguir reaquisiar o microfone no novo dispositivo, resultando no envio de zero bytes de áudio.
  • Alteração do dispositivo de áudio no início da sessão: Mudar a entrada de áudio logo no início da sessão — nos primeiros 1 a 2 segundos após a publicação — tende a causar esse problema com maior frequência.
  • Faixa de áudio encerrada pelo navegador ou pelo sistema operacional (trackEndedEvent): O navegador pode encerrar a faixa de áudio subjacente independentemente de qualquer ação do usuário. O SDK detecta isso por meio de um track.ended evento e aumentos audioAcquisitionProblem com method: trackEndedEvent.
  • Detecção baseada em estatísticas (ausência de fluxo de bytes de áudio): Depois que a conexão entre pares atinge o estado “conectada”, o SDK consulta as estatísticas do editor a cada poucos segundos, aproximadamente. Se a saída da trilha de áudio bytesSent não aumenta entre pesquisas consecutivas, audioAcquisitionProblem é levantada (com method: getStats). Quando bytesSent começa a aumentar novamente, audioAcquisitionProblemResolved é levantada.

Observação: Esse problema nem sempre indica uma falha grave. Em algumas sessões, o áudio se recupera sozinho (e audioAcquisitionProblemResolved é disparada); em outros, o fluxo de áudio nunca se recupera e os assinantes a jusante podem acabar perdendo a conexão por tempo limite.

Os eventos

Esses eventos são emitidos na instância do publisher:

  • audioAcquisitionProblem — é acionado quando o SDK detecta que o editor parou de enviar áudio (com base nas estatísticas do editor) ou quando a faixa de áudio subjacente aciona um ended evento. Isso não significa necessariamente que a transmissão irá falhar, mas é um indicador de que a captura de áudio foi interrompida.
  • audioAcquisitionProblemResolved — disparado quando a transmissão de áudio é restabelecida após uma audioAcquisitionProblem. Se esse evento ocorrer, não é necessária nenhuma ação corretiva.

Observação: Esses eventos estão ocorrendo atualmente não faz parte da API pública documentada/definida (eles não estão declarados nas definições do TypeScript do SDK). Trate-os como sinais fornecidos na medida do possível, que podem sofrer alterações entre versões, e verifique a disponibilidade em relação à sua versão do SDK antes de utilizá-los em ambiente de produção. Quando emitidos no publisher, eles incluem um method propriedade que indica como o problema foi detectado ('getStats' ou 'trackEndedEvent').

Observação: As verificações de aquisição de áudio se baseiam nas estatísticas do publisher; portanto, pode haver um pequeno atraso entre a interrupção real do áudio e a geração do evento.

Padrão de recuperação recomendado

Inicie um temporizador curto ao receber audioAcquisitionProblem. Se audioAcquisitionProblemResolved Se o problema for resolvido antes que o temporizador expire, o áudio terá se recuperado sozinho e não será necessária nenhuma ação. Se o temporizador expirar sem que o problema tenha sido resolvido, troque a fonte de áudio como medida de recuperação.

publisher.on('audioAcquisitionProblem', () => {
  // Start a 3-second timer
  const timeout = setTimeout(() => {
    // Problem not resolved — attempt recovery by switching audio source
    publisher.setAudioSource(newDeviceId);
  }, 3000);

  publisher.on('audioAcquisitionProblemResolved', () => {
    // Audio recovered — clear the timer, no action needed
    clearTimeout(timeout);
  });
});

Principais considerações

  • Não é um indicador de falha garantida: audioAcquisitionProblem nem sempre resulta em uma falha na assinatura. Utilize-o como um sinal precoce para monitorar e, eventualmente, tomar medidas, e não como um evento definitivo de falha.
  • Acompanhar as estatísticas de áudio do editor: Após receber audioAcquisitionProblem, você também pode acompanhar as estatísticas de áudio do editor (por exemplo, por meio de publisher.getStats()) para confirmar se a transmissão de áudio realmente foi interrompida antes de tomar qualquer medida.
  • Medida de recuperação: Chamando publisher.setAudioSource(newDeviceId) é o principal mecanismo de recuperação. Isso alterna o dispositivo de entrada de áudio sem exigir um ciclo completo de retirada da publicação e nova publicação.
  • Relação com os prazos de validade das assinaturas: Se o áudio não for recuperado e o emissor continuar sem enviar pacotes de áudio, os assinantes poderão eventualmente se deparar com OT_TIMEOUT (1501). Tratamento proativo de audioAcquisitionProblem pode ajudar a evitar essa falha em etapas posteriores.