2 Arquivamento de mensagens de bate-papo da Web com registro de bate-papo do Amazon IVS |
3 Reprodução de transmissão ao vivo do Amazon IVS com reprodução de bate-papo |
Em nossas últimas postagens, vimos como gravar automaticamente transmissões ao vivo do Amazon Interactive Video Service (Amazon IVS) para o Amazon S3 e como registrar mensagens enviadas para uma sala de bate-papo do Amazon IVS (consulte os links acima). Nesta postagem, reuniremos esses dois conceitos para completar a experiência do usuário e criar reprodução sob demanda de transmissões ao vivo anteriores com reprodução completa do bate-papo.
Conforme visto em nossa última postagem, as mensagens de bate-papo registradas no destino de registro incluem um carimbo de data/hora baseado em GMT que representa a data e a hora em que o evento foi postado pela sala de bate-papo do Amazon IVS. Para realizar uma verdadeira reprodução sob demanda de uma transmissão ao vivo completa com a reprodução interativa de uma mensagem no momento mais próximo possível, precisamos obter um fluxo regular de carimbos de data/hora baseados em GMT do fluxo gravado que podemos usar para determinar o que as mensagens de bate-papo devem estar visíveis a qualquer momento na reprodução do stream. No momento, não há uma fonte documentada que forneça essas informações, mas vamos dar uma olhada no Amazon IVS Player SDK e ver se podemos encontrar algo que nos ajude nessa tarefa.
Ao tentar resolver esse problema, meu primeiro pensamento foi dar uma olhada nos metadados associados a uma transmissão ao vivo para ver se havia alguma informação valiosa escondida nela. Felizmente, parece haver um valor no fluxo regular de metadados que pode ser usado para nossos propósitos de reprodução de bate-papo. Em meus testes, cada stream contém metadados ID3 que parecem ser injetados pelo processo de transcodificação do Amazon IVS. Essas tags ID3 contêm um registro de data e hora útil que podemos usar para ajudar na reprodução do bate-papo. Para escutar esses eventos, podemos anexar um manipulador que escuta o tipo de evento IVSPlayer.MetadataEventType.ID3
. Este tipo de evento está documentado, mas os documentos não dizem muito sobre ele ou dão qualquer garantia sobre o que ele pode conter.
Quer evitar recursos não documentados ? Se estiver preocupado em usar um recurso não documentado, você pode injetar seus próprios metadados cronometrados em sua transmissão ao vivo com o carimbo de data/hora adequado quando novas mensagens forem postadas em suas salas de bate-papo do Amazon IVS. Lembre-se de que há limites para o tamanho e a frequência de postagem de eventos
PutMetadata
por meio da API.
Vamos configurar um player Amazon IVS para reproduzir um stream gravado usando o Player SDK. Primeiro, incluiremos o SDK do player Amazon IVS mais recente por meio de uma tag <script>
.
Novo no Amazon IVS? Confira a série de blogs Getting Started with Amazon Interactive Video Service . Se você tiver dúvidas sobre como começar, poste um comentário em qualquer postagem dessa série (ou abaixo)!
<script src="https://player.live-video.net/1.16.0/amazon-ivs-player.min.js"></script>
Como de costume, precisaremos incluir um elemento <video>
em nossa marcação HTML que será usado para reprodução.
<video id="video-player" muted controls autoplay playsinline></video>
Agora podemos criar uma instância do player IVS. Estou codificando a URL abaixo, mas você pode obter essa URL por meio do método descrito neste post [todo: link].
const streamUrl = 'https://[redacted].cloudfront.net/ivs/v1/[redacted]/[redacted]/2022/11/17/18/6/[redacted]/media/hls/master.m3u8'; const videoEl = document.getElementById('video-player'); const ivsPlayer = IVSPlayer.create(); ivsPlayer.attachHTMLVideoElement(videoEl); ivsPlayer.load(streamUrl); ivsPlayer.play();
Conforme mencionado acima, para ser útil para esse propósito, precisamos de um fluxo regular de timestamps. Para descobrir com que frequência os metadados ID3 são recebidos, vamos adicionar algum tempo. Primeiro, vamos capturar um carimbo de data/hora assim que o stream começar a ser reproduzido.
ivsPlayer.addEventListener(IVSPlayer.PlayerState.PLAYING, (evt) => { window.time = Date.now(); });
Em seguida, adicionaremos o ouvinte de evento ID3, registraremos o tempo e redefiniremos o cronômetro.
ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const now = Date.now(); console.log(`${(now - window.time) / 1000} seconds since last event`); window.time = now; });
Agora podemos iniciar a reprodução e observar o console para ver com que frequência os eventos são acionados.
Em meus testes, os eventos são disparados a cada 1-2 segundos. Não é em tempo real, mas provavelmente é bom o suficiente para a maioria dos cenários. Agora vamos dar uma olhada no evento para ver quais dados ele contém.
window.ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { console.log(evt); });
Quando iniciamos a reprodução com o ouvinte acima anexado, podemos ver as seguintes informações registradas no console do navegador.
Esta é uma informação muito interessante, mas um pouco enigmática. Com base em meus testes, transc_s
parece ser o carimbo de data/hora que procuramos. Vamos modificar o manipulador de eventos para obter esse carimbo de data/hora e registrá-lo.
window.ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const segmentMetadata = evt.find((tag) => tag.desc === 'segmentmetadata'); const segmentMetadataInfo = JSON.parse(segmentMetadata.info[0]); const timestamp = segmentMetadataInfo['transc_s']; const timestampWithMs = timestamp * 1000; console.log(timestampWithMs); console.log(new Date(timestamp)); });
Isso produz a seguinte saída para o meu teste.
Se eu procurar um momento aleatório no vídeo, o carimbo de data/hora adequado estará sempre correto. Isso significa que a cada 1-2 segundos sabemos um carimbo de data/hora GMT válido do momento em que o evento foi capturado no fluxo. Isso significa que podemos assumir que todas as mensagens de bate-papo enviadas antes desse registro de data e hora foram postadas no bate-papo e devem estar visíveis no contêiner do bate-papo.
Quando minha página é carregada, posso utilizar o método descrito na postagem anterior [todo: link] desta série para recuperar todo o log de bate-papo do stream e renderizá-lo no contêiner de bate-papo <div>
. Como nenhuma mensagem deve estar visível no início do stream, garantirei que elas contenham uma classe que as oculte do usuário e armazene um atributo de dados com o carimbo de data/hora adequado para que eu possa saber quais mensagens devem ser visíveis dado qualquer carimbo de data/hora no fluxo.
window.chatLog = await getChatLogs(logGroupName, chatArn, startTime, endTime); renderChat();
Minha função renderChat()
lida com a postagem de cada mensagem no contêiner de bate-papo.
const renderChat = () => { const chatContainer = document.getElementById('chat'); window.chatLog.forEach(msg => { const msgTemplate = document.getElementById('chatMsgTemplate'); const msgEl = msgTemplate.content.cloneNode(true); const ts = new Date(msg.event_timestamp).getTime() * 1000; msgEl.querySelector('.msg-container').setAttribute('data-timestamp', ts); msgEl.querySelector('.chat-username').innerHTML = msg.payload.Attributes.username; msgEl.querySelector('.msg').innerHTML = msg.payload.Content; chatContainer.appendChild(msgEl); }); };
Agora posso modificar o ouvinte ID3 para chamar uma função replayChat()
e passar o registro de data e hora atual.
window.ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const segmentMetadata = evt.find((tag) => tag.desc === 'segmentmetadata'); const segmentMetadataInfo = JSON.parse(segmentMetadata.info[0]); const timestamp = segmentMetadataInfo['transc_s']; const timestampWithMs = timestamp * 1000; replayChat(timestampWithMs); });
Em replayChat()
, posso encontrar todos os nós de bate-papo que contêm um registro de data e hora menor ou igual ao registro de data e hora atual do fluxo gravado e mostrar/ocultar qualquer mensagem de bate-papo com base nesse registro de data e hora.
const replayChat = (currentTimestamp) => { Array.from(document.querySelectorAll('[data-timestamp]')).forEach(node => { const chatMsgTs = Number(node.getAttribute('data-timestamp')); const isVisible = chatMsgTs <= currentTimestamp; if (isVisible) { node.classList.remove('d-none'); } else { node.classList.add('d-none'); } }); const chatContainer = document.getElementById('chat'); chatContainer.scrollTop = chatContainer.scrollHeight; }
Neste ponto, atingimos o objetivo de reproduzir uma transmissão ao vivo do Amazon IVS gravada com uma reprodução completa do bate-papo.
Nesta postagem, vimos como combinar streams ao vivo do Amazon IVS gravados com mensagens de bate-papo registradas para criar uma repetição sob demanda de um fluxo com mensagens de bate-papo cronometradas corretamente.