/*
 * pvidchan.cxx
 *
 * Video Channel implementation.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-1998 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.
 *
 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
 * All Rights Reserved.
 *
 * Contributor(s): Derek Smithies (derek@indranet.co.nz)
 *
 * $Log: pvidchan.cxx,v $
 * Revision 1.15  2003/05/27 04:22:54  dereksmithies
 * Test grabber size before issuing a grabber resize command.
 *
 * Revision 1.14  2003/04/14 21:18:41  dereks
 * Formatting change.
 *
 * Revision 1.13  2003/03/20 23:40:51  dereks
 * Fix minor problems with using null pointers.
 *
 * Revision 1.12  2003/03/17 07:47:42  robertj
 * Removed redundant "render now" function.
 * Made significant enhancements to PVideoOutputDevice class.
 *
 * Revision 1.11  2003/01/06 18:41:08  rogerh
 * Add NetBSD patches, taken from the NetBSD pkg patches.
 * Submitted by Andreas Wrede
 *
 * Revision 1.10  2002/05/08 22:38:53  dereks
 * Adjust formatting to the pwlib standard.
 *
 * Revision 1.9  2002/02/08 00:57:33  dereks
 * Modify PTRACE level to reduce debug information to reasonable level.
 *
 * Revision 1.8  2002/01/04 04:11:45  dereks
 * Add video flip code from Walter Whitlock, which flips code at the grabber.
 *
 * Revision 1.7  2001/12/03 03:44:52  dereks
 * Add method to retrive pointer to the attached video display class.
 *
 * Revision 1.6  2001/11/28 00:07:32  dereks
 * Locking added to PVideoChannel, allowing reader/writer to be changed mid call
 * Enabled adjustment of the video frame rate
 * New fictitous image, a blank grey area
 *
 * Revision 1.5  2001/10/23 02:11:00  dereks
 * Extend video channel so it can display raw data, using attached video devices.
 *
 * Revision 1.4  2001/09/10 02:51:23  robertj
 * Major change to fix problem with error codes being corrupted in a
 *   PChannel when have simultaneous reads and writes in threads.
 *
 * Revision 1.3  2001/06/19 00:51:57  dereks
 * The ::Write method now returns the result of mpOutput->Redraw(), rather than
 *   always true.
 *
 * Revision 1.2  2001/03/23 20:24:23  yurik
 * Got rid of "unknown pragma" for WinCE port
 *
 * Revision 1.1  2000/12/19 22:20:26  dereks
 * Add video channel classes to connect to the PwLib PVideoInputDevice class.
 * Add PFakeVideoInput class to generate test images for video.
 *
 *
 *
 */

#ifndef _WIN32_WCE
#pragma implementation "video.h"
#endif

#include <ptlib.h>


PVideoChannel::PVideoChannel() 
{
  mpInput = NULL;
  mpOutput = NULL;
}


PVideoChannel::PVideoChannel(const PString & device,
                             Directions dir)
{
  mpInput = NULL;
  mpOutput = NULL;
  Open(device, dir);
}

PVideoChannel::~PVideoChannel()
{
  Close();
}


PStringList PVideoChannel::GetDeviceNames(Directions /*dir*/)
{
  PStringList list;

  list.AppendString("Video Channel Base");

  return list;
}


PString PVideoChannel::GetDefaultDevice(Directions /*dir*/)
{
#if defined(P_FREEBSD) || defined(P_OPENBSD)  || defined(P_NETBSD) || defined(P_DRAGONFLY)
  return "/dev/bktr0";
#endif

#ifndef DEFAULT_VIDEO
     return "/dev/video0";
#else
  return DEFAULT_VIDEO;
#endif
}


BOOL PVideoChannel::Open(const PString & dev,
                         Directions dir)
{
  PWaitAndSignal m(accessMutex);

  Close();
	
  deviceName = dev;
  direction = dir;
  
  return TRUE;
}



BOOL PVideoChannel::Read(void * buf, PINDEX  len)
{
  PWaitAndSignal m(accessMutex);

  if (mpInput == NULL)  
    return FALSE;

  BYTE * dataBuf;
  PINDEX dataLen;
  dataBuf = (BYTE *)buf;
  dataLen = len;
  mpInput->GetFrameData(dataBuf, &dataLen);

  return TRUE;
}

BOOL PVideoChannel::Write(const void * buf,  //image data to be rendered
                          PINDEX      /* len */)
{
  PWaitAndSignal m(accessMutex);

  if (mpOutput == NULL)
    return FALSE;


  if (mpInput == NULL) {
    PTRACE(6,"PVC\t::Write, frame size is "
	 << mpOutput->GetFrameWidth() << "x" << mpOutput->GetFrameHeight() << 
	 " VideoGrabber is unavailabile");
    return mpOutput->SetFrameData(0, 0,
				  mpOutput->GetFrameWidth(), mpOutput->GetFrameHeight(),
				  (const BYTE *)buf, TRUE);
  }

  PTRACE(6,"PVC\t::Write, frame size is "
	 << mpInput->GetFrameWidth() << "x" << mpInput->GetFrameHeight() << 
	 " VideoGrabber is source of size");
  return mpOutput->SetFrameData(0, 0,
				mpInput->GetFrameWidth(), mpInput->GetFrameHeight(),
			     (const BYTE *)buf, TRUE);  
}

BOOL PVideoChannel::Close()
{
  PWaitAndSignal m(accessMutex);

  CloseVideoReader();
  CloseVideoPlayer();

  return TRUE;
}

/*returns true if either input or output is open */
BOOL PVideoChannel::IsOpen() const 
{
   PWaitAndSignal m(accessMutex);

   return (mpInput != NULL) || (mpOutput != NULL);
}


PString PVideoChannel::GetName() const
{
  return deviceName;
}

void PVideoChannel::AttachVideoPlayer(PVideoOutputDevice * device, BOOL keepCurrent)
{
  PWaitAndSignal m(accessMutex);

  if (mpOutput && keepCurrent)
    PAssertAlways("Error: Attempt to add video player while one is already defined");
  
  CloseVideoPlayer();
   
  mpOutput = device;
}

void PVideoChannel::AttachVideoReader(PVideoInputDevice * device, BOOL keepCurrent)
{
  PWaitAndSignal m(accessMutex);

  if ((mpInput != NULL) && keepCurrent)
    PAssertAlways("Error: Attempt to add video reader while one is already defined");
  
  CloseVideoReader();
  
  mpInput = device;
}

void PVideoChannel::CloseVideoPlayer()
{
  PWaitAndSignal m(accessMutex);

  if (mpOutput != NULL)
    delete mpOutput;
  
  mpOutput = NULL;
}

void PVideoChannel::CloseVideoReader()
{
  PWaitAndSignal m(accessMutex);

  if (mpInput != NULL)
    delete mpInput;
  
  mpInput = NULL;
}

PINDEX  PVideoChannel::GetGrabHeight() 
{
   PWaitAndSignal m(accessMutex);
   if (mpInput != NULL)
     return mpInput->GetFrameHeight();
   else
     return 0;
}


PINDEX  PVideoChannel::GetGrabWidth()
{
   PWaitAndSignal m(accessMutex);

   if (mpInput != NULL)
     return mpInput->GetFrameWidth();
   else
     return 0;
}

BOOL PVideoChannel::IsGrabberOpen()
{
  PWaitAndSignal m(accessMutex);

  if (mpInput != NULL)
    return mpInput->IsOpen();
  else
    return FALSE; 
}

BOOL PVideoChannel::IsRenderOpen()      
{
  PWaitAndSignal m(accessMutex);

  if (mpOutput != NULL)
    return mpOutput->IsOpen();
  else
    return FALSE; 
}

BOOL PVideoChannel::DisplayRawData(void *videoBuffer)
{
  PWaitAndSignal m(accessMutex);

  if ((mpOutput == NULL) || (mpInput == NULL))
    return FALSE;
  
  PINDEX length=0;

  int frameWidth  = GetGrabWidth();
  int frameHeight = GetGrabHeight();
  PTRACE(6,"Video\t data direct:: camera-->render, size "
	 << frameWidth << "x" << frameHeight );
  
  SetRenderFrameSize(frameWidth, frameHeight);
  Read(videoBuffer, length);
  Write((const void *)videoBuffer, length);
  
  return TRUE;      
}

void  PVideoChannel::SetGrabberFrameSize(int _width, int _height)     
{ 
  PTRACE(6, "PVC\t Set Grabber frame size to " << _width << "x" << _height);
  PWaitAndSignal m(accessMutex);

  if (mpInput != NULL) {
    if ((GetGrabWidth() != _width) || (GetGrabHeight() != _height))
      mpInput->SetFrameSize((unsigned)_width, (unsigned)_height);
  } 
}

void  PVideoChannel::SetRenderFrameSize(int _width, int _height) 
{ 
  PTRACE(6, "PVC\t Set Renderer frame size to " << _width << "x" << _height);
  PWaitAndSignal m(accessMutex);

  if (mpOutput != NULL)
    mpOutput->SetFrameSize(_width, _height); 
}

PVideoInputDevice *PVideoChannel::GetVideoReader()
{
  return mpInput;
}

PVideoOutputDevice *PVideoChannel::GetVideoPlayer()
{
  return mpOutput;
}

BOOL  PVideoChannel::Redraw(const void * frame) 
{ 
  PTRACE(6,"PVC\t::Redraw a frame");
  return Write(frame, 0);
}

PINDEX   PVideoChannel::GetRenderWidth()
{ 
  PWaitAndSignal m(accessMutex);

  if (mpOutput != NULL)
    return mpOutput->GetFrameWidth(); 

  return 0;
}

PINDEX  PVideoChannel::GetRenderHeight()
{
  PWaitAndSignal m(accessMutex);

 if (mpOutput != NULL)
  return mpOutput->GetFrameHeight(); 

 return 0;
}


void PVideoChannel::RestrictAccess()
{
  accessMutex.Wait();
}

void PVideoChannel::EnableAccess()
{
  accessMutex.Signal();
}


BOOL PVideoChannel::ToggleVFlipInput()
{
  PWaitAndSignal m(accessMutex);

 if (mpOutput != NULL)
  return mpInput->SetVFlipState(mpInput->GetVFlipState()); 

 return FALSE;
}


///////////////////////////////////////////////////////////////////////////
// End of file

