React Native’de görüntü ve ses aktarımı için WebRTC kullanımı
Önceki yazımda mesajlaşma için WebSocket’in nasıl kullanıldığına değinmiştim. Bu yazımda ise WebRTC kullanılarak nasıl bir görüntülü ve sesli arama uygulaması geliştirilir ona değineceğim. WebRTC’ye geçmeden önce RTC’nin ne olduğuna değinelim.
RTC nedir?
RTC (Real Time Communication) (Gerçek zamanlı İletişim), adından da anlaşılacağı üzere gerçek zamanlı yani anlık hızlı bir şekilde gönderim yapabilmek için oluşturulan yazılım protokolleri ve donanımların yer aldığı kategoriye denir.
Örneğin uzay aracı ile dünya arasında bir iletişim gerçekleştirmek istiyorsunuz diyelim. Bu amaçla verimli bir şekilde haberleşmek için bir RTC protokolü olarak IEEE 1355 standardı üzerinde geliştirilen SpaceWire ağını kullanmanız gereklidir.
Biz web ortamında gerçek zamanlı iletişim kullanacağımız için WebRTC en doğru çözümdür. Şimdi WebRTC’ye değinelim.
WebRTC nedir?
WebRTC, internet tarayıcıları, mobil uygulamalar ve IoT cihazları için gerçek zamanlı iletişim yeteneklerini kazandırmak amacıyla Google tarafından oluşturulmuş bir API’dir. WebRTC sayesinde arada sunucu gerekmeksizin uçtan uca gönderim yapılabilmektedir. WebRTC, video ve ses akışları için kullanılabileceği gibi bazı keyfî verilerin gönderimi için de bir veri kanalı sunmaktadır. Günümüzde Internet Explorer haricinde tüm internet tarayıcılarında WebRTC desteklenmektedir.
WebSocket’ten farkı nedir?
WebSocket’te yer alan üçüncü bir sunucu bulunması gereksinimi, WebRTC için geçerli değildir. Fakat arada kurulacak bağlantı için session açılması gibi sinyalizasyon işlemleri WebRTC’nin görevi değildir. Bu nedenle sinyalizasyon işlemleri için harici bir sunucuya ihtiyaç vardır. Bu bağlamda WebRTC, WebSocket’in yerine geçen değil, WebSocket’i kapsayarak tümleyici bir yapıda kullanılmaktadır.
Bazı temel kavramlar
WebRTC’yi anlamak için bazı temel kavramları incelememiz gerekir.
NAT nedir?
Bildiğiniz gibi, internet sağlayıcınız tarafından size sadece bir adet IP adresi tahsis edilmektedir. Eğer evinizde birden fazla cihaz varsa, bu IP adresini cihazlar arasında bölüştürerek kullanmak gerekecektir. Bu amaçla NAT (Network Address Translation) (Ağ adresi çevrimi) yöntemi kullanılır. Bu yöntem, cihazınızın yerel IP adresinin, public IP adresine dönüştürülmesini sağlar. Bu işlem router (yönlendirici) üzerinde gerçekleştirilir. Router, her bir cihazın yerel IP ve public IP adres bilgilerinin yer aldığı bir tabloyu barındırır. Bu sayede tek bir public IP ile birçok cihazın haberleşmesi sağlanır.
ICE Framework’ü nedir?
ICE (Interactive Connectivity Establishment) (Etkileşimli Bağlantı Kurulumu) iki cihaz arasında bağlantının kurulmasını sağlayan framework’tür.
WebRTC iletişiminin kurulması için bir sunucu gereksinimi olmadığına değinmiştik. Fakat internete bağlanan cihazların firewall’lar ardında yer alması, router’ların direkt bağlantı kurulumuna izin vermemesi ve NAT’lardan dolayı iki uç arasında direkt olarak bağlantı kurulması teknik olarak mümkün değildir. İlgili bağlantının açılması için:
- Firewall’ların bypass edilmesi gereklidir,
- Bağlantıyı gerçekleştirecek eşlerde yer alan cihazların public IP adresleri bulunmadığında, birbiri ile haberleşmesi için benzersiz bir adrese sahip olması gereklidir,
- Router’lar direkt bağlantıya izin vermediğinde, bir sunucu vasıtasıyla verilerin alınmasına ihtiyaç vardır.
Bu nedenlerden dolayı ICE framework’ü, STUN ve TURN sunucularını kullanır. Bu sayede WebRTC iletişimi kurulmadan önce gereken iletişim bilgilerinin alınması sağlanır.
STUN nedir?
STUN (Session Traversal Utilities for NAT) (NAT için oturum dolaşma araçları), bağlantının kurulması için client’ın public adresinin bulunması ve router’da yer alan herhangi bir kısıtlamanın tespit edilmesini sağlayan bir protokoldür. NAT tarafından ayrılan IP adres ve port bilgisinin alınmasını gerçekleştirir.
ICE framework’ünü kullanacak olan uygulama, internet ağında yer alan STUN sunucusuna “Ben kimim?” (Who am I?) isteği gönderir ve STUN sunucusu, uygulamanın çalıştığı cihazın public IP ve port bilgisini geri döndürür. Eğer cihaz, router’ın NAT’ı arkasında yer alıyorsa, cihazın erişilebilir olmadığı hatasını geri döndürür.
Diğer bir görsel:
Bazı router’larda, network üzerinde yer alan cihazlara, hangi IP adreslerinin bağlantı kurabileceğine dair kısıtlamalar bulunabilir. Bu durumda, STUN sunucusundan cihazın public IP adresini alabilsek dahi, bağlantı kurulumu gerçekleşmeyebilir. Bu durumun çözümü için TURN sunucuları kullanılmaktadır.
STUN hakkında daha fazla bilgi için: https://www.3cx.com/blog/voip-howto/stun-details/
TURN nedir?
Bazı router’larda ‘Symmetric NAT’ denilen bir kısıtlama bulunabilir. Bu kısıtlama nedeniyle router, yalnızca daha önceden bağlanmış olduğu eşlerden gelen bağlantı isteklerini kabul edecektir.
TURN (Traversal Using Relays around NAT) (NAT etrafında Röleleri Kullanarak Gezinme) sayesinde ‘Symmetric NAT’ kısıtlamasının bypass edilmesi sağlanır. Bu işlem şu şekilde gerçekleşir: TURN sunucusu ile bir bağlantı açılarak, bilgilerin TURN sunucusu üzerinden iletilmesi ve alınması sağlanır. TURN sunucusu ile bağlantı oluşturularak, diğer eşlere bu TURN sunucusuna bağlanmaları ve internet paketlerini bu sunucuya göndermeleri bildirilir. Devamında ise gönderilen bilgilerin yine TURN sunucusundan alınması sağlanır. Bu çözüm bir yük getirse de, alternatif bir çözüm yolu olmadığında kullanmanız gereken tek yöntemdir.
Diğer bir görsel:
SDP nedir?
SDP (Session Description Protocol) (Oturum Tanımlama Protokolü), kurulan bağlantıda gidip/gelen multimedya içeriğinin hangi özelliklere sahip olduğunun tanımlanması için kullanılır. Bu içerik bilgisinde, çözünürlük değeri, medya formatı, kullanılan codec’ler, bilgi güvenliği varsa gerekli şifreleme bilgisi vb. bulunur. Bu betimleme sayesinde eşler arasında veri iletişimi için bir anlaşma sağlanmış olur. Bu içeriği, multimedya verinin kendisi değil de metadata bilgisi olarak düşünebilirsiniz.
Aslında SDP’nin kendisi tam olarak bir protokol değildir. Bunun yerine cihazlar arasında paylaşılan ortam bilgisinin tasvir edilmesi için kullanılan bir veri formatıdır. Bunu JSON mesajları gibi düşünebilirsiniz.
SDP Veri Yapısı
SDP’nin veri yapısı UTF-8 olarak kodlanan ve = karakteri ile belirlenen bir özelliklerin yer aldığı satırlardan oluşur:
v = 0
o = Serdar 2894567634 1567878465 IN IP4 192.168.1.235
s = VoIP Guvenlik
c = IN IP4 192.168.1.235
t = 0 0
m = audio 49170 RTP / AVP 0 8 97
a = rtpmap: 0 PCMU / 8000
a = rtpmap: 8 PCMA / 8000
a = rtpmap: 97 iLBC / 8000
m = video 51372 RTP / AVP 31 32
a = rtpmap: 31 H261 / 90000
Ayrıntılı bilgi için: http://voipguvenlik.com/oturum-tanimlama-protokolu-session-description-protocol-sdp/
WebRTC algoritması
WebRTC ile iletişim yaparken izlememiz gereken algoritma aşağıdaki gibidir:
React Native projesi içerisinde kullanımı
React Native projemizde telefondaki kamera üzerinden video akışını alıp karşıya göndererek yine kendi telefonumuzda açacağız. Başka bir deyişle local olarak stream edeceğiz. Android emulator üzerinde açıldığında aşağıdaki gibi bir ekran görüntüsü olacak. Üstte kendi yerel video akışımız, altta ise gönderip tekrar bize gecikmeli olarak gelen video akışı yer alacaktır:
Öncelikle projeminizi oluşturalım ve boş haliyle çalıştıralım:
npx react-native init SampleRNWebRTC --template react-native-template-typescript
cd SampleRNWebRTC
npx react-native run-ios
Projemizi git’e ekleyelim ve vscode ile açalım
yarn add react-native-webrtc
yarn add -D @types/react-native-webrtc
npx pod-install
git init
git add .
git commit -m "First commit"
code .
Kamera ve ses kaydının yapılabilmesi için AndroidManifest.xml dosyası içerisine aşağıdaki satırları ekleyelim:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
getUserMedia() metodu ile kameranın açılması ve akışa başlanması
Bunun için App.tsx’i aşağıdaki gibi değiştirelim:
“Kamerayı aç ve akışa başla” butonuna tıklandığında, ön kameradaki video akışı aşağıdaki gibi ekrana basılacaktır:
Kodu açıklayacak olursak:
- const [localStream, setLocalStream]: Bu değişkende kameranın video akışını tutuyoruz ve daha sonra <RTCView> bileşenine streamURL özelliğine parametre olarak veriyoruz. .
- startLocalStream: Butona tıklandığında kameranın açılarak ekrana basılması sağlanır.
- const isFrontCamera = true: Burada öncelikle ön kameranın açılmasını istiyoruz. Bu nedenle bir değişken oluşturup true olarak atadık.
- const devices = await mediaDevices.enumerateDevices(): Telefonda bulunan medya cihazlarının hepsinin alınmasını ve devices isimli array değişkenine atanmasını sağlar.
- const facing = isFrontCamera ? ‘front’ : ‘environment’ : Ön/arka kamera bilgisini string olarak tutulmasını sağlar.
- const videoSourceId: devices array’inden ön/arka olarak belirtilen kamera nesnesinin alınması sağlanır.
- const facingMode: constraints değişkeninde kullanılmak üzere kameranın string değerinin alınması sağlanır.
- const constraints: getUserMedia() metoduna parametre olarak geçilir. Elde edilecek olan video akışı için gerekli bilgiler yer alır.
- const newStream: any = await mediaDevices.getUserMedia(constraints); Belirtilen kameradan video akışı alınır ve newStream değişkenine atanır.
- setLocalStream(newStream): State’te bulunan localStream değişkenine newStream’in atanması sağlanır.
Yerel video akışını aldığımıza göre artık uzak bağlantının kurulmasını gerçekleştirebiliriz.
ICE framework’ü kullanılarak bağlantının kurulması ve arama işleminin gerçekleştirilmesi
Bu adımda ise ICE ile bağlantı işlemini gerçekleştirerek video akışını alıp ekrana basacağız. Bunun için startCall metodunu kodladığımızda, App.tsx aşağıdaki gibi olacaktır:
Kodu çalıştırıp, arama yap butonuna tıkladığınızda, üstte kameradan alınan video içeriği, altta ise gönderilip tekrar geri alınan video içeriği yer alacaktır:
Arama yap tuşuna bastığımızda çalışan startCall metodunun içeriğine değinecek olursak:
- const configuration = {iceServers: [{url: ‘stun:stun.l.google.com:19302’}]}: Burada IP adresinin alınması için Google stun server’ı parametre olarak veriyoruz.
- const localPC = new RTCPeerConnection(configuration): configuration değişkenini baz alarak bir RTCPeerConnection değişkeni oluşturuyoruz.
- localPC.onicecandidate: ICE mesajı alındığında tetiklenen event handler’dır. İlgili iceCandidate’in diğer cihaza iletilmesini sağlar.
- remotePC.onaddstream: Uzak cihazın stream’inin alınmasını sağlar. setRemoteStream ile state’e atanma işlemi gerçekleştirilir.
- localPC.addStream(localStream): Halihazırdaki kamera video akışının, ilgili bağlantıya aktarılmasını sağlar.
Artık uzak bağlantıyı da oluşturduğumuza göre arama işleminin sonlandırılması ve video sesinin kapatılması gibi son dokunuşlara geçebiliriz.
Arama işleminin sonlandırılması ve video sesinin kapatılması gibi diğer işlerin gerçekleştirilmesi
switchCamera(), toggleMute() ve closeStreams() metotları eklenir. App.tsx’in son hali aşağıdaki gibi olacaktır:
Ekran görüntüsü aşağıdaki gibi olacaktır:
Sonuç Olarak
Bu yazımızda WebRTC’yi kullanarak local cihazda video/ses aktarımını sağlamış olduk. Uzak bir cihaz ile haberleşme yapabilmeniz için harici bir REST servis gibi bir sinyalleşme servisine ihtiyacınız var. Basit olması açısından local streaming’de konuyu noktalıyorum. Siz de bu sayfadaki yönergeleri izleyerek kendi WebRTC uygulamanızı yapabilirsiniz.
Projenin bitmiş halini react-native-webrtc-sample reposunda bulabilirsiniz. Bu yazı hakkında soru ve görüşlerinizi aşağıdaki yorumlar kısmından yazabilirsiniz. Bana destek vermek için alkış simgesine tıklayabilirsiniz. Sonraki yazımda görüşmek üzere…
Kaynaklar:
- https://tr.wikipedia.org/wiki/Ger%C3%A7ek_zamanl%C4%B1_ileti%C5%9Fim
- https://tr.wikipedia.org/wiki/WebRTC
- https://developer.mozilla.org/en-US/docs/Web/Guide/API/WebRTC/Peer-to-peer_communications_with_WebRTC
- https://webrtc.org/getting-started/peer-connections
- https://github.com/baconcheese113/react-native-webrtc-minimal