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

📄 multipartmixedreplaceparser.java

📁 FMJ(freedom media for java)是java视频开发的新选择
💻 JAVA
字号:
package net.sf.fmj.media.parser;import java.awt.Dimension;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.Properties;import java.util.logging.Level;import java.util.logging.Logger;import javax.imageio.ImageIO;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.JPEGFormat;import javax.media.format.VideoFormat;import javax.media.protocol.ContentDescriptor;import javax.media.protocol.DataSource;import javax.media.protocol.PullDataSource;import javax.media.protocol.PullSourceStream;import net.sf.fmj.media.AbstractDemultiplexer;import net.sf.fmj.media.AbstractTrack;import net.sf.fmj.utility.LoggerSingleton;/** * Parser for multipart/x-mixed-replace - used in some cases for streaming jpegs. * TODO: check out the jipcam project, which has Mjpeg parsing info. * This project also has some ip camera info: http://www.codeproject.com/cs/media/cameraviewer.asp?print=true * Some camera links from that project: * Not responding: http://chipmunk.uvm.edu/cgi-bin/webcam/nph-update.cgi?dummy=garb * Works: http://webcam-1.duesseldorf.it-on.net/cgi-bin/nph-update.cgi * Works: http://webcam-2.duesseldorf.it-on.net/cgi-bin/nph-update.cgi * Works: http://towercam.uu.edu/axis-cgi/mjpg/video.cgi * Works: http://136.165.99.86/axis-cgi/mjpg/video.cgi * Works: http://217.114.115.192/axis-cgi/mjpg/video.cgi * Works: http://129.78.249.81/axis-cgi/mjpg/video.cgi *  * Others: * Works: http://camera.baywatch.tv/axis-cgi/mjpg/video.cgi?camera=1&resolution=352x240&compression=50 * Works: http://www.surfshooterhawaii.com//cgi-bin/axispush555.cgi?dummy=garb *  * More camera links: * http://www.axis.com/solutions/video/gallery.htm *  * @author Ken Larson * */public class MultipartMixedReplaceParser extends AbstractDemultiplexer {	private static final Logger logger = LoggerSingleton.logger;	private ContentDescriptor[] supportedInputContentDescriptors = new ContentDescriptor[] {			new ContentDescriptor("multipart.x_mixed_replace")	};		private PullDataSource source;		private PullSourceStreamTrack[] tracks;		public MultipartMixedReplaceParser()	{	super();	}			@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 (!(source instanceof PullDataSource))			throw new IncompatibleSourceException();				this.source = (PullDataSource) source;			}		// @Override	@Override	public void open() throws ResourceUnavailableException	{		try		{			source.connect();			source.start();	// TODO: stop/disconnect on stop/close.					final PullSourceStream[] streams = source.getStreams();						tracks = new PullSourceStreamTrack[streams.length];						for (int i = 0; i < streams.length; ++i)			{				tracks[i] = new VideoTrack(streams[i]);				}				} catch (IOException e)		{			logger.log(Level.WARNING, "" + e, e);			throw new ResourceUnavailableException("" + e);		}		super.open();	}	@Override	public void close()	{		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)//	{//	}		@Override	public boolean isRandomAccess()	{		return super.isRandomAccess();	// TODO: can we determine this from the data source?	}	 	private abstract class PullSourceStreamTrack extends AbstractTrack	{		public abstract void deallocate();	}			private class VideoTrack extends PullSourceStreamTrack	{		// TODO: track listener				private final PullSourceStream stream;		private final VideoFormat format;				public VideoTrack(PullSourceStream stream) throws ResourceUnavailableException		{			super();							this.stream = stream;			// set format						// read first frame to determine format			final Buffer buffer = new Buffer();			readFrame(buffer);			if (buffer.isDiscard() || buffer.isEOM())				throw new ResourceUnavailableException("Unable to read first frame");			// TODO: catch runtime exception too?						// parse jpeg			final java.awt.Image image;			try			{				image = ImageIO.read(new ByteArrayInputStream((byte []) buffer.getData(), buffer.getOffset(), buffer.getLength()));			} catch (IOException e)			{				logger.log(Level.WARNING, "" + e, e);				throw new ResourceUnavailableException();			}						if (image == null)			{				logger.log(Level.WARNING, "Failed to read image (ImageIO.read returned null).");				throw new ResourceUnavailableException();			}						format = new JPEGFormat(new Dimension(image.getWidth(null), image.getHeight(null)), Format.NOT_SPECIFIED, Format.byteArray, -1.f, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED);						// TODO: this discards first image.  save and return first time readFrame is called.	    			}						@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;		}		/** handles pushbacks. */		private byte[] pushbackBuffer;		private int pushbackBufferLen;		private int pushbackBufferOffset;				/** push bytes back, to be read again later. */		private void pushback(byte[] bytes, int len)		{			if (pushbackBufferLen == 0)			{	pushbackBuffer = bytes;	// TODO: copy?				pushbackBufferLen = len;				pushbackBufferOffset = 0;			}			else			{				final byte[] newPushbackBuffer = new byte[pushbackBufferLen + len];				System.arraycopy(pushbackBuffer, 0, newPushbackBuffer, 0, pushbackBufferLen);				System.arraycopy(bytes, 0, newPushbackBuffer, pushbackBufferLen, len);				pushbackBuffer = newPushbackBuffer;				pushbackBufferLen = pushbackBufferLen + len;				pushbackBufferOffset = 0;			}		}				/** supports pushback. */		private int read(byte[] buffer, int offset, int length) throws IOException		{			if (pushbackBufferLen > 0)			{	// read from pushback buffer				final int lenToCopy = length < pushbackBufferLen ? length : pushbackBufferLen;				System.arraycopy(pushbackBuffer, pushbackBufferOffset, buffer, offset, lenToCopy);				pushbackBufferLen -= lenToCopy;				pushbackBufferOffset += lenToCopy;				return lenToCopy;			}			else			{	return stream.read(buffer, offset, length);			}		}				private static final int MAX_LINE_LENGTH = 255;				private class MaxLengthExceededException extends IOException		{		}		/** return null on eom */		private String readLine(int max) throws IOException		{			final byte[] buffer = new byte[max];			int offset = 0;			final int length = 1;			while (true)			{				if (offset >= max)					throw new MaxLengthExceededException();	// no newline up to max.				final int lenRead = read(buffer, offset, length);				if (lenRead < 0)					return null;	// EOS				if (buffer[offset] == '\n')				{	if (offset > 0 && buffer[offset - 1] == '\r')						offset -= 1; // don't include \r					return new String(buffer, 0, offset);				}				offset += 1;			}		}				private byte[] readFully(int bytes) throws IOException		{			final byte[] buffer = new byte[bytes];			int offset = 0;			int length = bytes;						while (true)			{				final int lenRead = read(buffer, offset, length);				if (lenRead < 0)					return null;	// EOS				if (lenRead == length)					return buffer;				length -= lenRead;				offset += lenRead;			}		}				// We put a limit on how much we will read, to prevent running out of memory in case something goes wrong.		private final int MAX_IMAGE_SIZE = 1000000;					/** Read until we see the specified boundary. */		private byte[] readUntil(String boundary) throws IOException		{			// TODO: is there a blank line before the boundary?			final ByteArrayOutputStream os = new ByteArrayOutputStream();			final byte[] boundaryBytes = boundary.getBytes();			final byte[] matchBuffer = new byte[boundaryBytes.length];						int matchOffset = 0;	// will be nonzero when checking a potential match			while (true)			{				if (os.size() >= MAX_IMAGE_SIZE)					throw new IOException("No boundary found in " + MAX_IMAGE_SIZE + " bytes.");				// TODO: read more efficiently, not 1 at a time.				final int lenRead = read(matchBuffer, matchOffset, 1);				if (lenRead < 0)					return null;	// EOS				if (matchBuffer[matchOffset] == boundaryBytes[matchOffset])				{					if (matchOffset == boundaryBytes.length - 1)					{	// found the boundary						pushback(matchBuffer, matchOffset + 1);	// push it back to be read again						break;					}					else					{						// keep matching the boundary						++matchOffset;					}														}				else				{					if (matchOffset > 0)					{	// we had a partial, but not full match - dump it all into the buffer						os.write(matchBuffer, 0, matchOffset + 1);						matchOffset = 0;					}					else					{													// completely nonmatching byte						os.write(matchBuffer, 0, 1);					}				}			}						final byte[] result = os.toByteArray();			return result;						//			// the crlf before the boundary is part of the boundary, see//			// http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html//			// TODO: we should basically add this to the boundary////			System.out.println("\\r=" + Integer.toHexString('\r'));//			System.out.println("\\n=" + Integer.toHexString('\n'));//			System.out.println("" + Integer.toHexString(result[result.length-3]));//			System.out.println("" + Integer.toHexString(result[result.length-2]));//			System.out.println("" + Integer.toHexString(result[result.length-1]));//			//			final int trim = 2;//			final byte[] trimmedResult = new byte[result.length - trim];//			System.arraycopy(result, 0, trimmedResult, 0, trimmedResult.length);//			////			//			return trimmedResult;		}						/** Property keys converted to all uppercase. */		private boolean parseProperty(String line, Properties properties)		{			final int index = line.indexOf(':');			if (index < 0)				return false;			final String key = line.substring(0, index).trim();			final String value = line.substring(index + 1).trim();			properties.setProperty(key.toUpperCase(), value);			return true;					}				private String boundary;	// content boundary		//		  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)		{			// example data:			//--ssBoundary8345			//Content-Type: image/jpeg			//Content-Length: 114587						try			{				String line;				// eat leading blank lines				while (true)				{					line = readLine(MAX_LINE_LENGTH);					if (line == null)					{	buffer.setEOM(true);						buffer.setLength(0);						return;					}										if (!line.trim().equals(""))						break;	// end of header														}								if (boundary == null)				{	boundary = line.trim();	// TODO: we should be able to get this from the content type, but the content type has this stripped out.  So we'll just take the first nonblank line to be the boundary.					//System.out.println("boundary: " + boundary);				}				else				{	if (!line.trim().equals(boundary))						throw new IOException("Expected boundary: " + line);				}								final Properties properties = new Properties();				while (true)				{					line = readLine(MAX_LINE_LENGTH);					if (line == null)					{	buffer.setEOM(true);						buffer.setLength(0);						return;					}										if (line.trim().equals(""))						break;	// end of header										if (!parseProperty(line, properties))						throw new IOException("Expected property: " + line);														}								final String contentType = properties.getProperty("Content-Type".toUpperCase());				if (contentType == null)				{	logger.warning("Header properties: " + properties);					throw new IOException("Expected Content-Type in header");				}								final byte[] data;								final String contentLenStr = properties.getProperty("Content-Length".toUpperCase());				if (contentLenStr != null)				{	// if we know the content length, use it					final int contentLen;					try					{						contentLen = Integer.parseInt(contentLenStr);					}					catch (NumberFormatException e)					{	throw new IOException("Invalid content length: " + contentLenStr);					}										// now, read the content-length bytes					data = readFully(contentLen);	// TODO: don't realloc each time				}				else				{						// if we don't know the content length, just read until we find the boundary.					// Some IP cameras don't specify it, like					// http://webcam-1.duesseldorf.it-on.net/cgi-bin/nph-update.cgi					data = readUntil(boundary);									}												if (data == null)				{	buffer.setEOM(true);					buffer.setLength(0);					return;				}								buffer.setData(data);				buffer.setOffset(0);				buffer.setLength(data.length);																	}			catch (IOException e)			{	throw new RuntimeException(e);			}					}				@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		}	}	}

⌨️ 快捷键说明

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