https://d226lax1qjow5r.cloudfront.net/blog/blogposts/share-your-screen-with-the-vonage-video-api-dr/Blog_Share-Your-Screen_1200x600.png

Comparte tu pantalla con la Video API de Vonage

Publicado el May 5, 2021

Tiempo de lectura: 4 minutos

Esta serie de tutoriales explorará la Video API de Vonage (anteriormente TokBox OpenTok) y lo que puedes crear con ella. La Video API es muy robusta y altamente personalizable, y en cada post, mostraremos cómo implementar una función específica usando la API. En esta ocasión veremos cómo añadir pantalla compartida a tu chat básico de audio-vídeo.

Como esta aplicación requerirá algo de código del lado del servidor, utilizaremos Glitch para facilitar la configuración. También puede descargar el código de este proyecto Glitch y desplegarlo en su servidor o plataforma de alojamiento de elección (probablemente puede requerir algunos ajustes de configuración en función de los requisitos de su plataforma).

No vamos a utilizar ningún marco de front-end para esta serie, sólo Javascript vainilla, para mantener el foco en la propia Video API. Al final de este tutorial, usted debe ser capaz de compartir su pantalla con la persona en su video chat.

Screenshot of active screen-sharing

El código final de esta aplicación se puede encontrar en este repositorio GitHub o en remezclado en Glitch.

Requisitos previos

Antes de comenzar, necesitarás una cuenta de Video API de Vonage, que puedes crear gratis aquí. También necesitarás Node.js instalado (si no estás usando Glitch).

Este tutorial se basa en el primer post introductorio de la serie: Creación de un Video Chat Básico. Si es la primera vez que utilizas la Video API, te recomendamos que lo leas porque cubre la siguiente configuración básica:

  • Crear un proyecto de Video API de Vonage

  • Instalación en Glitch

  • Estructura básica del proyecto

  • Iniciar una sesión de OpenTok

  • Conectarse a la sesión, suscribirse y publicar

  • Estilos básicos de diseño para un chat de vídeo

Prepárese para múltiples flujos publicados

En la aplicación anterior, su navegador se conectaba a la sesión y publicaba un único flujo (su cámara). Sin embargo, con la incorporación de la pantalla compartida, es posible que publique dos secuencias en la misma sesión. Para ello, en public/client.jsmueva el session a una variable global.

Antes:

function initializeSession(apiKey, sessionId, token) {
  const session = OT.initSession(apiKey, sessionId);
  // more code below
}

Después:

let session;
function initializeSession(apiKey, sessionId, token) {
  session = OT.initSession(apiKey, sessionId);
  // more code below
}

En views/index.htmldebe proporcionar un elemento de marcador de posición para el vídeo de pantalla compartida que se mostrará y un botón para activar la compartición. Cree también un botón para detener la pantalla compartida, que se utilizará más adelante:

<main>
  <!-- This element is new -->
  <div id="screen" class="screen"></div>

  <!-- These two elements already exist from the first tutorial -->
  <div id="subscriber" class="subscriber"></div>
  <div id="publisher" class="publisher"></div>

  <!-- These are both new too -->
  <button id="startScreenShare" class="screen-share">Share Screen</button>
  <button id="stopScreenShare" class="screen-share hidden">Stop Sharing Screen</button>
</main>

Comprobar la capacidad de compartir pantalla

Cuando se pulsa el botón de compartir, la aplicación debe comprobar primero que puede compartir la pantalla. Añade este código al final de client.js:

const startShareBtn = document.getElementById("startScreenShare");
startShareBtn.addEventListener("click", event => {
  OT.checkScreenSharingCapability(response => {
    if (!response.supported || response.extensionRegistered === false) {
      alert("Screen sharing not supported");
    } else if (response.extensionInstalled === false) {
      alert("Browser requires extension");
    } else {
      // Share screen code
    }
  });
});

El método OT.checkScreenSharingCapability() devuelve información sobre las capacidades del navegador actual. En base a esto, puedes determinar si el navegador no lo soporta, requiere una extensión en navegadores antiguos, o puede compartir usando APIs nativas.

En Chrome 71 y anteriores, Firefox 51 y anteriores, y Opera 58 y anteriores, el usuario tendrá que instalar una extensión para compartir su pantalla. Este post no cubre las extensiones, pero puedes encontrar más información en la documentación.

Comparte tu pantalla

Añada el siguiente código en el bloque de sentencia else anterior:

const screenSharePublisher = OT.initPublisher(
  "screen",
  {
    insertMode: "append",
    width: "100%",
    height: "100%",
    videoSource: "screen",
    publishAudio: true
  },
  handleCallback
);
session.publish(screenSharePublisher, handleCallback);

El primer parámetro es el id del elemento HTML que rellenará el vídeo del editor. Compartir una pantalla es muy parecido a compartir una cámara para los navegadores modernos. Al añadir videoSource: "screen" a sus opciones de editor, el navegador solicitará los permisos correctos en su nombre. publishAudio es opcional.

Una vez creado el nuevo editor, puede publicarlo en nuestra sesión.

Video application with screen sharing, but it is not displaying correctly

Funciona, pero como habrás notado, el video de pantalla compartida está empujado hacia arriba contra el lado de la ventana, y los botones están en un lugar extraño. Añada lo siguiente a su archivo public/style.css archivo:

.screen {
  width: 100%;
  height: 100%;
  display: flex;
}
.screen-share {
  position: absolute;
  bottom: 0;
  right: 0;
}
.hidden {
  display: none;
}

Deja de compartir tu pantalla

Para dejar de compartir un flujo publicado, necesitas acceder a la variable a la que está asignado. Encima del receptor de eventos, crea una variable vacía screenSharePublisher vacía:

let screenSharePublisher;

En la lista de eventos, asigna OT.initPublisher(...) a la nueva variable eliminando la palabra clave const la palabra clave.

En la parte inferior de client.js añada un receptor de eventos para el botón de dejar de compartir:

const stopShareBtn = document.getElementById("stopScreenShare");
stopShareBtn.addEventListener("click", event => {
  screenSharePublisher.destroy();
});

Solucionar los problemas de diseño pendientes

A estas alturas, tu aplicación tendría este aspecto:

Slightly better but still somewhat broken layout

Es ligeramente mejor que al principio, pero aún parece roto. Arreglémoslo con algo de CSS y Javascript (para alternar las clases CSS necesarias).

Eliminemos los .screen originales de style.css:

/* We don't need these any more */
.screen {
  width: 100%;
  height: 100%;
  display: flex;
}

Modifique los .subscriber estilos de clase en el style.css como sigue:

.subscriber,
.screen.pub-active,
.screen.sub-active {
  width: 100%;
  height: 100%;
  display: flex;
}
.screen.sub-active ~ .subscriber,
.screen.pub-active ~ .subscriber {
  position: absolute;
  width: 25vmin;
  height: 25vmin;
  min-width: 8em;
  min-height: 8em;
  align-self: flex-end;
  right: 0;
}

Lo que estamos haciendo aquí es hacer que el elemento que aloja el flujo de pantalla compartida ocupe todo el espacio de la ventana cuando está activo, mientras que el flujo de la cámara se sitúa en la esquina inferior derecha de la ventana.

A continuación, tendremos que asegurarnos de que se añaden las clases correctas a los elementos adecuados cuando se inicie la compartición de pantalla:

screenSharePublisher = OT.initPublisher(
  "screen",
  {
    insertMode: "append",
    width: "100%",
    height: "100%",
    videoSource: "screen",
    publishAudio: true
  },
  handleCallback
);
session.publish(screenSharePublisher, handleCallback);
// CSS classes when screen-sharing starts
startShareBtn.classList.toggle("hidden");
stopShareBtn.classList.toggle("hidden");
document.getElementById("screen").classList.add("pub-active");

Lo contrario debe ocurrir cuando se deja de compartir la pantalla:

stopShareBtn.addEventListener("click", event => {
  screenSharePublisher.destroy();
  
  // CSS classes when screen-sharing stops
  startShareBtn.classList.toggle("hidden");
  stopShareBtn.classList.toggle("hidden");
  document.getElementById("screen").classList.remove("pub-active");
});

Ahora las cosas se ven bien cuando se inicia la pantalla compartida. Pero para la persona que está en el otro extremo de la llamada, el diseño sigue siendo un poco roto.

layout is broken for the person viewing the screenshare

Para solucionarlo, vamos a modificar el streamCreated que se suscribe a cualquier flujo recién creado. Comprobaremos si el flujo creado es un flujo de cámara o un flujo de pantalla compartida. Si es una pantalla compartida, añadiremos la clase sub-active clase CSS.

Antes:

session.connect(token, error => {
  // Other code not included for brevity
  // Subscribe to a newly created stream
  session.on("streamCreated", event => {
    session.subscribe(
      event.stream,
      "subscriber",
      {
        insertMode: "append",
        width: "100%",
        height: "100%"
      },
      handleCallback
    );
  });
});

Después:

// Subscribe to a newly created stream
session.on("streamCreated", event => {
  const streamContainer =
    event.stream.videoType === "screen" ? "screen" : "subscriber";
  session.subscribe(
    event.stream,
    streamContainer,
    {
      insertMode: "append",
      width: "100%",
      height: "100%"
    },
    handleScreenShare(event.stream.videoType)
  );
});
// Function to handle screen share layout
function handleScreenShare(streamType, error) {
  if (error) {
    console.log("error: " + error.message);
  } else {
    if (streamType === "screen") {
      document.getElementById("screen").classList.add("sub-active");
    }
  }
}

Y tendremos que añadir un receptor de eventos para cuando la pantalla compartida se detiene también:

session.on("streamDestroyed", event => {
  document.getElementById("screen").classList.remove("sub-active");
});

Al final, la persona que recibe la pantalla compartida debería ver algo parecido a esto:

No longer broken layout

Después de todo esto, su archivo client.js tendría este aspecto:

let session;
fetch(location.pathname, { method: "POST" })
  .then(res => {
    return res.json();
  })
  .then(res => {
    const apiKey = res.apiKey;
    const sessionId = res.sessionId;
    const token = res.token;
    initializeSession(apiKey, sessionId, token);
  })
  .catch(handleCallback);
function initializeSession(apiKey, sessionId, token) {
  // Create a session object with the sessionId
  session = OT.initSession(apiKey, sessionId);
  // Create a publisher
  const publisher = OT.initPublisher(
    "publisher",
    {
      insertMode: "append",
      width: "100%",
      height: "100%"
    },
    handleCallback
  );
  // Connect to the session
  session.connect(token, error => {
    // If the connection is successful, initialize the publisher and publish to the session
    if (error) {
      handleCallback(error);
    } else {
      session.publish(publisher, handleCallback);
    }
  });
  // Subscribe to a newly created stream
  session.on("streamCreated", event => {
    const streamContainer =
      event.stream.videoType === "screen" ? "screen" : "subscriber";
    session.subscribe(
      event.stream,
      streamContainer,
      {
        insertMode: "append",
        width: "100%",
        height: "100%"
      },
      handleScreenShare(event.stream.videoType)
    );
  });
  session.on("streamDestroyed", event => {
    document.getElementById("screen").classList.remove("sub-active");
  });
}
// Function to handle screenshare layout
function handleScreenShare(streamType, error) {
  if (error) {
    console.log("error: " + error.message);
  } else {
    if (streamType === "screen") {
      document.getElementById("screen").classList.add("sub-active");
    }
  }
}
// Callback handler
function handleCallback(error) {
  if (error) {
    console.log("error: " + error.message);
  } else {
    console.log("callback success");
  }
}
let screenSharePublisher;
const startShareBtn = document.getElementById("startScreenShare");
startShareBtn.addEventListener("click", event => {
  OT.checkScreenSharingCapability(response => {
    if (!response.supported || response.extensionRegistered === false) {
      alert("Screen sharing not supported");
    } else if (response.extensionInstalled === false) {
      alert("Browser requires extension");
    } else {
      screenSharePublisher = OT.initPublisher(
        "screen",
        {
          insertMode: "append",
          width: "100%",
          height: "100%",
          videoSource: "screen",
          publishAudio: true
        },
        handleCallback
      );
      session.publish(screenSharePublisher, handleCallback);
      startShareBtn.classList.toggle("hidden");
      stopShareBtn.classList.toggle("hidden");
      document.getElementById("screen").classList.add("pub-active");
    }
  });
});
const stopShareBtn = document.getElementById("stopScreenShare");
stopShareBtn.addEventListener("click", event => {
  screenSharePublisher.destroy();
  startShareBtn.classList.toggle("hidden");
  stopShareBtn.classList.toggle("hidden");
  document.getElementById("screen").classList.remove("pub-active");
});

¿Y ahora qué?

El código final de Glitch y GitHub contiene todo lo que cubrimos en este post bastante largo, pero reorganizado, por lo que el código es más limpio y más fácil de mantener. Siéntete libre de remezclar o clonar el código y jugar con él.

Existen funcionalidades adicionales que podemos crear con la Video API de Vonage, que se tratarán en futuros tutoriales, pero mientras tanto, puedes obtener más información en nuestro sitio de documentación integral. Si tienes algún problema o alguna pregunta, comunícate con nosotros en nuestra Slack de la comunidad. Gracias por leernos.

Compartir:

https://a.storyblok.com/f/270183/384x384/46621147f0/huijing.png
Hui Jing ChenAntiguos alumnos de Vonage

Hui Jing es defensora de los desarrolladores en Nexmo. Tiene un amor desmesurado por CSS y la tipografía, y en general es una apasionada de todo lo relacionado con la web.