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

📄 depacketizer.java

📁 FMJ(freedom media for java)是java视频开发的新选择
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
package net.sf.fmj.media.codec.video.jpeg;import java.awt.Dimension;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Map.Entry;import javax.media.Buffer;import javax.media.Codec;import javax.media.Format;import javax.media.ResourceUnavailableException;import javax.media.format.JPEGFormat;import javax.media.format.VideoFormat;import net.sf.fmj.codegen.MediaCGUtils;import net.sf.fmj.media.AbstractCodec;import net.sf.fmj.utility.StringUtils;/** * FMJ's functional equivalent of com.sun.media.codec.video.jpeg.DePacketizer. * Reassembles JPEG RTP packets into JPEG frames, as per * RFC 2035 - RTP Payload Format for JPEG Video.  See http://www.rfc-archive.org/getrfc.php?rfc=2035 * TODO: support restart markers * TODO: support q table headers * TODO: lunarphases.mov: when received, JMF puts extra stuff on the end that we don't. *  * @author Ken Larson * */public class DePacketizer extends AbstractCodec implements Codec{	private static final boolean COMPARE_WITH_BASELINE = false;	private static final boolean TRACE = false;	private static final boolean EXIT_AFTER_ONE_FRAME = false;	// for testing only.	private static final int MAX_ACTIVE_FRAME_ASSEMBLERS = 3;		private final Format[] supportedInputFormats = new Format[] {			new VideoFormat(VideoFormat.JPEG_RTP, null, -1, Format.byteArray, -1.0f),		};	private final Format[] supportedOutputFormats = new Format[] {			new VideoFormat(VideoFormat.JPEG, null, -1, Format.byteArray, -1.0f),		};	private Codec baselineCodec;	// when debugging/testing, we can set this to an instance of com.sun.media.codec.video.jpeg.DePacketizer and compare the results.	/**	 * Because packets can come out of order, it is possible that some packets for a newer frame	 * may arrive while an older frame is still incomplete.  However, in the case where we get nothing	 * but incomplete frames, we don't want to keep all of them around forever.	 */	public DePacketizer()	{		if (COMPARE_WITH_BASELINE)		{			try			{				baselineCodec = (Codec) Class.forName("com.sun.media.codec.video.jpeg.DePacketizer").newInstance();			}			catch (Exception e)			{	System.out.println("Unable to instantiate com.sun.media.codec.video.jpeg.DePacketizer"); // will happen if JMF not in classpath.			}		}	}		public void close()	{		if (baselineCodec != null)			baselineCodec.close();				super.close();				frameAssemblers.clear();			}	public Object getControl(String controlType)	{		if (baselineCodec != null)		{			return baselineCodec.getControl(controlType);		}		else		{	return super.getControl(controlType);		}	}	public Object[] getControls()	{		if (baselineCodec != null)			return baselineCodec.getControls();		else			return super.getControls();	}	public String getName()	{		return "JPEG DePacketizer";	}	public Format[] getSupportedInputFormats()	{		return supportedInputFormats;	}	public Format[] getSupportedOutputFormats(Format input)	{		if (input == null)			return supportedOutputFormats;		VideoFormat inputCast = (VideoFormat) input;		final Dimension HARD_CODED_SIZE = new java.awt.Dimension(320, 240);		final Format[] result = new Format[] {				new JPEGFormat(HARD_CODED_SIZE, -1, Format.byteArray, -1.0f, -1, -1)};				if (baselineCodec != null)		{			final Format[] baselineResult = baselineCodec.getSupportedOutputFormats(input);			System.out.println("input:  " + MediaCGUtils.formatToStr(input));			for (int i = 0; i < baselineResult.length; ++i)				System.out.println("output: " + MediaCGUtils.formatToStr(baselineResult[0]));		}		// TODO: JMF returns a format with dimensions of 320x200 - not sure where this comes from,		// seems like it might be hard-coded.  We'll do the same, otherwise we can get exceptions 		// downstream.						return result;	}	public void open() throws ResourceUnavailableException	{		if (baselineCodec != null)			baselineCodec.open();		super.open();	}		public void reset()	{		if (baselineCodec != null)			baselineCodec.reset();		super.reset();		frameAssemblers.clear();	}	public Format setInputFormat(Format format)	{		if (baselineCodec != null)		{	super.setInputFormat(format);			return baselineCodec.setInputFormat(format);		}		else		{	return super.setInputFormat(format);		}			}	public Format setOutputFormat(Format format)	{		if (baselineCodec != null)		{	super.setOutputFormat(format);			return baselineCodec.setOutputFormat(format);		}		else		{	return super.setOutputFormat(format);		}	}			public int process(Buffer input, Buffer output)	{		/*		 * Flags to look out for:		 * 	public static final int FLAG_RTP_MARKER = 2048;			public static final int FLAG_RTP_TIME = 4096;	// TODO: what does this mean?		 */				if (!input.isDiscard())		{						if (baselineCodec != null)			{				final int baselineResult = baselineCodec.process(input, output);				//if (TRACE) System.out.println("result=" + baselineResult + " output.getFlags()=" + Integer.toHexString(output.getFlags()) + " output.getLength()=" + output.getLength());			}						final JpegRTPHeader jpegRtpHeader = input.getLength() >= JpegRTPHeader.HEADER_SIZE ? JpegRTPHeader.parse((byte[]) input.getData(), input.getOffset()) : null;			final long timestamp = input.getTimeStamp();			final boolean rtpMarker = (input.getFlags() & Buffer.FLAG_RTP_MARKER) != 0;			if (TRACE) System.out.println("ts=" + input.getTimeStamp() + " flags=" + Integer.toHexString(input.getFlags()) + " offset=" + input.getOffset() + " length=" + input.getLength() + " jpegRtpHeader: " + jpegRtpHeader);			FrameAssembler assembler = frameAssemblers.findOrAdd(timestamp);			assembler.put((Buffer) input.clone());						dump(input, "Input");			if (assembler.complete())			{	Buffer bComplete = baselineCodec == null ? output : new Buffer();					final int offsetAfterHeaders = assembler.copyToBuffer(bComplete);				frameAssemblers.remove(assembler);				frameAssemblers.removeOlderThan(timestamp);	// we have a complete frame, so any earlier fragments are not needed, as they are for older (incomplete) frames.				if (TRACE) System.out.println ("COMPLETE: ts=" + timestamp + " bComplete.getLength()=" + bComplete.getLength());				dump(bComplete, "bComplete");				dump(output, "output");								// TODO: the length of the FMJ buffer is generally shorter than that of the JMF buffer.  This is probably because				// there is trailing garbage, which JMF knows how to remove, and FMJ does not currently.								// flags 0x12 // TODO: JMF is setting these flags, should we?  or are they residual data?				// bComplete.setDiscard(false); // not necessary, this flag should be clear on entry.				if (EXIT_AFTER_ONE_FRAME)					System.exit(0);				return BUFFER_PROCESSED_OK;							}			else			{					frameAssemblers.removeAllButNewestN(MAX_ACTIVE_FRAME_ASSEMBLERS);	// weed out incomplete frames that build up.				output.setDiscard(true);				return OUTPUT_BUFFER_NOT_FILLED;			}							}		else		{			output.setDiscard(true);			return OUTPUT_BUFFER_NOT_FILLED;		}				// TODO: copy over other flags, like EOM?	}		private final FrameAssemblerCollection frameAssemblers = new FrameAssemblerCollection();	/**	 * Used to assemble fragments with the same timestamp into a single frame.	 * @author Ken Larson	 *	 */	static class FrameAssembler	{		private final List list = new ArrayList();	// of Buffer		private boolean rtpMarker; // have we received the RTP marker that signifies the end of a frame?				/**		 * Add the buffer (which contains a fragment) to the assembler.  Should be a clone of a real buffer, since the buffer will		 * be kept around.		 */		public void put(Buffer buffer)		{			if (!rtpMarker)			{	rtpMarker = (buffer.getFlags() & Buffer.FLAG_RTP_MARKER) != 0;			}			if (buffer.getLength() <= JpegRTPHeader.HEADER_SIZE)				return; // no actual data in buffer, no need to keep.  Typically happens when RTP marker is set.						// TODO: interestingly, when JMF sends the RTP marker, it occurs in a fragment with no data - not even			// a header.  However, looking at the buffer, the header is there, but buffer.getLength() returns zero.			// the header has the correct offset of the "end" of the frame.  This would be useful since we can then			// determine whether we have missing trailing fragments.						list.add(buffer);			Collections.sort(list, bufferFragmentOffsetComparator);	// TODO: incremental sort, or bubble sort - since the list is probably already sorted.		}				/**		 * Is the frame complete?		 */		public boolean complete()		{	if (!rtpMarker)				return false;	// need an rtp marker to signify end					if (list.size() <= 0)				return false;	// need at least one fragments with data beyond the header					if (!contiguous())				return false;	// missing fragments.  TODO: theoretically we could display a degraded image, but for now we'll only display complete ones.					// TODO: if some of the last ones come in after the marker, we'll see blank squares in the lower right.			return true;		}					/**		 * Convenience method.		 */		private JpegRTPHeader parseJpegRTPHeader(Buffer b)		{	return JpegRTPHeader.parse((byte[]) b.getData(), b.getOffset());		}				/**		 * @return false if any fragments are missing.  Does not detect fragments missing at the end.		 */		private boolean contiguous()		{			int expect = 0;	// next expected offset.						for (int i = 0; i < list.size(); ++i)			{	final Buffer b = (Buffer) list.get(i);				final JpegRTPHeader jpegRtpHeader = parseJpegRTPHeader(b);				if (jpegRtpHeader.getFragmentOffset() != expect)					return false;

⌨️ 快捷键说明

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