Aurora.js is a JavaScript/CoffeeScript media framework that makes implementing audio formats, both containers and codecs much easier. Here are a few things it has:
- Sources (HTTP, streaming, File API, etc.)
- Shared Code (buffers, queues, bitstreams, etc.)
- Containers (demuxers)
- Codecs (decoders)
- Sink.js
Here is how one ought to be able to play a file:
player = Player.fromURL(“test.caf”);
player.play();
or:
player = Player.fromFile(file);
player.play();
First the player creates a source, HTTP or otherwise. All sources implement this common interface:
class Source extends EventEmitter
constructor: (input...)
start:
emit ‘data’, chunk
emit ‘error’, ...
emit ‘end’
pause:
reset:
seek: (offset)
Most classes extend the EventEmitter
class, which handles common event emitting tasks and connects the pieces together. The framework creates the source, passing in some input. In the case of HTTP, this would be the URL to the audio file. Then it calls the start method, which at some point in the future emits a number of events, the most important of which being the data
event.
Once the first chunk is emitted by the source, the player must find the proper demuxer for the format. Each registered demuxer's probe
method is called and when one returns true, an instance is created. Here is the interface each demuxer must implement:
class SomeDemuxer extends Demuxer
Demuxer.register(SomeDemuxer)
@probe: (buffer)
return true or false
readChunk: (chunk)
emit ‘format’, ...
emit ‘cookie’, ...
emit ‘metadata’, ...
emit ‘data’, chunk
emit ‘error’, ...
seek: (timestamp)
return byte position
The readChunk
method is called by the framework whenever data is received from the source and is responsible for emitting a number of different events. The format
event contains the general information about the audio format including the following fields:
- formatID
- sampleRate
- bytesPerPacket
- framesPerPacket
- channelsPerFrame
- bitsPerChannel
When this event is received, the player creates the proper decoder based on the formatID
field. The cookie
and data
events are forwarded to the decoder, and any metadata
contained in the file (title, artist, album art, etc.) should be collected and emitted as a single metadata
event.
Here is the interface for all decoders:
class SomeDecoder extends Decoder
setCookie: (cookie)
readChunk:
emit ‘data’, chunk
emit ‘error’, ...
seek: (position)
If one wants to write a new decoder using Aurora.js, they must only implement those three methods: the rest is taken care of by the framework. The constructor is implemented by the Decoder
superclass. The setCookie
method is called with the cookie if there is one when it is received from the demuxer, and the readChunk
method is called when the framework is ready to receive the next decoded chunk.
Finally, the Player
interface at the top of this, and what users interact with, looks like this:
class Player extends EventEmitter
@fromFile: (file)
@fromURL: (url)
preload:
play:
pause:
seek: (timestamp)
property currentTime
property duration
property volume
property metadata
property buffered
event ‘progress’
event ‘buffer’
event ‘ready’
Anyway, those are my ideas. I've actually implemented most of them already as well. :)