ValkkaFS
VMS Architecture
When creating a video management system capable of recording and playing simultaneously a large number of streams, here are some of the problems one encounters:
Recorded video streams are playbacked in sync, i.e. their timestamps are matched together
There can be large temporal chunks of video missing from any of the streams (i.e. the video stream is not continuous)
Video is recorded simultaneously while it is being played, to/from the same file
Only a finite amount of frames can be stored/buffered into memory for playback
Book-keeping of the key-frames and rewinding from these key-frames to target time instants as per user’s requests
In order to solve this (surprisingly nasty) problem, we have developed several objects and thread classes. Here is an overview of them:
core.ValkkaFS2
book-keeping of the framescore.ValkkaFSReaderThread
reads frames from a filecore.ValkkaFSWriterThread
writes frames to a filecore.FileCacherThread
caches frames into memory and passes them down the filterchainBoth
core.ValkkaFSReaderThread
andcore.ValkkaFSWriterThread
read/manipulate the book-keeping entity (core.ValkkaFS2
) simultaneously
core.ValkkaFS2
, core.ValkkaFSReaderThread
and core.ValkkaFSWriterThread
can be used for simple
dumping & reading streams to/from disk. Please see the tutorial.
For more complex solution, i.e. the mentioned simultaneous reading, writing & caching, the following filterchain (a) is used:
(core.ValkkaFSWriterThread) --> FILE --> (core.ValkkaFSReaderThread) --> (core.FileCacherThread)
slot-to-id id-to-slot |
|
(DecoderThread) <--------------+
Typically, when recorded frames are played, the following takes place:
Blocks of frames are requested from
core.ValkkaFSReaderThread
. From there they flow tocore.FileCacherThread
When play is requested from
core.FileCacherThread
it passes frames to the decoder at play speed
To make matter simpler for the API user the filterchain in (a) is further encapsulated into an fs.FSGroup
object.
Several FSGroup
objects are further encapsulated into a fs.ValkkaFSManager
object, the final hierarchical object encapsulation
looking like this:
fs.ValkkaFSManager
fs.FSGroup
fs.ValkkaSingleFS
core.ValkkaFS2
core.ValkkaFSWriterThread
core.ValkkaFSReaderThread
core.FileCacherThread
fs.FSGroup
fs.ValkkaSingleFS
core.ValkkaFS2
core.ValkkaFS2
core.ValkkaFSWriterThread
...
ValkkaFSManager
being the “end-point”, from where a user can request synchronized playing and seeking for a number of streams. ValkkaFSManager
would be typically connected
to a GUI component for interactive playback.
Please refer to the PyQt testsuite on how to use FSGroup
and ValkkaFSManager
.
Filesystem
ValkkaSingleFS is a simple filesystem for storing streaming media data. Video frames (H264) are organized in “blocks” and written into a “dumpfile”. The dumpfile can be a regular file or a dedicated disk partition.
Dumpfile is pre-reserved, which makes the life easier for the underlying filesystem and avoids fragmentation (in contrast to creating a huge amount of small, timestamped video segment files).
The size of a single block (S) and the number of blocks (N) are predefined by the user. The total disk space for a recording is then N*S bytes.
Once the last block is written, writing is “wrapped” and resumed from block number 1. This way the oldest recorded stream is overwritten automatically.
Per each ValkkaFS, a directory is created with the following files:
directory/
blockfile # book-keeping of the frames
dumpfile # recorded H264 stream
valkkafs.json # metadata
blockfile
is simple binary file that encapsulates a table with N (number of blocks) rows and two columns. Each column represents a millisecond timestamp:
mstime1 mstime2
... ...
where mstime1
indicates the first key-frame available in a block, while mstime2
indicates the last frame available in that block.
valkkafs.json
saves data about the current written block, number of blocks and the block size.
For efficient recording and playback with ValkkaFS (or with any VMS system), consider this:
For efficient seeking, set your camera to emit one key-frame per second (or even two)
Be aware of the bitrate of your camera and adjust the blocksize in ValkkaFS to that: ideally you’d want 1-2 key frames per block
Consult the tutorial for more details.
Multiple Streams per File
You can also dump multiple streams into a single ValkkaFS
. The variant for this is valkka.fs.ValkkaMultiFS
.
This requires that all cameras have the same bitrate and key-frame interval!
The advantage of this approach is, that all frames from all your cameras are streamed continuously into the same (large) file or a dedicated block device, minimizing the wear and tear on your device if you are using a hdd.
The architecture is identical to ValkkaSingleFS
, with a very small modification to the blockfile
format: mstime1
presents
now the last key-frame among all keyframes of all the streams.
WARNING: writing multiple streams to the same file / block device is at very experimental stage and not well tested
Using an entire partition
WARNING: this makes sense only if you are using ValkkaMultiFS, i.e. streaming several cameras into a same ValkkaFS
An entire hard-drive/partition can be dedicated to ValkkaFS. In the following example, we assume that your external hard-disk appears under /dev/sdb
To grant access to a linux user to read and write block devices directly, use the following command:
sudo usermod -a -G disk username
After that you still need to logout and login again.
Now you can verify that block devices can be read and written as regular files. Try this command:
head -n 10 /dev/sdb
to read the first ten bytes of that external hard-drive.
ValkkaFS uses devices with GPT partition tables, having Linux swap partitions, located on block devices.
Why such a scheme? We’ll be writing over that partition, so we just wan’t to be sure it’s not a normal user filesystem. :)
The next thing we need, is to create a Linux swap partition on that external (or internal) hard disk. The recommended tool for this is gparted.
Start gparted with:
sudo gparted /dev/sdb
Once in gparted, choose device => create partition table. Choose gpt partition table and press apply. Next choose partition, and there, choose linux swap.
Let’s see how it worked out, so type
sudo fdisk -l
You should get something like this:
Device Start End Sectors Size Type
/dev/sdb1 2048 976773134 976771087 465,8G Linux swap
To get the exact size in bytes, type:
blockdev --getsize64 /dev/sdb1
So, in this case we’d be dedicating an external USB drive of 465 GB for recording streaming video.
To identify disks, Valkka uses uuid partition identification. The uuid can be found with
blkid /dev/sdb1
Suppose you get:
/dev/sdb1: UUID="db572185-2ac1-4ef5-b8af-c2763e639a67" TYPE="swap" PARTUUID="37c591e3-b33b-4548-a1eb-81add9da8a58"
Then “37c591e3-b33b-4548-a1eb-81add9da8a58” is what you are looking for.
In this example case, you would instantiate the ValkkaFS like this:
valkkafs = ValkkaMultiFS.newFromDirectory(
dirname="/home/sampsa/tmp/testvalkkafs",
partition_uuid="37c591e3-b33b-4548-a1eb-81add9da8a58",
blocksize=YOUR_BLOCKSIZE,
device_size=1024*1024*1024*465) # 465 GB