Hi all,
I put together basic audio support for media in mojo here:
Long term this isn't really the right approach though. Instead we'll want a service which allows clients to create and render audio input and output streams. Before talking about any design I'd like to share what the current audio output system looks like; it's a bit simplified, but this is the gist of what happens today:
-----> is an IPC
#####> is a SyncSocket write.
<===== is an action by the OS level AudioOutputStream.
AudioOutputDevice (Renderer) AudioOutputController (Browser)
Create --------> Create ShMem, SyncSocket, AudioOutputStream.
Play --------> Start AudioOutputStream (asynchronous)
| <######## Request first buffer. (delay=0)
Prepared #0 ########> Buffer #0 complete.
<======== AudioOutputStream reads buffer #0 from ShMem.
| <######## Request next buffer. (delay=hardware delay + received)
....................................
... Process repeats ~ every 20ms ...
....................................
... <======== Observe #N not ready, AudioOutputStream times out after ~10ms.
... <######## Request next buffer. (delay=hardware delay + silence)
Prepared #N ########> Buffer #N complete.
Prepared #N+1 ########> Buffer #N+1 complete (overwrites buffer #N).
<======== Observe #N, #N+1 completed. Read ShMem.
I realize that's a bit complicated, so here are the most notable aspects:
- The first buffer is requested before the OS level stream starts. The first OS level callback gets the previously requested buffer.
- We perform a "timed wait" (via select()) if buffers are not ready when the OS level callback comes in.
- Each buffer fulfilled by the renderer sends a counter over the SyncSocket to the browser so the browser knows which buffer has been filled.
- Without timeouts, the flow is a simple producer consumer problem. Browser requests buffer, renderer provides.
- When a timeout occurs, counter values are discarded until the received counter matches the expected counter or a timeout occurs.
I'd expect any API we design in mojo to support the concept of failed fulfillment (a.k.a client timeouts). Practically, and as it is today, what this likely means is that both a shared memory interface and a message passing interface. Towards that end, for output only, I think we'll want a mojom kind of like this:
[Client=AudioOutputStreamClient]
interface AudioOutputStream {
Create(AudioParameters params) => (bool success);
// Starts or stops the OS level stream from reading from the shared memory.
// Client has written the next buffer to the shared memory.
interface AudioOutputStreamClient {
// OS or other type error occurred.
// Client should write the next buffer into the shared memory.
OnPrepareNextBuffer(uint32 audio_delay_bytes);
If all this sounds reasonable I'll start codifying it into a design document of sorts.
- dale