Статьи

Передача видео контента HTML5 через ColdFusion WebSockets

Я уже давно играю с функцией WebSocket, представленной в ColdFusion 10. Я пытался протолкнуть изображения через канал ColdFusion WebSocket, и он работал просто отлично. Но на этот раз я хотел протестировать WebSockets и хотел регулярно отправлять большие данные. Я подумал, что, может быть, я смогу протолкнуть видеоданные через WebSockets, и оказалось, что нет прямого способа передавать видеоданные многим клиентам. Я наткнулся на функцию — drawImage, которую можно использовать для рисования изображения или видео на холсте HTML5. После того, как изображение нарисовано на Canvas, его закодированные в base64 данные можно получить, вызвав toDataURLфункция на объекте Canvas. Затем эти данные могут быть переданы через ColdFusion WebSocket всем подписчикам, которые затем могут использовать эти данные для рисования изображения (видеокадра) на холсте.

Вот демо-видео:

Unable to display content. Adobe Flash is required.

Обратите внимание: я не передаю аудиодорожку, представленную в видео, и все еще пытаюсь понять, как этого можно достичь.

Вот код издателя:

<!DOCTYPE html> 
<html>
    <body> 
    <cfwebsocket name="socket" onmessage="messageHandler"> 
    <video id="videoElement" controls muted> 
        <source src="windowsill.webm" type="video/webm"> </video>
    <br> 
    <canvas id="canvasElement" style="border: solid 1px;"> 
    </canvas> 
    
    <script type="text/javascript"> 
      var context,canvasElement,videoElement, previous, current; 

      //message handler for CF WebSocket 
      messageHandler = function(msg){ 
      } 

      //function to call once the DOM content has been loaded 
      document.addEventListener('DOMContentLoaded', function(){ 
        videoElement = document.getElementById('videoElement'); 
        canvasElement = document.getElementById('canvasElement'); 
        context = canvasElement.getContext('2d'); 
      }); 
        
      //function to call once the videos meta data is available 
      document.getElementById('videoElement').addEventListener('loadedmetadata', function(){ 
         
         //set the canvas width and height to videos width and height 
          canvasElement.width = videoElement.videoWidth; 
          canvasElement.height = videoElement.videoHeight; 
          
          //event listener when the video is played 
          videoElement.addEventListener('play', function(){
            //call the draw function 
            draw(this, videoElement.videoWidth, videoElement.videoHeight); 

          }); 
        }); 

        //function to draw the video frame on a temporary canvas at 20fps 
        function draw(video, width, height){ 
          
          //if the video has been paused or ended return false 
          if (video.paused || video.ended) return false; 
          
          //draw the current video frame onto a canvas 
          context.drawImage(video, 0, 0, width, height); 
          
          //get base64 encoded data from Canvas 
          current = canvasElement.toDataURL("image/png"); 

          //just in case if the previous frame is same as current 
          if (previous != current) {
           
           //transfer the base64 encode image over a WebSocket 
           socket.publish("myChannel", current); 

         } 

          previous = current; //draw the video frame on the canvas at 20fps by calling the draw function every 50ms setTimeout(draw, 50, video, width, height); 
        } 
      </script> 
    </body>
</html>

Как видно из приведенного выше кода, после начала воспроизведения видео вызывается функция draw. Здесь я нарисовал видео на холсте, используя функцию drawImage, а затем использовал функцию toDataURL, чтобы получить закодированные в base64 данные изображения. Затем он передается по каналу ColdFusion WebSocket («myChannel»). Я вызываю эту функцию (‘draw’) каждые 50 мс, чтобы нарисовать текущий видеокадр на холсте (для достижения 20 кадров в секунду) и передать изображение через WebSocket.

Клиент \ подписчик при получении данных рисует изображение (видеокадр) на холсте. Вот код подписчика:

<!DOCTYPE HTML> 
<html> 
  <body> 
      <cfwebsocket name="socket" onmessage="messageHandler" onopen="openHandler"> 
      <canvas id="canvasElement" style="border: solid 1px;" width="426" height="240"> 
      </canvas> 
  </body> 
  <script type="text/javascript"> 
    var canvas, context, count = 0, flag = false; 
    var newImage = new Image(); 
    document.addEventListener('DOMContentLoaded', function(){ 
      canvas = document.getElementById('canvasElement'); 
      context = canvas.getContext('2d'); 
    }); 

    function openHandler(){ 
      
      //subscribe to the CF WebSocket channel 
      socket.subscribe("myChannel", {}, dataHandler); 
    } 
    
    function messageHandler(msg){ 
    } 

    //function that receives the data from the WebSocket channel 
    function dataHandler(msg){ 
      if (msg.type == 'data') { 
        
        //function to call when the image is loaded with base64 data 
        newImage.onload = function(){ 
          
          //draw the image on the canvas 
          context.drawImage(newImage, 0, 0); 
          
          //set the flags when the above function is complete 
          flag = true; count = 1; 

        } 

        //if ready to be drawn on the canvas 
        if (count == 0 || flag == true) { 
          flag = false; 

          //assign base64 data to the source of the image 
          newImage.src = msg.data; 

        } 
      } 
    } 
  </script> 
</html>

На стороне клиента, когда данные получены через WebSocket, они назначаются источнику объекта Image. Причина, по которой я это делаю, заключается в том, что функция drawImage принимает либо изображение, либо видео в качестве первого аргумента и не разрешает данные base64. Как только изображение загружено, оно готово для рисования на холсте. Этот процесс продолжается до тех пор, пока видео не закончится или пользователь не приостановит видео.