Circular Encoder
Encodes video in a fixed-size circular buffer.
The obvious way to do this would be to store each packet in its own buffer and hook it into a linked list. The trouble with this approach is that it requires constant allocation, which means we'll be driving the GC to distraction as the frame rate and bit rate increase. Instead we create fixed-size pools for video data and metadata, which requires a bit more work for us but avoids allocations in the steady state.
Video must always start with a sync frame (a/k/a key frame, a/k/a I-frame). When the circular buffer wraps around, we either need to delete all of the data between the frame at the head of the list and the next sync frame, or have the file save function know that it needs to scan forward for a sync frame before it can start saving data.
When we're told to save a snapshot, we create a MediaMuxer, write all the frames out, and then go back to what we were doing.