Pelajari alur WebRTC: menyiapkan autentikasi, mengatur arah media, bertukar SDP dan peristiwa model, serta melepas sumber daya.
WebRTC membawa audio dan video melalui trek media, serta peristiwa model dan teks melalui DataChannel. Aplikasi Web dapat memakai API bawaan browser; platform lain dapat memakai pustaka WebRTC standar. Model Studio tidak menyediakan SDK WebRTC khusus.
Lihat Ringkasan Realtime API untuk memastikan dukungan WebRTC pada model tujuan. Kategori yang didukung mencakup interaksi multimodal real-time, percakapan suara, penerjemahan ucapan, dan paket interaksi multimodal. Untuk sintesis atau pengenalan ucapan, gunakan AOQ atau WebSocket sesuai dukungan model.
Siapkan API key untuk wilayah dan ruang kerja di server aplikasi (AppServer), yang memproksikan pertukaran SDP. Untuk kolom autentikasi, lihat Autentikasi token.
Gunakan konteks browser yang aman dan minta izin mikrofon atau kamera sesuai kebutuhan. Tangani pembatasan autoplay browser saat menerima audio.
Siapkan API key dan parameter model tujuan di AppServer.
Buat objek koneksi klien dan daftarkan callback.
Atur arah media dan DataChannel tanpa memasang trek media upstream.
Tukarkan SDP melalui AppServer dan tunggu koneksi serta kanal peristiwa siap.
Konfigurasikan model sesuai protokolnya. Pasang trek media upstream yang diperlukan setelah konfirmasi.
Hentikan pengambilan, tutup koneksi, dan lepas sumber daya setelah interaksi selesai.
Trek media WebRTC membawa audio dan video. Aplikasi memperoleh trek lokal, menghubungkan pemutar jarak jauh, dan bertukar pesan DataChannel sesuai definisi peristiwa model tujuan.
Autentikasi WebRTC berlangsung saat pertukaran SDP; token AOQ tidak diperlukan. Proksikan autentikasi dan SDP melalui AppServer agar API key jangka panjang tidak berada dalam kode frontend.
Koneksi yang berhasil berarti transport telah terbentuk, tetapi pengiriman media harus menunggu inisialisasi model tujuan berhasil. Misalnya, model Realtime harus menunggu session.updated. Klien hanya dapat mengaktifkan media upstream yang diperlukan setelah inisialisasi model berhasil.
AppServer mengatur pertukaran SDP untuk model tujuan, wilayah, dan ruang kerja. Parameter berikut berlaku untuk koneksi Realtime standar. Untuk parameter khusus aplikasi, ikuti praktik terbaik yang sesuai.
Untuk wilayah, endpoint, dan autentikasi, lihat Autentikasi token. Implementasikan antarmuka AppServer dalam aplikasi; antarmuka ini bukan API SDK Model Studio.
2. Menginisialisasi objek koneksi dan mendaftarkan callback
Buat RTCPeerConnection di browser, atau inisialisasi pustaka yang sesuai di platform lain. Sebelum terhubung, daftarkan penangan untuk:
Perubahan status koneksi, termasuk berhasil, gagal, dan terputus.
Pembuatan, pembukaan, pesan, dan penutupan DataChannel.
Trek media jarak jauh untuk menerima dan memutar audio atau video.
Copy
const pc = new RTCPeerConnection({ iceServers: [ ] });const channels = new Set();const senders = new Map();const remoteAudio = document.createElement("audio");remoteAudio.controls = true; // Allow manual playback if autoplay is blocked.remoteAudio.autoplay = true;document.body.appendChild(remoteAudio);let localStream = null;let eventChannel = null;let stopped = false;let started = false;let sessionCreated = false;let updateSent = false;let modelReady = false;let mediaStarted = false;const requestController = new AbortController();
Daftarkan listener pesan sebelum menginisialisasi model agar peristiwa sesi yang dikirim server tidak terlewat. Jangan hanya mendengarkan kanal buatan klien: server mengirim peristiwa melalui kanal bernama txt, jadi tangani juga callback datachannel.
Arah dilihat dari sisi klien. Transceiver WebRTC menegosiasikan pengiriman dan penerimaan: sendrecv dua arah, sendonly hanya mengirim, dan recvonly hanya menerima. Kemampuan yang tersedia juga bergantung pada jawaban SDP server dan dukungan model.
Arah
Konfigurasi
Mengirim audio
Negosiasikan pengiriman audio dan siapkan trek mikrofon atau audio eksternal; pasang setelah model siap
Menerima audio
Negosiasikan penerimaan audio dan hubungkan pemutaran dalam callback trek jarak jauh
Mengirim video
Negosiasikan pengiriman video hanya jika model mendukung input visual; siapkan trek kamera atau video eksternal
Menerima video
Negosiasikan penerimaan dan terapkan rendering hanya jika model atau aplikasi secara eksplisit mendukung output video
Percakapan suara biasanya memerlukan audio dua arah; input visual juga memerlukan video upstream. Offer SDP harus menyertakan bagian media m=audio. Jangan menghapusnya hanya karena aplikasi tidak memerlukan pemutaran audio.
Hanya jika didukung secara eksplisit oleh aplikasi
Tabel ini menjelaskan arah media yang dibutuhkan aplikasi; tidak menjamin semua kombinasi dapat dinegosiasikan secara terpisah. Konfigurasi bergantung pada model tujuan dan kemampuan negosiasi server.
Negosiasikan arah yang diperlukan sebelum membuat Offer. Untuk model yang memerlukan konfirmasi inisialisasi, siapkan kemampuan pengiriman tanpa memasang trek yang diambil. Setelah model siap, gunakan replaceTrack pada sender terkait untuk memulai transmisi. Daftarkan penangan penerimaan media terlebih dahulu.Atur pengiriman audio, penerimaan audio, pengiriman video, dan penerimaan video secara terpisah. Membuka mikrofon, memutar audio, dan menginisialisasi model merupakan operasi terpisah.
Contoh ini menggunakan audio dua arah dengan video upstream opsional. Empat sakelar media menyatakan kebutuhan aplikasi. Aktifkan penerimaan video hanya jika aplikasi tujuan mendukungnya secara eksplisit. addTransceiver menegosiasikan arah terlebih dahulu; trek yang diambil tetap lokal sampai dipasang pada sender setelah model siap.
Copy
const media = { sendAudio: true, receiveAudio: true, sendVideo: false, receiveVideo: false,};function mediaDirection(send, receive) { if (send && receive) return "sendrecv"; if (send) return "sendonly"; if (receive) return "recvonly"; return "inactive";}async function configureMedia() { for (const kind of ["audio", "video"]) { const send = kind === "audio" ? media.sendAudio : media.sendVideo; const receive = kind === "audio" ? media.receiveAudio : media.receiveVideo; // Keep m=audio; the target service must support the selected directions. if (kind === "audio" || send || receive) { const transceiver = pc.addTransceiver(kind, { direction: mediaDirection(send, receive), }); if (send) senders.set(kind, transceiver.sender); } } if (!media.sendAudio && !media.sendVideo) return; const stream = await navigator.mediaDevices.getUserMedia({ audio: media.sendAudio, video: media.sendVideo ? { width: { ideal: 640 }, height: { ideal: 480 }, frameRate: { ideal: 2, max: 2 }, } : false, }); // The user may end the session while the permission prompt is open. if (stopped) { stream.getTracks().forEach(track => track.stop()); throw new Error("Session ended"); } localStream = stream; // sender.track is still null; captured data is not sent to the model.}
Atur resolusi dan frame rate video sesuai model tujuan. Jika frame rate pratinjau lokal dan upstream perlu berbeda, lihat implementasi Canvas dalam praktik terbaik.
Buat DataChannel sebelum Offer agar SDP menyertakan negosiasi kanal data. Untuk kanal buatan klien dan server, lihat praktik terbaik WebRTC di bawah.DataChannel membawa inisialisasi model, teks, dan peristiwa kontrol, serta mengembalikan hasil, status, dan kesalahan model. Trek media membawa audio dan video.
Copy
// See Send and receive events over DataChannels for bindDataChannel.pc.ondatachannel = ({ channel }) => bindDataChannel(channel);const clientChannel = pc.createDataChannel("oai-events");bindDataChannel(clientChannel);
Kumpulkan kandidat menggunakan mode ICE yang didukung server. Contoh ini menunggu pengumpulan ICE selesai sebelum mengirim SDP lokal.
Kirim SDP lokal ke AppServer, yang memanggil endpoint pertukaran SDP Model Studio dengan API key.
Periksa status HTTP. Jika berhasil, berikan Answer SDP yang diterima ke setRemoteDescription.
Tunggu koneksi berhasil dan pastikan DataChannel untuk mengirim peristiwa model telah terbuka.
Pertukaran SDP yang berhasil atau selesainya setRemoteDescription hanya menyelesaikan tahap negosiasi tersebut; hal itu tidak membuktikan koneksi media siap. Peristiwa model hanya dapat dikirim setelah DataChannel berstatus open.
Dengarkan connectionstatechange untuk menentukan status koneksi dan icegatheringstatechange untuk menunggu SDP lokal lengkap. Inisialisasi model juga bergantung pada DataChannel yang terbuka dan peristiwa model; pertukaran SDP saja tidak cukup.
Copy
pc.onconnectionstatechange = () => { console.log("WebRTC state:", pc.connectionState); if (pc.connectionState === "connected") { tryInitializeModel(); } else if (["failed", "disconnected", "closed"].includes(pc.connectionState)) { // This example ends the session; implement recovery as needed. endSession(); }};function waitForIceComplete() { return new Promise((resolve, reject) => { const timer = setTimeout(() => finish(new Error("ICE gathering timed out")), 15000); function finish(error) { clearTimeout(timer); pc.removeEventListener("icegatheringstatechange", check); requestController.signal.removeEventListener("abort", cancel); error ? reject(error) : resolve(); } function check() { if (pc.iceGatheringState === "complete") finish(); } function cancel() { finish(new Error("Session ended")); } pc.addEventListener("icegatheringstatechange", check); requestController.signal.addEventListener("abort", cancel, { once: true }); if (stopped) cancel(); else check(); });}function normalizeAnswerSdp(sdp) { return String(sdp).trim().replace(/\r?\n/g, "\r\n") + "\r\n";}async function startSession() { if (started || stopped) return; started = true; try { await configureMedia(); if (stopped) return; const offer = await pc.createOffer(); await pc.setLocalDescription(offer); await waitForIceComplete(); // Implement this same-origin application endpoint; it is not a Model Studio API. // AppServer forwards SDP with the API key and returns the raw Answer SDP. const response = await fetch("/api/realtime/sdp", { method: "POST", headers: { "Content-Type": "application/sdp" }, body: pc.localDescription.sdp, signal: requestController.signal, }); if (!response.ok) { throw new Error(`SDP exchange failed:${response.status} ${await response.text()}`); } const answerSdp = normalizeAnswerSdp(await response.text()); if (stopped) return; await pc.setRemoteDescription({ type: "answer", sdp: answerSdp }); // Continue through connectionstatechange, DataChannel, and model events. } catch (error) { if (!stopped) console.error("Connection failed:", error); endSession(); }}
AppServer harus meneruskan Offer menggunakan endpoint dan header pada bagian 1. Antarmuka contoh mengembalikan teks SDP biasa. Jangan berikan header HTTP, log, atau pembungkus JSON ke setRemoteDescription.
Saat koneksi dan kanal peristiwa tersedia, inisialisasi model tujuan sesuai protokolnya. Simpan peristiwa inisialisasi server dan gabungkan dengan status kanal untuk melanjutkan alur; jangan menganggap urutan kedatangan callback selalu tetap.Misalnya, model yang dikonfigurasi dengan session.update memerlukan peristiwa sesuai protokol dan konfirmasi sebelum input dikirim. Dapatkan parameter suara, modalitas output, VAD, dan sample rate dari dokumentasi model saat ini. Paket interaksi multimodal menggunakan protokol peristiwanya sendiri.session.created berarti sesi telah dibuat, bukan berarti konfigurasi pasti berlaku. Untuk model yang memerlukan konfirmasi konfigurasi sesi, tunggu session.updated. Model atau aplikasi lain memiliki kondisi kesiapannya sendiri.Navigasi model dalam Ringkasan koneksi WebSocket menautkan definisi peristiwa. Gunakan kembali hanya definisi tersebut; transport media dan pembatasan WebRTC tetap mengikuti halaman ini.
Contoh ini menunjukkan alur Omni session.created → session.update → session.updated. Untuk model lain, ganti peristiwa inisialisasi dan penanganan respons sesuai definisi peristiwa yang ditautkan di bawah. Pasang trek upstream hanya setelah session.updated mengonfirmasi konfigurasi.
Copy
function tryInitializeModel() { if (stopped || updateSent || !sessionCreated || pc.connectionState !== "connected" || eventChannel?.readyState !== "open") return; sendModelEvent({ event_id: `event_${crypto.randomUUID()}`, type: "session.update", session: { modalities: media.receiveAudio ? ["text", "audio"] : ["text"], input_audio_format: "pcm", output_audio_format: "pcm", turn_detection: { type: "server_vad", threshold: 0.5, silence_duration_ms: 800 }, }, }); updateSent = true;}async function handleModelEvent(event, channel) { if (stopped) return; if (event.type === "session.created") { sessionCreated = true; eventChannel = channel; // Send configuration on the channel that delivered the session event. tryInitializeModel(); } else if (event.type === "session.updated" && updateSent) { modelReady = true; await startSendingMedia(); } else if (event.type === "error") { console.error("Model error:", event.error); endSession(); } else { // Handle text, transcripts, and response state; receive audio on media tracks. console.log("Model event:", event); }}
Setelah model siap, pasang trek audio dan video yang diperlukan untuk pengiriman upstream. Tangani output model dalam callback trek jarak jauh dan terus uraikan peristiwa DataChannel.
Audio dikirim melalui trek media RTP; peristiwa input_audio_buffer.append tidak diperlukan.
Gambar dikirim melalui trek video. WebRTC tidak mendukung input_image_buffer.append.
DataChannel membawa teks serta peristiwa status, kontrol, dan kesalahan.
Copy
async function startSendingMedia() { if (stopped || !modelReady || mediaStarted) return; mediaStarted = true; for (const track of localStream?.getTracks() ?? []) { if (stopped) return; const sender = senders.get(track.kind); if (sender) await sender.replaceTrack(track); }}// Register before startSession(); also handle tracks with no streams.const remoteAudioStream = new MediaStream();const remoteVideo = media.receiveVideo ? document.createElement("video") : null;if (remoteVideo) { remoteVideo.autoplay = true; remoteVideo.playsInline = true; remoteVideo.controls = true; document.body.appendChild(remoteVideo);}pc.ontrack = ({ track }) => { if (stopped) return; if (track.kind === "audio" && media.receiveAudio) { remoteAudioStream.addTrack(track); remoteAudio.srcObject = remoteAudioStream; remoteAudio.play().catch(() => { console.info("Autoplay blocked. Use the audio controls to play."); }); } if (track.kind === "video" && remoteVideo) { remoteVideo.srcObject = new MediaStream([track]); remoteVideo.play().catch(() => console.info("Use the video controls to play.")); }};
Saat koneksi tidak diperlukan lagi, hentikan trek pengambilan lokal milik aplikasi, tutup DataChannel dan RTCPeerConnection, lepas pemutar, dan hapus status aplikasi.Hapus status kesiapan model setelah kegagalan atau koneksi terputus secara tidak terduga. Inisialisasi kembali model pada koneksi baru, bukan memakai ulang status sesi koneksi sebelumnya.
Panggil endSession() saat pengguna mengakhiri panggilan atau meninggalkan halaman. Jika aplikasi menambahkan fitur Canvas atau perekaman dari praktik terbaik, batalkan juga loop animasi dan hentikan trek media Canvas serta MediaRecorder.
Mengirim dan menerima peristiwa melalui DataChannel
Pastikan kanal berstatus open sebelum memanggil send dengan peristiwa yang diserialisasikan. Uraikan pesan masuk dalam callback message, termasuk pada kanal buatan server.Ikuti definisi peristiwa klien model tujuan untuk nama, kolom, parameter, dan waktu pengiriman. Ikuti definisi peristiwa server untuk penguraian respons, perubahan status, hasil, dan kesalahan. Kirim peristiwa inisialisasi sesuai persyaratan model, lalu peristiwa teks dan kontrol sesuai kebutuhan aplikasi.
Gunakan fungsi pengikatan yang sama untuk kanal buatan klien dan server. Fungsi ini menangani transport dan penguraian JSON; handleModelEvent pada bagian 5 menangani semantik model. Gunakan sendModelEvent untuk inisialisasi, teks, atau kontrol hanya jika diizinkan protokol model.
Copy
function sendModelEvent(event, channel = eventChannel) { if (stopped || pc.connectionState !== "connected" || channel?.readyState !== "open") { throw new Error("Data channel is not ready"); } channel.send(JSON.stringify(event));}function bindDataChannel(channel) { channels.add(channel); channel.onopen = () => { if (stopped) return; console.log("DataChannel opened:", channel.label); tryInitializeModel(); }; channel.onmessage = ({ data }) => { if (stopped) return; let event; try { event = JSON.parse(data); } catch (error) { console.warn("Cannot parse model event:", error); return; } if (!event || typeof event !== "object") return; handleModelEvent(event, channel).catch(error => { if (!stopped) console.error("Model event handling failed:", error); endSession(); }); }; channel.onerror = error => console.error("DataChannel error:", error); channel.onclose = () => { channels.delete(channel); if (channel === eventChannel) endSession(); }; if (channel.readyState === "open") channel.onopen();}
Letakkan semua cuplikan dalam skrip yang sama. Setelah semua deklarasi dan callback terdaftar, panggil startSession() dari penangan tombol mulai dan endSession() dari penangan tombol selesai. Keduanya adalah fungsi contoh, bukan API bawaan WebRTC.
Paket interaksi multimodal mendefinisikan kedua arah dalam satu protokol interaksi. Pilih definisi peristiwa sesuai versi model yang digunakan; model tidak berbagi satu skema peristiwa tetap.Ikuti juga aturan berikut:
Identifikasi jenis peristiwa dan pengenal korelasi sesuai model. Realtime biasanya menggunakan type; paket interaksi multimodal menggunakan header.action untuk peristiwa klien, header.event untuk peristiwa server, dan task_id untuk korelasi.
Tangani konfirmasi inisialisasi, pengiriman input, pembatalan respons, dan penyelesaian tugas melalui DataChannel sesuai protokol. Operasi ini berbeda dari menutup koneksi transport.
Media menggunakan trek media. Saat merujuk dokumentasi peristiwa model, ikuti halaman WebRTC ini untuk transport dan mode interaksi yang tersedia; jangan menyalin mekanisme unggah media WebSocket.
Panggilan Omni real-time melalui WebRTC: antarmuka aplikasi, perekaman, dan pengurangan frame rate Canvas. Inisialisasi model dan pasang media sesuai urutan pada halaman ini.