From 7faba88a70c6a819349de5f4966db149ba7b16ca Mon Sep 17 00:00:00 2001 From: blaue-fuchs Date: Fri, 6 Sep 2024 14:45:08 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=82=92=E4=B8=80=E9=80=9A=E3=82=8A=E7=94=A8=E6=84=8F=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/index.html | 1 + examples/sendonly_audio_codec/index.html | 16 ++++++++++++++++ examples/sendonly_audio_codec/main.tms | 0 vite.config.mts | 1 + 4 files changed, 18 insertions(+) create mode 100644 examples/sendonly_audio_codec/index.html create mode 100644 examples/sendonly_audio_codec/main.tms diff --git a/examples/index.html b/examples/index.html index c3955bca..47569c57 100644 --- a/examples/index.html +++ b/examples/index.html @@ -17,6 +17,7 @@
  • サイマルキャスト配信/視聴サンプル
  • メッセージングサンプル
  • 送信音声ビットレートサンプル
  • +
  • 送信音声コーディックサンプル
  • diff --git a/examples/sendonly_audio_codec/index.html b/examples/sendonly_audio_codec/index.html new file mode 100644 index 00000000..44c1c9f6 --- /dev/null +++ b/examples/sendonly_audio_codec/index.html @@ -0,0 +1,16 @@ + + + + + + Sendonly Audio Codec tes + + + +
    +

    Sendonly Audio Codec test

    +
    + + + + \ No newline at end of file diff --git a/examples/sendonly_audio_codec/main.tms b/examples/sendonly_audio_codec/main.tms new file mode 100644 index 00000000..e69de29b diff --git a/vite.config.mts b/vite.config.mts index ac84d1f2..baf68aac 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -17,6 +17,7 @@ export default defineConfig({ spotlight_sendonly: resolve(__dirname, 'examples/spotlight_sendonly/index.html'), spotlight_recvonly: resolve(__dirname, 'examples/spotlight_recvonly/index.html'), sendonly_audio_bit_rate: resolve(__dirname, 'examples/sendonly_audio_bit_rate/index.html'), + sendonly_audio_codec: resolve(__dirname, 'examples/sendonly_audio_codec/index.html'), messaging: resolve(__dirname, 'examples/messaging/index.html'), }, }, From 2760e9284b82d877e282fbfdf816751399f3149f Mon Sep 17 00:00:00 2001 From: blaue-fuchs Date: Fri, 6 Sep 2024 14:56:48 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E3=81=96=E3=81=A3=E3=81=8F=E3=82=8A?= =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=9A=E3=81=A0=E3=81=91=E3=81=A7=E6=B8=88?= =?UTF-8?q?=E3=82=80=E9=83=A8=E5=88=86=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sendonly_audio_codec/index.html | 27 ++++++++--- examples/sendonly_audio_codec/main.mts | 58 ++++++++++++++++++++++++ examples/sendonly_audio_codec/main.tms | 0 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 examples/sendonly_audio_codec/main.mts delete mode 100644 examples/sendonly_audio_codec/main.tms diff --git a/examples/sendonly_audio_codec/index.html b/examples/sendonly_audio_codec/index.html index 44c1c9f6..3af019ec 100644 --- a/examples/sendonly_audio_codec/index.html +++ b/examples/sendonly_audio_codec/index.html @@ -2,15 +2,30 @@ - - Sendonly Audio Codec tes + + Sendonly Audio Codec tes -
    -

    Sendonly Audio Codec test

    -
    - +
    +

    Sendonly Audio Codec test

    + + + +
    +
    + + +
    + +
    +
    +
    + \ No newline at end of file diff --git a/examples/sendonly_audio_codec/main.mts b/examples/sendonly_audio_codec/main.mts new file mode 100644 index 00000000..a21445e1 --- /dev/null +++ b/examples/sendonly_audio_codec/main.mts @@ -0,0 +1,58 @@ +import Sora, { + type SignalingNotifyMessage, + type ConnectionPublisher, + type SoraConnection, +} from 'sora-js-sdk' + +document.addEventListener('DOMContentLoaded', async () => { + const SORA_SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL + const SORA_CHANNEL_ID_PREFIX = import.meta.env.VITE_SORA_CHANNEL_ID_PREFIX || '' + const SORA_CHANNEL_ID_SUFFIX = import.meta.env.VITE_SORA_CHANNEL_ID_SUFFIX || '' + const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN || '' + + const client = new SoraClient( + SORA_SIGNALING_URL, + SORA_CHANNEL_ID_PREFIX, + SORA_CHANNEL_ID_SUFFIX, + ACCESS_TOKEN, + ) +}) + +class SoraClient { + private debug = false + private channelId: string + private metadata: { access_token: string } + private options: object = {} + + private sora: SoraConnection + private connection: ConnectionPublisher + + constructor( + signaling_url: string, + channel_id_prefix: string, + channel_id_suffix: string, + access_token: string, + ) { + this.sora = Sora.connection(signaling_url, this.debug) + + // channel_id の生成 + this.channelId = `${channel_id_prefix}sendonly_audio_codec${channel_id_suffix}` + // access_token を指定する metadata の生成 + this.metadata = { access_token: access_token } + + this.connection = this.sora.sendonly(this.channelId, this.metadata, this.options) + this.connection.on('notify', this.onnotify.bind(this)) + } + + private onnotify(event: SignalingNotifyMessage): void { + if ( + event.event_type === 'connection.created' && + this.connection.connectionId === event.connection_id + ) { + const connectionIdElement = document.querySelector('#connection-id') + if (connectionIdElement) { + connectionIdElement.textContent = event.connection_id + } + } + } +} diff --git a/examples/sendonly_audio_codec/main.tms b/examples/sendonly_audio_codec/main.tms deleted file mode 100644 index e69de29b..00000000 From de0afedebaab066141f48b8671ad92af83b15ac5 Mon Sep 17 00:00:00 2001 From: blaue-fuchs Date: Fri, 6 Sep 2024 16:30:02 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E3=82=B5=E3=83=B3=E3=83=97=E3=83=AB?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sendonly_audio_codec/main.mts | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/examples/sendonly_audio_codec/main.mts b/examples/sendonly_audio_codec/main.mts index a21445e1..0cfcbd38 100644 --- a/examples/sendonly_audio_codec/main.mts +++ b/examples/sendonly_audio_codec/main.mts @@ -16,6 +16,45 @@ document.addEventListener('DOMContentLoaded', async () => { SORA_CHANNEL_ID_SUFFIX, ACCESS_TOKEN, ) + + document.querySelector('#start')?.addEventListener('click', async () => { + const stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true }) + + const audioCodecType = document.getElementById('audio-codec-type') as HTMLSelectElement + const selectedCodecType = audioCodecType.value === 'OPUS' ? audioCodecType.value : undefined + + await client.connect(stream, selectedCodecType) + }) + + document.querySelector('#stop')?.addEventListener('click', async () => { + await client.disconnect() + }) + + document.querySelector('#get-stats')?.addEventListener('click', async () => { + const statsReport = await client.getStats() + const statsDiv = document.querySelector('#stats-report') as HTMLElement + const statsReportJsonDiv = document.querySelector('#stats-report-json') + if (statsDiv && statsReportJsonDiv) { + let statsHtml = '' + const statsReportJson: Record[] = [] + // biome-ignore lint/complexity/noForEach: + statsReport.forEach((report) => { + statsHtml += `

    Type: ${report.type}

      ` + const reportJson: Record = { id: report.id, type: report.type } + for (const [key, value] of Object.entries(report)) { + if (key !== 'type' && key !== 'id') { + statsHtml += `
    • ${key}: ${value}
    • ` + reportJson[key] = value + } + } + statsHtml += '
    ' + statsReportJson.push(reportJson) + }) + statsDiv.innerHTML = statsHtml + // データ属性としても保存(オプション) + statsDiv.dataset.statsReportJson = JSON.stringify(statsReportJson) + } + }) }) class SoraClient { @@ -44,6 +83,35 @@ class SoraClient { this.connection.on('notify', this.onnotify.bind(this)) } + async connect(stream: MediaStream, audioCodecType?: string): Promise { + if (audioCodecType && audioCodecType === 'OPUS') { + // 音声コーディックを上書きする + this.connection.options.audioCodecType = audioCodecType + } + await this.connection.connect(stream) + + const audioElement = document.querySelector('#local-audio') + if (audioElement !== null) { + audioElement.srcObject = stream + } + } + + async disconnect(): Promise { + await this.connection.disconnect() + + const audioElement = document.querySelector('#local-audio') + if (audioElement !== null) { + audioElement.srcObject = null + } + } + + getStats(): Promise { + if (this.connection.pc === null) { + return Promise.reject(new Error('PeerConnection is not ready')) + } + return this.connection.pc.getStats() + } + private onnotify(event: SignalingNotifyMessage): void { if ( event.event_type === 'connection.created' && From f631d563cd10d637b1524a90d7df512c1e922629 Mon Sep 17 00:00:00 2001 From: blaue-fuchs Date: Fri, 6 Sep 2024 17:09:13 +0900 Subject: [PATCH 4/5] =?UTF-8?q?E2E=20=E3=83=86=E3=82=B9=E3=83=88=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/sendonly_audio_codec.spec.ts | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/sendonly_audio_codec.spec.ts diff --git a/tests/sendonly_audio_codec.spec.ts b/tests/sendonly_audio_codec.spec.ts new file mode 100644 index 00000000..18b2b653 --- /dev/null +++ b/tests/sendonly_audio_codec.spec.ts @@ -0,0 +1,57 @@ +import { expect, test } from '@playwright/test' + +test('sendonly audio codec type pages', async ({ browser }) => { + const sendonly = await browser.newPage() + await sendonly.goto('http://localhost:9000/sendonly_audio_codec/') + + // select 要素から直接オプションを取得してランダムに選択 + const randomAudioCodec = await sendonly.evaluate(() => { + const select = document.querySelector('#audio-codec-type') as HTMLSelectElement + const options = Array.from(select.options) + const randomOption = options[Math.floor(Math.random() * options.length)] + select.value = randomOption.value + return randomOption.value + }) + + // 選択したコーディックタイプをログに出力 + console.log('Selected codec:', randomAudioCodec) + + // Start ボタンクリック + await sendonly.click('#start') + await sendonly.waitForSelector('#connection-id:not(:empty)') + + // #connection-id 要素の内容を取得 + const sendonlyConnectionId = await sendonly.$eval('#connection-id', (el) => el.textContent) + console.log(`Selected codec: connectionId=${sendonlyConnectionId}`) + + // レース対策 + await sendonly.waitForTimeout(3000) + + // 'Get Stats' ボタンをクリックして統計情報を取得 + await sendonly.click('#get-stats') + // 統計情報が表示されるまで待機 + await sendonly.waitForSelector('#stats-report') + + // データセットから統計情報を取得 + const sendonlyStatsReportJson: Record[] = await sendonly.evaluate(() => { + const statsReportDiv = document.querySelector('#stats-report') as HTMLDivElement + return statsReportDiv ? JSON.parse(statsReportDiv.dataset.statsReportJson || '[]') : [] + }) + + const sendonlyAudioCodecStats = sendonlyStatsReportJson.find( + (report) => report.type === 'codec' && report.mimeType === 'audio/opus', + ) + + // 今は指定してもしなくても OOPUS のみ + expect(sendonlyAudioCodecStats).toBeDefined() + const sendonlyAudioOutboundRtp = sendonlyStatsReportJson.find( + (report) => report.type === 'outbound-rtp' && report.kind === 'audio', + ) + // 音声が正常に送れているかの確認 + expect(sendonlyAudioOutboundRtp).toBeDefined() + expect(sendonlyAudioOutboundRtp?.bytesSent).toBeGreaterThan(0) + expect(sendonlyAudioOutboundRtp?.packetsSent).toBeGreaterThan(0) + + await sendonly.click('#stop') + await sendonly.close() +}) From 50eba0f72c4a67d662efecc9b1b8b12558c69c44 Mon Sep 17 00:00:00 2001 From: blaue-fuchs Date: Fri, 6 Sep 2024 17:35:51 +0900 Subject: [PATCH 5/5] =?UTF-8?q?typo=20=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sendonly_audio_codec/index.html | 2 +- tests/sendonly_audio_codec.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/sendonly_audio_codec/index.html b/examples/sendonly_audio_codec/index.html index 3af019ec..1c2983fa 100644 --- a/examples/sendonly_audio_codec/index.html +++ b/examples/sendonly_audio_codec/index.html @@ -3,7 +3,7 @@ - Sendonly Audio Codec tes + Sendonly Audio Codec test diff --git a/tests/sendonly_audio_codec.spec.ts b/tests/sendonly_audio_codec.spec.ts index 18b2b653..30535a77 100644 --- a/tests/sendonly_audio_codec.spec.ts +++ b/tests/sendonly_audio_codec.spec.ts @@ -42,7 +42,7 @@ test('sendonly audio codec type pages', async ({ browser }) => { (report) => report.type === 'codec' && report.mimeType === 'audio/opus', ) - // 今は指定してもしなくても OOPUS のみ + // 今は指定してもしなくても OPUS のみ expect(sendonlyAudioCodecStats).toBeDefined() const sendonlyAudioOutboundRtp = sendonlyStatsReportJson.find( (report) => report.type === 'outbound-rtp' && report.kind === 'audio',