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

📄 qtparser.java

📁 FMJ(freedom media for java)是java视频开发的新选择
💻 JAVA
字号:
package net.sf.fmj.qt;import java.awt.Dimension;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.util.logging.Level;import java.util.logging.Logger;import javax.media.BadHeaderException;import javax.media.Buffer;import javax.media.Duration;import javax.media.Format;import javax.media.IncompatibleSourceException;import javax.media.ResourceUnavailableException;import javax.media.Time;import javax.media.Track;import javax.media.format.AudioFormat;import javax.media.format.RGBFormat;import javax.media.format.VideoFormat;import javax.media.protocol.ContentDescriptor;import javax.media.protocol.DataSource;import javax.media.protocol.FileTypeDescriptor;import javax.media.protocol.PullDataSource;import javax.media.util.ImageToBuffer;import quicktime.QTException;import net.sf.fmj.media.AbstractDemultiplexer;import net.sf.fmj.media.AbstractTrack;import net.sf.fmj.utility.LoggerSingleton;import net.sf.fmj.utility.URLUtils;/** *  * @author Ken Larson * */public class QTParser extends AbstractDemultiplexer {	private static final Logger logger = LoggerSingleton.logger;		private static final boolean ENABLE_AUDIO = false;				// if USE_DATASOURCE_URL_ONLY is true, this is a bit of a hack - we don't really use the DataSource, we just grab its URL.  So arbitrary data sources won't work.	private final boolean USE_DATASOURCE_URL_ONLY = true;		private ContentDescriptor[] supportedInputContentDescriptors = new ContentDescriptor[] {			new ContentDescriptor(FileTypeDescriptor.MSVIDEO),	// .avi			new ContentDescriptor(FileTypeDescriptor.QUICKTIME),	// .mov			// TODO: others			// TODO: query dynamically.				};		private QTSnapper qtSnapper;			public QTParser()	{	}		private static final Object QT_SYNC_OBJ = new Boolean(true);	// synchronize on this before using the libraries, to prevent threading problems.		private PullDataSource source;		private PullSourceStreamTrack[] tracks;			@Override	public ContentDescriptor[] getSupportedInputContentDescriptors()	{		return supportedInputContentDescriptors;	}		@Override	public Track[] getTracks() throws IOException, BadHeaderException	{		return tracks;	}		@Override	public void setSource(DataSource source) throws IOException, IncompatibleSourceException	{		final String protocol = source.getLocator().getProtocol();				if (USE_DATASOURCE_URL_ONLY)		{			if (!(protocol.equals("file")) && !(protocol.equals("http")))				throw new IncompatibleSourceException();		}		else		{				if (!(source instanceof PullDataSource))			throw new IncompatibleSourceException();		}				this.source = (PullDataSource) source;			}				// @Override	@Override	public void open() throws ResourceUnavailableException	{		synchronized (QT_SYNC_OBJ)		{			try			{				final String urlStr;				if (USE_DATASOURCE_URL_ONLY)				{					// just use the file URL from the datasource					// FMJ supports relative file URLs, but not sure if qt does.  So we'll rewrite the URL here:					// TODO: perhaps we should only do this if qt has a problem.					if (source.getLocator().getProtocol().equals("file"))						urlStr = URLUtils.createUrlStr(new File(URLUtils.extractValidPathFromFileUrl(source.getLocator().toExternalForm())));					else						urlStr = source.getLocator().toExternalForm(); 					qtSnapper = new QTSnapper(urlStr);				} else				{					throw new RuntimeException();									}								VideoTrack videoTrack = null;				AudioTrack audioTrack = null;				videoTrack = new VideoTrack();								if (audioTrack == null && videoTrack == null)					throw new ResourceUnavailableException("No audio or video track found");				else if (audioTrack != null && videoTrack != null)					tracks = new PullSourceStreamTrack[] { videoTrack, audioTrack };				else if (audioTrack != null)					tracks = new PullSourceStreamTrack[] { audioTrack };				else					tracks = new PullSourceStreamTrack[] { videoTrack };			} catch (Exception e)			{				logger.log(Level.WARNING, "" + e, e);				throw new ResourceUnavailableException("" + e);			}		}		super.open();	}	@Override	public void close()	{		synchronized (QT_SYNC_OBJ)		{			if (tracks != null)			{				for (int i = 0; i < tracks.length; ++i)				{					if (tracks[i] != null)					{						tracks[i].deallocate();						tracks[i] = null;					}				}				tracks = null;			}		}		super.close();	}	// @Override	@Override	public void start() throws IOException	{	}		// TODO: should we stop data source in stop?//	// @Override//	public void stop()//	{//		try //		{//			source.stop();//		} catch (IOException e) //		{//			logger.log(Level.WARNING, "" + e, e);//		}//	}				@Override	public boolean isPositionable()	{		return false;	// TODO	}	//	@Override//	public Time setPosition(Time where, int rounding)//	{//		synchronized (QT_SYNC_OBJ)//		{	//			//		}//	}		@Override	public boolean isRandomAccess()	{		return super.isRandomAccess();	// TODO: can we determine this from the data source?	}	    public static VideoFormat convertCodecPixelFormat(QTSnapper qtSnapper)    {    	// resulting format based on what QTSnapper will return.  Depends on a bit of internal     	// knowledge of how QTSnapper and ImageToBuffer work.    	    	// TODO: we are ignoring any cropping here.    	    	//final Dimension size = new Dimension(128, 128);		final Dimension size = new Dimension(qtSnapper.getImageWidth(), qtSnapper.getImageHeight());		final int maxDataLength;		final Class dataType;		final int bitsPerPixel;		final float frameRate = -1.f; // TODO				final int red;		final int green;		final int blue;		// QTSnapper returns BufferedImage.TYPE_INT_RGB		final int bufferedImageType = BufferedImage.TYPE_INT_RGB;				if (bufferedImageType == BufferedImage.TYPE_3BYTE_BGR)		{			maxDataLength = qtSnapper.getImageWidth() * qtSnapper.getImageHeight() * 3; 			dataType = Format.byteArray;			bitsPerPixel = 24;			red = 1;			green = 2;			blue = 3;		}		else if (bufferedImageType == BufferedImage.TYPE_INT_BGR)		{			maxDataLength = qtSnapper.getImageWidth() * qtSnapper.getImageHeight(); 			dataType = Format.intArray;			bitsPerPixel = 32;				// TODO: test			red = 0xFF;			green = 0xFF00;				blue = 0xFF0000;		}		else if (bufferedImageType == BufferedImage.TYPE_INT_RGB)		{			maxDataLength = qtSnapper.getImageWidth() * qtSnapper.getImageHeight(); 			dataType = Format.intArray;			bitsPerPixel = 32;				red = 0xFF0000;			green = 0xFF00;			blue = 0xFF;		}		else if (bufferedImageType == BufferedImage.TYPE_INT_ARGB)		{			maxDataLength = qtSnapper.getImageWidth() * qtSnapper.getImageHeight(); 			dataType = Format.intArray;			bitsPerPixel = 32;				red = 0xFF0000;			green = 0xFF00;			blue = 0xFF;			// just ignore alpha		}		else			throw new IllegalArgumentException("Unsupported buffered image type: " + bufferedImageType);					return new RGBFormat(size, maxDataLength, dataType, frameRate, bitsPerPixel, red, green, blue);    	    }         	private abstract class PullSourceStreamTrack extends AbstractTrack	{		public abstract void deallocate();	}			private class VideoTrack extends PullSourceStreamTrack	{		// TODO: track listener				private final VideoFormat format;				public VideoTrack() throws ResourceUnavailableException		{			super();							synchronized (QT_SYNC_OBJ)	    	{			    // set format				format = convertCodecPixelFormat(qtSnapper);	    	}		}						@Override		public void deallocate()		{		}		/**		 * 		 * @return nanos skipped, 0 if unable to skip.		 * @throws IOException		 */		public long skipNanos(long nanos) throws IOException		{			return 0;	// TODO					}				public boolean canSkipNanos()		{			return false;		}		@Override		public Format getFormat()		{			return format;		}//		  TODO: from JAVADOC://		   This method might block if the data for a complete frame is not available. It might also block if the stream contains intervening data for a different interleaved Track. Once the other Track is read by a readFrame call from a different thread, this method can read the frame. If the intervening Track has been disabled, data for that Track is read and discarded.////			Note: This scenario is necessary only if a PullDataSource Demultiplexer implementation wants to avoid buffering data locally and copying the data to the Buffer passed in as a parameter. Implementations might decide to buffer data and not block (if possible) and incur data copy overhead. 		 		@Override		public void readFrame(Buffer buffer)		{			synchronized (QT_SYNC_OBJ)			{				BufferedImage bi;				try				{					qtSnapper.next();					bi = qtSnapper.getFrame();				} catch (QTException e)				{					throw new RuntimeException(e);	// TODO: how to handle.				}					if (bi != null)				{					final Buffer b = ImageToBuffer.createBuffer(bi, format.getFrameRate());					buffer.setData(b.getData());					buffer.setLength(b.getLength());					buffer.setOffset(b.getOffset());					buffer.setEOM(false);					buffer.setDiscard(false);					buffer.setTimeStamp((qtSnapper.getFrameTime() * 1000000000L) / qtSnapper.getTimeScale());					} else				{					buffer.setEOM(true);					buffer.setLength(0);									}			}		}				@Override		public Time mapFrameToTime(int frameNumber)		{			return TIME_UNKNOWN;			}		@Override		public int mapTimeToFrame(Time t)		{				return FRAME_UNKNOWN;				}							@Override		public Time getDuration()		{			synchronized (QT_SYNC_OBJ)	    	{				if (qtSnapper.getDuration() <= 0 || qtSnapper.getTimeScale() <= 0)					return Duration.DURATION_UNKNOWN;				return new Time((qtSnapper.getDuration() * 1000000000L) / qtSnapper.getTimeScale());	    	}					}	}		private class AudioTrack extends PullSourceStreamTrack	{		// TODO: track listener				private final AudioFormat format;				public AudioTrack() throws ResourceUnavailableException		{			super();			synchronized (QT_SYNC_OBJ)	    	{				format = null;	// TODO	    	}		    		}						@Override		public void deallocate()		{		}		// TODO: implement seeking using av_seek_frame		/**		 * 		 * @return nanos skipped, 0 if unable to skip.		 * @throws IOException		 */		public long skipNanos(long nanos) throws IOException		{			return 0;					}				public boolean canSkipNanos()		{			return false;		}		@Override		public Format getFormat()		{			return format;		}//		  TODO: from JAVADOC://		   This method might block if the data for a complete frame is not available. It might also block if the stream contains intervening data for a different interleaved Track. Once the other Track is read by a readFrame call from a different thread, this method can read the frame. If the intervening Track has been disabled, data for that Track is read and discarded.////			Note: This scenario is necessary only if a PullDataSource Demultiplexer implementation wants to avoid buffering data locally and copying the data to the Buffer passed in as a parameter. Implementations might decide to buffer data and not block (if possible) and incur data copy overhead. 		 		@Override		public void readFrame(Buffer buffer)		{			synchronized (QT_SYNC_OBJ)			{								throw new UnsupportedOperationException();			}		}				@Override		public Time mapFrameToTime(int frameNumber)		{			return TIME_UNKNOWN;			}		@Override		public int mapTimeToFrame(Time t)		{				return FRAME_UNKNOWN;				}		@Override		public Time getDuration()		{			return Duration.DURATION_UNKNOWN; // TODO		}	}	//	private static final double secondsToNanos(double secs)//	{	return secs * 1000000000.0;//	}}

⌨️ 快捷键说明

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