codecWorker function

Future<void> codecWorker(
  1. WorkerChannel channel
)

Hosts one decoder — video or audio — for the life of the worker.

One decoder per worker, matching the seam it implements: a PlatformDecoder is created, used and closed as a unit, so its thread can be too. An A/V stream therefore gets two workers, which is the right trade: they decode concurrently instead of interleaving on one thread, and the payload is fetched once and cached by the browser either way.

Implementation

Future<void> codecWorker(WorkerChannel channel) async {
  final init = channel.initialMessage! as Map<String, Object?>;
  final role = init['role'] as String?;

  PlatformDecoder? video;
  PlatformAudioDecoder? audio;

  channel.handleRequests((Object? request) async {
    switch (request) {
      case 'open':
        // Configure here rather than at spawn time so a codec the browser
        // declines surfaces as a failed request the host can fall back from,
        // not as a worker that started and is quietly useless.
        switch (role) {
          case kRoleVideo:
            video = await WebCodecsVideoDecoder.create(_videoConfig(init));
          case kRoleAudio:
            audio = await WebCodecsAudioDecoder.create(_audioConfig(init));
          default:
            throw CodecInitException(_kBackend, 'unknown worker role: $role');
        }
        return null;

      case ['decode', final Map<String, Object?> packet]:
        final encoded = _packet(packet);
        if (video != null) return _sendFrame(await video!.decode(encoded));
        return <Object?>[
          for (final chunk in await audio!.decode(encoded)) _chunk(chunk),
        ];

      case 'flush':
        if (video != null) {
          return <Object?>[
            for (final frame in await video!.flush()) _sendFrame(frame),
          ];
        }
        return <Object?>[
          for (final chunk in await audio!.flush()) _chunk(chunk),
        ];

      default:
        throw StateError('unknown op: $request');
    }
  });

  await channel.onClose;
  try {
    await video?.close();
    await audio?.close();
  } on Object {
    // Teardown is best effort; the host is already gone.
  }
}