Felipe Cesar

    Como criar um player HLS do zero (Parte 3)

    Se você chegou até aqui, já está familiarizado com a arquitetura orientada a eventos e o parser M3U8 que construímos anteriormente. Agora, vamos elevar o nível conectando o elemento <video> ao nosso player HLS. Essa etapa é fundamental para transformar o código em um player funcional. Para acompanhar o projeto do início você pode ver a parte 1 e a parte 2 e o repositório.

    Como vincular o elemento <video> ao código

    Vamos começar adicionando o método attachMedia à classe Hls. Ele será responsável por conectar o elemento <video> ao nosso player. Confira o código:

    export default class Hls {
      // ...
      
      attachMedia(media) {
        this.trigger(Events.MEDIA_ATTACHING, { media });
      }
    }

    Este método recebe o elemento <video> e dispara um evento. Para que isso funcione, precisamos incluir o evento no arquivo events.js:

    export const Events = {
      MEDIA_ATTACHING: 'hlsMediaAttaching',
      // ...
    };

    Agora, basta capturar o elemento <video> no DOM e utilizar o método attachMedia no arquivo main.js:

    import Hls from './hls';
    
    const video = document.querySelector('#video');
    const videoSrc = '/video/index.m3u8';
    
    const hls = new Hls();
    hls.loadSource(videoSrc);
    hls.attachMedia(video);

    Com isso, nosso player já está vinculado ao elemento de vídeo. Mas o que acontece por trás dessa conexão? Vamos explorar na próxima etapa.

    Criando a classe MSEMediaController

    Para gerenciar a interação entre o player e o Media Source Extensions API (MSE), criamos a classe MSEMediaController. Sua estrutura é similar à do PlaylistLoader:

    export default class MSEMediaController {
      constructor(hls) {
        this.hls = hls;
        this.registerListeners();
      }
    
      registerListeners() {
        this.hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
      }
    }

    Esse código escuta o evento MEDIA_ATTACHING e executa o método onMediaAttaching. Antes de seguir, vamos criar o arquivo utils.js com a função waitForEvent. Ela será essencial para lidar com eventos de maneira assíncrona, como veremos a seguir.

    export function waitForEvent(
      target,
      types,
      signal
    ) {
      types = Array.isArray(types) ? types : [types];
      return new Promise((resolve, reject) => {
        if (signal?.aborted) {
          return reject(signal.reason);
        }
        const listener = (event) => {
          for (const type of types) {
            target.removeEventListener(type, listener);
          }
          signal?.removeEventListener("abort", abortListener);
          resolve(event);
        };
        const abortListener = () => {
          reject(signal.reason);
        };
        for (const type of types) {
          target.addEventListener(type, listener, { once: true, signal });
        }
        signal?.addEventListener("abort", abortListener, { once: true });
      });
    }

    O método onMediaAttaching

    O método onMediaAttaching é o coração da integração com o MSE. Ele instância a API e prepara o player para o streaming de mídia:

    async onMediaAttaching(event, data) {
      if (!data.media) return;
    
      const media = data.media;
      const mediaSource = new MediaSource();
    
      media.src = URL.createObjectURL(mediaSource);
    
      await waitForEvent(mediaSource, "sourceopen");
    
      this.hls.trigger(Events.MEDIA_ATTACHED, { media, mediaSource });
    }

    O que esse código faz?

    1. Validação inicial: Verifica se data.media está presente. Caso contrário, interrompe a execução.
    2. Criação do MediaSource: Instancia a API MSE e cria um novo MediaSource.
    3. Vinculação ao elemento <video>: Associa o MediaSource ao vídeo usando um Object URL.
    4. Espera pelo evento sourceopen: Garante que o MediaSource esteja pronto para receber dados.
    5. Dispara o evento MEDIA_ATTACHED: Indica que a mídia foi anexada com sucesso, permitindo que outras partes do player continuem o processamento.

    Conclusão

    A API Media Source Extensions é o que permite ao navegador fazer streaming de vídeo adaptativo, fundamental para lidar com formatos como HLS. Essa etapa é o primeiro passo para tornar o player HLS dinâmico e pronto para exibir vídeos em diferentes condições de rede.

    Fique ligado e acompanhe os próximos capítulos dessa jornada!