23 August 2007

create a datasource from a stream using JMF

Two things you need before you can do this. First, you need to take the stream and convert it to a byte buffer (easy enough). Secondly, you need to know the content type of the stream

import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.PullDataSource;

import java.nio.ByteBuffer;
import java.io.IOException;

import javax.media.MediaLocator;
import javax.media.Duration;
import javax.media.Time;


/**
*
* @author Chad McMillan
*/

public class ByteBufferDataSource extends PullDataSource {

protected ContentDescriptor contentType;
protected SeekableStream[] sources;
protected boolean connected;
protected ByteBuffer anInput;

protected ByteBufferDataSource(){
}

/**
* Construct a ByteBufferDataSource from a ByteBuffer.
* @param source The ByteBuffer that is used to create the
* the DataSource.
*/
public ByteBufferDataSource(ByteBuffer input, String contentType) throws IOException {
anInput = input;
this.contentType = new ContentDescriptor(contentType);
connected = false;
}

/**
* Open a connection to the source described by
* the ByteBuffer/CODE>.
*


*
* The connect method initiates communication with the source.
*
* @exception IOException Thrown if there are IO problems
* when connect is called.
*/
public void connect() throws java.io.IOException {
connected = true;
sources = new SeekableStream [1];
sources[0] = new SeekableStream(anInput);
}

/**
* Close the connection to the source described by the locator.
*


* The disconnect method frees resources used to maintain a
* connection to the source.
* If no resources are in use, disconnect is ignored.
* If stop hasn't already been called,
* calling disconnect implies a stop.
*
*/
public void disconnect() {
if(connected) {
sources[0].close();
connected = false;
}
}

/**
* Get a string that describes the content-type of the media
* that the source is providing.
*


* It is an error to call getContentType if the source is
* not connected.
*
* @return The name that describes the media content.
*/
public String getContentType() {
if( !connected) {
throw new java.lang.Error("Source is unconnected.");
}
return contentType.getContentType();
}

public Object getControl(String str) {
return null;
}

public Object[] getControls() {
return new Object[0];
}

public javax.media.Time getDuration() {
return Duration.DURATION_UNKNOWN;
}

/**
* Get the collection of streams that this source
* manages. The collection of streams is entirely
* content dependent. The MIME type of this
* DataSource provides the only indication of
* what streams can be available on this connection.
*
* @return The collection of streams for this source.
*/
public javax.media.protocol.PullSourceStream[] getStreams() {
if( !connected) {
throw new java.lang.Error("Source is unconnected.");
}
return sources;
}

/**
* Initiate data-transfer. The start method must be
* called before data is available.
*(You must call connect before calling start.)
*
* @exception IOException Thrown if there are IO problems with the source
* when start is called.
*/
public void start() throws IOException {
}

/**
* Stop the data-transfer.
* If the source has not been connected and started,
* stop does nothing.
*/
public void stop() throws IOException {
}
}

(and for your stream)

import java.lang.reflect.Method;
import java.lang.reflect.Constructor;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;


import javax.media.protocol.PullSourceStream;
import javax.media.protocol.Seekable;
import javax.media.protocol.ContentDescriptor;
/**
*
* @author Chad McMillan
*/
public class SeekableStream implements PullSourceStream, Seekable {


protected ByteBuffer inputBuffer;

/**
* a flag to indicate EOF reached
*/


/** Creates a new instance of SeekableStream */
public SeekableStream(ByteBuffer byteBuffer) {
inputBuffer = byteBuffer;
this.seek((long)(0)); // set the ByteBuffer to to beginning
}

/**
* Find out if the end of the stream has been reached.
*
* @return Returns true if there is no more data.
*/
public boolean endOfStream() {
return (! inputBuffer.hasRemaining());
}

/**
* Get the current content type for this stream.
*
* @return The current ContentDescriptor for this stream.
*/
public ContentDescriptor getContentDescriptor() {
return null;
}

/**
* Get the size, in bytes, of the content on this stream.
*
* @return The content length in bytes.
*/
public long getContentLength() {
return inputBuffer.capacity();
}

/**
* Obtain the object that implements the specified
* Class or Interface
* The full class or interface name must be used.
*


*
* The control is not supported.
* null is returned.
*
* @return null.
*/
public Object getControl(String controlType) {
return null;
}

/**
* Obtain the collection of objects that
* control the object that implements this interface.
*


*
* No controls are supported.
* A zero length array is returned.
*
* @return A zero length array
*/
public Object[] getControls() {
Object[] objects = new Object[0];

return objects;
}

/**
* Find out if this media object can position anywhere in the
* stream. If the stream is not random access, it can only be repositioned
* to the beginning.
*
* @return Returns true if the stream is random access, false if the stream can only
* be reset to the beginning.
*/
public boolean isRandomAccess() {
return true;
}

/**
* Block and read data from the stream.
*


* Reads up to length bytes from the input stream into
* an array of bytes.
* If the first argument is null, up to
* length bytes are read and discarded.
* Returns -1 when the end
* of the media is reached.
*
* This method only returns 0 if it was called with
* a length of 0.
*
* @param buffer The buffer to read bytes into.
* @param offset The offset into the buffer at which to begin writing data.
* @param length The number of bytes to read.
* @return The number of bytes read, -1 indicating
* the end of stream, or 0 indicating read
* was called with length 0.
* @throws IOException Thrown if an error occurs while reading.
*/
public int read(byte[] buffer, int offset, int length) throws IOException {

// return n (number of bytes read), -1 (eof), 0 (asked for zero bytes)

if ( length == 0 )
return 0;
try {
inputBuffer.get(buffer,offset,length);
return length;
}
catch ( BufferUnderflowException E ) {
return -1;
}
}

public void close() {

}
/**
* Seek to the specified point in the stream.
* @param where The position to seek to.
* @return The new stream position.
*/
public long seek(long where) {
try {
inputBuffer.position((int)(where));
return where;
}
catch (IllegalArgumentException E) {
return this.tell(); // staying at the current position
}
}

/**
* Obtain the current point in the stream.
*/
public long tell() {
return inputBuffer.position();
}

/**
* Find out if data is available now.
* Returns true if a call to read would block
* for data.
*
* @return Returns true if read would block; otherwise
* returns false.
*/
public boolean willReadBlock() {
return (inputBuffer.remaining() == 0);
}
}

4 comments:

Aleksandar Janković said...

Hi,
I am excited to have found this kind of code, because I need to implement custom data source in JFM as a part of a school project.
Its a video conferencing software and project consist of creating my own application protocol, so I can not use RTP.
As much as I can see you are very familiar with JFM subject, so it would be very helpful to me if you could clarify some things about this code.
What is the purpose of this code, how can someone use it and does this code allow transforming data source stream in byte array for sending trough UDP.
In hope that my questions weren't to stupid :) and out of context, I am looking forward to hear from you. Any information is helpful. tnx

Anonymous said...

i keep getting null pointer exception at the seek method
does anyone know the reason fro this

Anonymous said...

I had this error, there is a variable with a 'null' value.
You may have declared data type more than once

jeevantony said...

Hi All,

I want to make a simple web application in servlet or jsp, that all it does is to allow a user to record his voice, using his microphone, and then save this audio file on my server or in database(maybe allow him to hear it again).
Any link or code.

Thanks