⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 filtergraph.java

📁 FMJ(freedom media for java)是java视频开发的新选择
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
package net.sf.fmj.filtergraph;import java.io.IOException;import java.util.ArrayList;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Set;import java.util.Vector;import java.util.logging.Level;import java.util.logging.Logger;import javax.media.BadHeaderException;import javax.media.Buffer;import javax.media.Codec;import javax.media.Demultiplexer;import javax.media.Format;import javax.media.IncompatibleSourceException;import javax.media.Multiplexer;import javax.media.PlugIn;import javax.media.PlugInManager;import javax.media.Renderer;import javax.media.ResourceUnavailableException;import javax.media.Track;import javax.media.control.BufferControl;import javax.media.protocol.ContentDescriptor;import javax.media.protocol.DataSource;import net.sf.fmj.utility.LoggerSingleton;import com.lti.utils.ObjUtils;/** * Build and execute functions on filter graphs.  Does not actually represent the graph itself, that  * would be simply a FilterGraphNode being used as a root. * TODO: if a demux for example sets both discard and eom, what are the semantics?  It appears that * we will discard the EOM. * @author Ken Larson * */public final class FilterGraph{	private static final int MAX_GRAPH_DEPTH = 10;	// prevent searches from taking too long.	private static final int TYPICAL_GRAPH_DEPTH = 0;		// disable this feature, too slow.		// Most graphs have 4 or less nodes: demux, codec, renderer is typical, but we'll add an extra in case there are 2 codecs.		// for a mux, this  might be demux, codec, encoder, packetizer, mux	private static final int BEST_FIRST_SEARCH_DEPTH = 8;	// 0 will disable (but this is slow)	private static final int BEST_FIRST_SEARCH_BREADTH = 5;		private static final Logger logger = LoggerSingleton.logger;	private static final boolean TRACE = true;	private FilterGraph()	{	super();	}	public static FilterGraphNode getTail(FilterGraphNode n)	{		if (n == null) {			return n;		}		while (n.getNumDestLinks() > 0)			n = n.getDestLink(0).getDestNode();	// TODO: this doesn't handle demuxs		return n;	}	public static FilterGraphNode getBeforeTail(FilterGraphNode n)	{		while (n.getNumDestLinks() > 0 && n.getDestLink(0).getDestNode().getNumDestLinks() > 0)		{	n = n.getDestLink(0).getDestNode();	// TODO: this doesn't handle demuxs		}		return n;	}			public static void start(FilterGraphNode n) throws IOException	{		n.start();				for (int i = 0; i < n.getNumDestLinks(); ++i)		{	final FilterGraphLink destLink = n.getDestLink(i);			if (destLink != null)				start(destLink.getDestNode());	// track doesn't matter for this.  TODO: prevent starting same node twice.		}		}		public static void stop(FilterGraphNode n) throws IOException	{		n.stop();		for (int i = 0; i < n.getNumDestLinks(); ++i)		{	final FilterGraphLink destLink = n.getDestLink(i);			if (destLink != null)				stop(destLink.getDestNode());	// track doesn't matter for this.  TODO: prevent starting same node twice.		}		}			public static void open(FilterGraphNode n) throws ResourceUnavailableException	{		n.open();				for (int i = 0; i < n.getNumDestLinks(); ++i)		{	final FilterGraphLink destLink = n.getDestLink(i);			if (destLink != null)				open(destLink.getDestNode());	// track doesn't matter for this.  TODO: prevent opening same node twice.		}		}		private static String tabs(int i)	{		StringBuffer b = new StringBuffer();		while (i-- >0)			b.append('\t');		return b.toString();	}	public static void print(FilterGraphNode n, int tabs)	{		print(new FilterGraphLink(n), tabs);	}	public static void print(FilterGraphLink link, int tabs)	{		final FilterGraphNode n = link.getDestNode();				String trackStr = "";		if (link.getDestTrack() >= 0)			trackStr = "[Track " + link.getDestTrack() + " of] ";		n.print(logger, tabs(tabs) + trackStr);						for (int j = 0; j < n.getNumDestLinks(); ++j)		{	final FilterGraphLink linkChild = n.getDestLink(j);			if (linkChild != null)				print(linkChild, tabs + 1);		}		}		public static final int PROCESS_DEFAULT 		= 0;	public static final int SUPPRESS_TRACK_READ 	= 0x0001;	// used to be able to re-display a frame.  For example, when we first open a movie, we usually want to get the first frame to see how big it is	/**	 * Process a graph starting with n	 * @param n	 * @param input the source buffer	 * sourceTrackNumber only used for demux, and destTrackNumber only used for mux	 */	public static void process(final FilterGraphNode n, final Buffer input, final int sourceTrackNumber, final int destTrackNumber, final int flags)	{		//if (sourceTrackNumber < 0)		//	throw new IllegalArgumentException();		// EOM propagation needs to go all the way to renderer, that's what JMF does.				int result = PlugIn.BUFFER_PROCESSED_OK;				do		{//			if (result == PlugIn.INPUT_BUFFER_NOT_CONSUMED)//			{//				try//				{//					Thread.sleep(20);//				} catch (InterruptedException e)//				{//					// TODO Auto-generated catch block//					e.printStackTrace();//				}//			}			result = n.process(input, sourceTrackNumber, destTrackNumber, flags);			// TODO: sleep on retry??						if (result != PlugIn.BUFFER_PROCESSED_OK && result != PlugIn.INPUT_BUFFER_NOT_CONSUMED) // TODO				return;	// TODO: error code return?						for (int i = 0; i < n.getNumDestLinks(); ++i)			{					if (n instanceof DemuxNode && sourceTrackNumber >= 0 && i != sourceTrackNumber)				{	continue;				}									final FilterGraphLink linkDest = n.getDestLink(i);				if (linkDest != null)				{	if (n.getOutputBuffer(i) == null)						throw new NullPointerException("Buffer " + i + " is null, trackNumber=" + sourceTrackNumber + ", flags=" + flags);					final Buffer b = (Buffer) n.getOutputBuffer(i);					// TODO: what is the proper place to check for and/or propagate EOM/discard?//					if (b.isEOM())//						continue;					if (b.isDiscard())					{	//						try//						{//							Thread.sleep(20);//						} catch (InterruptedException e)//						{//							// TODO Auto-generated catch block//							e.printStackTrace();//						}						continue;					}					//linkDest.getDestNode().process(b, -1, linkDest.getDestTrack(), flags);					process(linkDest.getDestNode(), b, -1, linkDest.getDestTrack(), flags);				}			}		}		while (result == PlugIn.INPUT_BUFFER_NOT_CONSUMED);					}			// does not set renderer's input format or open	private static Renderer findRenderer(final Format f)	{		final Vector renderers = PlugInManager.getPlugInList(f, null, PlugInManager.RENDERER);				if (renderers.size() == 0)			if (TRACE) logger.fine("No renderers found for: " + f);				for (int k = 0; k < renderers.size(); ++k)	    {	    	final String rendererClassName = (String) renderers.get(k);	    	if (TRACE) logger.fine("Found renderer for " + f + ": " + rendererClassName);	    }				for (int k = 0; k < renderers.size(); ++k)	    {	    	final String rendererClassName = (String) renderers.get(k);	    	if (TRACE) logger.fine("Trying renderer for " + f + ": " + rendererClassName);	    		    	// TODO: instantiate renderer, take first successful one for each track.	    	// Or, how do we pick the "best" one?  // how do we pick the "best" format?	    		    	final Renderer renderer = (Renderer) instantiate(rendererClassName);	    	if (renderer == null)	    		continue;	    				return renderer;	    		    		    }				return null;	}		// does not set mux's input format or open	public static Multiplexer findMux(/*final Format f, */final Format destFormat)	{		// TODO: normally we would pass in f as the first parameter.  But all the mux's are registered with		// an empty input format array.  Not sure whether getPlugInList should deal with this.		final Vector muxs = PlugInManager.getPlugInList(null, destFormat, PlugInManager.MULTIPLEXER);				if (muxs.size() == 0)			if (TRACE) logger.fine("No muxs found for: " + destFormat);				for (int k = 0; k < muxs.size(); ++k)	    {	    	final String muxClassName = (String) muxs.get(k);	    	if (TRACE) logger.fine("Found mux for " + destFormat + ": " + muxClassName);	    }				for (int k = 0; k < muxs.size(); ++k)	    {	    	final String muxClassName = (String) muxs.get(k);	    	if (TRACE) logger.fine("Trying mux for " + destFormat + ": " + muxClassName);	    		    	// TODO: instantiate mux, take first successful one for each track.	    	// Or, how do we pick the "best" one?  // how do we pick the "best" format?	    		    	final Multiplexer mux = (Multiplexer) instantiate(muxClassName);	    	if (mux == null)	    		continue;			return mux;	    }				return null;	}	/**	 * Get all multiplexers 	 */	public static List/*<Multiplexer>*/ findMuxs()	{		// TODO: normally we would pass in f as the first parameter.  But all the mux's are registered with		// an empty input format array.  Not sure whether getPlugInList should deal with this.		final Vector muxs = PlugInManager.getPlugInList(null, null, PlugInManager.MULTIPLEXER);				if (muxs.size() == 0)			if (TRACE) logger.fine("No muxs found");				for (int k = 0; k < muxs.size(); ++k)	    {	    	final String muxClassName = (String) muxs.get(k);	    	if (TRACE) logger.fine("Found mux: " + muxClassName);	    }				final List result = new ArrayList();				for (int k = 0; k < muxs.size(); ++k)	    {	    	final String muxClassName = (String) muxs.get(k);	    	if (TRACE) logger.fine("Trying mux: " + muxClassName);	    		    	final Multiplexer mux = (Multiplexer) instantiate(muxClassName);	    	if (mux == null)	    		continue;	    	result.add(mux);	    }				return result;	}		private static Format negotiate(Format f, Multiplexer dest, Format muxInputFormat, int muxDestTrack, PlugIn src)	{			// format must equal mux input format exactly to match.  No negotiation possible since		// input format of mux is already fixed.		// TODO: what if mux input format is incompletely specified? should we use a match?		if (muxInputFormat.matches(f))		{	//System.out.println("" + src + " will use " + muxInputFormat);			return muxInputFormat;		}		else			return null;	// will need codec(s) between demux and mux.			}		private static Format negotiate(Format f, Renderer dest, PlugIn src)	{		int iterations = 0;		while (true)		{			if (f == null)				return null;			if (iterations >= 1000)			{	// just a sanity check, not sure it can or will ever happen.				logger.warning("negotiate iterated 1000 times, probably stuck in infinite loop - abandoning format negotiation");				logger.warning("src=" + src);				logger.warning("dest=" + dest);				logger.warning("f=" + f);				return null;			}			++iterations;						final Format f2;						f2 = dest.setInputFormat(f);			if (f2 == null)				{	logger.warning("Input format rejected by " + dest + ": " + f);	// this shouldn't happen, since we chose which nodes to try based on the input formats they offered.				return null;			}			final Format f3;			if (src instanceof Codec)			{				f3 = ((Codec) src).setOutputFormat(f2);			}			else			{	return f2;	// no other plug ins can have their output formats set?  TODO			}						if (f2.equals(f3))			{					//dest.setInputFormat(f3);	// TODO: is this necessary?				return f3;			}						f = f3;		}	}	private static String indent(int depth)	{		StringBuffer b = new StringBuffer();		for (int i = 0; i < depth; ++i)			b.append(' ');		return b.toString();	}			/** First tries a best-first search.	 * Then does Breadth-first search by doing depth-first searches of different depths. 	 * Generally, we want the best-first search to succeed, because it is fast.	 * Breadth-first can be unacceptably slow. 	 * 	 * Standard Best-first can find non-shortest graphs, for example:	 * 	 *	net.sf.fmj.media.parser.JavaSoundParser	 *		[Track 0 of] AudioFormat [LINEAR, 22050.0 Hz, 8-bit, Mono, Unsigned, 22050.0 frame rate, FrameSize=8 bits]	 *		[Track 0 of] net.sf.fmj.media.codec.audio.RateConverter	 *			[Track 0 of] AudioFormat [LINEAR, 8000.0 Hz, 8-bit, Mono, Signed, 8000.0 frame rate, FrameSize=8 bits]	 *			[Track 0 of] net.sf.fmj.media.codec.JavaSoundCodec	 *				[Track 0 of] AudioFormat [LINEAR, 8000.0 Hz, 8-bit, Mono, Unsigned, 8000.0 frame rate, FrameSize=8 bits]	 *				[Track 0 of] net.sf.fmj.media.codec.audio.RateConverter	 *					[Track 0 of] AudioFormat [LINEAR, 8000.0 Hz, 16-bit, Mono, BigEndian, Signed, 8000.0 frame rate, FrameSize=16 bits]	 *					[Track 0 of] net.sf.fmj.media.codec.audio.ulaw.Encoder	 *						[Track 0 of] AudioFormat [ULAW, 8000.0 Hz, 8-bit, Mono, Signed, 8000.0 frame rate, FrameSize=8 bits]	 *						[Track 0 of] com.sun.media.codec.audio.ulaw.Packetizer	 *							[Track 0 of] AudioFormat [ULAW/rtp, 8000.0 Hz, 8-bit, Mono]	 * this is because there is no search from the destination format backwards.	 * 	 * TODO: this can be unacceptably slow, need to find other ways to improve.	 */	private static FilterGraphNode findCodecPathTo(int destPlugInType, final Format f, final PlugIn from, final Multiplexer mux, final Format muxInputFormat, final int muxDestTrack, final int depth)	{		// first, increasing depths of breadth-first search, up to a shorter depth.  		for (int cutoffDepth = 0; cutoffDepth < TYPICAL_GRAPH_DEPTH; ++cutoffDepth)		{	//System.out.println("Trying depth " + cutoffDepth);			FilterGraphNode n = findCodecPathTo(destPlugInType, f, from, new HashSet(), mux, muxInputFormat, muxDestTrack, depth, cutoffDepth, -1);			if (n != null)				return n;		}		

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -