diff --git a/CHANGES.md b/CHANGES.md
index b96a545e..0af31c6d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -11,6 +11,8 @@
## develop
+- [CHANGE] オーディオコーデック `LYRA` のサポートを削除する
+ - @tnamao
- [CHANGE] シグナリングオプションの `multistream` は false の時のみレガシーストリームを使用する
- レガシーストリームを使用したい場合は `multistream: false` の指定が必須となる
- @tnamao
diff --git a/examples/check.html b/examples/check.html
index 716ce47e..b3cc1acc 100644
--- a/examples/check.html
+++ b/examples/check.html
@@ -57,27 +57,7 @@
sendrecv, Opus
recvonly
- チャネル ID: sora-js-sdk:check:multistream:lyra
-
- 要確認ブラウザ:
-
- - Chrome / Edge: (Linux or macOS or Windows) and Android
- - Safari: macOS and iOS
-
-
-
-
-
- multistream, sendrecv, Opus
-
-
- multistream, sendrecv, Lyra
-
-
- multistream, recvonly
-
-
- チャネル ID: sora-js-sdk:check:multistream:e2ee:lyra
+ チャネル ID: sora-js-sdk:check:multistream:e2ee:opus
要確認ブラウザ:
@@ -86,19 +66,19 @@ チャネル ID: sora-js-sdk:check:multistream:e2ee:lyra
現在は E2EE は1ページ1コネクションまでなので、以下のパターンを別々のページで実行すること。
multistream, E2EE, sendrecv, Opus
-
-
-
+
+
+
- multistream, E2EE, sendrecv, Lyra
-
-
-
+ multistream, E2EE, sendrecv, Opus
+
+
+
multistream, E2EE, recvonly
-
-
-
+
+
+
@@ -121,44 +101,40 @@ multistream, E2EE, recvonly
document.getElementById('signalingUrl').value = searchParams.get('signalingUrl');
}
- let multistreamE2eeLyraCheckConnections = [];
- async function startMultistreamE2eeLyraCheck(videoId, role, audioCodecType) {
- stopMultistreamE2eeLyraCheck();
+ let multistreamE2eeOpusCheckConnections = [];
+ async function startMultistreamE2eeOpusCheck(videoId, role, audioCodecType) {
+ stopMultistreamE2eeOpusCheck();
await Sora.initE2EE("https://sora-e2ee-wasm.shiguredo.app/2020.2/wasm.wasm");
- initLyra();
- const channelId = 'sora-js-sdk:check:multistream:e2ee:lyra';
- multistreamE2eeLyraCheckConnections = [
+ const channelId = 'sora-js-sdk:check:multistream:e2ee:opus';
+ multistreamE2eeOpusCheckConnections = [
await connect(channelId, videoId, role, {multistream: true, e2ee: true, audioCodecType})
];
}
- function stopMultistreamE2eeLyraCheck() {
- multistreamE2eeLyraCheckConnections.forEach(x => x.disconnect());
- multistreamE2eeLyraCheckConnections = [];
- document.querySelectorAll('.multistream-e2ee-lyra').forEach(x => x.innerHTML = null);
+ function stopMultistreamE2eeOpusCheck() {
+ multistreamE2eeOpusCheckConnections.forEach(x => x.disconnect());
+ multistreamE2eeOpusCheckConnections = [];
+ document.querySelectorAll('.multistream-e2ee-opus').forEach(x => x.innerHTML = null);
}
- let multistreamLyraCheckConnections = [];
- async function startMultistreamLyraCheck() {
- stopMultistreamLyraCheck();
- initLyra();
+ let multistreamOpusCheckConnections = [];
+ async function startMultistreamOpusCheck() {
+ stopMultistreamOpusCheck();
- const channelId = 'sora-js-sdk:check:multistream:lyra';
- multistreamLyraCheckConnections = [
- await connect(channelId, 'multistream-lyra-sendrecv-opus-videos', 'sendrecv',
+ const channelId = 'sora-js-sdk:check:multistream:opus';
+ multistreamOpusCheckConnections = [
+ await connect(channelId, 'multistream-opus-sendrecv-opus-videos', 'sendrecv',
{multistream: true, audioCodecType: "OPUS"}),
- await connect(channelId, 'multistream-lyra-sendrecv-lyra-videos', 'sendrecv',
- {multistream: true, audioCodecType: "LYRA"}),
- await connect(channelId, 'multistream-lyra-recvonly-videos', 'recvonly',
+ await connect(channelId, 'multistream-opus-recvonly-videos', 'recvonly',
{multistream: true}),
];
}
- function stopMultistreamLyraCheck() {
- multistreamLyraCheckConnections.forEach(x => x.disconnect());
- multistreamLyraCheckConnections = [];
- document.querySelectorAll('.multistream-lyra').forEach(x => x.innerHTML = null);
+ function stopMultistreamOpusCheck() {
+ multistreamOpusCheckConnections.forEach(x => x.disconnect());
+ multistreamOpusCheckConnections = [];
+ document.querySelectorAll('.multistream-opus').forEach(x => x.innerHTML = null);
}
let multistreamCheckConnections = [];
@@ -169,10 +145,6 @@ multistream, E2EE, recvonly
multistreamCheckConnections.push(
await connect(channelId, 'multistream-sendrecv-videos', 'sendrecv', {multistream: true}));
- // Lyra が使えない環境で Lyra 用の初期化処理が悪影響を与えないことを確認するために
- // ここで initLyra() を呼び出しておく。
- initLyra();
-
multistreamCheckConnections.push(
await connect(channelId, 'multistream-recvonly-videos', 'recvonly', {multistream: true}));
}
@@ -202,12 +174,6 @@ multistream, E2EE, recvonly
document.querySelectorAll('.legacystream').forEach(x => x.innerHTML = null);
}
- function initLyra() {
- const modelPath = "https://lyra-wasm.shiguredo.app/2023.1.0/";
- const wasmPath = modelPath;
- Sora.initLyra({wasmPath, modelPath});
- }
-
async function connect(channelId, videoId, role, options) {
const debug = false;
const signalingUrl = document.querySelector('#signalingUrl').value;
diff --git a/packages/sdk/package.json b/packages/sdk/package.json
index dba8a6fc..8247a306 100644
--- a/packages/sdk/package.json
+++ b/packages/sdk/package.json
@@ -12,7 +12,6 @@
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.5",
"@rollup/plugin-typescript": "11.1.6",
- "@shiguredo/lyra-wasm": "2023.1.0",
"jsdom": "24.0.0",
"rollup": "4.12.0",
"rollup-plugin-delete": "2.0.0"
diff --git a/packages/sdk/rollup.config.mjs b/packages/sdk/rollup.config.mjs
index 8f57f062..262f1fdc 100644
--- a/packages/sdk/rollup.config.mjs
+++ b/packages/sdk/rollup.config.mjs
@@ -1,10 +1,9 @@
-import fs from "fs";
-import commonjs from '@rollup/plugin-commonjs';
-import resolve from '@rollup/plugin-node-resolve';
-import typescript from '@rollup/plugin-typescript';
-import replace from '@rollup/plugin-replace';
-import del from "rollup-plugin-delete";
-import pkg from '../../package.json';
+import commonjs from '@rollup/plugin-commonjs'
+import resolve from '@rollup/plugin-node-resolve'
+import replace from '@rollup/plugin-replace'
+import typescript from '@rollup/plugin-typescript'
+import del from 'rollup-plugin-delete'
+import pkg from '../../package.json'
const banner = `/**
* ${pkg.name}
@@ -13,36 +12,19 @@ const banner = `/**
* @author: ${pkg.author}
* @license: ${pkg.license}
**/
-`;
+`
export default [
- {
- input: 'src/lyra_worker.ts',
- plugins: [
- resolve(),
- typescript({
- tsconfig: './tsconfig.json'
- }),
- commonjs(),
- ],
- output: {
- sourcemap: false,
- file: './tmp/lyra_worker.js',
- format: 'umd',
- }
- },
{
input: 'src/sora.ts',
plugins: [
replace({
__SORA_JS_SDK_VERSION__: pkg.version,
- __LYRA_WORKER_SCRIPT__: () => fs.readFileSync("./tmp/lyra_worker.js", "base64"),
- preventAssignment: true
+ preventAssignment: true,
}),
resolve(),
typescript({
tsconfig: './tsconfig.json',
- exclude: 'src/lyra_worker.ts',
}),
commonjs(),
],
@@ -51,26 +33,24 @@ export default [
file: '../../dist/sora.js',
format: 'umd',
name: 'Sora',
- banner: banner
- }
+ banner: banner,
+ },
},
{
input: 'src/sora.ts',
plugins: [
replace({
__SORA_JS_SDK_VERSION__: pkg.version,
- __LYRA_WORKER_SCRIPT__: () => fs.readFileSync("./tmp/lyra_worker.js", "base64"),
- preventAssignment: true
+ preventAssignment: true,
}),
resolve(),
typescript({
tsconfig: './tsconfig.json',
- exclude: 'src/lyra_worker.ts',
}),
commonjs(),
del({
targets: './tmp/',
- hook: 'buildEnd'
+ hook: 'buildEnd',
}),
],
output: {
@@ -78,7 +58,7 @@ export default [
file: '../../dist/sora.mjs',
format: 'module',
name: 'Sora',
- banner: banner
- }
- }
-];
+ banner: banner,
+ },
+ },
+]
diff --git a/packages/sdk/src/base.ts b/packages/sdk/src/base.ts
index ea136400..26499f32 100644
--- a/packages/sdk/src/base.ts
+++ b/packages/sdk/src/base.ts
@@ -2,19 +2,10 @@ import { unzlibSync, zlibSync } from 'fflate'
import SoraE2EE from '@sora/e2ee'
import {
- LyraState,
- createLyraWorker,
- isLyraInitialized,
- transformLyraToPcm,
- transformPcmToLyra,
-} from './lyra'
-import {
- AudioCodecType,
Callbacks,
ConnectionOptions,
DataChannelConfiguration,
JSONType,
- RTCEncodedAudioFrame,
SignalingConnectMessage,
SignalingMessage,
SignalingNotifyMessage,
@@ -197,19 +188,6 @@ export default class ConnectionBase {
* @internal
*/
protected e2ee: SoraE2EE | null
- /**
- * mid と AudioCodecType の対応づけを保持するマップ
- *
- * Lyra などのカスタム音声コーデック使用時に RTCRtpReceiver をどのコーデックでデコードすべきかを
- * 判別するために使われる
- *
- * カスタム音声コーデックが有効になっていない場合には空のままとなる
- */
- private midToAudioCodecType: Map = new Map()
- /**
- * Lyra インスタンス
- */
- private lyra?: LyraState
/**
* キーとなる sender が setupSenderTransform で初期化済みかどうか
*/
@@ -288,9 +266,6 @@ export default class ConnectionBase {
this.signalingOfferMessageDataChannels = {}
this.connectedSignalingUrl = ''
this.contactSignalingUrl = ''
- if (isLyraInitialized()) {
- this.lyra = new LyraState()
- }
}
/**
@@ -1308,7 +1283,7 @@ export default class ConnectionBase {
*/
protected async connectPeerConnection(message: SignalingOfferMessage): Promise {
let config = Object.assign({}, message.config)
- if (this.e2ee || this.lyra) {
+ if (this.e2ee) {
// @ts-ignore https://w3c.github.io/webrtc-encoded-transform/#specification
config = Object.assign({ encodedInsertableStreams: true }, config)
}
@@ -1423,19 +1398,12 @@ export default class ConnectionBase {
// setRemoteDescription 後でないと active が反映されないのでもう一度呼ぶ
await this.setSenderParameters(transceiver, this.encodings)
const sessionDescription = await this.pc.createAnswer()
- // TODO(sile): 動作確認
- if (sessionDescription.sdp !== undefined) {
- sessionDescription.sdp = this.processAnswerSdpForLocal(sessionDescription.sdp)
- }
await this.pc.setLocalDescription(sessionDescription)
this.trace('TRANSCEIVER SENDER GET_PARAMETERS', transceiver.sender.getParameters())
return
}
}
const sessionDescription = await this.pc.createAnswer()
- if (sessionDescription.sdp !== undefined) {
- sessionDescription.sdp = this.processAnswerSdpForLocal(sessionDescription.sdp)
- }
this.writePeerConnectionTimelineLog('create-answer', sessionDescription)
await this.pc.setLocalDescription(sessionDescription)
this.writePeerConnectionTimelineLog('set-local-description', sessionDescription)
@@ -1460,57 +1428,7 @@ export default class ConnectionBase {
sdp = sdp.replace(/^m=(audio|video) 0 /gm, (_match, kind: string) => `m=${kind} 9 `)
}
- this.midToAudioCodecType.clear()
- if (this.lyra === undefined || !sdp.includes('109 lyra/')) {
- return sdp
- }
-
- // mid と音声コーデックの対応を保存する
- for (const media of sdp.split(/^m=/m).slice(1)) {
- if (!media.startsWith('audio')) {
- continue
- }
-
- const mid = /a=mid:(.*)/.exec(media)
- if (mid) {
- const codecType = media.includes('109 lyra/') ? 'LYRA' : 'OPUS'
- this.midToAudioCodecType.set(mid[1], codecType)
- }
- }
-
- return this.lyra.processOfferSdp(sdp)
- }
-
- /**
- * カスタムコーデック用に answer SDP を処理するメソッド
- *
- * 処理後の SDP は setLocalDescription() メソッドに渡される
- *
- * @param answer SDP
- * @returns 処理後の SDP
- */
- private processAnswerSdpForLocal(sdp: string): string {
- if (this.lyra === undefined) {
- return sdp
- }
-
- return this.lyra.processAnswerSdpForLocal(sdp)
- }
-
- /**
- * カスタムコーデック用に answer SDP を処理するメソッド
- *
- * 処理後の SDP は Sora に送信される
- *
- * @param answer SDP
- * @returns 処理後の SDP
- */
- private processAnswerSdpForSora(sdp: string): string {
- if (this.lyra === undefined) {
- return sdp
- }
-
- return this.lyra.processAnswerSdpForSora(sdp)
+ return sdp
}
/**
@@ -1519,7 +1437,7 @@ export default class ConnectionBase {
* @param sender 対象となる RTCRtpSender インスタンス
*/
protected async setupSenderTransform(sender: RTCRtpSender): Promise {
- if ((this.e2ee === null && this.lyra === undefined) || sender.track === null) {
+ if (this.e2ee === null || sender.track === null) {
return
}
// 既に初期化済み
@@ -1527,47 +1445,17 @@ export default class ConnectionBase {
return
}
- const isLyraCodec = sender.track.kind === 'audio' && this.options.audioCodecType === 'LYRA'
-
if ('transform' in RTCRtpSender.prototype) {
// WebRTC Encoded Transform に対応しているブラウザ
+ return
+ }
- if (!isLyraCodec || this.lyra === undefined) {
- return
- }
+ // 古い API (i.e., createEncodedStreams) を使っているブラウザ
- const lyraWorker = createLyraWorker()
- const lyraEncoder = await this.lyra.createEncoder()
-
- // @ts-ignore
- sender.transform = new RTCRtpScriptTransform(
- lyraWorker,
- {
- name: 'senderTransform',
- lyraEncoder,
- },
- [lyraEncoder.port],
- )
- } else {
- // 古い API (i.e., createEncodedStreams) を使っているブラウザ
-
- // @ts-ignore
- const senderStreams = sender.createEncodedStreams() as TransformStream
- let readable = senderStreams.readable
- if (isLyraCodec && this.lyra !== undefined) {
- const lyraEncoder = await this.lyra.createEncoder()
- const transformStream = new TransformStream({
- transform: (data: RTCEncodedAudioFrame, controller) =>
- transformPcmToLyra(lyraEncoder, data, controller),
- })
- readable = senderStreams.readable.pipeThrough(transformStream)
- }
- if (this.e2ee) {
- this.e2ee.setupSenderTransform(readable, senderStreams.writable)
- } else {
- readable.pipeTo(senderStreams.writable).catch((e) => console.warn(e))
- }
- }
+ // @ts-ignore
+ const senderStreams = sender.createEncodedStreams() as TransformStream
+ const readable = senderStreams.readable
+ this.e2ee.setupSenderTransform(readable, senderStreams.writable)
this.senderStreamInitialized.add(sender)
}
@@ -1581,52 +1469,21 @@ export default class ConnectionBase {
mid: string | null,
receiver: RTCRtpReceiver,
): Promise {
- if (this.e2ee === null && this.lyra === undefined) {
+ if (this.e2ee === null) {
return
}
- const codecType = this.midToAudioCodecType.get(mid || '')
-
if ('transform' in RTCRtpSender.prototype) {
// WebRTC Encoded Transform に対応しているブラウザ
+ return
+ }
- if (codecType !== 'LYRA' || this.lyra === undefined) {
- return
- }
+ // 古い API (i.e., createEncodedStreams) を使っているブラウザ
- const lyraWorker = createLyraWorker()
- const lyraDecoder = await this.lyra.createDecoder()
-
- // @ts-ignore
- receiver.transform = new RTCRtpScriptTransform(
- lyraWorker,
- {
- name: 'receiverTransform',
- lyraDecoder,
- },
- [lyraDecoder.port],
- )
- } else {
- // 古い API (i.e., createEncodedStreams) を使っているブラウザ
-
- // @ts-ignore
- const receiverStreams = receiver.createEncodedStreams() as TransformStream
- let writable = receiverStreams.writable
- if (codecType === 'LYRA' && this.lyra !== undefined) {
- const lyraDecoder = await this.lyra.createDecoder()
- const transformStream = new TransformStream({
- transform: (data: RTCEncodedAudioFrame, controller) =>
- transformLyraToPcm(lyraDecoder, data, controller),
- })
- transformStream.readable.pipeTo(receiverStreams.writable).catch((e) => console.warn(e))
- writable = transformStream.writable
- }
- if (this.e2ee) {
- this.e2ee.setupReceiverTransform(receiverStreams.readable, writable)
- } else {
- receiverStreams.readable.pipeTo(writable).catch((e) => console.warn(e))
- }
- }
+ // @ts-ignore
+ const receiverStreams = receiver.createEncodedStreams() as TransformStream
+ const writable = receiverStreams.writable
+ this.e2ee.setupReceiverTransform(receiverStreams.readable, writable)
}
/**
@@ -1635,7 +1492,7 @@ export default class ConnectionBase {
protected sendAnswer(): void {
if (this.pc && this.ws && this.pc.localDescription) {
this.trace('ANSWER SDP', this.pc.localDescription.sdp)
- const sdp = this.processAnswerSdpForSora(this.pc.localDescription.sdp)
+ const sdp = this.pc.localDescription.sdp
const message = { type: 'answer', sdp }
this.ws.send(JSON.stringify(message))
this.writeWebSocketSignalingLog('send-answer', message)
diff --git a/packages/sdk/src/lyra.ts b/packages/sdk/src/lyra.ts
deleted file mode 100644
index 4798f2a8..00000000
--- a/packages/sdk/src/lyra.ts
+++ /dev/null
@@ -1,428 +0,0 @@
-import { type RTCEncodedAudioFrame } from './types'
-
-import {
- LYRA_VERSION,
- LyraDecoder,
- LyraDecoderOptions,
- LyraEncoder,
- LyraEncoderOptions,
- LyraModule,
-} from '@shiguredo/lyra-wasm'
-
-/**
- * ビルド時に lyra_worker.ts のビルド結果(の base64 )で置換される文字列
- */
-const LYRA_WORKER_SCRIPT = '__LYRA_WORKER_SCRIPT__'
-
-/**
- * Lyra を使用するために必要な設定を保持するためのグローバル変数
- *
- * undefined の場合には Lyra が無効になっていると判断され、
- * その状態で Lyra で音声をエンコード・デコード使用とすると実行時エラーとなる
- */
-let LYRA_CONFIG: LyraConfig | undefined
-
-/**
- * Lyra のエンコード・デコードに必要な WebAssembly インスタンスを保持するためのグローバル変数
- */
-let LYRA_MODULE: LyraModule | undefined
-
-/**
- * Lyra の設定情報
- */
-export interface LyraConfig {
- /**
- * Lyra の WebAssembly ビルドファイルが配置されているディレクトリのパス(URL)
- */
- wasmPath: string
-
- /**
- * Lyra のモデルファイルが配置されているディレクトリのパス(URL)
- */
- modelPath: string
-}
-
-/**
- * Lyra の初期化を行うメソッド
- *
- * このメソッドの呼び出し時には設定情報の保存のみを行い、
- * Lyra での音声エンコード・デコードに必要な WebAssembly ファイルおよびモデルファイルは、
- * 実際に必要になったタイミングで初めてロードされます
- *
- * Lyra を使うためには以下の機能がブラウザで利用可能である必要があります:
- * - クロスオリジン分離(内部で SharedArrayBuffer クラスを使用しているため)
- * - WebRTC Encoded Transform
- *
- * これらの機能が利用不可の場合には、このメソッドは警告メッセージを出力した上で、
- * 返り値として false を返します
- *
- * @param config Lyra の設定情報
- * @returns Lyra の初期化に成功したかどうか
- *
- * @public
- */
-export function initLyra(config: LyraConfig): boolean {
- if (
- !('createEncodedStreams' in RTCRtpSender.prototype || 'transform' in RTCRtpSender.prototype)
- ) {
- console.warn(
- "This browser doesn't support WebRTC Encoded Transform feature that Lyra requires.",
- )
- return false
- }
-
- if (typeof SharedArrayBuffer === 'undefined') {
- console.warn('Lyra requires cross-origin isolation to use SharedArrayBuffer.')
- return false
- }
-
- LYRA_CONFIG = config
- LYRA_MODULE = undefined
-
- return true
-}
-
-/***
- * Lyra が初期化済みかどうか
- *
- * @returns Lyra が初期化済みかどうか
- * @internal
- */
-export function isLyraInitialized(): boolean {
- return LYRA_CONFIG !== undefined
-}
-
-/**
- * Lyra のエンコーダを生成して返す
- *
- * @param options エンコーダに指定するオプション
- * @returns Lyra エンコーダのプロミス
- * @throws Lyra が未初期化の場合 or LyraConfig で指定したファイルの取得に失敗した場合
- * @internal
- */
-async function createLyraEncoder(options: LyraEncoderOptions = {}): Promise {
- return (await loadLyraModule()).createEncoder(options)
-}
-
-/**
- * Lyra のデコーダを生成して返す
- *
- * @param options デコーダに指定するオプション
- * @returns Lyra デコーダのプロミス
- * @throws Lyra が未初期化の場合 or LyraConfig で指定したファイルの取得に失敗した場合
- * @internal
- */
-async function createLyraDecoder(options: LyraDecoderOptions = {}): Promise {
- return (await loadLyraModule()).createDecoder(options)
-}
-
-/**
- * Lyra 用の WebAssembly インスタンスをロードする
- *
- * 既にロード済みの場合には、そのインスタンスを返す
- *
- * @returns LyraModule インスタンスのプロミス
- * @throws Lyra が未初期化の場合 or LyraConfig で指定したファイルの取得に失敗した場合
- * @internal
- */
-async function loadLyraModule(): Promise {
- if (LYRA_CONFIG === undefined) {
- throw new Error('Lyra has not been initialized. Please call `Sora.initLyra()` beforehand.')
- }
-
- if (LYRA_MODULE === undefined) {
- LYRA_MODULE = await LyraModule.load(LYRA_CONFIG.wasmPath, LYRA_CONFIG.modelPath)
- }
-
- return LYRA_MODULE
-}
-
-/**
- * WebRTC Encoded Transform に渡される Lyra 用の web worker を生成する
- *
- * @returns Lyra でエンコードおよびデコードを行う web worker インスタンス
- * @internal
- */
-export function createLyraWorker() {
- const lyraWorkerScript = atob(LYRA_WORKER_SCRIPT)
- const lyraWorker = new Worker(
- URL.createObjectURL(new Blob([lyraWorkerScript], { type: 'application/javascript' })),
- )
- return lyraWorker
-}
-
-/**
- * PCM(L16)の音声データを Lyra でエンコードする
- *
- * @param encoder Lyra エンコーダ
- * @param encodedFrame PCM 音声データ
- * @param controller 音声データの出力キュー
- * @internal
- */
-export async function transformPcmToLyra(
- encoder: LyraEncoder,
- encodedFrame: RTCEncodedAudioFrame,
- controller: TransformStreamDefaultController,
-): Promise {
- const view = new DataView(encodedFrame.data)
- const rawData = new Int16Array(encodedFrame.data.byteLength / 2)
- for (let i = 0; i < encodedFrame.data.byteLength; i += 2) {
- rawData[i / 2] = view.getInt16(i, false)
- }
- const encoded = await encoder.encode(rawData)
- if (encoded === undefined) {
- // DTX が有効、かつ、 encodedFrame が無音(ないしノイズのみを含んでいる)場合にはここに来る
- return
- }
- encodedFrame.data = encoded.buffer
- controller.enqueue(encodedFrame)
-}
-
-/**
- * Lyra でエンコードされた音声データをデコードして PCM(L16)に変換する
- *
- * @param decoder Lyra デコーダ
- * @param encodedFrame Lyra でエンコードされた音声データ
- * @param controller 音声データの出力キュー
- * @internal
- */
-export async function transformLyraToPcm(
- decoder: LyraDecoder,
- encodedFrame: RTCEncodedAudioFrame,
- controller: TransformStreamDefaultController,
-): Promise {
- if (encodedFrame.data.byteLength === 3) {
- // e2ee を有効にした場合には、e2ee モジュールが不明なパケットを受信した場合に
- // opus の無音パケットを生成するのでそれを無視する。
- // なお、sendrecv or sendonly で接続直後に生成されたパケットを受信すると常にここにくる模様。
- //
- // Lyra では圧縮後の音声データサイズが固定調で、3 バイトとなることはないので、
- // この条件で正常な Lyra パケットが捨てられることはない。
- //
- // FIXME(size): e2ee 側から opus を仮定した無音生成コードがなくなったらこのワークアラウンドも除去する
- return
- }
-
- const decoded = await decoder.decode(new Uint8Array(encodedFrame.data))
- const buffer = new ArrayBuffer(decoded.length * 2)
- const view = new DataView(buffer)
- for (const [i, v] of decoded.entries()) {
- view.setInt16(i * 2, v, false)
- }
- encodedFrame.data = buffer
- controller.enqueue(encodedFrame)
-}
-
-/**
- * SDP に記載される Lyra のエンコードパラメータ
- * @internal
- */
-export class LyraParams {
- /**
- * Lyra のエンコードフォーマットのバージョン
- */
- readonly version: string
-
- /**
- * エンコードビットレート
- */
- readonly bitrate: 3200 | 6000 | 9200
-
- /**
- * DTX を有効にするかどうか
- */
- readonly enableDtx: boolean
-
- private constructor(version: string, bitrate: number, enableDtx: boolean) {
- if (version !== LYRA_VERSION) {
- throw new Error(`Unsupported Lyra version: ${version} (supported version is ${LYRA_VERSION})`)
- }
- if (bitrate !== 3200 && bitrate !== 6000 && bitrate !== 9200) {
- throw new Error(`Unsupported Lyra bitrate: ${bitrate} (must be one of 3200, 6000, or 9200)`)
- }
-
- this.version = version
- this.bitrate = bitrate
- this.enableDtx = enableDtx
- }
-
- /**
- * SDP の media description 部分をパースして Lyra のエンコードパラメータを取得する
- *
- * @param media SDP の media description 部分
- * @returns パース結果
- * @throws SDP の内容が期待通りではなくパースに失敗した場合
- */
- static parseMediaDescription(media: string): LyraParams {
- const version = /^a=fmtp:109.*[ ;]version=([0-9.]+)([;]|$)/m.exec(media)
- if (!version) {
- throw new Error(`Lyra parameter 'version' is not found in media description: ${media}`)
- }
-
- const bitrate = /^a=fmtp:109.*[ ;]bitrate=([0-9]+)([;]|$)/m.exec(media)
- if (!bitrate) {
- throw new Error(`Lyra parameter 'bitrate' is not found in media description: ${media}`)
- }
-
- const usedtx = /^a=fmtp:109.*[ ;]usedtx=([01])([;]|$)/m.exec(media)
- if (!usedtx) {
- throw new Error(`Lyra parameter 'usedtx' is not found in media description: ${media}`)
- }
-
- return new LyraParams(version[1], Number(bitrate[1]), usedtx[1] === '1')
- }
-
- /**
- * このエンコードパラメータに対応する SDP の fmtp 行を生成する
- *
- * @returns SDP の fmtp 行
- */
- toFmtpString(): string {
- return `a=fmtp:109 version=${this.version};bitrate=${this.bitrate};usedtx=${
- this.enableDtx ? 1 : 0
- }`
- }
-}
-
-/**
- * 接続単位の Lyra 関連の状態を保持するためのクラス
- *
- * @internal
- */
-export class LyraState {
- private encoderOptions: LyraEncoderOptions = {}
- private midToLyraParams: Map = new Map()
-
- /**
- * offer SDP を受け取り Lyra 対応のために必要な置換や情報の収集を行う
- *
- * @param sdp offer SDP
- * @returns 処理後の SDP
- */
- processOfferSdp(sdp: string): string {
- if (!sdp.includes('109 lyra/')) {
- // 対象外なので処理する必要はない
- return sdp
- }
-
- this.midToLyraParams = new Map()
-
- const splited = sdp.split(/^m=/m)
- let replacedSdp = splited[0]
- for (let media of splited.slice(1)) {
- const midResult = /a=mid:(.*)/.exec(media)
- if (midResult === null) {
- continue
- }
- const mid = midResult[1]
-
- if (media.startsWith('audio') && media.includes('109 lyra/')) {
- if (media.includes('a=fmtp:109 ')) {
- const params = LyraParams.parseMediaDescription(media)
- if (media.includes('a=recvonly')) {
- // sora からの offer SDP で recvonly ということは client から見れば送信側なので
- // このパラメータをエンコード用に保存しておく
- this.encoderOptions.bitrate = params.bitrate
- this.encoderOptions.enableDtx = params.enableDtx
- }
- this.midToLyraParams.set(mid, params)
- }
-
- // SDP を置換する:
- // - libwebrtc は lyra を認識しないので L16 に置き換える
- // - ただし SDP に L16 しか含まれていないと音声なし扱いになってしまうので、それを防ぐために 110 で opus を追加する
- media = media
- .replace(/SAVPF([0-9 ]*) 109/, 'SAVPF$1 109 110')
- .replace(/109 lyra[/]16000[/]1/, '110 opus/48000/2')
- .replace(/a=fmtp:109 .*/, 'a=rtpmap:109 L16/16000\r\na=ptime:20')
- }
- replacedSdp += `m=${media}`
- }
- return replacedSdp
- }
-
- /**
- * setLocalDescription() に渡される answer SDP を受け取り、Lyra 対応のために必要な処理を行う
- *
- * @param answer SDP
- * @returns 処理後の SDP
- */
- processAnswerSdpForLocal(sdp: string): string {
- if (!sdp.includes('a=rtpmap:110 ')) {
- // Lyra は使われていないので書き換えは不要
- return sdp
- }
-
- const splited = sdp.split(/^m=/m)
- let replacedSdp = splited[0]
- for (let media of splited.slice(1)) {
- if (media.startsWith('audio') && media.includes('a=rtpmap:110 ')) {
- // opus(110) ではなく L16(109) を使うように SDP を書き換える
- //
- // なお libwebrtc 的にはこの置換を行わなくても内部的には L16 が採用されるが、
- // SDP と実際の動作を一致させるためにここで SDP を置換しておく
- media = media
- .replace(/SAVPF([0-9 ]*) 110/, 'SAVPF$1 109')
- .replace(/a=rtpmap:110 opus[/]48000[/]2/, 'a=rtpmap:109 L16/16000')
- .replace(/a=fmtp:110 .*/, 'a=ptime:20')
- }
- replacedSdp += `m=${media}`
- }
- return replacedSdp
- }
-
- /**
- * Sora に渡される answer SDP を受け取り、Lyra 対応のために必要な処理を行う
- *
- * @param answer SDP
- * @returns 処理後の SDP
- */
- processAnswerSdpForSora(sdp: string): string {
- if (!sdp.includes('a=rtpmap:109 L16/16000')) {
- // Lyra は使われていないので書き換えは不要
- return sdp
- }
-
- const splited = sdp.split(/^m=/m)
- let replacedSdp = splited[0]
- for (let media of splited.splice(1)) {
- const midResult = /a=mid:(.*)/.exec(media)
- if (midResult === null) {
- continue
- }
-
- const mid = midResult[1]
- if (mid && media.startsWith('audio') && media.includes('a=rtpmap:109 L16/16000')) {
- // Sora 用に L16 を Lyra に置換する
- const params = this.midToLyraParams.get(mid)
- if (params === undefined) {
- throw new Error(`Unknown audio mid ${mid}`)
- }
- media = media
- .replace(/a=rtpmap:109 L16[/]16000/, 'a=rtpmap:109 lyra/16000/1')
- .replace(/a=ptime:20/, params.toFmtpString())
- }
- replacedSdp += `m=${media}`
- }
-
- return replacedSdp
- }
-
- /**
- * Lyra のエンコーダを生成する
- *
- * @returns 生成されたエンコーダ
- */
- async createEncoder(): Promise {
- return await createLyraEncoder(this.encoderOptions)
- }
-
- /**
- * Lyra のデコーダを生成する
- *
- * @returns 生成されたデコーダ
- */
- async createDecoder(): Promise {
- return await createLyraDecoder({})
- }
-}
diff --git a/packages/sdk/src/lyra_worker.ts b/packages/sdk/src/lyra_worker.ts
deleted file mode 100644
index 55f2389c..00000000
--- a/packages/sdk/src/lyra_worker.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { transformLyraToPcm, transformPcmToLyra } from './lyra'
-import { type RTCEncodedAudioFrame } from './types'
-
-import { LyraDecoder, LyraDecoderState, LyraEncoder, LyraEncoderState } from '@shiguredo/lyra-wasm'
-
-function createSenderTransform(encoderState: LyraEncoderState) {
- const encoder = LyraEncoder.fromState(encoderState)
- return new TransformStream({
- async transform(
- encodedFrame: RTCEncodedAudioFrame,
- controller: TransformStreamDefaultController,
- ) {
- await transformPcmToLyra(encoder, encodedFrame, controller)
- },
- flush() {
- encoder.destroy()
- },
- })
-}
-
-function createReceiverTransform(decoderState: LyraDecoderState) {
- const decoder = LyraDecoder.fromState(decoderState)
- return new TransformStream({
- async transform(
- encodedFrame: RTCEncodedAudioFrame,
- controller: TransformStreamDefaultController,
- ) {
- await transformLyraToPcm(decoder, encodedFrame, controller)
- },
- flush() {
- decoder.destroy()
- },
- })
-}
-
-type OnTransformMessage = {
- transformer: {
- readable: ReadableStream
- writable: WritableStream
- options:
- | { name: 'senderTransform'; lyraEncoder: LyraEncoderState }
- | { name: 'receiverTransform'; lyraDecoder: LyraDecoderState }
- }
-}
-
-declare global {
- // biome-ignore lint/style/useConst: 判断が現時点で難しいため
- let onrtctransform: (msg: OnTransformMessage) => void
-}
-
-onrtctransform = (msg: OnTransformMessage) => {
- if (msg.transformer.options.name === 'senderTransform') {
- const transform = createSenderTransform(msg.transformer.options.lyraEncoder)
- msg.transformer.readable
- .pipeThrough(transform)
- .pipeTo(msg.transformer.writable)
- .catch((e) => console.warn(e))
- } else if (msg.transformer.options.name === 'receiverTransform') {
- const transform = createReceiverTransform(msg.transformer.options.lyraDecoder)
- msg.transformer.readable
- .pipeThrough(transform)
- .pipeTo(msg.transformer.writable)
- .catch((e) => console.warn(e))
- } else {
- console.warn('unknown message')
- console.warn(msg)
- }
-}
diff --git a/packages/sdk/src/sora.ts b/packages/sdk/src/sora.ts
index 499ab0ea..85e74c80 100644
--- a/packages/sdk/src/sora.ts
+++ b/packages/sdk/src/sora.ts
@@ -2,7 +2,6 @@ import SoraE2EE from '@sora/e2ee'
import ConnectionBase from './base'
import { applyMediaStreamConstraints } from './helpers'
-import { LyraConfig, initLyra } from './lyra'
import ConnectionPublisher from './publisher'
import ConnectionSubscriber from './subscriber'
import type {
@@ -184,12 +183,6 @@ export default {
initE2EE: async (wasmUrl: string): Promise => {
await SoraE2EE.loadWasm(wasmUrl)
},
- /**
- * Lyra の初期化を行うメソッド
- *
- * 詳細は lyra.ts の initLyra() メソッドのドキュメントを参照
- */
- initLyra,
/**
* SoraConnection インスタンスを生成するメソッド
*
@@ -237,7 +230,6 @@ export type {
DataChannelEvent,
DataChannelMessageEvent,
JSONType,
- LyraConfig,
Role,
SignalingEvent,
SignalingNotifyConnectionCreated,
diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts
index 6c6de3e8..292c3e99 100644
--- a/packages/sdk/src/types.ts
+++ b/packages/sdk/src/types.ts
@@ -12,7 +12,7 @@ export type SpotlightFocusRid = 'none' | SimulcastRid
export type Simulcast = boolean | { rid: SimulcastRid }
-export type AudioCodecType = 'OPUS' | 'LYRA'
+export type AudioCodecType = 'OPUS'
export type SignalingAudio =
| boolean
@@ -29,11 +29,6 @@ export type SignalingAudio =
useinbandfec?: boolean
usedtx?: boolean
}
- lyra_params?: {
- version?: string
- bitrate?: 3200 | 6000 | 9200
- usedtx?: boolean
- }
}
export type VideoCodecType = 'VP9' | 'VP8' | 'AV1' | 'H264' | 'H265'
@@ -294,8 +289,6 @@ export type ConnectionOptions = {
audioOpusParamsPtime?: number
audioOpusParamsUseinbandfec?: boolean
audioOpusParamsUsedtx?: boolean
- audioLyraParamsBitrate?: 3200 | 6000 | 9200
- audioLyraParamsUsedtx?: boolean
video?: boolean
videoCodecType?: VideoCodecType
videoBitRate?: number
@@ -399,28 +392,3 @@ export type SoraAbendTitle =
| 'INTERNAL-ERROR'
| 'WEBSOCKET-ONCLOSE'
| 'WEBSOCKET-ONERROR'
-
-// 以降は Lyra 対応に必要な insertable streams / webrtc encoded transform 用の型定義
-//
-// TODO(sile): insertable streams や webrtc encoded transform に対応した @types パッケージがリリースされたらそれを使うようにする
-
-// https://www.w3.org/TR/webrtc-encoded-transform/#RTCEncodedAudioFrame-interface
-export interface RTCEncodedAudioFrame {
- readonly timestamp: number
- data: ArrayBuffer
- getMetadata(): RTCEncodedAudioFrameMetadata
-}
-
-// https://www.w3.org/TR/webrtc-encoded-transform/#dictdef-rtcencodedaudioframemetadata
-export interface RTCEncodedAudioFrameMetadata {
- synchronizationSource: number
- payloadType: number
- contributingSources: [number]
-}
-
-// https://w3c.github.io/webrtc-encoded-transform/#rtcrtpscripttransform
-declare global {
- class RTCRtpScriptTransform {
- constructor(worker: Worker, options?: object, transfer?: object[])
- }
-}
diff --git a/packages/sdk/src/utils.ts b/packages/sdk/src/utils.ts
index 7b70aa4a..f2298632 100644
--- a/packages/sdk/src/utils.ts
+++ b/packages/sdk/src/utils.ts
@@ -19,8 +19,6 @@ import {
TransportType,
} from './types'
-import { LYRA_VERSION } from '@shiguredo/lyra-wasm'
-
function browser(): Browser {
const ua = window.navigator.userAgent.toLocaleLowerCase()
if (ua.indexOf('edge') !== -1) {
@@ -219,7 +217,6 @@ export function createSignalingMessage(
'audioOpusParamsUseinbandfec',
'audioOpusParamsUsedtx',
]
- const audioLyraParamsPropertyKeys = ['audioLyraParamsBitrate', 'audioLyraParamsUsedtx']
const videoPropertyKeys = [
'videoCodecType',
'videoBitRate',
@@ -242,9 +239,6 @@ export function createSignalingMessage(
if (0 <= audioOpusParamsPropertyKeys.indexOf(key) && copyOptions[key] !== null) {
return
}
- if (0 <= audioLyraParamsPropertyKeys.indexOf(key) && copyOptions[key] !== null) {
- return
- }
if (0 <= videoPropertyKeys.indexOf(key) && copyOptions[key] !== null) {
return
}
@@ -300,20 +294,6 @@ export function createSignalingMessage(
}
}
- if (message.audio && options.audioCodecType === 'LYRA') {
- if (typeof message.audio !== 'object') {
- message.audio = {}
- }
-
- message.audio.lyra_params = { version: LYRA_VERSION }
- if ('audioLyraParamsBitrate' in copyOptions) {
- message.audio.lyra_params.bitrate = copyOptions.audioLyraParamsBitrate
- }
- if ('audioLyraParamsUsedtx' in copyOptions) {
- message.audio.lyra_params.usedtx = copyOptions.audioLyraParamsUsedtx
- }
- }
-
if (copyOptions.video !== undefined) {
message.video = copyOptions.video
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 71320974..57dfa4bb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -75,9 +75,6 @@ importers:
'@rollup/plugin-typescript':
specifier: 11.1.6
version: 11.1.6(rollup@4.12.0)(tslib@2.6.2)(typescript@5.3.3)
- '@shiguredo/lyra-wasm':
- specifier: 2023.1.0
- version: 2023.1.0
jsdom:
specifier: 24.0.0
version: 24.0.0
@@ -718,10 +715,6 @@ packages:
dev: true
optional: true
- /@shiguredo/lyra-wasm@2023.1.0:
- resolution: {integrity: sha512-RVOyPYrZMCHYb5QM6S8BTAOnGHiqYpoBOG8+9uct4gg1ut9IK8LsVgwv5Q+/t+hWZeRhuyYNXt1PhBUm1cJZzg==}
- dev: true
-
/@sinclair/typebox@0.27.8:
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
dev: true