ืขืคืขืก ืืจืืื ืคึฟืื ืึทืืืข ืืื ืฉืืื ืคึฟืึทืจืืึธืื ืึทืจืืืงืืขื, ืืื ืืขืฉืจืืื ืืืขืื ืืื ืืืืื ืืื ืืขืืืืื ืืืขื ืืืจ ืงืขื ืขื ืืจืึธืืงืึทืกื ืืืืืขื ืคึฟืื ืืืึทืืื ื ืืืจื ืืืขืืกืึธืงืงืขืฅ. ืืื ืืขื ืึทืจืืืงื, ืืื ืืขืงืืฆืขืจ ืืขืจืขืื ืืืขืื ืืื ืฆื ืืึทืคึผื ืืืืืขื ืคึฟืื ืึท ืึทืคึผืึทืจืึทื ืืื ืืขืืื ื ืคึฟืื ืึท ืืืงืจืึธืคืึธื , ืืื ืฆื ืขื ืงืึธืื ืื ืืืงืืืขื ืืืึทื ืืื ืฉืืงื ืขืก ืืืจื ืืืขืืกืึธืงืงืขืฅ ืฆื ืื ืกืขืจืืืขืจ. ืึธืืขืจ, ืืื ืคืึทืงื, ืืื ืืึธื ื ืื ืืึธื ืืึธืก, ืคึฟืึทืจ ืืจืึธืืงืึทืกืฅ ืืื ื ืืฆื ืกืคึผืขืฆืืขื ืืืืืืืืืจื ืืืึธืก ืืึทืจืฃ ืืืื ืืื ืกืืึทืืืจื ืืื ืงืึทื ืคืืืืขืจื: ืึธืคืึทื ื, ืืึธืก ืงืขื ืืืื , ืึธืืขืจ ืืื ื ืืฆื WebRTC, ืืืึธืก ืึทืจืืขื ืจืขืื ืืืืก ืคืื ืื ืงืขืกืื, ืืึธืก ืืื, ืขืก ืืื ื ืืฉื ืืึทืจืคื ืื ืืึทื ืืึธื ืืืจืื ื ืคืื ืงืืื ืคึผืืืืื ืก ืึท ืืึท ืืืืฅ ืฉืคึผืืืขืจ, ืืืึธืก ืืืขื ืืืื ืฉื ืืึทืื ืืืืก ืคืื ืื Chromium ืืืขืืขืจืขืจ ืฉืืื ืืื ืืขืฆืขืืืขืจ.
ืืืึทื ื ืืืจ ืืืขืื ืจืขืื ืืืขืื WebRTC.
ืืืขื ืจืขืึทื-ืฆืืื ืงืึธืืื ืืงืึทืฆืืข (WebRTC) ืืื ื ืืฉื ืึท ืืืื ืคึผืจืึธืืึธืงืึธื, ืขืก ืืื ืึท ืืึทื ืฅ ืืึทืืืื ื ืคืื ืกืืึทื ืืึทืจืืก, ืคึผืจืึธืืึธืงืึธืืก ืืื ืืืฉืึทืืืึทืกืงืจืืคึผื ืึทืคึผืืก ืืืึธืก ืฆืืืึทืืขื ืฆืืฉืืขืื ืืึทื ืงืืงื -ืฆื-ืืึทื ืงืืงื ืคืึทืงืืืฉ-ืฆืืื ืืืืืขื-ืึทืืืืึธ ืงืึธืืื ืืงืึทืฆืืข, ืืื ืงืขื ืขื ืืืื ืืืื ืืขื ืืฆื ืฆื ืึทืจืืืขืจืคืืจื ืงืืื. ืืืื ืขืจื ืืึทืื. ืืืืฉืึทืืืึทืื ืืจืึทืืืขืจื ืึทืงื ืืื ืคึผืืจื, ืึธืืขืจ ืขืก ืงืขื ืืืื ืืืื ืึท ืจืืจืขืืืืืง ืึทืคึผืืึทืงืืืฉืึทื, ืืืฉื. ืืื ืกืืจ ืฆื ืึธืจืืึทื ืืืืจื ืคึผ2ืคึผ ืงืึธืืื ืืงืึทืฆืืข ืฆืืืืฉื ืงืืืืึทื ืฅ, ืืืขืืขืจืขืจ ืฉืืืฆื ืคึฟืึทืจ ืคืึทืจืฉืืื ืืืืคึผืก ืคืื ืืืืืขื ืืื ืึทืืืืึธ ืงืึธืืืจืื ื ืืื ืคืืจืืื ืื, ืฉืืืฆื ืคึฟืึทืจ ืคืึทืจืฉืืื ื ืขืฅ ืคึผืจืึธืืึธืงืึธืืก, ืื ืฉืืจืื ื ืื ืื ืืขืจืึทืงืฉืึทื ืคืื ืืึทืื ืืืึทืจื ืืื ืืขื ืืืขืืขืจืขืจ (ืืืจื ืึทืก ืืืึทืขืจืก): ืืืขืืงืึทืืก, ืืขืืื ื ืงืึทืจืืก. ืึทืืข ืืขื ืืึทืืืฉืคึผืึธืืืข ืคืื โโืืขืงื ืึทืืึทืืืฉืื ืืื ืคืึทืจืืึธืจืื ืืื ืืขืจ ืื ืืืฉืึทืืืึทืกืงืจืืคึผื ืึทืคึผื ืึทืืกืืจืึทืงืฆืืข ืคึฟืึทืจ ืื ืงืึทื ืืืื ืืึทื ืก ืคืื ืื ืืขืืืขืืึธืคึผืขืจ.
ืขืก ืึทืืข ืืืืื ืึทืจืึธืคึผ ืฆื ืืจืืึท APIs:
โ ืืขืฆืื ืืื ืืคืืขืืื ืืขืจื, ืืฒื ื ืฐืขื ืืื ืฐืขืื ืืื ืฉืจืฒืื ืืืืกื ืืขืจ. ืกืขืจืืืขืก ืฆื ืืึทืงืืืขื ืืืืืขื / ืึทืืืืึธ ืกืืจืืื ืคืื ืื ืืึทืื ืืืึทืจื
- ืืื ืงืึธืืื ืืงืึทืฆืืข ืฆืืืืฉื ืฆืืืื ืงืืืืึทื ืฅ (ืคึผ2ืคึผ)
- ืกืขืจืืืขืก ืฆื ืึทืจืืืขืจืคืืจื ืึทืจืืืืจืึทืจืืฉ ืืึทืื ืฆืืืืฉื ืฆืืืื ืงืืืืึทื ืฅ
ืคึผืจืืคึผืขืจืื ื ืึทืืืืึธ ืืื ืืืืืขื ืกืืจืืื ืคึฟืึทืจ ืืจืึทื ืกืืืกืืข
ืขืก ืึทืืข ืกืืึทืจืฅ ืืื "ืงืึทืคึผืืฉืขืจืื ื" ืื ืืืขืืงืึทื ืืื ืืืงืจืึธืคืึธื ืืขืืืข ืกืืจืืื. ืืึธื, ืจืื ืกืืจืืื ืืขื ืขื ื ืืฉื ืคึผืึทืกืืง ืคึฟืึทืจ ืึธืจืืึทื ืืืืื ื ืึท ืืขืืขืงืึธื ืคืขืจืขื ืกืข, ืืขืืขืจ ืืืึทื ืืึทืจืฃ ืืืื ืคึผืจืึทืกืขืกื: ืคึฟืึทืจืืขืกืขืจื ืื ืงืืืึทืืืืขื, ืกืื ืืงืจืึทื ืืื ืึทืืืืึธ ืืื ืืืืืขื, ืฉืืขืื ืกืื ืืงืจืึทื ืึทืืืืฉืึทื ืืึทืจืงืก ืืื ืื ืืืืืขื ืืืึทื ืืื ืขื ืฉืืจ ืื ืืืืจืึทืืข ืงืึธืจืึทืกืคึผืึทื ืืื ื ืฆื ืื ืงืึทื ืึทื ืงืึทื ืึทื ืืึทื ืืืืืื. . ืืขืจ ืืืขืืขืจืขืจ ื ืขืื ืืึธืจื ืคืื ืึทืืข ืืขื, ืืขืจ ืืขืืืขืืึธืคึผืขืจ ืืื ื ืืฉื ืืคืืื ืืึธืื ืฆื ืืึธืจื ืืืขืื ืคึผืจืึทืืืืืืื ื ืงืึธืืืจืื ื ืคึฟืึทืจ ืืขืืืข ืกืืจืืื. ืืื ืึท ืืึธืืขืจื ืืืขืืขืจืขืจ, โโืขืก ืืขื ืขื ืฉืืื ืืืืืืืืืจื ืืืึทืขืจืก ืคึฟืึทืจ ืงืึทืคึผืืฉืขืจืื ื, ืืืคึผืจืืืืื ื ืงืืืึทืืืืขื (ืึทืจืืืกื ืขืืขื ืืืืืขืจืงืึธื ืืื ืจืึทืฉ ืคืื ืืขืืื ื, ืคึฟืึทืจืืขืกืขืจื ืื ืืืื), ืืืืืขื ืืื ืึทืืืืึธ ืงืึธืืืจืื ื. ืื ืฉืืืืข ืกืืขืืข ืืื ืืขืืืืื ืืื Fig. 1:
ืจืืืก. 1. ืืืึทืขืจืก ืคืื ืึทืืืืึธ ืืื ืืืืืขื ืคึผืจืึทืกืขืกืื ื ืืื ืืขื ืืืขืืขืจืขืจ
ืื ืคึผืจืึทืกืขืกืื ื ื ืขืื ืึธืจื ืืืืึทื ืืื ืืขื ืืืขืืขืจืขืจ ืืื, ืงืืื ื ืึธื. ืงืืื ืคึผืืืืื ืก ืคืืจืืื ืื. ืึธืืขืจ, ืื ืืื ืื ืืขื ืขื ื ืึธื ื ืืฉื ืึทืืื ืจืึธืืขืืืข ืคึฟืึทืจ 2020. ืขืก ืืขื ืขื ืืจืึทืืืขืจื ืืืึธืก ืืึธื ื ืื ื ืึธื ืืึธืจ ืฉืืืฆื , ืืืจ ืงืขื ื ื ืึธืืืืื ืื ืืื ืง ืืื ืืขื ืื ืงืึทืืคึผืึทืืึทืืืืึทืื ืืืฉ ืืื ืื ืื ืึธ. IE ืืื ืืึทืืื ืืขืจ ืืื ืืืืืขืจ ืืืกืึทืคึผืืื ืื.
ืืืจ ืงืขื ืขื ืืึธื ืืืืขืจ ืืฉืืงืึทืืืข ืืื ืื ืืื ืื ืืืงืืืขื ืกืืจืืื: ืืืจ ืงืขื ืขื ืงืืึธืื, ืืืืฉื ืื ืืืืืขื ืืึทืืืึธืืข, ืืึทื ืืคึผืืืืจื ืื ืึทืืืืึธ ืงืืืึทืืืืขื, ืืืจ ืงืขื ืขื ื ืขืืขื ืืื "ืฆืืืฉืขืคึผืขื" ืื ืืขืืืข ืกืืจืื ืืืึทื ืฆื ืคืึทืจืืื ืื ืืื ืงืืง ืืื ืืืืฃ ืื HTML ืืืึทื. ืึธืืขืจ ืืืจ ืงืขื ืขื ืฆืืขื ืึท ืืืึทื ืืืืฃ ืืืึทืืื ื, ืืื ืฉืืขืื ืืืขืืื ืึธืืขืจ CSS3, ืืื ืฆืืืืืื ืคืึทืจืฉืืื ืคืืืืขืจืก ืฆื ืืืืืขื, ืืึทืคึผื ืื ืคึผืจืึทืกืขืกื ืืืืืขื ืคึฟืื ืืืึทืืื ื ืืื ืืขืจื ืึธื ืฉืืงื ืขืก ืืืืขืจ ืื ื ืขืฅ ืฆื ืื ืกืขืจืืืขืจ, ืืจืึทื ืกืงืึธืืข ืืื ืึทืจืืืกืืขืื ืฆื ืึทืืขืืขื (ืืขืื ืืืืึธื ืืขืื, ืืืืืืฉ). ืืื ืื ืืขืจืข). ืืึธ ืืื ืืืขื ื ืืฉื ืึทื ืึทืืืื ืืื ืึทืืึท ืืื ืื ืืขื ืขื ืืขืืื, ืืื ืืืขื ืืขืื ืึท ืคึผืึธืจ ืคืื ืืืืฉืคืืื ืืขืคืื ืขื ืืืืฃ ืื ืืืขื:
- ืื ืืืื ืืึธื ืคืึทืงืืืฉ-ืฆืืื CV ืืื ืืืฉืึทืืืึทืกืงืจืืคึผื. ืืื ืืึธืื ืึท ืืึทื ืฅ ืคืึทืจืฉืืื ืืืฉืก ืืืืืจืขืจืื ืคึฟืึทืจ ืืจืืขืื ืืื ืึท ืืืืืขื ืกืืจืื ืืืืฃ ืืืึทืืื ื: ืืืืขืงืืื ื ืคื ืืืขืจ, ืึทืืืืฉืขืงืฅ, ืึทืคึผืืืืื ื ืคืืืืขืจืก (ืืึทืกืงืก, ืืื ืืื ืื ืกืืึทืืจืึทื), ืืื"ื ื.
- ืึทืคึผื ืืึทืงืืืืขื ืืืืฉืึทื ืคึฟืึทืจ ืงืึทืคึผืืฉืขืจืื ื ืึท ืืืืืขื ืืืึทื ืคึฟืื ืืืึทืืื ื. ืฉืืื ืืขืฉืืืฆื ืืื ืงืจืึธืื, ืึธืคึผืขืจืึท ืืื ืคืืจืขืคืึธืงืก
RTCPeerConnection
ืึทืืื ืืืจ ืืขืงืืืขื ืฆื ืื ืคืื ื, ืึธืืขืจ ืืื ืฆื ืึทืงืฉืื ืึทืจืืืขืจืคืืจื ืื ืืืืืขื ืฆื ืื ืื ืืขืจ ืืึทื ืืฆืขืจ? ืงืืื ืฆื ืื ืคืึธืจ . ืืื ืงืืจืฅ, ืืื ืึผืืขื ืืขื ืฉืจืื ืืืจ ืืึทืจืคึฟื ืฆื ืฉืึทืคึฟื ืึท RTCPeerConnection ืืืืคืขืฅ:
const peerConnection = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
});ืืืจ ืกืคึผืขืฆืืคืืฆืืจื iceServers ืืื ืืืื ืขืจ ืคืื ืื ืึธืคึผืฆืืขืก - ืืึธืก ืืื ืึท ืกืขืจืืืขืจ ืืืึธืก ืืขืืคึผืก ืฆื ืฆืืฉืืขืื ืึท ืงืฉืจ ืฆืืืืฉื ืฆืืืื ืืจืึทืืืขืจื ืืื ืืขืจ NAT'om. ืึทื ืืื, ืื ืคึผืจืึธืืืขื ืืื ืกืึทืืืื ืืึธ: ืืื ืฆื ืืขืคึฟืื ืขื ืื IP ืคืื ืื ืื ืืขืจืืึทืงืืึทืืขืจ ืืืื ืขืจ ืืื ืืื ืืขืจ ืื NAT โโืคืื ืืืื ืฉืคึผืืึทืืขืจ? ืืขืจ ICE ืคึผืจืึธืืึธืงืึธื ืงืืื ืฆื ืจืึทืืขืืืขื, ืืื ืคืึทืงื, ICE ืืื ื ืืฉื ืึทืคึผืืืื ืฆื WebRTC, ืึธืืขืจ ืืขืจ ืืืืฃ ืืขื ืฉืคึผืขืืขืจ.
ืืื ืึทืืขืจ, ืืืจ ืืึทื Usermedia ืกืืจืืื:
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
// Usermedia-ะฟะพัะพะบะธ, ะพะฑััะฝะพ ััะพ ะฒะธะดะตะพ ะธ ะฐัะดะธะพ
const tracks = stream.getTracks();
for (const track of tracks) {
// ะบะฐะถะดัะน ััะตะบ ะฟัะธัะพะตะดะธะฝัะตะผ ะบ peerConnection
peerConnection.addTrack(track);
}
}).catch(console.error);ืืขืจื ืึธื, ืื ื ืืืืืง ืืขืฉืขืขื ืืฉ ืคืืจืขืก ืืืืฃ ืื ืคึผืขืขืจืงืึธื ื ืขืงืืืึธื, ืืื ืื ืืึทื ืืืขืจ ืคืื ืืืึธืก ืืืจ ืืืื ืืึทืื ืึท ืคืึธืจืฉืืึธื (ืืื ืืขืจืืื ืขื ืคืื SDP - ืกืขืกืืข ืืึทืฉืจืืึทืืื ื ืคึผืจืึธืืึธืงืึธื) ืืื ืืึทืฉืืืืขื ืขืก ืฆื ืื ืคึผืขืขืจืงืึธื ื ืขืงืืืึธื ืืืจื ืื setLocalDescription ืืืคึฟื. ืืืขืื SDP - ืืืึธืก ืขืก ืืื ืืื ืืืขืื ืื ืคืึธืจืฉืืึธืื ืืื ืขื ืืคึฟืขืจื ืคึฟืึธืจืืึทืืืจืื ืืขื - ืืืจ ืืืขืื ืจืขืื ืืืืึทืืขืจ.
ื ืึธื ืึทืกืืื ืื ื ืึท LocalDescription ืคึผืขืขืจืงืึธื ื ืขืงืืืึธื, ืืขืจ ืืืขืืขืจืขืจ "ืึทืกืขืืืึทืื" ืืืื ืงืึทื ืืึทืืืืฅ, ืืึธืก ืืื, ืืขืคืื ื ืคืึทืจืฉืืื ืืืขืื ืฆื ืืืขืจืืขืื ืืืจื NAT. ืื ืึธื ืฆืื ืืื ื ืคืื ืฉืืึทื ืขื ืืขืจืื ื ืืขืฉืขืขื ืืฉ ืคืืจืขืก. ืืื ืื onicegathering-ืกืืืืืืฉืึทื ืืข ืืึทื ืืืขืจ, ืืืจ ืืึธืื ืึท ืคึฟืึทืจืืื ืืื ื ืฆื ืื webrtc-signaling-server ืืืึทื ืฆื ืืืขืงืกื ืื ืกืขืกืืข ืืึทืฉืจืืึทืืื ื ืฆืืืืฉื ืคึผืืจื:
peerConnection.oniceconnectionstatechange = (event) => {
console.log('Connection state: ', peerConnection.iceConnectionState);
if (peerConnection.iceConnectionState === 'connected') {
// ะะพะถะตะผ ะฐะบัะธะฒะธัะพะฒะฐัั ะบะฝะพะฟะบั Start broadcast
setBroadcasting(true);
setBroadcastingBtnActive(true);
}
};
// ะกะพะฑััะธะต ััะฐะฑะฐััะฒะฐะตั ััะฐะทั, ะบะฐะบ ัะพะปัะบะพ ะดะพะฑะฐะฒะธะปัั ะผะตะดะฐะธะฐะฟะพัะพะบ ะฒ peerConnection
peerConnection.onnegotiationneeded = (event) => {
// ะกะพะทะดะฐะตะผ ะธ ะฝะฐะทะฝะฐัะฐะตะผ SDP offer
peerConnection.createOffer().
then((offer) => peerConnection.setLocalDescription(offer)).
catch(console.error);
};
// ะกะพะฑััะธะต ััะฐะฑะฐััะฒะฐะตั ะบะฐะถะดัะน ัะฐะท, ะบะฐะบ ะฟะพัะฒะปัะตััั ICE ะบะฐะฝะดะธะดะฐั
peerConnection.onicegatheringstatechange = (ev) => {
let connection = ev.target;
// Now we can activate broadcast button
if (connection.iceGatheringState === 'complete') {
let delay = 50;
let tries = 0;
let maxTries = 3;
let timerId = setTimeout(function allowStreaming() {
if (isOnline) {
setBroadcastingBtnActive(true);
return;
}
if (tries < maxTries) {
tries += 1;
delay *= 2;
timerId = setTimeout(allowStreaming, delay);
} else {
// TODO: show user notification
console.error("Can't connect to server");
alert("Can't connect to server");
}
}, delay);
}
};ืืขืจ ืืืขืืจืืง-ืกืืื ืึทืืื ื-ืกืขืจืืืขืจ ืืื ืืขืจ ืกืขืจืืืขืจ ืคืืจืืื ืื ืฆื ืืืขืงืกื ืื ืกืขืกืืข ืืึทืฉืจืืึทืืื ื ืฆืืืืฉื ืฆืืืื ืคึผืืจื, ืขืก ืงืขื ืืืื ืื ืกืืืคึผืืึทืกื ืืืขืืกืึธืงืงืขื ืึธืืขืจ ืงืกืืจ ืกืขืจืืืขืจ ืืืืฃ ืงืืื PL. ืืืื ืึทืจืืขื ืืื ืคึผืฉืื: ืฆื ืึธื ื ืขืืขื ืึท ืกืขืกืืข ืืึทืฉืจืืึทืืื ื ืคืื ืืืื ืืึทื ืงืืงื ืืื ืึทืจืืืขืจืคืืจื ืขืก ืฆื ืื ืืขืจื.
ื ืึธื ืืขืจ ืืืขืงืกื ืคืื ืกืขืกืืข ืืืกืงืจืืคึผืฉืึทื ื, ืืืืืข ืืืืื ืืขื ืขื ืืจืืื ืฆื ืืจืึธืืงืึทืกื ืืื ืืึทืงืืืขื ืืืืืขื ืกืืจืืื, ืืืืฃ ืืขืจ ืืืึทื ืืืึธืก ื ืขืื ืื ืืืืืขื ืืืึทื, ืื ืึธื ืืจืึทืงืง ืืขืฉืขืขื ืืฉ ืืื ืืจืืืขืจื ืืืืฃ ืื PeerConnection, ืืื ืื ืืึทื ืืืขืจ ืคืื ืืืึธืก ืื ืืืงืืืขื ืืจืึทืงืก ืงืขื ืขื ืืืื ืึทืกืืื ื ืฆื ืืื ืงืืง ืืื ืืืื ืืึทืืืืกืืข ืื ืืขืจืืึทืงืืึทืืขืจ. ืืขืจ ืืขืึธืจืืข ืืื ืคึผืจืืื.
ืืื ืงืก ืืื ืืืืขืจืึทืืืจ:
- ืืึทืงืืืืขื ืืืืฉืึทื
- ืืืคึผืืึทืืขื ืืืืฉืึทื ืคืื WebRTC ืคึผืจืึธืืึธืงืึธืืก ืืืืฃ ืืืื
- ืึท ืืื ืคืื ืื ืงืจืืืืืขืจื ืคืื ืคึผืืึธื
- ืืื ืืืื ืคืึธืจืฉืืขืืื ื ืืจืึทืืืขืจ ื ืขืืืืึธืจืงืื ื. ืื ืืฉืื ืคืื ืื ืฉืืจืื ื ืืืื ืคืึธืจืฉืืขืืื ื ืคืื ืืืขื ืึทืคึผืืึทืงืืืฉืึทื ื ืืขื ืขื ืืืกืงืึทืกื ืืื ืืขืืึทื. ืืื ืื ืกืืฃ, WebRTC ืืื ืืืกืงืจืืืื. ืืขืจ ืืื ืืื ืืืืขืจ ืึทืื (2013), ืึธืืขืจ ืืื ื ืืฉื ืคืึทืจืืืจื ืืืึทื ืฉืืืืืช.
ืืื ืืขืจ ืืืืึทืืขืจ ืืืื, ืืื ืืืืื ืฆื ืืขืื ืึท ืืืกื ืืขืจ ืืืื ืคืื ืืขืึธืจืืข ืืื ืืื ืคืืจ ืฆื ืคืื ืึทื ืืขืจืงืืืึทืื ืื ืึธืคึผืืจืึธื ืืื ืคึผืจืึทืกืขืกืื ื ืคืื ืึท ืืืืืขื ืืืึทื ืืืืฃ ืื ืกืขืจืืืขืจ ื ืืฆื ืคึผืืึธื, ืืจืึทื ืกืงืึธืืื ื ืฆื HLS ืืืจื ffmpeg ืคึฟืึทืจ ืกืึทืืกืึทืงืืืึทื ื ืืจืึธืืงืึทืกื ืฆื ืืืืืขืจื ืืื ืืขื ืืืขืืขืจืขืจ.
ืคึฟืึทืจ ืื ืืืืขืืืืืืง: (ืืึธืก ืืื ื ืึธืจ ืึทื ืขืงืกืคึผืขืจืืืขื ื).
ืืงืืจ: www.habr.com
