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() {
        // shuts 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 first 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 {
        // writes 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;
        }
    }
}

Was this information helpful? Send us your comments.