/*
 * video4beos.cxx
 *
 * Classes to support streaming video input (grabbing) and output.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-2000 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Contributor(s): Yuri Kiryanov, ykiryanov at users.sourceforge.net
 * Jac Goudsmit <jac@be.com>.
 *
 * $Revision: 20385 $
 * $Author: rjongbloed $
 * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
 */

#include <ptlib.h>
#include <ptlib/unix/ptlib/bevideo.h>

#include <stdio.h>
#include <fcntl.h>
#include <Buffer.h>
#include <unistd.h>
#include <string.h>
#include <NodeInfo.h>
#include <scheduler.h>
#include <TimeSource.h>
#include <MediaRoster.h>
#include <Application.h>
#include <BufferGroup.h>

#include <Bitmap.h>
#include <MediaNode.h>
#include <BufferConsumer.h>
#include <TimedEventQueue.h>
#include <MediaEventLooper.h>
	
// Define default trace level
#define TL 7   

///////////////////////////////////////////////////////////////////////////////
// VideoConsumer

class VideoConsumer : 
	public BMediaEventLooper,
	public BBufferConsumer
{
public:
  VideoConsumer(
    const char * name,
    BMediaAddOn *addon,
    const uint32 internal_id,
    int XSize,
    int YSize);
  ~VideoConsumer();
	
//  BMediaNode 
public:
  virtual BMediaAddOn *AddOn(long *cookie) const;
	
protected:
  virtual void Start(bigtime_t performance_time);
  virtual void Stop(bigtime_t performance_time, bool immediate);
  virtual void Seek(bigtime_t media_time, bigtime_t performance_time);
  virtual void TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time);

  virtual void NodeRegistered();
  virtual status_t RequestCompleted(
    const media_request_info & info);
							
  virtual status_t DeleteHook(BMediaNode * node);

// BMediaEventLooper 
protected:
  virtual void HandleEvent(
    const media_timed_event *event,
    bigtime_t lateness,
    bool realTimeEvent);

// BBufferConsumer 
public:
  virtual status_t AcceptFormat(
    const media_destination &dest,
    media_format * format);
  virtual status_t GetNextInput(
    int32 * cookie,
    media_input * out_input);
							
  virtual void DisposeInputCookie(
    int32 cookie);
	
protected:
  virtual void BufferReceived(
    BBuffer * buffer);
	
private:

  virtual void ProducerDataStatus(
    const media_destination &for_whom,
    int32 status,
    bigtime_t at_media_time);									

  virtual  status_t GetLatencyFor(
    const media_destination &for_whom,
    bigtime_t * out_latency,
    media_node_id * out_id);	

  virtual status_t Connected(
    const media_source &producer,
    const media_destination &where,
    const media_format & with_format,
    media_input * out_input);							

  virtual void Disconnected(
    const media_source &producer,
    const media_destination &where);							
   virtual status_t FormatChanged(
    const media_source & producer,
    const media_destination & consumer, 
    int32 from_change_count,
    const media_format & format);
							
// implementation 
public:
  status_t CreateBuffers(
    const media_format & with_format);
  void DeleteBuffers();

  PMutex mFrameMutex;
  void GetFrame(BYTE* buffer, PINDEX* bytesReturned, 
    PColourConverter* converter);
							
private:
  uint32 mInternalID;
  BMediaAddOn* mAddOn;

  media_input mIn;
  media_destination mDestination;
  bigtime_t mMyLatency;

  BBitmap* mFrames[3];
  bool mOurBuffers;
  BBufferGroup* mBuffers;
  uint32 mBufferMap[3];	

  int mXSize;
  int mYSize;
};

const media_raw_video_format vid_format = 
{ 29.97,1,0,175, // YK
  B_VIDEO_TOP_LEFT_RIGHT,1,1,
  {B_RGB32,
   176,144,176*4, // YK
   0,0}};

VideoConsumer::VideoConsumer(
  const char * name,
  BMediaAddOn *addon,
  const uint32 internal_id,
  int XSize,
  int YSize
  ) :
	
  BMediaNode(name),
  BMediaEventLooper(),
  BBufferConsumer(B_MEDIA_RAW_VIDEO),
  mInternalID(internal_id),
  mAddOn(addon),
  mMyLatency(20000),
  mOurBuffers(false),
  mBuffers(NULL),
  
  mXSize(XSize),
  mYSize(YSize)
{ 
  PTRACE(TL, "VideoConsumer::VideoConsumer");

  AddNodeKind(B_PHYSICAL_OUTPUT);
  SetEventLatency(0);
	
  for (uint32 j = 0; j < 3; j++)
  {
    mFrames[j] = NULL;
    mBufferMap[j] = 0;
  }
	
  SetPriority(B_LOW_PRIORITY);
}

VideoConsumer::~VideoConsumer()
{
  PTRACE(TL, "VideoConsumer::~VideoConsumer");
  Quit();

  DeleteBuffers();
}

// From BMediaNode
BMediaAddOn *
VideoConsumer::AddOn(long *cookie) const
{
  PTRACE(3, "VideoConsumer::AddOn");
  
  // do the right thing if we're ever used with an add-on
  *cookie = mInternalID;
  return mAddOn;
}

void 
VideoConsumer::Start(bigtime_t performance_time)
{
  BMediaEventLooper::Start(performance_time);
}

void 
VideoConsumer::Stop(bigtime_t performance_time, bool immediate)
{
  BMediaEventLooper::Stop(performance_time, immediate);
}

void 
VideoConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)
{
  BMediaEventLooper::Seek(media_time, performance_time);
}

void 
VideoConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
{
  BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
}

status_t 
VideoConsumer::DeleteHook(BMediaNode *node)
{
  return BMediaEventLooper::DeleteHook(node);
}

void
VideoConsumer::NodeRegistered()
{
  PTRACE(TL, "VideoConsumer::NodeRegistered");
  mIn.destination.port = ControlPort();
  mIn.destination.id = 0;
  mIn.source = media_source::null;
  mIn.format.type = B_MEDIA_RAW_VIDEO;

  mIn.format.u.raw_video = vid_format;

  mIn.format.u.raw_video.display.line_width = mXSize; 
  mIn.format.u.raw_video.display.line_count = mYSize; 
  mIn.format.u.raw_video.display.bytes_per_row = mXSize * 4; // RGB32 only

  Run();
}

status_t
VideoConsumer::RequestCompleted(const media_request_info & info)
{
  PTRACE(TL, "VideoConsumer::RequestCompleted");
  switch(info.what)
  {
    case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
    {
      if (info.status != B_OK)
        PTRACE(1, "VideoConsumer::RequestCompleted: Not using our buffers!");
    }
    break;
    
    case media_request_info::B_SET_VIDEO_CLIPPING_FOR:
    case media_request_info::B_REQUEST_FORMAT_CHANGE:
    case media_request_info::B_SET_OUTPUT_ENABLED:
    case media_request_info::B_FORMAT_CHANGED:
    default:

    break;
  }
  
  return B_OK;
}

void
VideoConsumer::BufferReceived(BBuffer * buffer)
{
  PTRACE(TL, "VideoConsumer::Buffer " << buffer->ID() << " received");

  if (RunState() == B_STOPPED)
  {
    buffer->Recycle();
    return;
  }

  media_timed_event event(buffer->Header()->start_time, 
    BTimedEventQueue::B_HANDLE_BUFFER,
    buffer,
    BTimedEventQueue::B_RECYCLE_BUFFER);
	
  EventQueue()->AddEvent(event);
}

void
VideoConsumer::ProducerDataStatus(
  const media_destination &for_whom,
  int32 status,
  bigtime_t at_media_time)
{
  PTRACE(TL, "VideoConsumer::ProducerDataStatus");

  if (for_whom != mIn.destination)	
    return;
}

status_t
VideoConsumer::CreateBuffers(
  const media_format & with_format)
{
  PTRACE(TL, "VideoConsumer::CreateBuffers");
	
  // delete any old buffers
  DeleteBuffers();	

  status_t status = B_OK;

  // create a buffer group

  mBuffers = new BBufferGroup();
  status = mBuffers->InitCheck();
  if (B_OK != status)
  {
    PTRACE(TL, "VideoConsumer::CreateBuffers - error while creating buffer group");
    return status;
  }

  uint32 XSize = with_format.u.raw_video.display.line_width;
  uint32 YSize = with_format.u.raw_video.display.line_count;	
  color_space mColorspace = with_format.u.raw_video.display.format;
  PTRACE(TL, "VideoConsumer::CreateBuffers - Colorspace = " << (unsigned) mColorspace);

  // and attach the  bitmaps to the buffer group
  for (uint32 j=0; j < 3; j++)
  {
    mFrames[j] = new BBitmap(BRect(0, 0, (XSize-1), (YSize - 1)), mColorspace, false, true);
    if (mFrames[j]->IsValid())
    {						
      buffer_clone_info info;
      if ((info.area = area_for(mFrames[j]->Bits())) == B_ERROR)
        PTRACE(1, "VideoConsumer::CreateBuffers - error in area");
      info.offset = 0;
      info.size = (size_t) mFrames[j]->BitsLength();
      info.flags = j;
      info.buffer = 0;

      if ((status = mBuffers->AddBuffer(info)) != B_OK)
      {
        PTRACE(1, "VideoConsumer::CreateBuffers - error adding buffer to group");
        return status;
      } // if status
      else
      { 
        PTRACE(1, "VideoConsumer::CreateBuffers - successfully added buffer to group");
      }
    } // if bitmap is valid
    else 
    {
      PTRACE(1, 
      "VideoConsumer::CreateBuffers - error creating ring buffer, status " << status);
      return B_ERROR;
    } // bitmap is not valid	
  } // for loop

  BBuffer ** buffList = new BBuffer * [3];
  for (int j = 0; j < 3; j++) buffList[j] = 0;
	
  if ((status = mBuffers->GetBufferList(3, buffList)) == B_OK)	
  {
    for (int j = 0; j < 3; j++)
    {
      if (buffList[j] != NULL)
      {
        mBufferMap[j] = (uint32) buffList[j];
        PTRACE(TL, " j = " << j << " buffer = " << mBufferMap[j]);
      } 
      else
      {
        PTRACE(1, "VideoConsumer::CreateBuffers - error mapping ring buffer");
        return B_ERROR;
      } // else buflist
    } // end for
  } // if status
  else
    PTRACE(1, "VideoConsumer::CreateBuffers - error in get buffer list");
		
  PTRACE(TL, "VideoConsumer::CreateBuffers - exit");
  return status;
}

void
VideoConsumer::DeleteBuffers()
{
  PTRACE(TL, "VideoConsumer::DeleteBuffers");
	
  if (mBuffers)
  {
    delete mBuffers;
    mBuffers = NULL;
		
    for (uint32 j = 0; j < 3; j++)
    {
      if (mFrames[j]->IsValid())
      {
        delete mFrames[j];
        mFrames[j] = NULL;
      } // if bitmap
    } // for
  } // if buffers

  PTRACE(TL, "VideoConsumer::DeleteBuffers - exit");
}

status_t
VideoConsumer::Connected(
  const media_source & producer,
  const media_destination & where,
  const media_format & with_format,
  media_input * out_input)
{
  PTRACE(TL, "VideoConsumer::Connected");
	
  mIn.source = producer;
  mIn.format = with_format;
  mIn.node = Node();
  sprintf(mIn.name, "PWLV");
  *out_input = mIn;

  uint32 user_data = 0;
  int32 change_tag = 1;	
  if (CreateBuffers(with_format) == B_OK)
  {
     BBufferConsumer::SetOutputBuffersFor(producer, mDestination, 
	mBuffers, (void *)&user_data, &change_tag, true);
  }
  else
  {
    PTRACE(1, "VideoConsumer::Connected - can't create buffers");
    return B_ERROR;
  }

  PTRACE(TL, "VideoConsumer::Connected - exit");
  return B_OK;
}

void
VideoConsumer::Disconnected(
	const media_source & producer,
	const media_destination & where)
{
  PTRACE(TL, "VideoConsumer::Disconnected");

  if (where == mIn.destination && producer == mIn.source)
  {
    // disconnect the connection
    mIn.source = media_source::null;
  }
}

//---------------------------------------------------------------

status_t
VideoConsumer::AcceptFormat(
  const media_destination & dest,
  media_format * format)
{
  PTRACE(TL, "VideoConsumer::AcceptFormat") ;
	
  if (dest != mIn.destination)
  {
    PTRACE(1, "VideoConsumer::AcceptFormat - bad destination");
    return B_MEDIA_BAD_DESTINATION;	
  }
	
  if (format->type == B_MEDIA_NO_TYPE)
    format->type = B_MEDIA_RAW_VIDEO;
	
  if (format->type != B_MEDIA_RAW_VIDEO)
  {
    PTRACE(1, "VideoConsumer::AcceptFormat - bad format");
    return B_MEDIA_BAD_FORMAT;
  }

  if (format->u.raw_video.display.format != B_RGB32 &&
    format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format)
  {
    PTRACE(1, "AcceptFormat - not a RGB32 format!");
    return B_MEDIA_BAD_FORMAT;
  }
		
  if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format)
  {
    format->u.raw_video.display.format = B_RGB32;
  }

  char format_string[256];		
  string_for_format(*format, format_string, 256);
  PTRACE(TL, "VideoConsumer::AcceptFormat: " << format_string);

  return B_OK;
}


status_t
VideoConsumer::GetNextInput(
	int32 * cookie,
	media_input * out_input)
{
  PTRACE(TL, "VideoConsumer::GetNextInput");

  // custom build a destination for this connection
  // put connection number in id

  if (*cookie < 1)
  {
    mIn.node = Node();
    mIn.destination.id = *cookie;
    sprintf(mIn.name, "PWLV");
    *out_input = mIn;
    (*cookie)++;
    return B_OK;
  }
  else
  {
    PTRACE(1, "VideoConsumer::GetNextInput - bad index");
    return B_MEDIA_BAD_DESTINATION;
  }
}

void
VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
{
}

status_t
VideoConsumer::GetLatencyFor(
  const media_destination &for_whom,
  bigtime_t * out_latency,
  media_node_id * out_timesource)
{
  PTRACE(TL, "VideoConsumer::GetLatencyFor");
	
  if (for_whom != mIn.destination)
    return B_MEDIA_BAD_DESTINATION;
	
  *out_latency = mMyLatency;
  *out_timesource = TimeSource()->ID();
  return B_OK;
}


status_t
VideoConsumer::FormatChanged(
  const media_source & producer,
  const media_destination & consumer, 
  int32 from_change_count,
  const media_format &format)
{
  PTRACE(TL, "VideoConsumer::FormatChanged");
	
  if (consumer != mIn.destination)
    return B_MEDIA_BAD_DESTINATION;

  if (producer != mIn.source)
    return B_MEDIA_BAD_SOURCE;

  mIn.format = format;
	
  return CreateBuffers(format);
}

void
VideoConsumer::HandleEvent(
  const media_timed_event *event,
  bigtime_t lateness,
  bool realTimeEvent)
{
  PTRACE(TL, "VideoConsumer::HandleEvent");
	
  BBuffer *buffer;
	
  switch (event->type)
  {
    case BTimedEventQueue::B_START:
    PTRACE(TL, "VideoConsumer::HandleEvent - start");
	break;

    case BTimedEventQueue::B_STOP:
    PTRACE(TL, "VideoConsumer::HandleEvent - stop");
    EventQueue()->FlushEvents(event->event_time, 
      BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
	break;

    case BTimedEventQueue::B_HANDLE_BUFFER:
    PTRACE(TL, "VideoConsumer::HandleEvent - handle buffer");
    buffer = (BBuffer *) event->pointer;
    if (RunState() == B_STARTED)
    {
      // see if this is one of our buffers
      uint32 index = 0;
      mOurBuffers = true;
      while(index < 3)
      if ((uint32)buffer == mBufferMap[index])
        break;
      else
        index++;
						
      if (index == 3)
      {
        // no, buffers belong to consumer
	    mOurBuffers = false;
	    index = 0;
      }

      PWaitAndSignal ws(mFrameMutex);
	  memcpy(mFrames[index]->Bits(), buffer->Data(), mFrames[index]->BitsLength());
  }
  else
   buffer->Recycle();
   break;

 default:
   PTRACE(1, "VideoConsumer::HandleEvent - bad event");
   break;
  }			
}

void VideoConsumer::GetFrame(BYTE* buffer, 
   PINDEX* bytesReturned, 
  PColourConverter* converter) 
{ 
    PAssertNULL(bytesReturned);
    
    PWaitAndSignal ws(mFrameMutex);
    {
      const BYTE* frame = (const BYTE*) mFrames[0]->Bits();
    
      if (converter != NULL) 
      {
        converter->Convert(frame, buffer, bytesReturned);
      } 
      else 
      {
        memcpy(buffer, frame, *bytesReturned);
      }
    }
}

///////////////////////////////////////////////////////////////////////////////
// PVideoDevice

PVideoInputDevice_BeOSVideo::PVideoInputDevice_BeOSVideo()
  : captureThread(NULL)
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo");
  
  captureThread = NULL;
  isCapturingNow = PFalse;
}

PBoolean PVideoInputDevice_BeOSVideo::Open(const PString & devName, PBoolean startImmediate)
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::Open");

  Close();

  frameWidth = CIFWidth;
  frameHeight = CIFHeight;
  colourFormat = "RGB32";

  deviceName = devName;

  if (startImmediate)
    return Start();
    
  return PTrue;
}


PBoolean PVideoInputDevice_BeOSVideo::IsOpen() 
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::IsOpen");
  return PTrue;
}


PBoolean PVideoInputDevice_BeOSVideo::Close()
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::Close");

  if (!IsOpen())
    return PFalse;
 
  Stop();

  return PTrue;
}


PBoolean PVideoInputDevice_BeOSVideo::Start()
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::Start");

  if (IsCapturing())
    return PTrue;
   
  StartNodes();    

  isCapturingNow = PTrue;

  return isCapturingNow;
}


PBoolean PVideoInputDevice_BeOSVideo::Stop()
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::Stop");

  if (!IsCapturing())
    return PFalse;

  StopNodes();
  ::snooze(100000);

  isCapturingNow = PFalse;

  return PTrue;
}


PBoolean PVideoInputDevice_BeOSVideo::IsCapturing()
{
  return isCapturingNow;
}


PBoolean PVideoInputDevice_BeOSVideo::TestAllFormats() 
{
  PBoolean running = IsCapturing();
  if (running)
    Stop();

  if (running)
    return Start();

  return PTrue;
}


PBoolean PVideoInputDevice_BeOSVideo::SetFrameRate(unsigned rate)
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::SetFrameRate");

  if (!PVideoDevice::SetFrameRate(rate))
    return PFalse;

  PBoolean running = IsCapturing();
  if (running)
    Stop();
  
  if (running)
    return Start();

  return PTrue;
}


PBoolean PVideoInputDevice_BeOSVideo::SetFrameSize(unsigned width, unsigned height)
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::SetFrameSize, width " << width << " height " << height);

  PBoolean running = IsCapturing();
  if (running)
    Stop();

  if (!PVideoDevice::SetFrameSize(width, height))
    return PFalse;

  if (running)
    return Start();

  return PTrue;
}

PBoolean PVideoInputDevice_BeOSVideo::SetColourFormat(const PString & colourFmt)
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::SetColourFormat, format" << colourFmt);
 
  PBoolean running = IsCapturing();
  if (running)
    Stop();

  if (!PVideoDevice::SetColourFormat(colourFmt)) {
    return PFalse;
  }

  converter = PColourConverter::Create("RGB32", colourFormat, frameWidth, frameHeight);
  if (converter == NULL) {
    PTRACE(1, "Failed to make a converter.");
    return PFalse;
  }

//  if (converter->SetSrcFrameSize(width,height) == PFalse) {
//    PTRACE(1, "Failed to set source frame size of a converter.");
//    return PFalse;
//  }
//  if (converter->SetDstFrameSize(desiredFrameWidth, desiredFrameHeight, PFalse) == PFalse) {
//    PTRACE(1, "Failed to set destination frame size (+scaling) of a converter.");
//    return PFalse;
//  }
    
  if (running)
    return Start();

  return PTrue;
}


PStringList PVideoInputDevice_BeOSVideo::GetInputDeviceNames()
{
  PStringList list;

  list.AppendString("MediaKit"); 
  return list;
}


PINDEX PVideoInputDevice_BeOSVideo::GetMaxFrameBytes()
{
  if (!IsOpen())
    return 0;
  
  return GetMaxFrameBytesConverted(frameWidth * frameHeight *4); // RGB32 only 
}


PBoolean PVideoInputDevice_BeOSVideo::GetFrameData(BYTE * buffer, PINDEX * bytesReturned)
{
  return GetFrameDataNoDelay(buffer, bytesReturned);
}

PBoolean PVideoInputDevice_BeOSVideo::GetFrameDataNoDelay(BYTE * buffer, PINDEX * bytesReturned)
{
  PTRACE(TL, "PVideoInputDevice_BeOSVideo::GetFrameDataNoDelay");
  if (!IsCapturing()) 
    return PFalse;
  
  *bytesReturned = GetMaxFrameBytes();
  fVideoConsumer->GetFrame(buffer, bytesReturned, converter);
   
  return PTrue;
}

status_t PVideoInputDevice_BeOSVideo::StartNodes()
{
  PTRACE(TL, "StartNodes!");
  status_t status = B_OK;

  // find the media roster 
  fMediaRoster = BMediaRoster::Roster(&status);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't find the media roster, status " << status);
    return status;
  }	

  // find the time source 
  status = fMediaRoster->GetTimeSource(&fTimeSourceNode);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't get a time source, status " << status);
    return status;
  }

  // find a video producer node 
  PTRACE(3, "PVideoInputDevice_BeOSVideo acquiring video input node");
  status = fMediaRoster->GetVideoInput(&fProducerNode);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't find a video input!, status " << status);
    return status;
  } 

  // create the video consumer node 
  fVideoConsumer = new VideoConsumer("PWLV", NULL, 0, frameWidth, frameHeight);

  if (!fVideoConsumer) 
  {
    PTRACE(3, "Can't create a video consumer");
    return B_ERROR;
  }
	
  // register the node 
  status = fMediaRoster->RegisterNode(fVideoConsumer);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't register the video consumer, status " << status);
    return status;
  }
  
  fPort = fVideoConsumer->ControlPort();
	
  // find free producer output 
  int32 cnt = 0;
  status = fMediaRoster->GetFreeOutputsFor(fProducerNode, 
    &fProducerOut, 1,  &cnt, B_MEDIA_RAW_VIDEO);

  if (status != B_OK || cnt < 1) 
  {
    status = B_RESOURCE_UNAVAILABLE;
    PTRACE(3, "Can't find an available video stream, status " << status);
    return status;
  }

  // find free consumer input 
  cnt = 0;
  status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(), 
    &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO);

  if (status != B_OK || cnt < 1) 
  {
    status = B_RESOURCE_UNAVAILABLE;
    PTRACE(3, 
     "Can't find an available connection to the video consumer, status " << status);
    return status;
  }

  // Connect The Nodes!!! 
  media_format format;
  format.type = B_MEDIA_RAW_VIDEO;
  media_raw_video_format vid_format = 
    { 0, 1, 0, 175, // YK
       B_VIDEO_TOP_LEFT_RIGHT, 1, 1, 
      {B_RGB32, 0, 0, 0, 0, 0}};

  vid_format.display.line_width = frameWidth;
  vid_format.display.line_count = frameHeight; 
  vid_format.display.bytes_per_row = frameWidth * 4; // RGB32 only

  format.u.raw_video = vid_format; 
	
  // connect producer to consumer 
  status = fMediaRoster->Connect(fProducerOut.source, fConsumerIn.destination,
				&format, &fProducerOut, &fConsumerIn);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't connect the video source to the video consumer, status " << status);
    return status;
  }
	
  // set time sources 
  status = fMediaRoster->SetTimeSourceFor(fProducerNode.node, fTimeSourceNode.node);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't set the timesource for the video source, status " << status);
    return status;
  }
	
  status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(), fTimeSourceNode.node);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't set the timesource for the video consumer, status " << status);
    return status;
  }
	
  // figure out what recording delay to use 
  bigtime_t latency = 0;
  status = fMediaRoster->GetLatencyFor(fProducerNode, &latency);
  status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency);

  // start the nodes 
  bigtime_t initLatency = 0;
  status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency);
  if (status < B_OK) 
  {
    PTRACE(3, "error getting initial latency for fCaptureNode, status " << status);	
  }
  initLatency += estimate_max_scheduling_latency();
	
  BTimeSource *timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode);
  bool running = timeSource->IsRunning();
	
  // workaround for people without sound cards 
  // because the system time source won't be running 
  bigtime_t real = BTimeSource::RealTime();
  if (!running)
  {
    status = fMediaRoster->StartTimeSource(fTimeSourceNode, real);
    if (status != B_OK) 
    {
      timeSource->Release();
      PTRACE(3, "Cannot start time source!, status" << status);
      return status;
    }
   
    status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real);
    if (status != B_OK) 
    {
       timeSource->Release();
       PTRACE(3, "Cannot seek time source!, status " << status);
       return status;
    } 
  }

  bigtime_t perf = timeSource->PerformanceTimeFor(real + latency + initLatency);
  timeSource->Release();
	
  // start the nodes 
  status = fMediaRoster->StartNode(fProducerNode, perf);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't start the video source, status " << status);
    return status;
  }

  status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf);
  if (status != B_OK) 
  {
    PTRACE(3, "Can't start the video consumer, status " << status);
    return status;
  }
	
  return status;
}

void PVideoInputDevice_BeOSVideo::StopNodes()
{
  PTRACE(TL, "StopNodes");
  if (!fMediaRoster)
    return;
	
  if (fVideoConsumer)
  {
    // stop 	
    PTRACE(TL, "Stopping nodes!");
    fMediaRoster->StopNode(fProducerNode, 0, true);
    fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true);
	
    // disconnect 
    fMediaRoster->Disconnect(
        fProducerOut.node.node, fProducerOut.source,
	fConsumerIn.node.node, fConsumerIn.destination);
								
    if(fProducerNode != media_node::null) 
    {
      PTRACE(TL, "Releasing fProducerNode");
      fMediaRoster->ReleaseNode(fProducerNode);
      fProducerNode = media_node::null;
    }

     fMediaRoster->ReleaseNode(fVideoConsumer->Node());		
     fVideoConsumer = NULL;
     
  } // if video consumer
}

// End Of File ///////////////////////////////////////////////////////////////
