Skip to content

Instantly share code, notes, and snippets.

@akirco
Last active August 11, 2023 12:53
Show Gist options
  • Save akirco/7721d19130f0d1a4f3a84cb28a5f3b37 to your computer and use it in GitHub Desktop.
Save akirco/7721d19130f0d1a4f3a84cb28a5f3b37 to your computer and use it in GitHub Desktop.
Test infinite Opus stream
tags categories cover
development
let rs;

self.addEventListener('message', (event) => {
  console.log(event.data);
  rs = event.data;
});

self.addEventListener('install', (event) => {
  console.log(event);
  event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', (event) => {
  console.log(event);
  event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', async (event) => {
  if (event.request.url.includes('stream')) {
    console.log(rs);
    event.respondWith(
      new Response(rs, {
        headers: { 'Content-Type': 'audio/webm;codecs=opus' },
      })
    );
    console.log(rs);
  }
});
<!DOCTYPE html>

<html>
  <head>
    <title>Test infinite Opus stream</title>
    <style>
      body *:not(script) {
        display: block;
      }
    </style>
  </head>

  <body>
    <p>Test infinite Opus stream</p>
    <a
      href="https://bugs.chromium.org/p/chromium/issues/detail?id=1161429"
      target="_blank"
      >Issue 1161429: FLAC and Opus Audio Streams Stop Playing</a
    >
    <h1>Click</h1>
    <data></data>
    <script>
      const unregisterServiceWorkers = async (_) => {
        const registrations = await navigator.serviceWorker.getRegistrations();
        for (const registration of registrations) {
          console.log(registration);
          try {
            await registration.unregister();
          } catch (e) {
            throw e;
          }
        }
        return `ServiceWorker's unregistered`;
      };
      const mimeType = 'audio/webm;codecs=opus';
      const output = document.querySelector('data');
      document.querySelector('h1').onclick = async (e) => {
        let ac, msd, osc, recorder, controller, readable, sw;
        const disconnect = async () => {
          console.log(e);
          msd.disconnect();
          msd.stream.getAudioTracks()[0].stop();
          osc.disconnect();
          await ac.close();
          await unregisterServiceWorkers();
          return;
        };
        try {
          e.target.onclick = null;
          console.log(await unregisterServiceWorkers());
          sw = await navigator.serviceWorker.register('sw.js', {
            scope: './',
          });
          console.log(sw);
          await sw.ready;
          let blobs = 0;
          let bytes = 0;
          ac = new AudioContext({
            latencyHint: 1.0,
          });
          osc = new OscillatorNode(ac, { detune: -1000 });
          osc.start();
          msd = new MediaStreamAudioDestinationNode(ac, {
            channelCount: 2,
          });
          osc.connect(msd);
          const { stream } = msd;
          const [track] = stream.getAudioTracks();
          const audio = new Audio();
          [
            'loadedmetadata',
            'loadeddata',
            'canplay',
            'canplaythrough',
            'play',
            'playing',
            'pause',
            'ended',
            'stalled',
            'waiting',
            'durationchange',
            'suspend',
            'abort',
            'emptied',
          ].forEach((event) =>
            audio.addEventListener(event, (e) => {
              console.log(e.type, audio.currentTime);
            })
          );
          audio.controls = audio.autoplay = true;
          document.body.appendChild(audio);

          readable = new ReadableStream({
            start(_) {
              return (controller = _);
            },
          });

          recorder = new MediaRecorder(stream, { mimeType });
          recorder.ondataavailable = async ({ data }) => {
            controller.enqueue(new Uint8Array(await data.arrayBuffer()));
            ++blobs;
            bytes += data.size;
            if (blobs % 100 === 0) {
              console.log(`bytes:${bytes}, blobs:${blobs}`);
            }
            // output.textContent = `bytes:${bytes}, blobs:${blobs}`;
            if (controller.desiredSize === -1 && !readable.locked) {
              sw.active.postMessage(readable, [readable]);
              audio.src = './stream';
              console.log(controller, blobs, bytes);
              if ('gc' in globalThis && typeof globalThis.gc === 'function') {
                globalThis.gc();
              }
            }
          };
          recorder.start(0);
          audio.onpause = async (e) => {
            console.log(await diconnect());
          };
        } catch (e) {
          console.error(e);
          disconnect();
        }
      };
    </script>
  </body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment