/*
* @(#)BasicFilterModule.java 1.63 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.media;
import javax.media.*;
import javax.media.Format;
import javax.media.format.RGBFormat;
import javax.media.control.FrameProcessingControl;
import java.awt.*;
/**
* BasicFilterModule is a module which is not threaded and have one InputConnector and one OutputConnector.
* It receives data from its input connector, pass the data to the level 3 plugIn codec and put the
* result in the OutputConnector.
* BasicFilterModule can be either Push or Pull driven.
* The plugIn codec might be media decoder, media encoder, effect etc.
*
**/
public class BasicFilterModule extends BasicModule {
protected Codec codec;
protected InputConnector ic;
protected OutputConnector oc;
protected FrameProcessingControl frameControl = null;
protected float curFramesBehind = 0f;
protected float prevFramesBehind = 0f;
protected java.awt.Frame controlFrame;
protected final boolean VERBOSE_CONTROL=false;
public BasicFilterModule(Codec c) {
ic=new BasicInputConnector();
registerInputConnector("input",ic);
oc=new BasicOutputConnector();
registerOutputConnector("output",oc);
setCodec(c);
protocol = Connector.ProtocolPush;
Object control = c.getControl("javax.media.control.FrameProcessingControl");
if (control instanceof FrameProcessingControl)
frameControl = (FrameProcessingControl) control;
}
public boolean doRealize() {
if (codec != null) {
try {
codec.open();
if (VERBOSE_CONTROL) {
controlFrame=new java.awt.Frame(codec.getName()+" Control");
controlFrame.setLayout(new com.sun.media.controls.VFlowLayout(1));
controlFrame.add(new Label(codec.getName()+" Control",Label.CENTER) );
controlFrame.add(new Label( " "));
Control[] c=(Control[]) codec.getControls();
for (int i=0;i<c.length;i++) {
controlFrame.add(c[i].getControlComponent() );
}
controlFrame.pack();
controlFrame.show();
}
} catch (ResourceUnavailableException rue) {
return false;
}
}
return true;
}
public boolean doPrefetch(){
return super.doPrefetch();
}
public void doClose() {
if (codec != null) {
codec.close();
}
if (controlFrame!=null) {
controlFrame.dispose();
controlFrame=null;
}
/*
if (monitorFrame != null)
monitorFrame.dispose();
*/
}
public void setFormat(Connector c, Format f) {
if (c == ic) {
// Input Connector
if (codec != null)
codec.setInputFormat(f);
} else if (c == oc) {
if (codec != null)
codec.setOutputFormat(f);
}
}
/**
* sets the plugIn codec of this filter.
* @param codec the plugIn codec (should we specify Codec class or String)
* @return true if successful
**/
public boolean setCodec(String codec){
return true;
}
public boolean setCodec(Codec codec){ // patch until codecmanager exists
this.codec=codec;
return true;
}
/**
* @return the plugIn codec of this filter, null if not yet set.
**/
public Codec getCodec(){
return codec;
}
public boolean isThreaded(){
if ( (getProtocol() == Connector.ProtocolSafe) )
return true;
return false;
}
public Object[] getControls() {
return codec.getControls();
}
public Object getControl(String s) {
return codec.getControl(s);
}
protected void setFramesBehind(float framesBehind) {
curFramesBehind = framesBehind;
}
/**
* A new input format has been detected, we'll check if the existing
* codec can handle it. Otherwise, we'll try to re-create a new codec
* to handle it.
*/
protected boolean reinitCodec(Format input) {
// Query the existing plugin to see if it supports the new input.
if (codec != null) {
if (codec.setInputFormat(input) != null) {
// Fine, the existing codec still works.
return true;
}
// close the previous codec.
codec.close();
codec = null;
}
// Find a new codec that supports the input
Codec c;
if ((c = SimpleGraphBuilder.findCodec(input, null, null, null)) == null)
return false;
setCodec(c);
return true;
}
protected Buffer storedInputBuffer,
storedOutputBuffer;
protected boolean readPendingFlag=false,
writePendingFlag=false;
private boolean failed = false;
private boolean markerSet = false;
private Object lastHdr = null;
public void process() {
// System.out.println("Filter.process: " + codec);
Buffer inputBuffer, outputBuffer;
do {
if (readPendingFlag)
inputBuffer = storedInputBuffer;
else {
Format incomingFormat;
inputBuffer = ic.getValidBuffer();
incomingFormat = inputBuffer.getFormat();
if (incomingFormat == null) {
// Something's weird, we'll just assume it's the previous
// format.
incomingFormat = ic.getFormat();
inputBuffer.setFormat(incomingFormat);
}
if (incomingFormat != ic.getFormat() &&
incomingFormat != null &&
!incomingFormat.equals(ic.getFormat()) &&
!inputBuffer.isDiscard()) {
// The format is changed mid-stream!
if (writePendingFlag) {
// Discard the pending output buffer.
storedOutputBuffer.setDiscard(true);
oc.writeReport();
writePendingFlag = false;
}
// Attempt to re-initialize the plugin codec.
// Bail out if failed.
if (!reinitCodec(inputBuffer.getFormat())) {
// Failed.
inputBuffer.setDiscard(true);
ic.readReport();
failed = true;
// Just signal an internal error for now.
if (moduleListener != null)
moduleListener.formatChangedFailure(this,
ic.getFormat(), inputBuffer.getFormat());
return;
}
Format oldFormat = ic.getFormat();
ic.setFormat(inputBuffer.getFormat());
if (moduleListener != null)
moduleListener.formatChanged(this, oldFormat,
inputBuffer.getFormat());
}
// The marker flag needs to be handle more delicately.
// For codec that takes multiple input buffers to generate
// a single output buffer (e.g. RTP depackizer), if there's
// one input buffer that has the market flag set, the
// corresponding output buffer will need to have the
// marker flag set.
if ((inputBuffer.getFlags() & Buffer.FLAG_SYSTEM_MARKER) != 0) {
markerSet = true;
}
if (PlaybackEngine.DEBUG && inputBuffer != null)
jmd.moduleIn(this, 0, inputBuffer, true);
}
if (writePendingFlag)
outputBuffer = storedOutputBuffer;
else {
outputBuffer = oc.getEmptyBuffer();
if (outputBuffer != null) {
if (PlaybackEngine.DEBUG)
jmd.moduleOut(this, 0, outputBuffer, true);
outputBuffer.setLength(0);
outputBuffer.setOffset(0);
lastHdr = outputBuffer.getHeader();
}
}
// Copy from input to output.
outputBuffer.setTimeStamp(inputBuffer.getTimeStamp());
outputBuffer.setDuration(inputBuffer.getDuration());
outputBuffer.setSequenceNumber(inputBuffer.getSequenceNumber());
outputBuffer.setFlags(inputBuffer.getFlags());
outputBuffer.setHeader(inputBuffer.getHeader());
// Check if we are in the resetted state.
if (resetted) {
// Check if the input buffer contains the zero-length
// flush flag. If so, we are almost done.
if ((inputBuffer.getFlags() & Buffer.FLAG_FLUSH) != 0) {
codec.reset();
resetted = false;
}
// In the resetted state, we won't process any of the
// data. We'll just return the buffers unprocessed.
readPendingFlag = writePendingFlag = false;
ic.readReport();
oc.writeReport();
return;
}
if (failed || inputBuffer.isDiscard()) {
// Reset the marker flag.
if (markerSet) {
outputBuffer.setFlags(outputBuffer.getFlags() &
~Buffer.FLAG_SYSTEM_MARKER);
markerSet = false;
}
// If the discard flag from the upstream module
// is set, it has probably dropped a frame. The
// curFramesBehind counter should be resetted.
// Otherwise, too many frames will be dropped.
curFramesBehind = 0;
ic.readReport();
// Propagate the discard flag downstream.
// This is needed so the renderer can keep track of
// a correct frame count even for the discarded frames.
if (!writePendingFlag)
oc.writeReport();
return;
}
if (frameControl != null && curFramesBehind != prevFramesBehind &&
(inputBuffer.getFlags() & Buffer.FLAG_NO_DROP) == 0) {
frameControl.setFramesBehind(curFramesBehind);
prevFramesBehind = curFramesBehind;
}
int rc = 0;
try {
rc = codec.process(inputBuffer , outputBuffer);
} catch (Throwable e) {
Log.dumpStack(e);
if (moduleListener != null)
moduleListener.internalErrorOccurred(this);
}
if (PlaybackEngine.TRACE_ON && !verifyBuffer(outputBuffer)) {
System.err.println("verify buffer failed: " + codec);
Thread.dumpStack();
if (moduleListener != null)
moduleListener.internalErrorOccurred(this);
}
if ((rc & PlugIn.PLUGIN_TERMINATED) != 0) {
failed = true;
if (moduleListener != null)
moduleListener.pluginTerminated(this);
readPendingFlag = writePendingFlag = false;
ic.readReport();
oc.writeReport();
return;
}
if (curFramesBehind > 0f && outputBuffer.isDiscard()) {
// One frame has been dropped. We'll need to update
// the framesBehind count.
// If we don't do that, we run into trouble that the
// renderer sometimes cannot update the frames behind
// count immediately (thread scheduling may prefer this
// thread to the rendering thread). As a result, the
// decoder will keep dropping frames without a correct
// frame count from the renderer.
curFramesBehind -= 1.0f;
if (curFramesBehind < 0)
curFramesBehind = 0f;
// We'll also need to propagate the discard flag
// downstream just so the the downstream module can
// update their framesbehind info accordingly. To
// do that, we override the plugin's return value to
// make sure that OUTPUT_BUFFER_NOT_FILLED is not used.
rc = rc & ~codec.OUTPUT_BUFFER_NOT_FILLED;
}
if ( (rc & codec.BUFFER_PROCESSED_FAILED) != 0 ) {
outputBuffer.setDiscard(true);
// Reset the marker flag.
if (markerSet) {
outputBuffer.setFlags(outputBuffer.getFlags() &
~Buffer.FLAG_SYSTEM_MARKER);
markerSet = false;
}
if (PlaybackEngine.DEBUG) jmd.moduleIn(this, 0, inputBuffer, false);
ic.readReport();
if (PlaybackEngine.DEBUG) jmd.moduleOut(this, 0, outputBuffer, false);
oc.writeReport();
readPendingFlag = writePendingFlag = false;
return;
}
// Do not propagate the EOM flag if the input buffer is
// not fully consumed or the output buffer is not fully filled.
if ( outputBuffer.isEOM() &&
((rc & codec.INPUT_BUFFER_NOT_CONSUMED) != 0 ||
(rc & codec.OUTPUT_BUFFER_NOT_FILLED) != 0) ) {
// We are not quite done yet, so the output buffer
// is not set to EOM.
outputBuffer.setEOM(false);
}
if ( (rc & codec.OUTPUT_BUFFER_NOT_FILLED) != 0 ) {
writePendingFlag=true;// next call to getEmptyBuffer will return the same Buffer
storedOutputBuffer=outputBuffer;
} else {
if (PlaybackEngine.DEBUG) jmd.moduleOut(this, 0, outputBuffer, false);
if (markerSet) {
outputBuffer.setFlags(outputBuffer.getFlags() |
Buffer.FLAG_SYSTEM_MARKER);
markerSet = false;
}
/*
if (outputBuffer.getFormat() instanceof RGBFormat)
monitorCheck(outputBuffer);
*/
oc.writeReport();
writePendingFlag=false;
}
if ( ((rc & codec.INPUT_BUFFER_NOT_CONSUMED) != 0 ||
(inputBuffer.isEOM() && !outputBuffer.isEOM())) ) {
readPendingFlag=true; // next call to getValidBuffer will return the same Buffer
storedInputBuffer=inputBuffer;
} else {
if (PlaybackEngine.DEBUG) jmd.moduleIn(this, 0, inputBuffer, false);
inputBuffer.setHeader(lastHdr);
ic.readReport();
readPendingFlag=false;
}
} while (readPendingFlag);
}
/*
private Frame monitorFrame = null;
private Panel monitorPanel = null;
private long oldTime = (long) -1E+9;
private Dimension monitorSize = null;
private void monitorCheck(Buffer buffer) {
if (buffer.getFormat() instanceof RGBFormat) {
RGBFormat rgb = (RGBFormat) buffer.getFormat();
if (monitorFrame == null) {
monitorSize = rgb.getSize();
monitorPanel = new Panel() {
public void update(Graphics g) {
}
public void paint(Graphics g) {
}
public Dimension getPreferredSize() {
return monitorSize;
}
};
monitorFrame = new Frame("Monitor");
monitorFrame.setLayout( new BorderLayout() );
monitorFrame.add("Center", monitorPanel);
monitorFrame.pack();
monitorFrame.setVisible(true);
}
long currentTime = System.currentTimeMillis();
if (currentTime >= oldTime + 1000) {
oldTime = currentTime;
Image image = javax.media.util.ImageConverter.convertToImage(buffer);
Graphics g = monitorPanel.getGraphics();
g.drawImage(image, 0, 0, monitorPanel);
}
}
}
*/
}
|