Would you like to tell us how we are doing?

You bet No thanks

Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

RIM proprietary video format (RIMM streaming file)

The RIM proprietary video format consists of a header, a list of frames, and a footer. All values of type int or short are little-endian.

Header

Field

Value

Size

ID tag

RIMM

4 B

version

0

3 B

descriptor location

  • 0 if recording to a file
  • 1 if recording to a stream

1 B

descriptor

descriptor values

  • 75 B if recording to a file
  • 0 B if recording to a stream

Frames

Field

Value

Size

stream type

  • 0 if this is a frame of audio
  • 1 if this is a frame of video

1 B

key frame

  • 1 if this is a key frame
  • 0 otherwise

1 b

config frame

  • 1 if this is a config frame
  • 0 otherwise

1 b

size

frame size, in bytes

30 b

duration

length of video, in milliseconds

2 B

data

the actual frame data

<size> B

stream type

  • 0 if this is a frame of audio
  • 1 if this is a frame of video

1 B

key frame

  • 1 if this is a key frame
  • 0 otherwise

1 b

config frame

  • 1 if this is a config frame
  • 0 otherwise

1 b

size

frame size, in bytes

30 b

duration

length of video, in milliseconds

2 B

The key frame, config frame, and size fields are stored in one 32-bit int with the key frame and config frame fields stored in the higher order (first two) bits of the 32-bit word. For example, video keyframe of size 16909060 bytes at 24fps would have the following header:

01 04 03 02 81 2A 00

Footer

Field

Value

Size

Descriptor

descriptor values

  • 75 bytes if recording to a stream
  • 0 bytes if recording to a file

Descriptor

Field

Value

Size

audio frames

number of audio frames

4 B

video frames

number of video frames

4 B

audio key frames

number of audio key frames

4 B

video key frames

number of video key frames

4 B

audio frame rates

number of audio frame rates (number of frame rate changes + 1)

4 B

video frame rates

number of video frame rates (number of frame rate changes + 1)

4 B

audio size

size of audio stream in bytes

4 B

video size

size of video stream in bytes

4 B

video frame rate

the initial video frame rate, in frames per second

4 B

video max frame size

size of largest video frame, in bytes

4 B

audio duration

length of audio stream, in milliseconds

4 B

video duration

length of video stream, in milliseconds

4 B

RESERVED

undefined

20 B

width

the width of the video, in pixels

2 B

height

the height of the video, in pixels

2 B

video codec

  • 2 if this video codec is mpeg4
  • 5 if this video codec is H.263
  • 6 if this video codec is H.264

2 B

audio codec

  • 0 if this audio codec is PCM
  • 7 if this audio codec is AMR
  • 0xA if this audio codec is AAC

1 B

Code sample: Parsing a RIMM streaming video file

import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.io.file.*;

public class KeyFrameOutputStream extends OutputStream {
    // Output locations on the sd card.
    private static final String OUT_DIR = "file:///SDCard/securitycam/";
    private static final String OUT_FILE = "output.frames";
    
    // Some size constants.
    private static final int HEADER_SIZE = 8;
    private static final int CHUNK_INFO_SIZE = 7;
    
    // Parsing states.
    private static final int STATE_HEADER          = 0;
    private static final int STATE_CHUNK_INFO      = 1;
    private static final int STATE_DATA            = 2;
    private static final int STATE_WAIT_FOR_NEXT   = 3;
    
    // Member variables.
    private int _state;
    private int _pos;
    private boolean _isVideoFrame;
    private boolean _isKeyFrame;
    private boolean _isConfigFrame;
    private boolean _startSaving;
    private boolean _saveFrame;
    private int _dataSize;
    private int _duration;
    
    // Temp buffer ref.
    private byte[] _buf;
    
    private FileConnection _file;
    private OutputStream _out;
    private WriteThread _writer;
    
    private boolean _reading;
    
    public KeyFrameOutputStream() {
        _state = STATE_HEADER;
        _pos = 0;
    }
    
    public void open() {
        _reading = true;
        try {
            // Create the file connection for our frame destination.
            FileConnection dir = (FileConnection)Connector.open( OUT_DIR );
            if( !dir.exists() ) {
                dir.mkdir();
            }
            dir.close();
            
            _file = (FileConnection)Connector.open( OUT_DIR + OUT_FILE );
            if( !_file.exists() ) {
                _file.create();
            } else {
                _file.truncate( 0L );
            }
            _out = _file.openOutputStream();
        } catch ( Exception e ) {
        }
        
        // Start the write thread.
        _writer = new WriteThread( _out );
        _writer.start();
    }
    
    public void startClosing() {
        // Shut down the write thread.
        _reading = false;
        if( _writer != null ) _writer.stop();
    }
    
    public void write( int b ) throws IOException {
        if( _reading ) {
            switch( _state ) {
                case STATE_HEADER:
                    // Read the video stream header.
                    _pos++;
                    if( _pos == HEADER_SIZE ) {
                        _state = STATE_CHUNK_INFO;
                        _buf = new byte[CHUNK_INFO_SIZE];
                        _pos = 0;
                    }
                    break;
                case STATE_CHUNK_INFO:
                    // Parse the information about the next chunk.
                    _buf[_pos] = (byte)b;
                    _pos++;
                    if( _pos == CHUNK_INFO_SIZE ) {
                        // 1 indicates video frame, 0 indicates audio.
                        _isVideoFrame =  (_buf[0] != 0);
                        
                        // Key frame and config frame flags are in the top two bits of 
                        // the data size value.
                        _isKeyFrame =    ((_buf[4] & 0x80) != 0);
                        _isConfigFrame = ((_buf[4] & 0x40) != 0);
                        _dataSize = ((int)(_buf[4] & 0x3f) << 24) |
                                    ((int)(_buf[3] & 0xff) << 16) |
                                    ((int)(_buf[2] & 0xff) <<  8) |
                                    ((int)(_buf[1] & 0xff));
                                    
                        // Duration is stored in the next two bytes.
                        _duration = ((int)(_buf[6] & 0xff) << 8) |
                                    ((int)(_buf[5] & 0xff));
                        
                        // We want the config frame to be the 1st frame in our output file.
                        if( !_startSaving ) {
                            if( _isVideoFrame && _isConfigFrame ) {
                                _startSaving = true;
                            }
                        }
                        
                        // After that, only save the key frames.
                        _saveFrame = 
                          _startSaving && _isVideoFrame && ( _isConfigFrame || _isKeyFrame );
                        
                        _state = STATE_DATA;
                        if( _saveFrame ) {
                            _buf = new byte[_dataSize];
                        }
                        _pos = 0;
                    }
                    break;
                case STATE_DATA:
                    // Buffer the frame for writing to file.
                    if( _saveFrame ) _buf[_pos] = (byte)b;
                    _pos++;
                    if( _pos == _dataSize ) {
                        if( _saveFrame ) {
                            _writer.addFrame( _buf );
                        }
                        _state = STATE_WAIT_FOR_NEXT;
                        _buf = new byte[CHUNK_INFO_SIZE];
                        _pos = 0;
                    }
                    break;
                case STATE_WAIT_FOR_NEXT:
                    // Skip over the chunk footer.
                    _pos++;
                    if( _pos == CHUNK_INFO_SIZE ) {
                        _state = STATE_CHUNK_INFO;
                        _buf = new byte[CHUNK_INFO_SIZE];
                        _pos = 0;
                    }
                    break;
            }
        }
    }
    
    public void close() throws IOException {
        // Shut down the write thread and close our file.
        try {
            _writer.join();
        } catch ( InterruptedException ie ) {
        }
        _out.close();
        _file.close();
    }
    
    private static final class WriteThread extends Thread {
        // Write key frames to a file as they are found by our parser.
        private Vector _frames;
        private boolean _running;
        private OutputStream _out;
        
        public WriteThread( OutputStream out ) {
            _frames = new Vector();
            _running = true;
            _out = out;
        }
        
        public void run() {
            for( ;; ) {
                ByteArray frame = null;
                synchronized( this ) {
                    if( _frames.size() > 0 ) {
                        frame = (ByteArray)_frames.elementAt( 0 );
                        if( frame == null ) break;
                        _frames.removeElementAt( 0 );
                    } else {
                        if( !_running ) break;
                        try {
                            wait();
                            if( _running ) continue;
                        } catch ( InterruptedException ie ) {
                        }
                    }
                }
                
                if( frame == null ) break;
                
                try {
                    byte[] bytes = frame.array;
                    _out.write( bytes, 0, bytes.length );
                    _out.flush();
                } catch ( Exception e ) {
                }
            }
        }
        
        public synchronized void addFrame( byte[] frame ) {
            _frames.addElement( new ByteArray( frame ) );
            notifyAll();
        }
        
        public synchronized void stop() {
            _running = false;
            notifyAll();
        }
    }
    
    private static final class ByteArray {
        public byte[] array;
        public ByteArray( byte[] array ) {
            this.array = array;
        }
    }
}