/* 
   ThreadedPlayer.java
   A multi-threaded raw video player makes use of a producer thread
   to fetch data from a shared cicular queue and a consumer thread 
   to render data.
*/

import java.io.*;
import java.awt.Frame;
import java.awt.image.*;
import java.awt.image.ColorModel;
import java.awt.color.ColorSpace;
import java.awt.*;

import javax.media.jai.widget.ScrollingImagePanel;
import javax.media.jai.*;

//A consumer, CircularQueue buf is shared
class Player extends Thread
{
  private CircularQueue buf;
  private TiledImage outImage;
  private int width;
  private int height;
  private Frame window;
  private ScrollingImagePanel panel; 

  //Constructor
  public Player( CircularQueue q )
  {
    super("Player");
    buf = q;
    width = buf.width;
    height = buf.height;

    //create a SampleModel object
    SampleModel  samplemodel = new BandedSampleModel ( DataBuffer.TYPE_BYTE, width, height, 3 );
    //use sRGB as our color space
    ColorSpace colorspace = ColorSpace.getInstance ( ColorSpace.CS_sRGB );

    //create a color model using our color space (sRGB), each color component is 8-bit,
    // no alpha, no alpha premultiplied, opaque, data type is BYTE
    int[] bits = { 8, 8, 8 };
    ColorModel  colormodel = new ComponentColorModel( colorspace, bits, false, false,
        Transparency.OPAQUE, DataBuffer.TYPE_BYTE );

    //create a TiledImage using the sample model and color model defined above
    outImage = new TiledImage( 0, 0, width, height, 0, 0, samplemodel, colormodel );
    /* Attach image to a scrolling panel to be displayed. */
    panel = new ScrollingImagePanel( outImage, width, height );
    
    /* Create a frame to contain the panel. */
    window = new Frame("Raw Video Player");
    window.add(panel);
    window.pack();
    window.show();
  }

  public void run()
  {
     long prev_time = System.currentTimeMillis();      //time in ms
     long current_time;
     int delay;

     while ( !buf.quit ) {
       buf.waitIfBufferEmpty();
       //consumes the data
       buf.setSamples ( outImage ); 
       buf.headInc();   //advance head pointer
       current_time = System.currentTimeMillis();      //time in ms
       if (current_time - prev_time < 50)              //20 fps = 50 ms / frame
         delay = 50 - (int) ( current_time - prev_time ); 
       else
         delay = 0;
       prev_time = current_time;
       try { Thread.sleep( delay );} catch ( InterruptedException e ){}
       panel.set ( outImage );
     } //while
     window.dispose();
  } 
}

//A Producer, CircularQueue buf is shared
class Decoder extends Thread
{
  private DataInputStream in;
  private CircularQueue buf;

  //Constructor
  public Decoder(DataInputStream ins,  CircularQueue q)
  {
    in = ins;
    buf = q;
  }

  public void run()
  {
     while ( !buf.quit ) {
       buf.waitIfBufferFull();
       //produce data
       buf.putSamples ( in );    //quit if out of data
       buf.tailInc();            //advance tail 
     } //while
  }
}
