FileDocCategorySizeDatePackage
VFWSourceStream.javaAPI DocJMF 2.1.1e31113Mon May 12 12:21:20 BST 2003com.sun.media.protocol.vfw

VFWSourceStream.java

/*
 * @(#)VFWSourceStream.java	1.46 03/04/24
 *
 * Copyright (c) 1996-2002 Sun Microsystems, Inc.  All rights reserved.
 */

package com.sun.media.protocol.vfw;

import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.io.IOException;
import javax.media.*;
import javax.media.protocol.*;
import com.sun.media.protocol.*;
import com.sun.media.*;
import javax.media.format.*;
import javax.media.control.*;
import com.sun.media.vfw.*;
import com.sun.media.ui.VideoFormatChooser;
import com.sun.media.util.WindowUtil;
import com.sun.media.controls.FrameRateAdapter;
import java.util.Vector;


/****************************************************************
 * SourceStream for the DataSource
 ****************************************************************/

public class VFWSourceStream
extends BasicSourceStream
implements PushBufferStream, Runnable,
    ActionListener, FormatControl, Owned {
    
    private MediaLocator   locator = null;
    private int            capHandle = 0;
    Integer                cbHandleLock = new Integer(0);
    private int            cbHandle = 0;
    private VideoFormat    capFormat = null;
    private float          capFrameRate = 0;
    private int            capPreviewRate = 33; // in millis
    private int            capWidth = -1;
    private int            capHeight = -1;
    private byte []        data = null;
    private int            maxDataSize = 1;
    BufferTransferHandler  transferHandler = null;
    private int            length = 0;
    private int            nativeParentWindow = 0; // Native window
    private static String STRING_CF = "Custom Format...";
    private static String STRING_SD = "Video Source...";

    private Integer        lockInit = new Integer(1);
    private Integer        vfwReqLock = new Integer(2);
    private int            vfwRequest = -1;

    private static final int REQ_START = 1;
    private static final int REQ_STOP  = 2;
    private static final int REQ_FORMATDIALOG = 3;
    private static final int REQ_SOURCEDIALOG = 4;
    private static final int REQ_DISCONNECT = 5;
    private static final int REQ_UPDATECAPTURESETUP = 6;
    private static final int REQ_MONITOR = 7;
    
    private boolean        started = false;
    private boolean        connected = false;
    private boolean        connectedOK = false;
    private Frame          capFrame = null;

    public static int      MAX_CARDS = 10;
    public static boolean  DEBUG = false;
    private int            cardNo = 0;
    
    private static boolean [] inUse = new boolean[MAX_CARDS];
    private static Integer VFWLock = new Integer(0);
    Integer readLock = new Integer(1);
    Integer waitForStop = new Integer(2);
    boolean doneReading = false;
    
    boolean waitingToStop = false;
    long lastTimeStamp = 0;
    long startTimeStamp = 0;
    long [] resultTimeStamp = new long[1];
    SystemTimeBase systemTimeBase = new SystemTimeBase();
    
    private Panel controlPanel = null;
    private Control [] controls = null;

    private static final String DEFAULT_PORT = "default";
    private boolean hasDlgVideoFormat = false;
    private boolean hasDlgVideoSource = false;
    private boolean hasOverlay = false;
    private boolean monitorEnabled = false;
    private float defaultFrameRate = 15.0f;
    private float requestedFrameRate = 15.0f;
    private PushThread pt = null;
    private TransferDataThread tdt = null;
    private VideoFormat defaultFormat = null;
    private VideoFormat requestedFormat = null;
    private VideoFormat fullFormat = null;
    private CaptureDeviceInfo cdi = null;
    CircularBuffer bufferQ = new CircularBuffer(8);

    private float FRAME_RATES [] = {
	1f, 2f, 5f, 7.5f, 10f, 12.5f, 15f, 20f, 24f, 25f, 30f
    };

    static {
	for (int i = 0; i < MAX_CARDS; i++)
	    inUse[i] = false;
    }
    
    public VFWSourceStream(MediaLocator ml) {
	super(new ContentDescriptor(ContentDescriptor.RAW),
	      LENGTH_UNKNOWN);
	this.locator = ml;
	cardNo = getCardNo(ml);
    }

    CaptureDeviceInfo getCaptureDeviceInfo() {
	if (cdi == null) {
	    String url = "vfw://" + cardNo;

	    Vector cdiList =
		CaptureDeviceManager.getDeviceList(new VideoFormat(null));
	    if (cdiList != null && cdiList.size() > 0) {
		for (int i = 0; i < cdiList.size(); i++) {
		    CaptureDeviceInfo tempCDI =
			(CaptureDeviceInfo) cdiList.elementAt(i);
		    if (tempCDI.getLocator().toString().equalsIgnoreCase(url)) {
			cdi = tempCDI;
			break;
		    }
		}
	    }

	    // If we couldn't find it registered in CaptureDeviceManager
	    if (cdi == null) {
		cdi = autoDetect(cardNo);
	    }
	}

	return cdi;
    }

    public static CaptureDeviceInfo autoDetect(int cardNo) {
	CaptureDeviceInfo cdi = null;
	try {
	    cdi = new VFWDeviceQuery(cardNo);
	    if ( cdi != null && cdi.getFormats() != null &&
		 cdi.getFormats().length > 0) {
		// Commit it to disk. Its a new device
		if (CaptureDeviceManager.addDevice(cdi)) {
		    CaptureDeviceManager.commit();
		}
	    }
	} catch (Throwable t) {
	    if (t instanceof ThreadDeath)
		throw (ThreadDeath)t;
	}
	
	return cdi;
    }

    static int getCardNo(MediaLocator locator) {
	int cardNo = 0;
	String remainder = locator.getRemainder();
	if (remainder != null && remainder.length() > 0) {
	    while (remainder.length() > 1 && remainder.charAt(0) == '/')
		remainder = remainder.substring(1);
	    try {
		Integer integer = Integer.valueOf(remainder);
		if (integer != null)
		    cardNo = integer.intValue();
	    } catch (Throwable t) {
	    }
	}
	return cardNo;
    }
    
    public Object getDataType() {
	return Format.byteArray;
    }

    public void setTransferHandler(BufferTransferHandler th) {
	transferHandler = th;
    }

    public void connect(Format format) throws IOException {
	synchronized (VFWLock) {
	    /*
	    try {
		String prop = System.getProperty("vfw.popup");
		if (prop != null)
		    disableFormatDialog = true;
	    } catch (Throwable t) {
		if (t instanceof ThreadDeath)
		    throw (ThreadDeath)t;
	    }
	    */
	    if (connected)
		return;
	    if (inUse[cardNo]) {
		//System.err.println("Card in use");
		throw new IOException("Capture device in use");
	    } else
		inUse[cardNo] = true;
	    connected = false;
	    if (format != null)
		requestedFormat = (VideoFormat) format;
	    
	    //System.err.println("Requested format is " + requestedFormat);
	    Thread initThread = new Thread(this, "VFW Request Thread");
	    initThread.start();
	    
	    if (!connected) {
		//synchronized (lockInit) {
		    try {
			//System.err.println("Waiting to finish connect");
			while (!connected) {
			    Thread.currentThread().sleep(50);
			    Thread.currentThread().yield();
			}
		    } catch (InterruptedException ie) {
		    }
		    //}
	    }

	    if (!connectedOK) {
		inUse[cardNo] = false;
		connected = false;
		throw new IOException("Could not connect to capture device");
	    } else
		updateComponents();
	}
    }

    private boolean doConnect() {
	//System.err.println("Before creating window");
	nativeParentWindow = VFWCapture.createWindow("Cap Parent");
	//System.err.println("After creating window");
	capHandle = VFWCapture.capCreateCaptureWindow("Test",
							  nativeParentWindow,
							  0, 0, 320, 240,
							  VFWCapture.getNextID());
	if (capHandle == 0) {
	    VFWCapture.destroyWindow(nativeParentWindow);
	    return false;
	}
	
	cbHandle = VFWCapture.createFrameCallback(capHandle);
	    
	if (cbHandle == 0 || !VFWCapture.capDriverConnect(capHandle, cardNo)) {
	    //System.err.println("capDriverConnect failed");
	    if (cbHandle != 0)
		VFWCapture.destroyFrameCallback(capHandle, cbHandle);
	    VFWCapture.destroyWindow(capHandle);
	    //capFrame.dispose();
	    VFWCapture.destroyWindow(nativeParentWindow);
	    return false;
	}

	CapDriverCaps cdc = new CapDriverCaps();
	VFWCapture.capDriverGetCaps(capHandle, cdc);

	CaptureParms cp = new CaptureParms();
	VFWCapture.capCaptureGetSetup(capHandle, cp);
	//System.err.println("Capture Params = " + cp);
	// Allow windows events to occur
	cp.fYield = true;
	// Dont capture audio by default
	cp.fCaptureAudio = false;
	// Dont abort on mouse clicks
	cp.fAbortLeftMouse = false;
	cp.fAbortRightMouse = false;
	// Dont prompt user to start
	cp.fMakeUserHitOKToCapture = false;

	if (cdc.fHasDlgVideoFormat) {
	    hasDlgVideoFormat = true;
	}
	if (requestedFormat == null) {
	    Dimension size = new Dimension(320, 240);
	    requestedFormat = new RGBFormat(size,
					    size.width * size.height * 3,
					    Format.byteArray,
					    requestedFrameRate,
					    24,
					    3, 2, 1,
					    3, size.width * 3,
					    RGBFormat.TRUE,
					    RGBFormat.NOT_SPECIFIED);
		
	}

	// Default frame rate requested is 15 fps.
	if (cp.dwRequestMicroSecPerFrame == 0)
	    cp.dwRequestMicroSecPerFrame = 66667;
	defaultFrameRate = 1000000F / cp.dwRequestMicroSecPerFrame;
	if (requestedFormat.getFrameRate() > 0 &&
	    requestedFormat.getFrameRate() < 61)
	    requestedFrameRate = requestedFormat.getFrameRate();
	cp.dwRequestMicroSecPerFrame = (int) (1000000F / requestedFrameRate);
	//FRAME_RATES[0] = requestedFrameRate;
	cp.wNumVideoRequested = 15;
	VFWCapture.capCaptureSetSetup(capHandle, cp);
	VFWCapture.capCaptureGetSetup(capHandle, cp);
	if (cp.dwRequestMicroSecPerFrame != 0)
	    capFrameRate = 1000000F / cp.dwRequestMicroSecPerFrame;
	else
	    capFrameRate = requestedFrameRate;

	if (cdc.fHasDlgVideoSource)
	    hasDlgVideoSource = true;
	if (cdc.fHasOverlay) {
	    VFWCapture.capOverlay(capHandle, monitorEnabled);
	    hasOverlay = true;
	} else {
	    VFWCapture.capPreview(capHandle, monitorEnabled);
	    VFWCapture.capPreviewRate(capHandle, 33);
	    hasOverlay = false;
	}
	
	VFWCapture.capSetWindowPos(capHandle, 0, 0, 320, 240);
	BitMapInfo bmi = new BitMapInfo();
	
	VFWCapture.capGetVideoFormat(capHandle, bmi);

	defaultFormat = bmi.createVideoFormat(Format.byteArray, capFrameRate);
	bmi = new BitMapInfo(requestedFormat);
	//System.err.println("Setting bmi = " + bmi);
	VFWCapture.capSetVideoFormat(capHandle, bmi);
	VFWCapture.capGetVideoFormat(capHandle, bmi);
	//System.err.println("Result of setting = " + bmi);
	capFormat = bmi.createVideoFormat(Format.byteArray);

	VideoFormat frOnly = new VideoFormat(null, null,
					     Format.NOT_SPECIFIED,
					     null,
					     capFrameRate);

	fullFormat = (VideoFormat) capFormat.intersects(frOnly);
	maxDataSize = bmi.biSizeImage;
	data = new byte[maxDataSize];

	return true;
    }

    void updateCaptureSetup() {
	CaptureParms cp = new CaptureParms();
	VFWCapture.capCaptureGetSetup(capHandle, cp);
	if (requestedFrameRate < 1)
	    requestedFrameRate = 1;
	cp.dwRequestMicroSecPerFrame = (int) (1000000F / requestedFrameRate);
	VFWCapture.capCaptureSetSetup(capHandle, cp);
	VFWCapture.capCaptureGetSetup(capHandle, cp);
	if (cp.dwRequestMicroSecPerFrame != 0)
	    capFrameRate = 1000000F / cp.dwRequestMicroSecPerFrame;
	updateFormatChooser();
    }

    void updateFormatChooser() {
    	if (formatChooser != null) {
	    formatChooser.setCurrentFormat(capFormat);
	    formatChooser.setFrameRate(capFrameRate);
	}
    }

    void requestUpdateCaptureSetup() {
	synchronized (vfwReqLock) {
	    vfwRequest = REQ_UPDATECAPTURESETUP;
	    vfwReqLock.notifyAll();
	}
    }
    
    synchronized void disconnect() {
	if (started) {
	    try {
		stop();
	    } catch (IOException ioe) {
	    }
	}
	connected = false;
	synchronized (vfwReqLock) {
	    vfwRequest = REQ_DISCONNECT;
	    vfwReqLock.notifyAll();
	}
	
	while (pt != null) {
	    try {
		wait(50);
	    } catch (InterruptedException ie) {
	    }
	}
	inUse[cardNo] = false;
	synchronized (bufferQ) {
	    bufferQ.notifyAll();
	}
	synchronized (readLock) {
	    readLock.notifyAll();
	}
    }

    private void doDisconnect() {
	VFWCapture.capCaptureAbort(capHandle);
	VFWCapture.stopFrameCallback(capHandle, cbHandle);
	synchronized (cbHandleLock) {
	    //VFWCapture.destroyFrameCallback(capHandle, cbHandle);
	    cbHandle = 0;
	}
	if (hasOverlay)
	    VFWCapture.capOverlay(capHandle, false);
	else
	    VFWCapture.capPreview(capHandle, false);
	VFWCapture.capDriverDisconnect(capHandle);
	synchronized (this) {
	    VFWCapture.destroyWindow(capHandle);
	    //capFrame.dispose();
	    VFWCapture.destroyWindow(nativeParentWindow);
	    notifyAll();
	}
    }

    void start() throws IOException {
	//System.err.println("@@@@ Incoming start 1/2");
	//if (started)
	//    return;
	//System.err.println("@@@@ Incoming start 2/2");
	started = true;
	updateComponents();
	synchronized (this) {
	    if (!connected) {
		started = false;
		return;
	    }
	    synchronized (bufferQ) {
		bufferQ.reset();
	    }
	    // Start the video call back polling thread
	    if (pt == null) {
		pt = new PushThread(this);
		pt.start();
	    }
	    synchronized (pt) {
		pt.buffer = null;
		pt.data = null;
		pt.dataBytes = 0;
	    }
	    
	    if (tdt == null) {
		tdt = new TransferDataThread(this);
		tdt.start();
	    }
	    // Send a request to the vfw thread to start capture
	    synchronized (vfwReqLock) {
		vfwRequest = REQ_START;
		vfwReqLock.notifyAll();
	    }
	}
    }

    private void handleFormatChange() {
	// Check if capture format changed
	VideoFormat newFormat = null;
	VideoFormat frOnly = null;
	
	// If we have a new format request....
	if ( requestedFormat != null ) {
	    //System.err.println("handleFormatChange: req = " + requestedFormat);
	    BitMapInfo bmi = new BitMapInfo(requestedFormat);
	    //System.err.println("handleFormatChange: try bmi = " + bmi);
	    // Try setting the format
	    VFWCapture.capSetVideoFormat(capHandle, bmi);
	    // What did it really set it to?
	    VFWCapture.capGetVideoFormat(capHandle, bmi);
	    //System.err.println("handleFormatChange: got bmi = " + bmi);
	    // This is the final format
	    capFormat = bmi.createVideoFormat(Format.byteArray);
	    if (requestedFormat.getFrameRate() > 0 &&
                requestedFormat.getFrameRate() < 61)
                requestedFrameRate = requestedFormat.getFrameRate();
	    requestedFormat = null;
	    maxDataSize = bmi.biSizeImage;	    
	    data = new byte[maxDataSize];
	    capWidth = bmi.biWidth;
	    capHeight = bmi.biHeight;
	}

	// Did the frame rate change in the format chooser?
	if (formatChooser != null) {
	    float chooserFrameRate = formatChooser.getFrameRate();
	    if (Math.abs(chooserFrameRate - capFrameRate) > 0.45)
		requestedFrameRate = chooserFrameRate;
	}

	if (requestedFrameRate != Format.NOT_SPECIFIED) {
	    updateCaptureSetup();
	    requestedFrameRate = Format.NOT_SPECIFIED;
	}

	frOnly = new VideoFormat(null,
				 null,
				 Format.NOT_SPECIFIED,
				 null,
				 capFrameRate);

	newFormat = (VideoFormat) capFormat.intersects(frOnly);

	if ( fullFormat == null ||
	     !newFormat.equals(fullFormat)) {
	    fullFormat = newFormat;
	}
	updateFormatChooser();
    }
    
    private void doStartCapture() {
	handleFormatChange();
	// Start the capture
	CapStatus cs = new CapStatus();
	VFWCapture.capGetStatus(capHandle, cs);
	if (cs.fCapturingNow)
	    return;
	VFWCapture.startFrameCallback(capHandle, cbHandle);
	VFWCapture.capCaptureSequenceNoFile(capHandle);
	startTimeStamp = systemTimeBase.getNanoseconds();
    }

    void stop() throws IOException {
	started = false;

	synchronized (vfwReqLock) {
	    vfwRequest = REQ_STOP;
	    waitingToStop = true;
	    vfwReqLock.notifyAll();
	}
	
	synchronized (waitForStop) {
	    while (waitingToStop) {
		try {
		    waitForStop.wait();
		} catch (InterruptedException ie) {
		}
	    }
	}
	
	updateComponents();
    }

    public void finalize() {
	if (connected)
	    disconnect();
    }

    boolean getStarted() {
	return started;
    }

    boolean getConnected() {
	return connected;
    }

    int getCapHandle() {
	return capHandle;
    }

    int getCBHandle() {
	return cbHandle;
    }

    byte [] getData() {
	return data;
    }

    void pushData(int length) {
	this.length = length;
	if (transferHandler != null)
	    transferHandler.transferData(this);
    }

    Object checkDataAllocation(Buffer buffer) {
	Object data;
	if (buffer instanceof ExtBuffer &&
	    ((ExtBuffer)buffer).isNativePreferred()) {

	    data = ((ExtBuffer)buffer).getNativeData();
	    if (data == null || ((NBA)data).getSize() < maxDataSize)
		data = new NBA(byte[].class, maxDataSize);
	    ((ExtBuffer)buffer).setNativeData((NBA) data);
	} else {
	    data = buffer.getData();
	    if (data == null || !(data instanceof byte[]) ||
		((byte[])data).length < maxDataSize) {
		data = new byte[maxDataSize];
		buffer.setData(data);
	    }
	}
	return data;
    }

    public void read(Buffer buffer) {
	synchronized (readLock) {
	    synchronized (bufferQ) {
		if (bufferQ.canRead()) {
		    // Get the available captured buffer
		    Buffer captureBuffer = bufferQ.read();
		    buffer.copy(captureBuffer, true); // swap data objects
		    buffer.setOffset(0);
		    buffer.setFormat(fullFormat);
		    buffer.setFlags(Buffer.FLAG_KEY_FRAME | Buffer.FLAG_SYSTEM_TIME |
				    Buffer.FLAG_LIVE_DATA);
		    /*
		    byte [] outgoingData = (byte[]) captureBuffer.getData();
		    captureBuffer.setData(checkDataAllocation(buffer.getData()));
		    // Copy attributes of captured buffer to outgoing
		    buffer.setData(outgoingData);
		    buffer.setOffset(0);
		    buffer.setLength(captureBuffer.getLength());
		    buffer.setTimeStamp(captureBuffer.getTimeStamp());
		    buffer.setFormat(fullFormat);
		    buffer.setFlags(Buffer.FLAG_KEY_FRAME | Buffer.FLAG_SYSTEM_TIME);
		    */
		    // Release buffer to buffer queue
		    bufferQ.readReport();
		} else
		    buffer.setDiscard(true);
	    }
	    doneReading = true;
	    readLock.notifyAll();
	}
    }

    public Object [] getControls() {
	if (controls == null) {
	    controls = new Control[2];

	    controls[0] = (FormatControl)this;
	    controls[1] = new MC();
	}
	return controls;
    }

    /****************************************************************
     * FormatControl
     ****************************************************************/

    public Format getFormat() {
	if (fullFormat == null)
	    fullFormat = (VideoFormat) getSupportedFormats()[0];
	return fullFormat;
    }

    public Format [] getSupportedFormats() {
	if (cdi == null)
	    cdi = getCaptureDeviceInfo();
	if (cdi == null)
	    return new Format[0];
	return cdi.getFormats();
    }

    public Format setFormat(Format in) {
	if (!(in instanceof VideoFormat))
	    return null;
	Format matched = BasicPlugIn.matches(in, getSupportedFormats());
	if (matched != null)
	    requestedFormat = (VideoFormat) in;
	if (connected && !started) {
	    handleFormatChange();
	    return fullFormat;
	} else {
	    return matched;
	}
    }

    public void setEnabled(boolean value) {
	// ignore
    }

    public boolean isEnabled() {
	return true;
    }

    Choice formatChoice = null;
    Choice frameRateChoice = null;
    Panel controlComponent = null;
    Button formatDialog = null;
    Button sourceDialog = null;
    VideoFormatChooser formatChooser = null;

    private void updateComponents() {
	if (formatChoice != null) 
	    formatChoice.setEnabled( !started );
	if (formatChooser != null)
	    formatChooser.setEnabled( connected && !started );
	if (formatDialog != null)
	    formatDialog.setEnabled( connected && !started );
	if (sourceDialog != null)
	    sourceDialog.setEnabled( connected && !started );
	if (frameRateChoice != null)
	    frameRateChoice.setEnabled( !started );
	if (cbMonitorEnable != null)
	    cbMonitorEnable.setEnabled( !started );
    }

    public Component getControlComponent() {
	if (controlComponent == null) {
	    controlComponent = new Panel( new BorderLayout() );
	    controlComponent.setName("Capture Control");
	    Format [] formats = getSupportedFormats();
	    formatChooser = new VideoFormatChooser(formats, (VideoFormat) getFormat(),
						   FRAME_RATES);
	    controlComponent.add("North", formatChooser);

	    formatDialog = new Button(STRING_CF);
	    
	    sourceDialog = new Button(STRING_SD);
	    sourceDialog.addActionListener(this);

	    Panel southPanel = new Panel();
	    southPanel.add(formatDialog);
	    southPanel.add(sourceDialog);

	    controlComponent.add("South", southPanel);
	    
	    updateComponents();
	    updateFormatChooser();
	    formatDialog.addActionListener(this);
	}
	return controlComponent;
    }

    public Object getOwner() {
	return this;
    }

    /****************************************************************
     * ActionListener
     ****************************************************************/
    
    public synchronized void actionPerformed(ActionEvent ae) {
	String source = ae.getActionCommand();
	if (source.equals(STRING_CF)) {
	    synchronized (vfwReqLock) {
		vfwRequest = REQ_FORMATDIALOG;
		vfwReqLock.notifyAll();
	    }
	} else if (source.equals(STRING_SD)) {
	    synchronized (vfwReqLock) {
		vfwRequest = REQ_SOURCEDIALOG;
		vfwReqLock.notifyAll();
	    }
	}
    }
    
    /****************************************************************
     * this.Thread to do VFW requests.
     ****************************************************************/
    
    public void run() {
	boolean wmQuit = false;
	int peekResult = 1;
	
	if (doConnect()) {
	    synchronized (lockInit) {
		connected = true;
		connectedOK = true;
		//System.err.println("Notifying doneConnect");
		lockInit.notifyAll();
	    }
	} else {
	    synchronized (lockInit) {
		connected = true;
		connectedOK = false;
		lockInit.notifyAll();
		return;
	    }
	}
	
	while (connected) {
	    while (vfwRequest < 0) {
		try {
		    Thread.currentThread().yield();
		    Thread.currentThread().sleep(50);
		} catch (InterruptedException ie) {
		}
		//System.err.println("Peeking..");
		
		peekResult = VFWCapture.peekWindowLoop(nativeParentWindow);
		if (peekResult == 0)
		    wmQuit = true;
		
	    }
	    int cpVFWRequest = vfwRequest;
	    vfwRequest = -1;
	    switch (cpVFWRequest) {
		case REQ_STOP:
		    if (DEBUG) System.err.println("capture - Stopping");
		    VFWCapture.stopFrameCallback(capHandle, cbHandle);
		    VFWCapture.capCaptureStop(capHandle);
		    
		    // Wait for atmost 5 seconds for capture to stop
		    int i = 0;
		    CapStatus cs = new CapStatus();
		    while (i < 100) {
			VFWCapture.capGetStatus(capHandle, cs);
			if (!cs.fCapturingNow) {
			    if (DEBUG) System.err.println("capture - Stopped");
			    break;
			}
			try {
			    Thread.currentThread().sleep(50);
			} catch (InterruptedException ie) {
			}
			
			i++;
		    }
		    waitingToStop = false;
		    synchronized (waitForStop) {
			waitForStop.notifyAll();
		    }
		    break;
		case REQ_START:
		    if (DEBUG) System.err.println("capture - Starting");
		    doStartCapture();
		    if (DEBUG) System.err.println("capture - Started");
		    break;
		case REQ_DISCONNECT:
		    if (DEBUG) System.err.println("capture - Disconnecting");
		    break;
		case REQ_FORMATDIALOG:
		    // Minimize monitor if its not enabled
		    if (!monitorEnabled)
			VFWCapture.showWindow(nativeParentWindow, 1, -1, -1);

		    // Invoke the dialog
		    VFWCapture.capDlgVideoFormat(capHandle);

		    // Monitor back to previous state
		    setMonitorEnabled(monitorEnabled);
		    
		    BitMapInfo bmi = new BitMapInfo();
		    VFWCapture.capGetVideoFormat(capHandle, bmi);
		    
		    Format newFormat =
			bmi.createVideoFormat(Format.byteArray, requestedFrameRate);
		    if (!newFormat.equals(capFormat)) {
			requestedFormat = (VideoFormat) newFormat;
			maxDataSize = requestedFormat.getMaxDataLength();
			data = new byte[maxDataSize];
		    }
		    updateFormatChooser();
		    break;
		case REQ_SOURCEDIALOG:
		    // Minimize monitor if its not enabled
		    if (!monitorEnabled)
			VFWCapture.showWindow(nativeParentWindow, 1, -1, -1);

		    // Invoke the dialog
		    VFWCapture.capDlgVideoSource(capHandle);

		    // Monitor back to previous state
		    setMonitorEnabled(monitorEnabled);
		    break;
		case REQ_UPDATECAPTURESETUP:
		    updateCaptureSetup();
		    break;
	        case REQ_MONITOR:
		    if (hasOverlay) {
			VFWCapture.capOverlay(capHandle, monitorEnabled);
		    } else {
			VFWCapture.capPreview(capHandle, monitorEnabled);
			VFWCapture.capPreviewRate(capHandle, capPreviewRate);
		    }
	    }
	}

	doDisconnect();
	if (DEBUG) System.err.println("capture - Disconnected");
	
	while (!wmQuit) {
	    try {
		Thread.currentThread().yield();
		Thread.currentThread().sleep(50);
	    } catch (InterruptedException ie) {
	    }
	    //System.err.println("Peeking...");
	    peekResult = VFWCapture.peekWindowLoop(nativeParentWindow);
	    if (peekResult == 0)
		wmQuit = true;
	}
	pt = null;
    }

    private Checkbox cbMonitorEnable = null;
    private Panel monitorComponent = null;
    private TextField textPreviewRate = null;
    
    Component getMonitorControlComponent() {
	if (monitorComponent == null) {
	    monitorComponent = new Panel();
	    monitorComponent.setLayout( new BorderLayout() );
	    cbMonitorEnable = new Checkbox("Video Monitor");
	    cbMonitorEnable.addItemListener(new ItemListener() {
		public void itemStateChanged(ItemEvent ie) {
		    setMonitorEnabled(cbMonitorEnable.getState());
		}
	    } );
	    Panel panelPreviewRate = new Panel( new BorderLayout() );
	    panelPreviewRate.add("East", new Label("frames/sec"));
	    
	    textPreviewRate = new TextField("30");
	    textPreviewRate.addActionListener( new ActionListener() {
		public void actionPerformed(ActionEvent ae) {
		    try {
			float previewRate = Float.valueOf(textPreviewRate.getText()).floatValue();
			if (previewRate < 0.1f)
			    previewRate = 0.1f;
			else if (previewRate > 30f)
			    previewRate = 30f;
			setMonitorPreviewRate(previewRate);
		    } catch (Exception e) {
		    }
		}
	    } );
	    panelPreviewRate.add("Center", textPreviewRate);
	    monitorComponent.add("West", cbMonitorEnable);
	    monitorComponent.add("Center", panelPreviewRate);
	}
	return monitorComponent;
    }

    boolean setMonitorEnabled(boolean value) {
	if (value) {
	    if (nativeParentWindow != 0)
		VFWCapture.showWindow(nativeParentWindow, 2,
				      capWidth, capHeight);
	    monitorEnabled = true;
	} else {
	    if (nativeParentWindow != 0)
		VFWCapture.showWindow(nativeParentWindow, 0, -1, -1);
	    monitorEnabled = false;
	}
	synchronized (vfwReqLock) {
	    vfwRequest = REQ_MONITOR;
	    vfwReqLock.notifyAll();
	}
	return value;
    }

    float setMonitorPreviewRate(float rate) {
	if (rate > 30)
	    rate = 30f;
	else if (rate < 0.1)
	    rate = 0.1f;
	int millis = (int) (1000 / rate);
	capPreviewRate = millis;
	synchronized (vfwReqLock) {
	    vfwRequest = REQ_MONITOR;
	    vfwReqLock.notifyAll();
	}
	return rate;
    }
    
    /****************************************************************
     * INNER CLASSES
     ****************************************************************/

    class MC implements MonitorControl, Owned {

	public MC() {
	}

	public Component getControlComponent() {
	    return getMonitorControlComponent();
	}

	public float setPreviewFrameRate(float value) {
	    return setMonitorPreviewRate(value);
	}

	public boolean setEnabled(boolean value) {
	    return setMonitorEnabled(value);
	}

	public Object getOwner() {
	    return VFWSourceStream.this;
	}
    }

    class PushThread extends Thread {
	VFWSourceStream stream;

	public PushThread(VFWSourceStream stream) {
	    super("VFW VideoPollThread");
	    this.stream = stream;
	}
	
	/*
	public void run2() {
	    int handle = getCapHandle();
	    int cbHandle = getCBHandle();
	    int time = 0;
	    byte [] data = null; //getData();
	    //System.err.println("In PushThread.run()");
	    while (getConnected()) {
		try {
		    sleep(10);
		    yield();
		    time += 10;
		} catch (Exception e) {
		}
		if (getStarted()) {
		    data = checkDataAllocation(data);
		    synchronized (VFWSourceStream.this.cbHandleLock) {
			if ((cbHandle = getCBHandle()) != 0) {
			    int result = VFWCapture.getAvailableData(
							     handle,
							     cbHandle,
							     data,
							     data.length,
							     resultTimeStamp);
			    if (result > 0) {
				lastTimeStamp = startTimeStamp +
				    (resultTimeStamp[0] * 1000000);
				synchronized (bufferQ) {
				    if (!bufferQ.canWrite()) {
					// Discard an old one
					bufferQ.read();
					bufferQ.readReport();
				    }
				    Buffer incoming = bufferQ.getEmptyBuffer();
				    byte [] temp = data;
				    data = checkDataAllocation(incoming.getData());
				    incoming.setData(temp);
				    incoming.setOffset(0);
				    incoming.setTimeStamp(lastTimeStamp);
				    incoming.setLength(result);
				    bufferQ.writeReport();
				    bufferQ.notifyAll();
				}
			    }
			}
		    }
		}
	    }
	}
	*/
	
	public Object data = null;
	public long dataBytes = 0;
	public Buffer buffer = null;

	public void run() {
	    int handle = getCapHandle();
	    int cbHandle = getCBHandle();
	    int time = 0;
	    //System.err.println("In PushThread.run()");
	    while (getConnected()) {
		try {
		    sleep(10);
		    yield();
		    time += 10;
		} catch (Exception e) {
		}
		synchronized (this) {
		    if (getStarted()) {
			
			if (buffer == null) {
			    synchronized (bufferQ) {
				if (!bufferQ.canWrite()) {
				    // Discard an old one
				    bufferQ.read();
				    bufferQ.readReport();
				}
				buffer = bufferQ.getEmptyBuffer();
			    }
			    data = checkDataAllocation(buffer);
			    if (data instanceof NBA)
				dataBytes = ((NBA)data).getNativeData();
			    else
				dataBytes = 0;
			}
			
			
			synchronized (VFWSourceStream.this.cbHandleLock) {
			    if ((cbHandle = getCBHandle()) != 0) {
				int result = VFWCapture.getAvailableData(
							     handle,
							     cbHandle,
							     data,
							     dataBytes,
							     maxDataSize,
							     resultTimeStamp);
				if (result > 0) {
				    lastTimeStamp = startTimeStamp +
					(resultTimeStamp[0] * 1000000);
				    synchronized (bufferQ) {
					buffer.setOffset(0);
					buffer.setTimeStamp(lastTimeStamp);
					buffer.setLength(result);
					bufferQ.writeReport();
					bufferQ.notifyAll();
					buffer = null;
				    }
				}
			    }
			}
		    }
		}
	    }
	}
    }

    class TransferDataThread extends Thread {
	VFWSourceStream stream;

	public TransferDataThread(VFWSourceStream stream) {
	    super("VFW TransferDataThread");
	    this.stream = stream;
	}

	public void run() {
	    while (getConnected()) {
		synchronized (bufferQ) {
		    while (!bufferQ.canRead() && getConnected()) {
			try {
			    bufferQ.wait(250);
			} catch (InterruptedException ie) {
			}
		    }
		}

		synchronized (stream.readLock) {
		    stream.doneReading = false;
		}

		if (bufferQ.canRead() && getConnected() && getStarted()) {
		    pushData(1);
		}

		synchronized (stream.readLock) {
		    if (!doneReading) {
			try {
			    stream.readLock.wait(1000);
			} catch (InterruptedException ie2) {
			}
		    }
		}
	    }
	}
    }
}