diff --git a/public/database.js b/public/database.js index d5bc532..a835daa 100644 --- a/public/database.js +++ b/public/database.js @@ -1,102 +1,109 @@ // 使用示例: -// const db = new IndexedDB('myDatabase', 1, 'myStore'); -// await db.open(); -// await db.add({ id: 1, name: 'John' }); -// const data = await db.get(1); -// console.log(data); -// await db.delete(1); +// const db = new IndexedDB('myDatabase', 1, 'myStore') +// await db.open() +// await db.add({ id: 1, name: 'John' }) +// const data = await db.get(1) +// console.log(data) +// await db.delete(1) export default class IndexedDB { constructor(databaseName, databaseVersion, storeName) { - this.databaseName = databaseName; - this.databaseVersion = databaseVersion; - this.storeName = storeName; - this.db = null; + this.databaseName = databaseName + this.databaseVersion = databaseVersion + this.storeName = storeName + this.db = null } - + open() { - return new Promise((resolve, reject) => { - const request = indexedDB.open(this.databaseName, this.databaseVersion); - - request.onerror = (event) => { - reject(event.target.error); - }; - - request.onsuccess = (event) => { - this.db = event.target.result; - resolve(this.db); - }; - - request.onupgradeneeded = (event) => { - const db = event.target.result; - if (!db.objectStoreNames.contains(this.storeName)) { - db.createObjectStore(this.storeName, { keyPath: 'id' }); - } - }; - }); + console.log('open') + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.databaseName, this.databaseVersion) + request.onerror = (event) => { + reject(event.target.error) + } + request.onsuccess = (event) => { + this.db = event.target.result + resolve(this.db) + } + request.onupgradeneeded = (event) => { + const db = event.target.result + if (!db.objectStoreNames.contains(this.storeName)) { + db.createObjectStore(this.storeName, { keyPath: 'id' }) + } + } + }) } - + add(data) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction([this.storeName], 'readwrite'); - const objectStore = transaction.objectStore(this.storeName); - const request = objectStore.add(data); - - request.onerror = (event) => { - reject(event.target.error); - }; - - request.onsuccess = (event) => { - resolve(event.target.result); - }; - }); + console.log('add', data) + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], 'readwrite') + const objectStore = transaction.objectStore(this.storeName) + + // 判断是否已经存在 + const request = objectStore.get(data.id) + request.onerror = (event) => { + reject(event.target.error) + } + request.onsuccess = (event) => { + if (event.target.result) return resolve(event.target.result) + const request = objectStore.add(data) + request.onerror = (event) => { + reject(event.target.error) + } + request.onsuccess = (event) => { + resolve(event.target.result) + } + } + }) } - + get(id) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction([this.storeName], 'readonly'); - const objectStore = transaction.objectStore(this.storeName); - const request = objectStore.get(id); - - request.onerror = (event) => { - reject(event.target.error); - }; - - request.onsuccess = (event) => { - resolve(event.target.result); - }; - }); + console.log('get', id) + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], 'readonly') + const objectStore = transaction.objectStore(this.storeName) + const request = objectStore.get(id) + + request.onerror = (event) => { + reject(event.target.error) + } + + request.onsuccess = (event) => { + resolve(event.target.result) + } + }) } - + getAll() { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction([this.storeName], 'readonly'); - const objectStore = transaction.objectStore(this.storeName); - const request = objectStore.getAll(); - - request.onerror = (event) => { - reject(event.target.error); - }; - - request.onsuccess = (event) => { - resolve(event.target.result); - }; - }); + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], 'readonly') + const objectStore = transaction.objectStore(this.storeName) + const request = objectStore.getAll() + + request.onerror = (event) => { + reject(event.target.error) + } + + request.onsuccess = (event) => { + resolve(event.target.result) + } + }) } - + delete(id) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction([this.storeName], 'readwrite'); - const objectStore = transaction.objectStore(this.storeName); - const request = objectStore.delete(id); - - request.onerror = (event) => { - reject(event.target.error); - }; - - request.onsuccess = (event) => { - resolve(event.target.result); - }; - }); + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], 'readwrite') + const objectStore = transaction.objectStore(this.storeName) + const request = objectStore.delete(id) + + request.onerror = (event) => { + reject(event.target.error) + } + + request.onsuccess = (event) => { + resolve(event.target.result) + } + }) } - } +} diff --git a/public/index.html b/public/index.html index fa69882..818e6e1 100644 --- a/public/index.html +++ b/public/index.html @@ -40,6 +40,38 @@ const ws = new WebSocket(`${protocol}://${host}/webrtc/music`) const pc = new RTCPeerConnection() + var audioSource = null + // 监听音乐列表播放事件 + musicList.on('play', async item => { + audioSource?.stop() // 先停止可能在播放的音乐 + console.log('播放音乐', item.arrayBuffer) + // 复制一份 item.arrayBuffer + const arrayBuffer = item.arrayBuffer.slice(0) + // 传输音乐文件向远程端 + const audioContext = new AudioContext() + audioContext.decodeAudioData(arrayBuffer, async audioBuffer => { + // 将音乐流添加到 RTCPeerConnection + const mediaStreamDestination = audioContext.createMediaStreamDestination() + mediaStreamDestination.stream.getAudioTracks().forEach(function (track) { + pc.addTrack(track, mediaStreamDestination.stream) + }) + // 播放音乐(远程) + audioSource = audioContext.createBufferSource() + audioSource.buffer = audioBuffer + audioSource.connect(mediaStreamDestination) + audioSource.start() + // 创建SDP offer并将其设置为本地描述, 发送给指定的远程端 + const id = clients[0].id + await pc.setLocalDescription(await pc.createOffer()) // 设置本地描述为 offer + ws.send(JSON.stringify({ id, offer: pc.localDescription })) // 发送给远程终端 offer + }) + }) + // 监听音乐列表停止事件 + musicList.on('stop', async () => { + audioSource?.stop() + audioSource = null + }) + // 监听 ICE 候选事件 pc.onicecandidate = event => { if (event.candidate) { diff --git a/public/music.js b/public/music.js index e5f2bfc..8f3e41e 100644 --- a/public/music.js +++ b/public/music.js @@ -3,6 +3,7 @@ import { Button, List, ListItem } from './weigets.js' export default class MusicList { constructor() { + this.EventListeners = {} this.ul = List({}) this.store = new IndexedDB('musicDatabase', 1, 'musicObjectStore') this.store.open().then(() => { @@ -18,9 +19,9 @@ export default class MusicList { this.audio.addEventListener('ended', () => { this.next() }) - this.audio.addEventListener('timeupdate', () => { - console.log(this.audio.currentTime) - }) + //this.audio.addEventListener('timeupdate', () => { + // console.log(this.audio.currentTime) + //}) // 添加音乐按钮 const input = document.createElement('input') @@ -49,14 +50,14 @@ export default class MusicList { innerText: `${item.name} - ${item.size} - ${item.type} - ${item.id}`, onclick: event => { event.stopPropagation() - this.play(item.id) + this.play(item) }, children: [ Button({ innerText: '播放', onclick: event => { event.stopPropagation() - this.play(item.id) + this.play(item) } }), Button({ @@ -70,22 +71,38 @@ export default class MusicList { innerText: '移除', onclick: event => { event.stopPropagation() - this.delete(item.id) + this.delete(item) } }) ] }) this.ul.appendChild(li) } - delete(id) { - // 如果是正在播放的歌曲,停止播放 - this.stop(id) - const li = this.ul.querySelector(`li[data-id="${id}"]`) - this.ul.removeChild(li) - this.store.delete(id) + delete(item) { + this.store.delete(item.id) + this.ul.removeChild(this.ul.querySelector(`#${item.id}`)) + this.stop() // 停止播放 + } + play(item) { + this.audio.src = URL.createObjectURL(new Blob([item.arrayBuffer], { type: item.type })) + this.audio.play() + this._on('play', item) + } + stop() { + this.audio.pause() + this.audio.src = '' + this._on('stop') } - play(id) { } - stop(id) { } next() { } prev() { } + // 添加回调函数 + on(name, callback) { + this.EventListeners[name] = callback + } + // 执行回调函数 + _on(name, ...args) { + if (this.EventListeners[name]) { + this.EventListeners[name](...args) + } + } }