imgsrv - Simple and fast HTTP server to provide still images and metadata acquired by the camera
This server was developed to increase the transfer rate of individual images acquired by the Elphel 353 series cameras as the CGI (and even fastCGI applications) connected through the universal web servers (Boa, included with Axis SDK, currently on the default port 80 in camera and lighttpd - now used in Elphel cameras together with php in fast CGI mode, port 81) failed to reach the top speed of the 100mbps network. Specialized imgsrv is connected through a different port (currently listens to 8081) and writes GET responses directly to the socket (reading image data from the circbuf using zero-copy memory mapping mmap), reaching 9-10MB/sec - virtually the full bandwidth of the network. This server does not provide any control over the sensor or FPGA compressor operation, its only purpose is to get data acquired to the (currently 19 MB) circular buffer in the system RAM. It is intended to have the functionality similar to the camera video streamers that also deal with the data already being acquired to the system buffer to be used when individual images are needed rather than the continuous video stream .
The imgsrv makes use of the new functionality of the /dev/circbuf driver providing it with a convenient web front end. It serves JPEG images (with Exif data attached) as well as metadata and circbuf status formatting output as the xml files.
imgsrv listens to the camera http port (currently 8081) and parses the rest of the url string as a series of commands, i.e.:
The above command will be interpreted as the following sequences:
First command (needed once):
- set the current image pointer after the last image acquired;
- save the pointer to the "global" (shared by all applications that use it) image pointer
Second command (to be repeated for each image to be transfered):
- set the current image pointer to the global read pointer stored in the driver;
- wait for the image at that pointer to become available (it is possible that no waiting will actually take place if the image acquisition is ahead of the reading images out process. As far as the image readout process does not fall behind too far (by the size of the circbuf) it is OK.
- transfer compressed image to the client
- advance frame pointer to the next one
- and finally - save the new pointer to the global read pointer, so that the same url can be used over and over each time providing the next acquired image
If issued with empty arguments (just http://<camera_ip>:8081/ ) imgsrv outputs a text file with the list of the valid commands.
Commands are separated from the server url and from each other by the slashes ("/"), "?" and "&" also work. Each command is interpreted sequentially, from the left to the right. The /dev/circbuf file stays open for all the command sequence, so local image pointer stays valid from one command to the other.
First group of the commands generates response data sent to the client, they are mutually exclusive and can appear only once in the command line (url). Each subsequent instances of such commands will be ignored. If none of the response-generating commands appear in the URL (but the url is not empty) the server will generate a minimalistic response - 1x1 pixel image. The commands of that group are the following:
- img - send image at the current pointer (if it is the first command - use last acquired image - implied last command), if no image is available and the current pointer server will not wait for the image to be acquired (see wait below) but rather immediately return 1x1 GIF image to the client;
- bimg -same as above, but the whole image is stored in the camera outside of the circbuf before being sent to the client. It makes it safe from being overwritten by the new frames acquired by the camera. This option is useful when the camera is accessed from outside of the fast LAN over the slow network, it is safer and is recommended if the full frame rate is not required;
- simg, sbimg - similar to img, bimg but will imemdiately suggest to save instead of opening image in the browser
- mimg[n] - send images as a multipart JPEG (all commands after will be ignored), possible to specify optional fps reduction i.e. mimg4 - skip 3 frames for each frame output (1/4 fps)
- bmimg[n] - same as above, buffered
- meta - send XML-formated metadata of the current frame instead of the image;
- pointers - send XML-formatted data about the circbuf frame pointers.
- frame - return current frame number as plain text
- wframe - wait for the next frame sync, return current frame number as plain text
Next commands are designed on top of circbuf control commands:
- torp - set frame pointer to the global read pointer, maintained by the camera driver. If frame at that pointer is invalid (i.e. overran by the next images), the scnd command (see below) is used instead;
- towp - set frame pointer to hardware write pointer - position where next frame will be acquired;
- prev - move to previous frame in buffer, nop if none are available;
- next - move to the next frame in the buffer. Pointer will not go beyond write pointer that defines where the next frame will be acquired;
- last - move to the most recently acquired frame, or to write pointer if there are none already acquiured. This command is automatically executed each time the imgsrv is called with a new line;
- first - move to the oldest frame still available in the buffer. It is not safe to rely on it if more frames are expected - data might be overwritten at any moment and the output image/meta data will be corrupted;
- second - move to the second oldest frame in the buffer - somewhat safer than the 'first' - there will be time for the "next" command at least before frame data (and pointer structures) will be overwritten;
- save - save the current frame pointer as a global read pointer that holds it values between the server requests. This pointer is shared between all the clients and applications that use it;
- wait - Delay execution until there will be a frame at the current pointer ready.
imgsrv and Exif data
imgsrv supports Exif format and includes frame metadata in the image header. The Exif headers are generated in several steps.
- First, the Exif template (/var/state/exif.template) file is generated by the Exif_init.php script (during the system start up). The template generated is a binary file that is a valid Exif header itself. This script provides the "static" data (data that does not change between the frames), it also includes specific data that the script collects from various parts of the camera and camera software (like current firmware file name). Finally it provides a place holders for dynamic frame data to be replaced by the actual values by the imgsrv server;
- Next step happens during initialization of the imgsrv. At that time the /var/state/exif.template is read in, parsed and the pointers to the Exif data that imgsrv is aware of are stored in as internal list together with the maximal string lengths that imgsrv may use.
- Last step - imgsrv reads current frames meta data and uses it to fill the Exif fields at the pointers.
You may find documentation about Exif tags in Exif tags or download an official Exif2-2.PDF The fields that are currently updated by imgsrv are the following (all the other fields are preserved as they are generated by the Exif_init.php):
|0x0132||IFD0||DateTime||ASCII||Date/time of the image last modified. This field is likely to be modified later by image-processing software|
|0x9003||IFD1||DateTimeOriginal||ASCII||Date/time of the image acquisition. Here - start of the frame readout (or start of the exposure where applicable)|
|0x9291||IFD1||SubSecTimeOriginal||ASCII||Fractional seconds (digits after the decimal point) to be added to DateTimeOriginal field, 6 digits|
|0x829a||IFD1||ExposureTime||RATIONAL||This field consists of 2 unsigned shorts that make exposure time (in seconds) as a rational number (the nominator divided by the denominator). With the current software denominator is a constant of 10000, imgsrv only updates the nominato|
|0x927c||IFD1||MakerNote||BYTE||This custom field is defined in the camera as 36 bytes, they are filled with raw frame metadata - structure frame_params_t defined in include/asm-cris/elphel/c313a.h. Bytes go in CPU order, so the data is little-endian (the rest is big-endian)|