Hi ,
I'm trying to create a video conferencing application using JMF. I need to access the frames before it is transmitted to the remote end, so I have a custom codec class. Below is the code:
public void startMedia(String peerIP, int peerPort, int recvPort, int fmt) {
try {
/* initialization start */
InetAddress inLocalAddr = InetAddress.getLocalHost();
InetAddress destAddr = InetAddress.getByName(peerIP);
SessionAddress localAddr =
new SessionAddress(inLocalAddr, recvPort, inLocalAddr, recvPort + 1);
SessionAddress remoteAddr = new SessionAddress(destAddr, peerPort, destAddr, peerPort + 1);
myVideoSessionManager.startSession(localAddr, localAddr, remoteAddr, null);
/* initialization complete */
int m = 0;
if (inputSource == null) {
MediaLocator ml = new MediaLocator("vfw://0");
myDS = Manager.createDataSource(ml);
} else {
myDS = ((SourceCloneable) inputSource).createClone();
}
myProcessor = Manager.createProcessor(myDS);
myProcessor.addControllerListener(this);
myProcessor.configure();
while (myProcessor.getState() != Processor.Configured) {
}
myProcessor.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP));
TrackControl track[] = myProcessor.getTrackControls();
String videoFormat = track[0].getFormat().toString();
Dimension videoSize = parseVideoSize(videoFormat);
// Instantiate and set the frame access codec to the data flow path.
try {
Codec codec[] = {new PostAccessCodec(videoSize)};
track[0].setCodecChain(codec);
} catch (UnsupportedPlugInException e) {
System.err.println("The process does not support effects.");
}
myProcessor.prefetch();
if (!waitForState(Processor.Prefetched)) {
System.err.println("Failed to realise the processor.");
// return false;
}
ds = myProcessor.getDataOutput();
try {
ss = myVideoSessionManager.createSendStream(ds, 0);
} catch (Exception e) {
e.printStackTrace();
}
//We start capture and transmission
myProcessor.start();
ss.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public class PreAccessCodec implements Codec {
/**
* Callback to access individual video frames.
*/
void accessFrame(Buffer frame) {
// For demo, we'll just print out the frame #, time &
// data length.
long t = (long) (frame.getTimeStamp() / 10000000f);
System.err.println(
"Pre: frame #: "
+ frame.getSequenceNumber()
+ ", time: "
+ ((float) t) / 100f
+ ", len: "
+ frame.getLength());
}
/**
* The code for a pass through codec.
*/
protected Format supportedIns[] = new Format[]{new VideoFormat(null)};
protected Format supportedOuts[] = new Format[]{new VideoFormat(null)};
Format input = null, output = null;
public String getName() {
return "Pre-Access Codec";
}
//these dont do anything
public void open() {
}
public void close() {
}
public void reset() {
}
public Format[] getSupportedInputFormats() {
return supportedIns;
}
public Format[] getSupportedOutputFormats(Format in) {
if (in == null) {
return supportedOuts;
} else {
Format outs[] = new Format[1];
outs[0] = in;
return outs;
}
}
public Format setInputFormat(Format format) {
input = format;
return input;
}
public Format setOutputFormat(Format format) {
output = format;
return output;
}
public int process(Buffer in, Buffer out) {
// This is the "Callback" to access individual frames.
accessFrame(in);
// Swap the data between the input & output.
Object data = in.getData();
in.setData(out.getData());
out.setData(data);
// Copy the input attributes to the output
out.setFlags(Buffer.FLAG_NO_SYNC);
out.setFormat(in.getFormat());
out.setLength(in.getLength());
out.setOffset(in.getOffset());
return BUFFER_PROCESSED_OK;
}
public Object[] getControls() {
return new Object[0];
}
public Object getControl(String type) {
return null;
}
}
public class PostAccessCodec extends PreAccessCodec {
long count = 0;
// We'll advertize as supporting all video formats.
public PostAccessCodec(Dimension size) {
supportedIns = new Format[]{new RGBFormat()};
this.size = size;
}
/**
* Callback to access individual video frames.
*/
void accessFrame(Buffer frame) {
count = count + 1;
long t = (long) (frame.getTimeStamp() / 10000000f);
System.err.println(
"Post: frame #: "
+ frame.getSequenceNumber()
+ ", time: "
+ ((float) t) / 100f
+ ", len: "
+ frame.getLength());
}
public String getName() {
return "Post-Access Codec";
}
private Dimension size;
}
/**
* Block until the processor has transitioned to the given state.
* Return false if the transition failed.
*/
boolean waitForState(int state) {
synchronized (waitSync) {
try {
while (myProcessor.getState() != state && stateTransitionOK) {
waitSync.wait();
}
} catch (Exception e) {
}
}
return stateTransitionOK;
}
public Dimension parseVideoSize(String videoSize) {
int x = 352, y = 288;
StringTokenizer strtok = new StringTokenizer(videoSize, ", ");
strtok.nextToken();
String size = strtok.nextToken();
StringTokenizer sizeStrtok = new StringTokenizer(size, "x");
try {
x = Integer.parseInt(sizeStrtok.nextToken());
y = Integer.parseInt(sizeStrtok.nextToken());
} catch (NumberFormatException e) {
System.out.println("unable to find video size, assuming default of 352*288");
}
System.out.println("Image width = " + String.valueOf(x) + "\nImage height = " + String.valueOf(y));
return new Dimension(x, y);
}
Below is the exception I got while executing the code:
HTML Code:
Class: com.ibm.media.codec.video.h263.NativeEncoder@1b7ae22
can only encode in sizes: 128x96, 176x144, 352x288.
Failed to realize: com.sun.media.ProcessEngine@9903f4
Cannot build a flow graph with the customized options:
Unable to add customed codecs:
splibraries.VideoTool$PostAccessCodec@1926e90
Error: Unable to realize com.sun.media.ProcessEngine@9903f4
Failed to realise the processor.
javax.media.NotRealizedError: getDataOutput cannot be called before realized
at com.sun.media.ProcessEngine.getDataOutput(ProcessEngine.java:379)
I was able to access the frame when I set the content descriptor for processor to null as below:
myProcessor.setContentDescriptor(null);
But this will not allow me to get the data output from processor . Kindly help me to resolve the issue.