WebRTC ile gerçek zamanlı iletişimleri etkinleştir

1. Başlamadan önce

Bu codelab'de, WebRTC'yle video çekmek, web kameranızla anlık görüntüler almak ve bunları eşler arası paylaşım için nasıl oluşturacağınızı öğrenebilirsiniz. Ayrıca, temel WebRTC API'lerini nasıl kullanacağınızı ve Node.js ile bir mesajlaşma sunucusunu nasıl ayarlayacağınızı öğreneceksiniz.

Ön koşullar

  • HTML, CSS ve JavaScript ile ilgili temel bilgiler

Derlemeniz istenen nedir?

  • Web kameranızdan video alın.
  • RTCPeerConnection ile video yayını yap.
  • Verileri RTCDataChannel ile kullanın.
  • Mesaj alışverişi için bir sinyal hizmeti ayarlayın.
  • Eş bağlantısı ile sinyali birleştirme.
  • Fotoğraf çekin ve paylaşmak için bir veri kanalı kullanın.

Gerekenler

  • Chrome 47 veya sonraki sürümler
  • Chrome için Web Sunucusu veya tercih ettiğiniz bir web sunucusu
  • İstediğiniz bir metin düzenleyici
  • Node.js

2. Örnek kodu alın

Kodu indirin

  1. Git'i tanıyorsanız GitHub'dan bu codelab'in kodunu klonlamak için şu komutu çalıştırın:
git clone https://github.com/googlecodelabs/webrtc-web

Alternatif olarak, kodun zip dosyasını indirmek için şu bağlantıyı tıklayın:

  1. Bu codelab'in her adımı ve ihtiyaç duyduğunuz tüm kaynaklar için bir klasör içeren webrtc-web-master adlı proje klasörünün paketini açmak için indirilen zip dosyasını açın.

Tüm kodunuzu work adlı dizinde yaparsınız.

step-nn klasörleri, bu codelab'in her adımı için tamamlanmış bir sürüm içerir. Referans için yanınızdalar.

Web sunucusunu yükleyip doğrulayın

Kendi web sunucunuzu kullanabilirsiniz ancak bu codelab, Chrome için Web Sunucusu ile uyumlu çalışacak şekilde tasarlanmıştır.

  1. Chrome için Web Sunucusu'nu bilmiyorsanız Chrome Web Mağazası'ndan yüklemek için şu bağlantıyı tıklayın:

d0a4649b4920cf3.png

  1. Chrome için Web Sunucusu'nu yükleyen ve Google uygulamalarınızı otomatik olarak yeni bir sekmede açan Chrome'a ekle'yi tıklayın.
  2. Web Server'ı (Web Sunucusu) tıklayın:

27fce4494f641883.png

Yerel web sunucunuzu yapılandırmanıza olanak tanıyan bir iletişim kutusu görünür:

a300381a486b9e22.png

  1. Klasör Seç'i tıklayın.
  2. Oluşturduğunuz work klasörünü seçin.

Web Sunucusu URL'leri altında, devam eden çalışmalarınızı görüntülemek için kullanabileceğiniz URL'yi görürsünüz.

Chrome.

  1. Seçenekler (yeniden başlatma gerekebilir) altında, index.html'yi otomatik olarak göster onay kutusunu seçin.
  2. Sunucuyu durdurup yeniden başlatmak için Web Server: Başlatıldı seçeneğini iki kez etkinleştirin.

f23cafb3993dfac1.png

  1. Çalışmanızı web tarayıcınızda görmek için Web Sunucusu URL'leri altında URL'yi tıklayın.

work/index.html sayfasına benzeyen bir sayfa görürsünüz:

18a705cb6ccc5181.png

Elbette bu uygulama henüz ilgi çekici bir şey yapmıyor. Web sunucunuzun düzgün çalışmasını sağlamak için yalnızca minimum düzeyde bir iskelettir. Sonraki adımlarda işlevsellik ve düzen özelliklerini eklersiniz.

3. Web kameranızla video yayını yapın

Bu adımın tam sürümü step-01 klasöründedir.

Kısa çizgi ekleyin

Bir video ve script öğesi eklemek için bu kodu kopyalayın ve work dizininizdeki index.html dosyasına yapıştırın:

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <video autoplay playsinline></video>

  <script src="js/main.js"></script>

</body>

</html>

Bir tutam JavaScript ekleyin

Bu kodu kopyalayıp js klasörünüzdeki main.js dosyasına yapıştırın:

'use strict';

// In this codelab, you  only stream video, not audio (video: true).
const mediaStreamConstraints = {
  video: true,
};

// The video element where the stream is displayed
const localVideo = document.querySelector('video');

// The local stream that's displayed on the video
let localStream;

// Handle success and add the MediaStream to the video element
function gotLocalMediaStream(mediaStream) {
  localStream = mediaStream;
  localVideo.srcObject = mediaStream;
}

// Handle error and log a message to the console with the error message
function handleLocalMediaStreamError(error) {
  console.log('navigator.getUserMedia error: ', error);
}

// Initialize media stream
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
  .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);

Dene

index.html dosyasını tarayıcınızda açın. Böyle bir şey göreceksiniz, ancak web kameranızın görünümünden yararlanırsınız:

9297048e43ed0f3d.png

İşleyiş şekli

getUserMedia() çağrısından sonra tarayıcı, geçerli kaynak için kamera erişimi için yapılan ilk istekse kameranıza erişim izni ister.

Başarılı olursa MediaStream döndürülür, media öğesi bu srcObject özelliği aracılığıyla kullanılabilir:

navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
  .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);


}
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
}

constraints bağımsız değişkeni, alacağınız medyayı belirtmenizi sağlar. Bu örnekte, ses varsayılan olarak devre dışı bırakılmış olduğundan medya yalnızca videodur:

const mediaStreamConstraints = {
  video: true,
};

Video çözünürlüğü gibi ek gereksinimler için kısıtlamaları kullanabilirsiniz:

const hdConstraints = {
  video: {
    width: {
      min: 1280
    },
    height: {
      min: 720
    }
  }
}

MediaTrackConstraints spesifikasyonu tüm potansiyel sınırlama türlerini listeler, ancak tüm seçenekler tüm tarayıcılar tarafından desteklenmez. İstenen çözünürlük şu anda seçili olan kamera tarafından desteklenmiyorsa getUserMedia(), OverconstrainedError ile reddedilir ve kameranıza erişim izni vermeniz istenir.

getUserMedia() başarılı olursa web kamerasındaki video akışı, video öğesinin kaynağı olarak ayarlanır:

function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
}

Bonus puan kazanın

  • getUserMedia() nesnesine iletilen localStream nesnesi genel kapsamda olduğundan tarayıcı konsolundan inceleyebilirsiniz. Konsolu açın, stream, yazın ve Enter (Mac'te Return) tuşuna basın. Konsolu Chrome'da görüntülemek için Control+Shift+J (veya Mac'te Command+Option+J) tuşuna basın.
  • localStream.getVideoTracks() ne döndürür?
  • localStream.getVideoTracks()[0].stop() Hizmetleri İçin Arayın.
  • Kısıtlamalar nesnesine bakın. Bunu {audio: true, video: true} olarak değiştirdiğinizde ne olur?
  • Video öğesi hangi boyutta? Görüntü boyutunun yerine videonun doğal boyutunu JavaScript'ten nasıl alabilirsiniz? Kontrol etmek için Google Chrome Geliştirici Araçları'nı kullanın.
  • Video öğesine CSS filtreleri ekleyin. Örneğin:
video {
  filter: blur(4px) invert(1) opacity(0.5);
}
  • Aşağıdaki gibi SVG filtreleri ekleyin:
video {
   filter: hue-rotate(180deg) saturate(200%);
 }

İpuçları

En iyi uygulama

Video öğenizin, kapsayıcısından taşmadığından emin olun. Bu codelab, video için tercih edilen boyut ve maksimum boyut ayarlamak amacıyla width ve max-width değerlerini ekledi. Tarayıcınız yüksekliği otomatik olarak hesaplar.

video {
  max-width: 100%;
  width: 320px;
}

4. RTCPeerConnection API ile video akışı yapma

Bu adımın tam sürümü step-2 klasöründedir.

Video öğeleri ve kontrol düğmeleri ekleme

index.html dosyasında, tek video öğesini iki video ve üç button öğesiyle değiştirin:

<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>


<div>
  <button id="startButton">Start</button>
  <button id="callButton">Call</button>
  <button id="hangupButton">Hang Up</button>
</div>

Bir video öğesi getUserMedia() tarafından sağlanan akışı, diğeri ise RTCPeerconnection üzerinden yayınlanan videoyu gösterir. (Gerçek dünyadaki bir uygulamada, bir video öğesi yerel akışı, diğeri ise uzak akışı görüntüler.)

adaptör.js dolgusu ekleme

Bu komut dosyası öğesini kopyalayıp main.js için komut dosyası öğesinin üst kısmına yapıştırın:

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

index.html dosyanız şu şekilde görünmelidir:

<!DOCTYPE html>
<html>

<head>
  <title>Real-time communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Real-time communication with WebRTC</h1>

  <video id="localVideo" autoplay playsinline></video>
  <video id="remoteVideo" autoplay playsinline></video>

  <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

RTCPeerConnection kodunu yükleyin

main.js öğesini step-02 klasöründeki sürümle değiştirin.

Telefon et

  1. index.html dosyasını açın.
  2. Web kameranızdan video almak için Başlat'ı tıklayın.
  3. Eş bağlantısını yapmak için Ara'yı tıklayın

Her iki video öğesinde de web kameranızdan aynı videoyu görmeniz gerekir.

  1. WebRTC günlük kaydını görmek için tarayıcı konsolunu görüntüleyin.

İşleyiş şekli

Bu adım çok yararlı oluyor.

WebRTC, eş zamanlı olarak bilinen WebRTC istemcileri arasında video akışı oluşturmak için RTCPeerConnection API'yi kullanır.

Bu örnekte, iki RTCPeerConnection nesnesi aynı sayfada yer almaktadır: pc1 ve pc2.

WebRTC eşleri arasında arama kurulumu üç görev içerir:

  1. Görüşmenin her ucu için bir RTCPeerConnection oluşturun ve her iki ucun da getUserMedia() üzerinden yerel akışını ekleyin.
  2. Ağ bilgilerini alabilir ve paylaşabilirsiniz.

Olası bağlantı uç noktaları, ICE adayları olarak bilinir.

  1. Yerel ve uzaktan açıklamalar alın ve paylaşın.

Yerel medya ile ilgili meta veriler Oturum Açıklama Protokolü (SDP) biçimindedir.

Ayşe ve Ali'nin görüntülü sohbet oluşturmak için RTCPeerConnection uygulamasını kullanmak istediğini düşünün.

İlk olarak, Ayşe ve Ali, ağ bilgilerini değiş tokuş eder. Aday bulma ifadesi, ICE çerçevesini kullanarak ağ arayüzlerini ve bağlantı noktalarını bulma işlemini belirtir.

  1. Ayşe, onicecandidate (addEventListener('icecandidate')) işleyicisine sahip RTCPeerConnection nesnesi oluşturur.

Bu, main.js kaynağından gelen aşağıdaki koda karşılık gelir:

let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  1. Ayşe, getUserMedia() öğesini çağırır ve kendisine iletilen akışı ekler:
navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
  then(gotLocalMediaStream).
  catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
  localStream = mediaStream;
  trace('Received local stream.');
  callButton.disabled = false;  // Enable call button.
}
localPeerConnection.addStream(localStream);
trace('Added local stream to localPeerConnection.');
  1. Ağ adayları kullanılabilir olduğunda ilk adımdaki onicecandidate işleyici çağrılır.
  2. Ayşe, serileştirilmiş aday verilerini İbrahim'e gönderir.

Gerçek bir uygulamada, sinyal olarak bilinen bu işlem bir mesajlaşma hizmeti üzerinden gerçekleşir. Bunu nasıl yapacağınızı sonraki bir adımda öğreneceksiniz. Elbette bu adımda, iki RTCPeerConnection nesnesi aynı sayfada bulunur ve harici mesajlaşmaya gerek kalmadan doğrudan iletişim kurabilir.

  1. Barış, Ayşe'den bir aday mesajı aldığında adayı uzaktaki benzerler açıklamasına eklemek için addIceCandidate() numarasını arar:
function handleConnection(event) {
  const peerConnection = event.target;
  const iceCandidate = event.candidate;

  if (iceCandidate) {
    const newIceCandidate = new RTCIceCandidate(iceCandidate);
    const otherPeer = getOtherPeer(peerConnection);

    otherPeer.addIceCandidate(newIceCandidate)
      .then(() => {
        handleConnectionSuccess(peerConnection);
      }).catch((error) => {
        handleConnectionFailure(peerConnection, error);
      });

    trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
          `${event.candidate.candidate}.`);
  }
}

WebRTC eşlerinin ayrıca, çözünürlük ve codec özellikleri gibi yerel ve uzak ses ve video medya bilgilerini keşfetmesi ve alıp vermesi gerekir. Exchange medya yapılandırma bilgilerinin sinyali, teklif ve yanıt olarak bilinen meta veri blob'larının SDP biçimi kullanılarak aktarılmasıyla devam eder.

  1. Ayşe, RTCPeerConnection createOffer() yöntemini çalıştırır.

Alınan vaat RTCSessionDescription, Alice'in yerel oturum açıklamasını sunuyor:

trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. İşlem başarılı olursa Ayşe, yerel açıklamayı setLocalDescription() kullanarak ayarlar ve daha sonra bu oturum açıklamasını sinyal kanalları üzerinden Ali'ye gönderir.
  2. İbrahim, setRemoteDescription() adlı çocuğun uzaktan açıklaması olarak gönderdiği açıklamayı Ayşe'ye ayarlar.
  3. İbrahim, RTCPeerConnection createAnswer() yöntemini çalıştırır ve bu yöntemi Ayşe'den aldığı uzaktan açıklama yöntemini iletir. Böylece Ali'yle uyumlu bir yerel oturum oluşturulur.
  4. createAnswer() taahhüdü söz konusu RTCSessionDescription.
  5. Ayşe, Ali'nin oturum açıklamasını aldığında setRemoteDescription() ile uzaktan açıklama olarak belirler.
// Logs offer creation and sets peer connection session descriptions
function createdOffer(description) {
  trace(`Offer from localPeerConnection:\n${description.sdp}`);

  trace('localPeerConnection setLocalDescription start.');
  localPeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection setRemoteDescription start.');
  remotePeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection createAnswer start.');
  remotePeerConnection.createAnswer()
    .then(createdAnswer)
    .catch(setSessionDescriptionError);
}

// Logs answer to offer creation and sets peer-connection session descriptions
function createdAnswer(description) {
  trace(`Answer from remotePeerConnection:\n${description.sdp}.`);

  trace('remotePeerConnection setLocalDescription start.');
  remotePeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('localPeerConnection setRemoteDescription start.');
  localPeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);
}

Bonus puan kazanın

  1. chrome://webrtc-internals sayfasına gidin.

Bu sayfada WebRTC istatistikleri ve hata ayıklama verileri yer alır. (Chrome URL'lerinin tam listesini chrome://about adresinde bulabilirsiniz.)

  1. Sayfaya CSS ile stil ekleme:
  2. Videoları yan yana koyun.
  3. Düğmeleri daha büyük metinle aynı genişlikte yapın.
  4. Düzenin mobil cihazlarda çalıştığından emin olun.
  5. Chrome Geliştirici Araçları konsolunda localStream, localPeerConnection ve remotePeerConnection eklentilerine bakın.
  6. Konsoldan localPeerConnectionpc1.localDescription ürününe bakın.

SDP biçimi nasıl görünür?

İpuçları

En iyi uygulama

Kodunuzu geleceğe hazırlamak için yeni Promise tabanlı API'leri kullanın ve adapter.js ile desteklemeyen tarayıcılarla uyumluluğu etkinleştirin.

5. Veri alışverişi için veri kanalını kullanma

Bu adımın tam sürümü step-03 klasöründedir.

HTML'nizi güncelleyin

Bu adımda, aynı sayfadaki iki textarea öğesi arasında kısa mesaj göndermek için WebRTC veri kanallarını kullanırsınız. Bu çok yararlı bir yöntem olmasa da WebRTC'nin veri paylaşmak ve video akışı yapmak için nasıl kullanılabileceğini gösterir.

video ve button öğelerini index.html, öğesinden kaldırın ve aşağıdaki HTML ile değiştirin:

<textarea id="dataChannelSend" disabled
    placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>

<div id="buttons">
  <button id="startButton">Start</button>
  <button id="sendButton">Send</button>
  <button id="closeButton">Stop</button>
</div>

textarea metin girmek içindir, diğeri ise metni benzerler arasında akış şeklinde göstermek içindir.

index.html dosyanız şu şekilde görünmelidir:

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <textarea id="dataChannelSend" disabled
    placeholder="Press Start, enter some text, then press Send."></textarea>
  <textarea id="dataChannelReceive" disabled></textarea>

  <div id="buttons">
    <button id="startButton">Start</button>
    <button id="sendButton">Send</button>
    <button id="closeButton">Stop</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

JavaScript'inizi güncelleyin

  1. main.js içeriğini step-03/js/main.js içeriğiyle değiştirin.
  1. Benzerler arasında veri akışı yapmayı deneyin:
  2. index.html'yi açın.
  3. Eş bağlantısını ayarlamak için Başlat'ı tıklayın.
  4. Soldaki textarea alana bir metin girin.
  5. WebRTC veri kanalı kullanarak metin aktarmak için Gönder'i tıklayın.

İşleyiş şekli

Bu kod, kısa mesaj alışverişini etkinleştirmek için RTCPeerConnection ve RTCDataChannel işaretlemelerini kullanır.

Bu adımdaki kodun çoğu RTCPeerConnection örneğiyle aynıdır. sendData() ve createConnection() işlevleri yeni kodun büyük bölümüne sahiptir:

function createConnection() {
  dataChannelSend.placeholder = '';
  var servers = null;
  pcConstraint = null;
  dataConstraint = null;
  trace('Using SCTP based data channels');
  // For SCTP, reliable and ordered delivery is true by default.
  // Add localConnection to global scope to make it visible
  // from the browser console.
  window.localConnection = localConnection =
      new RTCPeerConnection(servers, pcConstraint);
  trace('Created local peer connection object localConnection');

  sendChannel = localConnection.createDataChannel('sendDataChannel',
      dataConstraint);
  trace('Created send data channel');

  localConnection.onicecandidate = iceCallback1;
  sendChannel.onopen = onSendChannelStateChange;
  sendChannel.onclose = onSendChannelStateChange;

  // Add remoteConnection to global scope to make it visible
  // from the browser console.
  window.remoteConnection = remoteConnection =
      new RTCPeerConnection(servers, pcConstraint);
  trace('Created remote peer connection object remoteConnection');

  remoteConnection.onicecandidate = iceCallback2;
  remoteConnection.ondatachannel = receiveChannelCallback;

  localConnection.createOffer().then(
    gotDescription1,
    onCreateSessionDescriptionError
  );
  startButton.disabled = true;
  closeButton.disabled = false;
}

function sendData() {
  var data = dataChannelSend.value;
  sendChannel.send(data);
  trace('Sent Data: ' + data);
}

RTCDataChannel söz dizimi, send() yöntemi ve message etkinliğiyle kasıtlı olarak WebSocket'e benzer.

dataConstraint kullanımına dikkat edin. Veri kanalları, performans üzerinde güvenilir yayına öncelik verme gibi farklı veri paylaşımı türlerini etkinleştirecek şekilde yapılandırılabilir.

Bonus puan kazanın

  1. SCTP ile WebRTC veri kanalları tarafından kullanılan protokol, güvenilir ve sıralı veri dağıtımı varsayılan olarak açıktır. RTCDataChannel veri kaybı yaşansa bile ne zaman güvenilir bir veri akışı sağlamak ve performansın daha önemli olabileceği durumlar olabilir.
  2. Sayfa düzenini iyileştirmek ve dataChannelReceive textarea öğesine bir yer tutucu özelliği eklemek için CSS kullanın.
  3. Sayfayı bir mobil cihazda test edin.

Daha fazla bilgi

6. Mesaj alışverişinde sinyal hizmeti ayarlama

Aynı sayfadaki benzerler arasında nasıl veri alışverişi yapılacağını öğrendiniz. Peki bunu farklı makineler arasında nasıl yaparsınız? Öncelikle, meta veri mesajlarını takas etmek için bir sinyal kanalı oluşturmanız gerekir.

Bu adımın tam sürümü step-04 klasöründedir.

Uygulama hakkında

WebRTC, istemci tarafı JavaScript API'si kullanır ancak gerçek dünyada kullanım için hem sinyal (mesajlaşma) sunucusunun hem de STUN ve TURN sunucularının kullanılması gerekir. Daha fazla bilgiye buradan ulaşabilirsiniz.

Bu adımda, mesajlar için Socket.IO Node.js modülünü ve JavaScript kitaplığını kullanarak basit bir Node.js sinyal sunucusu oluşturursunuz.

Bu örnekte, sunucu (Node.js uygulaması) index.js üzerinde ve istemci üzerinde çalışan istemci (web uygulaması) index.html konumunda uygulanır.

Bu adımdaki Node.js uygulamasında iki görev vardır.

Öncelikle, mesaj geçişi işlevi görür:

socket.on('message', function (message) {
  log('Got message: ', message);
  socket.broadcast.emit('message', message);
});

İkincisi, WebRTC görüntülü sohbet odalarını yönetir:

if (numClients === 0) {
  socket.join(room);
  socket.emit('created', room, socket.id);
} else if (numClients === 1) {
  socket.join(room);
  socket.emit('joined', room, socket.id);
  io.sockets.in(room).emit('ready');
} else { // max two clients
  socket.emit('full', room);
}

Basit WebRTC uygulamanız, en fazla iki benzer kullanıcının oda paylaşmasına izin verir.

HTML ve JavaScript

  1. index.html dosyasını şu şekilde görünecek şekilde güncelleyin:
<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <script src="/socket.io/socket.io.js"></script>
  <script src="js/main.js"></script>
  
</body>

</html>

Bu adımda sayfada hiçbir şey görmezsiniz. Tüm günlük kaydı, tarayıcı konsoluna yapılır. Konsolu Chrome'da görüntülemek için Control+Shift+J (veya Mac'te Command+Option+J) tuşuna basın.

  1. js/main.js ifadesini şununla değiştirin:
'use strict';

var isInitiator;

window.room = prompt("Enter room name:");

var socket = io.connect();

if (room !== "") {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room, clientId) {
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});

socket.on('ipaddr', function(ipaddr) {
  console.log('Message from client: Server IP address is ' + ipaddr);
});

socket.on('joined', function(room, clientId) {
  isInitiator = false;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

Socket.IO dosyasını Node.js'de çalışacak şekilde ayarlama

HTML dosyasında, bir Socket.IO dosyası kullandığınızı görmüş olabilirsiniz:

<script src="/socket.io/socket.io.js"></script>
  1. work dizininizin en üst düzeyinde aşağıdaki içeriğe sahip package.json adlı bir dosya oluşturun:
{
  "name": "webrtc-codelab",
  "version": "0.0.1",
  "description": "WebRTC codelab",
  "dependencies": {
    "node-static": "^0.7.10",
    "socket.io": "^1.2.0"
  }
}

Bu, Node Package Manager'a (npm) hangi projenin olduğunu bildiren bir uygulama manifestidir

yardımcı oluyor.

  1. /socket.io/socket.io.js gibi bağımlılıkları yüklemek için work dizininizdeki komut satırı terminalinden aşağıdakileri çalıştırın:
npm install

Şuna benzer bir yükleme günlüğü görürsünüz:

3ab06b7bcc7664b9.png

Gördüğünüz gibi npm, package.json içinde tanımlanan bağımlılıkları yükledi.

  1. work dizininizin en üst düzeyinde (js dizini değil) yeni bir dosya index.js oluşturun ve aşağıdaki kodu ekleyin:
'use strict';

var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');

var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
  fileServer.serve(req, res);
}).listen(8080);

var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {

  // Convenience function to log server messages on the client
  function log() {
    var array = ['Message from server:'];
    array.push.apply(array, arguments);
    socket.emit('log', array);
  }

  socket.on('message', function(message) {
    log('Client said: ', message);
    // For a real app, would be room-only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', function(room) {
    log('Received request to create or join room ' + room);

    var clientsInRoom = io.sockets.adapter.rooms[room];
    var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;

    log('Room ' + room + ' now has ' + numClients + ' client(s)');

    if (numClients === 0) {
      socket.join(room);
      log('Client ID ' + socket.id + ' created room ' + room);
      socket.emit('created', room, socket.id);

    } else if (numClients === 1) {
      log('Client ID ' + socket.id + ' joined room ' + room);
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room, socket.id);
      io.sockets.in(room).emit('ready');
    } else { // max two clients
      socket.emit('full', room);
    }
  });

  socket.on('ipaddr', function() {
    var ifaces = os.networkInterfaces();
    for (var dev in ifaces) {
      ifaces[dev].forEach(function(details) {
        if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
          socket.emit('ipaddr', details.address);
        }
      });
    }
  });

});
  1. Komut satırı terminalinden work dizininde aşağıdaki komutu çalıştırın:
node index.js
  1. Tarayıcınızdan http://localhost:8080 adresine gidin.

Bu URL'ye her gittiğinizde bir oda adı girmeniz istenir.

Aynı odaya katılmak için her seferinde aynı oda adını girin (ör. foo).

  1. Yeni bir sekme açıp http://localhost:8080 adresine tekrar gidin ve aynı oda adını tekrar girin.
  2. Başka bir yeni sekme açın, tekrar http://localhost:8080 adresine gidin ve aynı oda adını tekrar girin.
  3. Sekmelerin her birinde konsolu kontrol edin.

JavaScript'ten günlük kaydı göreceksiniz.

Bonus puan kazanın

  • Hangi alternatif mesajlaşma mekanizmaları kullanılabilir? Tam WebSocket kullanırken ne tür sorunlarla karşılaşabilirsiniz?
  • Bu uygulamanın ölçeklendirilmesiyle ilgili hangi sorunlar olabilir? Binlerce veya milyonlarca eş zamanlı oda isteğini test etmek için bir yöntem geliştirebilir misiniz?
  • Bu uygulama, oda adını almak için bir JavaScript istemi kullanır. URL'den oda adını nasıl alacağınızı öğrenin. Örneğin, http://localhost:8080/foo oda adı olarak foo verir.

Daha fazla bilgi

7. Eş bağlantı ve sinyal özelliklerini birleştirme

Bu adımın tam sürümü step-05 klasöründedir.

HTML ve JavaScript'i değiştirin

  1. index.html içeriğini aşağıdakilerle değiştirin:
<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <div id="videos">
    <video id="localVideo" autoplay muted></video>
    <video id="remoteVideo" autoplay></video>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
  
</body>

</html>
  1. js/main.js içeriğini step-05/js/main.js içeriğiyle değiştirin.

Node.js sunucusunu çalıştırma

work dizininizden bu codelab'i takip etmiyorsanız step-05 klasörüne veya geçerli çalışma klasörünüze ait bağımlılıkları yüklemeniz gerekebilir.

  1. Çalışma dizininizden aşağıdaki komutu çalıştırın:
npm install
  1. Yüklendikten sonra, Node.js sunucunuz çalışmıyorsa work dizininde aşağıdaki komutu çalıştırarak başlatın:
node index.js

Socket.IO'yu uygulayan önceki adımdaki index.js sürümünü kullandığınızdan emin olun. Düğüm ve Yuva IO hakkında daha fazla bilgi edinmek için Mesaj alışverişi için sinyal hizmeti ayarlama bölümünü inceleyin.

  1. Tarayıcınızdan http://localhost:8080 adresine gidin.
  2. Yeni bir sekme açıp tekrar http://localhost:8080 adresine gidin.

Bir video öğesi getUserMedia() tarafından sağlanan yerel akışı, diğeri RTCPeerconnection aracılığıyla yayınlanan uzaktan videoyu gösterir.

  1. Günlük kaydını tarayıcı konsolunda görüntüleyin.

Puan puanı puanı

  • Bu uygulama yalnızca bire bir görüntülü sohbeti desteklemektedir. Aynı görüntülü sohbet odasını birden fazla kişinin paylaşabilmesi için tasarımı nasıl değiştirebilirsiniz?
  • Örnekte, foo adlı odanın kodu sabit kodludur. Diğer oda adlarını etkinleştirmenin en iyi yolu hangisidir?
  • Kullanıcılar oda adını nasıl paylaşır? Oda adlarını paylaşmaya alternatif bir yöntem oluşturmaya çalışın.
  • Uygulamayı nasıl değiştirebilirsiniz?

İpuçları

  • chrome://webrtc-internals sayfasındaki WebRTC istatistiklerini bulabilir ve hata ayıklaması yapabilirsiniz.
  • Yerel ortamınızı kontrol etmek, kamera ve mikrofonunuzu test etmek için WebRTC Troubleshooter'ı kullanın.
  • Önbelleğe almayla ilgili sorun yaşıyorsanız aşağıdakileri deneyin:
  1. Control tuşuna basın ve Bu sayfayı yeniden yükle'yi tıklayın.
  2. Tarayıcıyı yeniden başlatın.
  3. Komut satırından npm cache clean çalıştırın.

8. Bir fotoğraf çekin ve veri kanalı üzerinden paylaşın

Bu adımın tam sürümü step-06 klasöründedir.

İşleyiş şekli

Önceden, RTCDataChannel kullanarak kısa mesaj gönderip almayı öğrendiniz. Bu adım tüm dosyaların paylaşılmasını sağlar. Bu örnekte, fotoğraflar getUserMedia() ile çekilmiştir.

Bu adımın temel bölümleri şunlardır:

  1. Veri kanalı oluşturun.

Bu adımda eş bağlantısına herhangi bir medya akışı eklemezsiniz.

  1. getUserMedia() ile web kamerası video akışınızı yakalayın:
var video = document.getElementById('video');

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}
  1. Video akışından anlık görüntü (video çerçevesi) almak ve bunu canvas öğesinde göstermek için Snap'i tıklayın:
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}
  1. Resmi baytlara dönüştürmek ve veri kanalı üzerinden göndermek için Gönder'i tıklayın:
function sendPhoto() {
  // Split the data-channel message in chunks of this byte length.
  var CHUNK_LEN = 64000;
  var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
    len = img.data.byteLength,
    n = len / CHUNK_LEN | 0;

  console.log('Sending a total of ' + len + ' byte(s)');
  dataChannel.send(len);

  // Split the photo and send in chunks of approximately 64KB.
  for (var i = 0; i < n; i++) {
    var start = i * CHUNK_LEN,
      end = (i + 1) * CHUNK_LEN;
    console.log(start + ' - ' + (end - 1));
    dataChannel.send(img.data.subarray(start, end));
  }

  // Send the reminder, if applicable.
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}

Alıcı taraf, veri kanalı mesaj baytlarını bir resme dönüştürür ve resmi kullanıcıya gösterir:

function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
      // we're done: all data chunks have been received
      console.log('Done. Rendering photo.');
      renderPhoto(buf);
    }
  };
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // The trail is the element that holds the incoming images.
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

Kodu alın

  1. work klasörünüzün içeriğini step-06 içeriğiyle değiştirin.

work içindeki index.html dosyanız şu şekilde görünmelidir**:**

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <h2>
    <span>Room URL: </span><span id="url">...</span>
  </h2>

  <div id="videoCanvas">
    <video id="camera" autoplay></video>
    <canvas id="photo"></canvas>
  </div>

  <div id="buttons">
    <button id="snap">Snap</button><span> then </span><button id="send">Send</button>
    <span> or </span>
    <button id="snapAndSend">Snap &amp; Send</button>
  </div>

  <div id="incoming">
    <h2>Incoming photos</h2>
    <div id="trail"></div>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>
  1. work dizininizden bu codelab'i takip etmiyorsanız step-06 klasörüne veya geçerli çalışma klasörünüze ait bağımlılıkları yüklemeniz gerekebilir. Çalışma dizininizden aşağıdaki komutu çalıştırmanız yeterlidir:
npm install
  1. Yüklendikten sonra, Node.js sunucunuz çalışmıyorsa work dizininizden aşağıdaki komutu çalıştırarak başlatın:
node index.js
    Make sure that you're using the version of `index.js` that implements Socket.IO and 

Değişiklik yaparsanız Node.js sunucunuzu yeniden başlatmayı unutmayın.

Node ve Socket.IO hakkında daha fazla bilgi için Sinyal ayarlama kurulumu bölümüne bakın

mesaj gönderip alma hizmeti.

  1. Gerekirse uygulamanın web kameranızı kullanmasına izin vermek için İzin ver'i tıklayın.

Uygulama rastgele bir oda kimliği oluşturur ve URL'ye kimliği ekler.

  1. Yeni bir tarayıcı sekmesinde veya penceresinde adres çubuğundaki URL'yi açın.
  2. Fotoğraf çek ve Gönder'i tıklayın, ardından sayfanın alt kısmındaki diğer sekmede Gelen fotoğraflar'a bakın.

Uygulama, fotoğrafları sekmeler arasında aktarır.

Aşağıdakine benzer bir tablo görürsünüz:

911b40f36ba6ba8.png

Bonus puan kazanın

Herhangi bir dosya türünü paylaşmayı mümkün kılmak için kodu nasıl değiştirebilirsiniz?

Daha fazla bilgi

9. Tebrikler

Gerçek zamanlı video akışı ve veri alışverişi için bir uygulama oluşturdunuz.

Daha fazla bilgi