/* * @(#)RawStreamParser.java 1.13 02/08/21 * * Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved. */ package com.sun.media.parser; import java.io.IOException; import javax.media.*; import javax.media.Buffer; import javax.media.protocol.*; import javax.media.format.*; import com.sun.media.*; public class RawStreamParser extends RawParser { protected SourceStream[] streams; protected Track[] tracks = null; static final String NAME = "Raw stream parser"; public String getName() { return NAME; } public RawStreamParser() { } public void setSource(DataSource source) throws IOException, IncompatibleSourceException { if (!(source instanceof PushDataSource)) { throw new IncompatibleSourceException("DataSource not supported: " + source); } else { streams = ((PushDataSource) source).getStreams(); } if ( streams == null) { throw new IOException("Got a null stream from the DataSource"); } if (streams.length == 0) { throw new IOException("Got a empty stream array from the DataSource"); } if (!supports(streams)) throw new IncompatibleSourceException("DataSource not supported: " + source); this.source = source; this.streams = streams; // System.out.println("content length is " + streams[0].getContentLength()); } /** * Override this if the Parser has additional requirements * from the PushSourceStream */ protected boolean supports(SourceStream[] streams) { return ( (streams[0] != null) && (streams[0] instanceof PushSourceStream) ); } public Track [] getTracks() { return tracks; } /** * Opens the plug-in software or hardware component and acquires * necessary resources. If all the needed resources could not be * acquired, it throws a ResourceUnavailableException. Data should not * be passed into the plug-in without first calling this method. */ public void open() { if (tracks != null) return; tracks = new Track[streams.length]; for (int i = 0; i < streams.length; i++) { tracks[i] = new FrameTrack(this, (PushSourceStream)streams[i], 5); } } /** * Closes the plug-in component and releases resources. No more data * will be accepted by the plug-in after a call to this method. The * plug-in can be reinstated after being closed by calling * <code>open</code>. */ public void close() { if (source != null) { try { source.stop(); // stop each of the tracks, so that readFrame() can be // released. for (int i = 0; i < tracks.length; i++) ((FrameTrack)tracks[i]).stop(); source.disconnect(); } catch (IOException e) { // Internal error? } source = null; } } /** * Start the parser. */ public void start() throws IOException { source.start(); for (int i = 0; i < tracks.length; i++) ((FrameTrack)tracks[i]).start(); } /** * Stop the parser. */ public void stop() { try { source.stop(); // stop each of the tracks, so that readFrame can be released for (int i = 0; i < tracks.length; i++) ((FrameTrack)tracks[i]).stop(); } catch (IOException e) { // Internal errors? } } //////////////////////// // // Inner class //////////////////////// class FrameTrack implements Track, SourceTransferHandler { Demultiplexer parser; PushSourceStream pss; boolean enabled = true; CircularBuffer bufferQ; Format format = null; TrackListener listener; Integer stateReq = new Integer(0); boolean stopped = true; public FrameTrack(Demultiplexer parser, PushSourceStream pss, int numOfBufs) { this.pss = pss; pss.setTransferHandler(this); bufferQ = new CircularBuffer(numOfBufs); } public Format getFormat() { return format; } public void setEnabled(boolean t) { if (t) pss.setTransferHandler(this); else pss.setTransferHandler(null); enabled = t; } public boolean isEnabled() { return enabled; } public Time getDuration() { return parser.getDuration(); } public Time getStartTime() { return new Time(0); } public void setTrackListener(TrackListener l) { listener = l; } public void readFrame(Buffer buffer) { // Retrieve a filled buffer. Buffer filled; synchronized (stateReq){ if (stopped){ buffer.setDiscard(true); buffer.setFormat(format); return; } } synchronized (bufferQ) { while (!bufferQ.canRead()) { try { bufferQ.wait(); synchronized(stateReq){ if (stopped){ buffer.setDiscard(true); buffer.setFormat(format); return; } } } catch (Exception e) {} } filled = bufferQ.read(); bufferQ.notifyAll(); } // exchange the buffers. byte data[] = (byte [])filled.getData();; filled.setData(buffer.getData()); buffer.setData(data); buffer.setLength(filled.getLength()); buffer.setFormat(format); buffer.setTimeStamp(Buffer.TIME_UNKNOWN); synchronized (bufferQ) { bufferQ.readReport(); bufferQ.notifyAll(); } } public void stop(){ // we basically need to ensure that readFrame will return // immediately.and also make sure that if it is called in // the stopped state, it returns w/o blocking. synchronized(stateReq){ stopped = true; } synchronized (bufferQ){ bufferQ.notifyAll(); } } public void start(){ // we need to ensure that readFrame is returned to its // original state and does not return w/o blocking. synchronized(stateReq){ stopped = false; } synchronized (bufferQ){ bufferQ.notifyAll(); } } public int mapTimeToFrame(Time t) { return -1; } public Time mapFrameToTime(int frameNumber) { return new Time(0); } public void transferData(PushSourceStream pss) { // Retrieve an empty buffer for the PSS to write into. Buffer buffer; synchronized (bufferQ) { while (!bufferQ.canWrite()) { try { bufferQ.wait(); } catch (Exception e) {} } buffer = bufferQ.getEmptyBuffer(); bufferQ.notifyAll(); } int size = pss.getMinimumTransferSize(); byte data[]; if ((data = (byte [])buffer.getData()) == null || data.length < size) { data = new byte[size]; buffer.setData(data); } try { int len = pss.read(data, 0, size); buffer.setLength(len); } catch (IOException e) { buffer.setDiscard(true); } // Put the filled buffer back to the queue for consumption. synchronized (bufferQ) { bufferQ.writeReport(); bufferQ.notifyAll(); } } } }