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

📄 handler.java

📁 FMJ(freedom media for java)是java视频开发的新选择
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
		{			logger.log(Level.WARNING, "" + e, e);			postControllerErrorEvent("" + e);			return false;		}				// now that nodes are open, add controls:				if (mode == PLAYER && audioTrackIndex >= 0)	// if it has a audio track		{			final RendererNode rendererNode = (RendererNode) FilterGraph.getTail(root.getDestLink(audioTrackIndex).getDestNode());			if (rendererNode != null) {				final Renderer renderer = rendererNode.getRenderer();				// add any controls from renderer				GainControl gainControl = (GainControl) renderer.getControl(GainControl.class.getName());				if (gainControl != null)					setGainControl(gainControl);			}			}				// TODO: when to call open and when to call start?		if (TRACE) logger.fine("Starting filter graph(s)");				try		{			FilterGraph.start(root);		} catch (IOException e)		{			logger.log(Level.WARNING, "" + e, e);			postControllerErrorEvent("" + e);			return false;		}				// TODO: the track threads need to coordinate better.  Perhaps each track thread		// should really be its own controller, and this controller is a multi-controller (See EJMF example).		trackThreads = new TrackThread[root.getNumDestLinks()];					for (int i = 0; i < root.getNumDestLinks(); ++i)		{			if (root.getDestLink(i) != null)			{	final DemuxNode rootCopy = (DemuxNode) root.duplicate();				trackThreads[i] = new TrackThread(rootCopy, i, videoTrackIndex == i ? FilterGraph.SUPPRESS_TRACK_READ : FilterGraph.PROCESS_DEFAULT); // use a copy of the graph for each thread, so that they don't interfere with each other.				if (videoTrackIndex == i)	// if it has a video track - read the first video frame				{	FilterGraph.process(rootCopy, null, videoTrackIndex, -1, FilterGraph.PROCESS_DEFAULT); // doesn't work because we have to use the actual filter graph clone that is used for the track.				}			}		}		//		if (videoTrackIndex >= 0)	// if it has a video track - read the first video frame//		{	FilterGraph.process(root, null, videoTrackIndex, FilterGraph.PROCESS_DEFAULT); // doesn't work because we have to use the actual filter graph clone that is used for the track.//		}//						// TODO: we need the first frame (video only) to size the window right.		//readAndProcessNextFrame();				// we have to wait for the component to be visible to start really playing, and do this in another thread	    				return true;	}		    public Component getControlPanelComponent() {        Component c = super.getControlPanelComponent();        if( c == null ) {            c = new StandardControlPanel(this, StandardControlPanel.USE_START_CONTROL | StandardControlPanel.USE_STOP_CONTROL | StandardControlPanel.USE_PROGRESS_CONTROL);            setControlPanelComponent(c);        }        return c;    }	private class TrackThread extends CloseableThread	{				private final int trackNumber;		private final DemuxNode root;		private final int firstFlags; 		private long nanoseconds;	// TODO: set this properly		private boolean eom = false;		   				public TrackThread(DemuxNode root, int trackNumber, int firstFlags)		{			super();			setName("TrackThread for track " + trackNumber);			this.root = root;			this.trackNumber = trackNumber;			this.firstFlags = firstFlags;		}		@Override		public void close()		{			// don't call super.close(), which interrupts.			// it seems risky to be interrupting the thread, which could be in any			// possible state.  We would basically lose frames this way.			// so the downside of not interrupting is that it may take longer than			// expected to fully stop.						// Interrupting would leave the media in an ungraceful state.			// we may have buffers that are read, that will never be rendered, etc.			// we may want to find a way to do this without interruption.						// TODO: what we really want to do is keep any buffers around, and simply 			// continue processing them if we resume.			// really, we want to basically suspend the thread and resume it.						setClosing();		}		// returns false on EOM.		private Buffer readAndProcessNextFrame(int flags)		{//			 TODO: we need to honor isEnabled when processing tracks			FilterGraph.process(root, null, trackNumber, -1, flags);			final Buffer b = (Buffer) root.getOutputBuffer(trackNumber);			if (b == null)				throw new NullPointerException();			return b;					}				public void run()		{			try			{								final float rate = getRate(); // what if this changes during playback?	It won't because we'll get stopped and started in that case.					try				{					final FilterGraphNode penultimateNode = FilterGraph.getBeforeTail(root.getDestLink(trackNumber).getDestNode());										boolean first = true;					boolean inputEOM = false;	// set to true when the input buffer reaches EOM, which may be before the last buffer in the chain reaches EOM.					while (true)					{						if (isClosing())							return;						final int flags;						if (first)							flags = firstFlags;	// if firstFlags == FilterGraph.SUPPRESS_TRACK_READ, first frame has already been read, only needs redisplay						else if (inputEOM)							flags = FilterGraph.SUPPRESS_TRACK_READ;						else							flags = FilterGraph.PROCESS_DEFAULT;						Buffer b = readAndProcessNextFrame(flags); 						if (isClosing())							return;						if (first)						{	first = false;						}						if (b.isEOM())							inputEOM = true;	// we continue processing, however, until the final buffer in the chain gets EOM.												// this is needed to handle the case where there is residual data in the codecs.												//TODO: how close is this to what JMF does?  How does JMF handle this?												// if we don't do this, the effect is that codecs that are not 1:1 in terms of buffers will have any												// data buffered up in the codecs cut off.  												if (b.isDiscard())							continue;																		if (!(penultimateNode instanceof RendererNode) && !(penultimateNode instanceof MuxNode))						{							// we need to sleep based on the time in the processed data, not the input data.  							// if the graph goes demux->renderer, then the output buffer of the demux,							// which is set to b above, is what we use to check.							b = penultimateNode.getOutputBuffer(0);							// TODO: this can cause a NPE below if b is null (not sure how it could be null)								if (b == null)								continue;	// this can be null if it has not been set yet, if codecs earlier in the chain have been 											// returning INPUT_BUFFER_NOT_CONSUMED or OUTPUT_BUFFER_NOT_FILLED.						}												if (b.isEOM())	// TODO: is there data in an eom buffer?							break;						if (b.isDiscard())							continue;									            //  Update the video time			            nanoseconds = b.getTimeStamp();					}										eom = true;					checkAllTracksEOM();				}				catch (Exception e)				{					logger.log(Level.WARNING, "" + e, e);					postControllerErrorEvent("" + e);				}					        			}			finally			{				setClosed();			}		}		public boolean isEOM()		{			return eom;		}					}		/**	 * If all tracks have reached eom, then we post an end of media event.	 *	 */	private void checkAllTracksEOM()	{		// TODO: synchronize on something?		for (int i = 0; i < trackThreads.length; ++i)		{			if (!trackThreads[i].isEOM())				return;		}		//		 TODO: not once for each track        try {            endOfMedia();        } catch(ClockStoppedException e) {            postEvent(                new InternalErrorEvent(Handler.this,                    "Controller not in Started state at EOM") );        }					}	//@Override	public void doPlayerSetMediaTime(Time t)	{		logger.info("Handler.doPlayerSetMediaTime" + t);				// TODO: bounds checking		root.getDemux().setPosition(t, 0);	// TODO: rounding?		        // TODO: //		long nanoseconds = t.getNanoseconds();//		//        //  Enforce bounds//        if( nanoseconds > duration.getNanoseconds() ) {//            nanoseconds = duration.getNanoseconds();//        } else////        if( nanoseconds < 0 ) {//            nanoseconds = 0;//        }////        //  If the video is currently playing and hasn't reached//        //  the end, then stop it, reset the nanosecond offset,//        //  and restart it.  Otherwise, just set the nanosecond//        //  offset.////        if( playthread != null &&//            playthread.isAlive() &&//            ! endOfMedia() )//        {//            stop();//            this.nanoseconds = nanoseconds;//            start();//        } else {//            this.nanoseconds = nanoseconds;//        }//        calcInitFrame();        	}		//@Override	public float doPlayerSetRate(float rate)	{		logger.info("Handler.doPlayerSetRate " + rate);        //  Zero is the only invalid rate        if( rate == 0 ) return getRate();	// TODO: neg. not supported        if( rate < 0 ) return getRate();	                // TODO:         //        //  If the video is currently playing and hasn't reached//        //  the end, then stop it, reset the rate, and restart it.//        //  Otherwise, just set the nanosecond offset.////        if( playthread != null &&//            playthread.isAlive() &&//            ! endOfMedia() )//        {//            stop();//            this.rate = rate;//            start();//        } else {//            this.rate = rate;//        }        //calcInitFrame();        return rate;	}	//@Override	public boolean doPlayerStop()	{		logger.info("Handler.doPlayerStop");				if (trackThreads == null)			return true;		final List<TrackThread> waitUntilClosed = new ArrayList<TrackThread>();				for (int i = 0; i < trackThreads.length; ++i)		{			final TrackThread t = trackThreads[i];			if (t != null && t.isAlive())			{	t.close(); // shut down gracefully				waitUntilClosed.add(t);			}		}		//		// TODO: is it legal to call stop (for example Renderer.stop), potentially while //		// Renderer.process is being called?  This seems like this could cause//		// more problems than it solves.  Perhaps we should wait for the tracks//		// threads to complete before doing this.  This requires that the//		// nodes themselves will successfully respond to an interruption.//		try//		{//			FilterGraph.stop(root);//		} catch (IOException e)//		{//			logger.log(Level.WARNING, "" + e, e);//			postControllerErrorEvent("" + e);//			return false;//		}				for (TrackThread t : waitUntilClosed)		{			// make sure threads are really stopped.  this can cause a thread deadlock.			try			{				t.waitUntilClosed();			} catch (InterruptedException e)			{				logger.log(Level.WARNING, "" + e, e);				return false;			}		}				// stop after the threads have completed, avoiding any threadin problems with		// process on the filter graph.		try

⌨️ 快捷键说明

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