/* * vfakeio.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): Derek J Smithies (derek@indranet.co.nz) * * $Log: vfakeio.cxx,v $ * Revision 1.34 2005/11/30 12:47:42 csoutheren * Removed tabs, reformatted some code, and changed tags for Doxygen * * Revision 1.33 2005/08/09 09:08:11 rjongbloed * Merged new video code from branch back to the trunk. * * Revision 1.32.6.3 2005/07/25 12:19:23 rjongbloed * Fixed correct device name for fake video * * Revision 1.32.6.2 2005/07/17 12:59:04 rjongbloed * Cleaned up pattern identifcation (enum) and max patterns (input channel). * * Revision 1.32.6.1 2005/07/17 09:27:08 rjongbloed * Major revisions of the PWLib video subsystem including: * removal of F suffix on colour formats for vertical flipping, all done with existing bool * working through use of RGB and BGR formats so now consistent * cleaning up the plug in system to use virtuals instead of pointers to functions. * rewrite of SDL to be a plug in compatible video output device. * extensive enhancement of video test program * * Revision 1.32 2005/01/31 08:05:41 csoutheren * More patches for MacOSX, thanks to Hannes Friederich * * Revision 1.31 2005/01/04 07:44:03 csoutheren * More changes to implement the new configuration methodology, and also to * attack the global static problem * * Revision 1.30 2004/07/11 07:56:36 csoutheren * Applied jumbo VxWorks patch, thanks to Eize Slange * * Revision 1.29 2004/03/17 22:24:29 rjongbloed * Fixed MSVC warnings * * Revision 1.28 2004/03/16 10:10:47 csoutheren * Proper fix that does not generate warnings * * Revision 1.27 2004/03/16 08:27:14 csoutheren * Removed illegal characters * * Revision 1.26 2003/12/14 10:01:02 rjongbloed * Resolved issue with name space conflict os static and virtual forms of GetDeviceNames() function. * * Revision 1.25 2003/11/19 09:29:45 csoutheren * Added super hack to avoid problems with multiple plugins in a single file * * Revision 1.24 2003/11/19 04:29:46 csoutheren * Changed to support video output plugins * * Revision 1.23 2003/08/12 22:04:18 dereksmithies * Add fix from Philippe Massicotte to fix segfaults on large images. Thanks! * * Revision 1.22 2003/06/14 03:28:50 rjongbloed * Further MSVC warning fix up * * Revision 1.21 2003/06/14 02:59:34 rjongbloed * Fixed MSVC warning. * * Revision 1.20 2003/06/11 22:17:54 dereksmithies * Add fake video device which display text, on --videoinput 5 * * Revision 1.19 2003/06/10 03:45:11 dereksmithies * Change so box on left moves all the way down left side of image. * * Revision 1.18 2003/06/10 00:36:57 dereksmithies * Formatting changes. Remove rounding errors. * * Revision 1.17 2003/06/03 04:21:49 dereksmithies * Add PTRACE statement, and tidy up format of one if statement. * * Revision 1.16 2003/03/17 07:46:49 robertj * Migrated vflip member variable and functions into PVideoDevice class. * * Revision 1.15 2002/09/23 07:17:24 robertj * Changes to allow winsock2 to be included. * * Revision 1.14 2002/01/28 21:22:10 dereks * Fix the method for returning the device name. * * Revision 1.13 2002/01/17 03:47:27 dereks * Fix latest addition to the fake images gallery. * * Revision 1.12 2002/01/16 08:02:06 robertj * MSVC compatibilty changes * * Revision 1.11 2002/01/16 03:49:23 dereks * Add new test image. * * Revision 1.10 2002/01/04 04:11:45 dereks * Add video flip code from Walter Whitlock, which flips code at the grabber. * * Revision 1.9 2001/11/28 04:39:25 robertj * Fixed MSVC warning * * Revision 1.8 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.7 2001/03/12 03:54:11 dereks * Make setting frame rate consistent with that for real video device. * * Revision 1.6 2001/03/09 00:12:40 robertj * Fixed incorrect number of channels returned on fake video. * * Revision 1.5 2001/03/08 22:56:25 robertj * Fixed compatibility with new meaning of channelNumber variable, cannot be negative. * * Revision 1.4 2001/03/03 05:06:31 robertj * Major upgrade of video conversion and grabbing classes. * * Revision 1.3 2001/03/02 06:52:33 yurik * Got rid of unknown for WinCE pragma * * Revision 1.2 2000/12/19 23:58:14 robertj * Fixed MSVC compatibility issues. * * 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. * * */ #define P_FORCE_STATIC_PLUGIN #include #include #if defined(_WIN32) && !defined(P_FORCE_STATIC_PLUGIN) #error "vfakeio.cxx must be compiled without precompiled headers" #endif #include #ifdef __MACOSX__ namespace PWLibStupidOSXHacks { int loadFakeVideoStuff; }; #endif enum { eMovingBlocks, eMovingLine, eBouncingBoxes, eBlankImage, eOriginalMovingBlocks, eText, eNTSCTest, eNumTestPatterns }; #define MAX_L_HEIGHT 11 typedef struct { char ascii; char *line[MAX_L_HEIGHT]; } OneVFakeLetterData; /**** * The fonts for these letters were written by Sverre H. Huseby, and have been included * in vfakeio by Derek Smithies. */ static OneVFakeLetterData vFakeLetterData[] = { { ' ', { " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " }}, { 'a', { " ", " ", " ", " **** ", " *", " *****", "* *", "* **", " *** *", " ", " " }}, { 'b', { "* ", "* ", "* ", "* *** ", "** *", "* *", "* *", "** *", "* *** ", " ", " " }}, { 'c', { " ", " ", " ", " **** ", "* *", "* ", "* ", "* *", " **** ", " ", " " }}, { 'd', { " *", " *", " *", " *** *", "* **", "* *", "* *", "* **", " *** *", " ", " " }}, { 'e', { " ", " ", " ", " **** ", "* *", "******", "* ", "* *", " **** ", " ", " " }}, { 'f', { " *** ", " * *", " * ", " * ", "**** ", " * ", " * ", " * ", " * ", " ", " " }}, { 'g', { " ", " ", " ", " *** *", "* * ", "* * ", " *** ", "* ", " **** ", "* *", " **** " }}, { 'h', { "* ", "* ", "* ", "* *** ", "** *", "* *", "* *", "* *", "* *", " ", " " }}, { 'i', { " ", " * ", " ", " ** ", " * ", " * ", " * ", " * ", "*****", " ", " " }}, { 'j', { " ", " *", " ", " **", " *", " *", " *", " *", "* *", "* *", " *** " }}, { 'k', { "* ", "* ", "* ", "* * ", "* * ", "*** ", "* * ", "* * ", "* *", " ", " " }}, { 'l', { " ** ", " * ", " * ", " * ", " * ", " * ", " * ", " * ", "*****", " ", " " }}, { 'm', { " ", " ", " ", "* * ** ", "** * *", "* * *", "* * *", "* * *", "* * *", " ", " " }}, { 'n', { " ", " ", " ", "* *** ", "** *", "* *", "* *", "* *", "* *", " ", " " }}, { 'o', { " ", " ", " ", " **** ", "* *", "* *", "* *", "* *", " **** ", " ", " " }}, { 'p', { " ", " ", " ", "* *** ", "** *", "* *", "** *", "* *** ", "* ", "* ", "* " }}, { 'q', { " ", " ", " ", " *** *", "* **", "* *", "* **", " *** *", " *", " *", " *" }}, { 'r', { " ", " ", " ", "* **", " * ", " * ", " * ", " * ", " * ", " ", " " }}, { 's', { " ", " ", " ", " **** ", "* *", " ** ", " ** ", "* *", " **** ", " ", " " }}, { 't', { " * ", " * ", " * ", "**** ", " * ", " * ", " * ", " * *", " ** ", " ", " " }}, { 'u', { " ", " ", " ", "* *", "* *", "* *", "* *", "* **", " *** *", " ", " " }}, { 'v', { " ", " ", " ", "* *", "* *", "* *", " * * ", " * * ", " * ", " ", " " }}, { 'w', { " ", " ", " ", "* *", "* *", "* * *", "* * *", "* * *", " ** ** ", " ", " " }}, { 'x', { " ", " ", " ", "* *", " * * ", " ** ", " ** ", " * * ", "* *", " ", " " }}, { 'y', { " ", " ", " ", "* *", "* *", "* *", "* **", " *** *", " *", "* *", " **** " }}, { 'z', { " ", " ", " ", "******", " * ", " * ", " * ", " * ", "******", " ", " " }}, { '\xe6', { " ", " ", " ", " ** * ", " * *", " *****", "* * ", "* * *", " ** * ", " ", " " }}, { '\xf8', { " ", " ", " *", " **** ", "* **", "* * *", "* * *", "** *", " **** ", "* ", " " }}, { '\xe5', { " ** ", " * * ", " ** ", " **** ", " *", " *****", "* *", "* **", " *** *", " ", " " }}, { 'A', { " ** ", " * * ", "* *", "* *", "* *", "******", "* *", "* *", "* *", " ", " " }}, { 'B', { "***** ", " * *", " * *", " * *", " **** ", " * *", " * *", " * *", "***** ", " ", " " }}, { 'C', { " *** ", "* *", "* ", "* ", "* ", "* ", "* ", "* *", " *** ", " ", " " }}, { 'D', { "***** ", " * *", " * *", " * *", " * *", " * *", " * *", " * *", "***** ", " ", " " }}, { 'E', { "*****", "* ", "* ", "* ", "**** ", "* ", "* ", "* ", "*****", " ", " " }}, { 'F', { "*****", "* ", "* ", "* ", "**** ", "* ", "* ", "* ", "* ", " ", " " }}, { 'G', { " **** ", "* *", "* ", "* ", "* ", "* ***", "* *", "* **", " *** *", " ", " " }}, { 'H', { "* *", "* *", "* *", "* *", "******", "* *", "* *", "* *", "* *", " ", " " }}, { 'I', { "***", " * ", " * ", " * ", " * ", " * ", " * ", " * ", "***", " ", " " }}, { 'J', { " ***", " *", " *", " *", " *", " *", " *", "* *", " *** ", " ", " " }}, { 'K', { "* *", "* * ", "* * ", "* * ", "** ", "* * ", "* * ", "* * ", "* *", " ", " " }}, { 'L', { "* ", "* ", "* ", "* ", "* ", "* ", "* ", "* ", "*****", " ", " " }}, { 'M', { "* *", "** **", "* * * *", "* * *", "* *", "* *", "* *", "* *", "* *", " ", " " }}, { 'N', { "* *", "* *", "** *", "* * *", "* * *", "* **", "* *", "* *", "* *", " ", " " }}, { 'O', { " *** ", "* *", "* *", "* *", "* *", "* *", "* *", "* *", " *** ", " ", " " }}, { 'P', { "**** ", "* *", "* *", "* *", "**** ", "* ", "* ", "* ", "* ", " ", " " }}, { 'Q', { " *** ", "* * ", "* * ", "* * ", "* * ", "* * ", "* * * ", "* ** ", " **** ", " *", " " }}, { 'R', { "**** ", "* *", "* *", "* *", "**** ", "** ", "* * ", "* * ", "* *", " ", " " }}, { 'S', { " *** ", "* *", "* ", "* ", " *** ", " *", " *", "* *", " *** ", " ", " " }}, { 'T', { "*****", " * ", " * ", " * ", " * ", " * ", " * ", " * ", " * ", " ", " " }}, { 'U', { "* *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", " *** ", " ", " " }}, { 'V', { "* *", "* *", "* *", " * * ", " * * ", " * * ", " * ", " * ", " * ", " ", " " }}, { 'W', { "* *", "* *", "* *", "* *", "* *", "* * *", "* * * *", "** **", "* *", " ", " " }}, { 'X', { "* *", "* *", " * * ", " * * ", " * ", " * * ", " * * ", "* *", "* *", " ", " " }}, { 'Y', { "* *", "* *", " * * ", " * * ", " * ", " * ", " * ", " * ", " * ", " ", " " }}, { 'Z', { "*****", " *", " * ", " * ", " * ", " * ", " * ", "* ", "*****", " ", " " }}, { '\xc6', { " ** ***", "* * ", "* * ", "* * ", "****** ", "* * ", "* * ", "* * ", "* ****", " ", " " }}, { '\xd8', { " *** ", "* **", "* * *", "* * *", "* * *", "* * *", "* * *", "** *", " *** ", "* ", " " }}, { '\xc5', { " ** ", " * * ", " ** ", " ** ", " * * ", "* *", "******", "* *", "* *", " ", " " }}, { '!', { "*", "*", "*", "*", "*", "*", "*", " ", "*", " ", " " }}, { '"', { "* *", "* *", " ", " ", " ", " ", " ", " ", " ", " ", " " }}, { '#', { " * * ", " * * ", "******", " * * ", " * * ", " * * ", "******", " * * ", " * * ", " ", " " }}, { '$', { " * ", " **** ", "* * *", "* * ", "* * ", " **** ", " * *", "* * *", " **** ", " * ", " " }}, { '%', { " * *", "* * *", " * * ", " * ", " * ", " * ", " * * ", "* * *", "* * ", " ", " " }}, { '&', { " ", " ", " ** ", "* * ", "* * ", " ** ", "* * *", "* * ", " *** *", " ", " " }}, { '\'', { " ***", " ** ", "* ", " ", " ", " ", " ", " ", " ", " ", " " }}, { '(', { " *", " * ", " * ", "* ", "* ", "* ", " * ", " * ", " *", " ", " " }}, { ')', { "* ", " * ", " * ", " *", " *", " *", " * ", " * ", "* ", " ", " " }}, { '*', { " ", " ", " ", " * * ", " ** ", "******", " ** ", " * * ", " ", " ", " " }}, { '+', { " ", " ", " ", " * ", " * ", "*****", " * ", " * ", " ", " ", " " }}, { ',', { " ", " ", " ", " ", " ", " ", " ", " ***", " ** ", "* ", " " }}, { '-', { " ", " ", " ", " ", " ", "*****", " ", " ", " ", " ", " " }}, { '.', { " ", " ", " ", " ", " ", " ", " ", "**", "**", " ", " " }}, { '/', { " *", " * ", " * ", " * ", " * ", " * ", " * ", "* ", "* ", " ", " " }}, { '0', { " * ", " * * ", "* *", "* *", "* *", "* *", "* *", " * * ", " * ", " ", " " }}, { '1', { " * ", " ** ", "* * ", " * ", " * ", " * ", " * ", " * ", "*****", " ", " " }}, { '2', { " *** ", "* *", " *", " *", " * ", " * ", " * ", "* ", "*****", " ", " " }}, { '3', { "*****", " *", " * ", " * ", " *** ", " *", " *", "* *", " *** ", " ", " " }}, { '4', { " * ", " ** ", " * * ", " * * ", "* * ", "******", " * ", " * ", " * ", " ", " " }}, { '5', { "*****", "* ", "* ", "* ** ", "** *", " *", " *", "* *", " *** ", " ", " " }}, { '6', { " *** ", "* *", "* ", "* ", "* ** ", "** *", "* *", "* *", " *** ", " ", " " }}, { '7', { "*****", " *", " * ", " * ", " * ", " * ", " * ", "* ", "* ", " ", " " }}, { '8', { " *** ", "* *", "* *", "* *", " *** ", "* *", "* *", "* *", " *** ", " ", " " }}, { '9', { " *** ", "* *", "* *", "* **", " ** *", " *", " *", "* *", " *** ", " ", " " }}, { ':', { " ", " ", "**", "**", " ", " ", " ", "**", "**", " ", " " }}, { ';', { " ", " ", " ", " **", " **", " ", " ", " **", " **", "* ", " " }}, { '<', { " *", " * ", " * ", " * ", "* ", " * ", " * ", " * ", " *", " ", " " }}, { '=', { " ", " ", " ", "*****", " ", "*****", " ", " ", " ", " ", " " }}, { '>', { "* ", " * ", " * ", " * ", " *", " * ", " * ", " * ", "* ", " ", " " }}, { '?', { " *** ", "* *", " *", " *", " * ", " * ", " * ", " ", " * ", " ", " " }}, { '@', { " **** ", "* *", "* *", "* ***", "* * *", "* * **", "* * *", "* ", " **** ", " ", " " }}, { '[', { "***", "* ", "* ", "* ", "* ", "* ", "* ", "* ", "***", " ", " " }}, { '\\', { "* ", "* ", " * ", " * ", " * ", " * ", " * ", " *", " *", " ", " " }}, { ']', { "***", " *", " *", " *", " *", " *", " *", " *", "***", " ", " " }}, { '^', { " * ", " * * ", "* *", " ", " ", " ", " ", " ", " ", " ", " " }}, { '_', { " ", " ", " ", " ", " ", " ", " ", " ", "*****", " ", " " }}, { '`', { "*** ", " ** ", " *", " ", " ", " ", " ", " ", " ", " ", " " }}, { '{', { " *", " * ", " * ", " * ", "* ", " * ", " * ", " * ", " *", " ", " " }}, { '|', { "*", "*", "*", "*", "*", "*", "*", "*", "*", " ", " " }}, { '}', { "* ", " * ", " * ", " * ", " *", " * ", " * ", " * ", "* ", " ", " " }}, { '~', { " * ", "* * *", " * ", " ", " ", " ", " ", " ", " ", " ", " " }}, }; /** This class defines a video input device that generates fictitous image data. */ class PVideoInputDevice_FakeVideo : public PVideoInputDevice { PCLASSINFO(PVideoInputDevice_FakeVideo, PVideoInputDevice); public: /** Create a new (fake) video input device. */ PVideoInputDevice_FakeVideo(); /**Open the device given the device name. */ BOOL Open( const PString & deviceName, /// Device name to open BOOL startImmediate = TRUE /// Immediately start device ); /**Determine of the device is currently open. */ BOOL IsOpen() ; /**Close the device. */ BOOL Close(); /**Start the video device I/O. */ BOOL Start(); /**Stop the video device I/O capture. */ BOOL Stop(); /**Determine if the video device I/O capture is in progress. */ BOOL IsCapturing(); /**Get a list of all of the drivers available. */ static PStringList GetInputDeviceNames(); virtual PStringList GetDeviceNames() const { return GetInputDeviceNames(); } /**Get the maximum frame size in bytes. Note a particular device may be able to provide variable length frames (eg motion JPEG) so will be the maximum size of all frames. */ virtual PINDEX GetMaxFrameBytes(); /**Grab a frame. There will be a delay in returning, as specified by frame rate. */ virtual BOOL GetFrameData( BYTE * buffer, /// Buffer to receive frame PINDEX * bytesReturned = NULL /// Optional bytes returned. ); /**Grab a frame. Do not delay according to the current frame rate. */ virtual BOOL GetFrameDataNoDelay( BYTE * buffer, /// Buffer to receive frame PINDEX * bytesReturned = NULL /// OPtional bytes returned. ); /**A test image that contains area of low and high resolution. The picture changes every second*/ void GrabMovingBlocksTestFrame(BYTE *resFrame); /**a test image consisting of a horizontal line moving down the image, with a constantly varying background. */ void GrabMovingLineTestFrame(BYTE *resFrame); /**Generate a constant image, which contains the colours for a NTSC test frame.*/ void GrabNTSCTestFrame(BYTE *resFrame); /**Generate three bouncing boxes, which bounce from a different height */ void GrabBouncingBoxes(BYTE *resFrame); /**Generate a static image, containing a constant field of grey. */ void GrabBlankImage(BYTE *resFrame); /**Generate the original form of the moving blocks test frame. */ void GrabOriginalMovingBlocksFrame(BYTE *resFrame); /**Generate a textual output on the fake video image */ void GrabTextVideoFrame(BYTE *resFrame); /**Get the stucture holding required letter for GetTextVideoFrame() */ OneVFakeLetterData *FindLetter(char ascii); /** Fills a region of the image with a constant colour. */ void FillRect(BYTE * frame, int x, int y, int rectWidth, int rectHeight, int r, int g, int b); /**Set the video format to be used. Default behaviour sets the value of the videoFormat variable and then returns the IsOpen() status. */ virtual BOOL SetVideoFormat( VideoFormat videoFormat /// New video format ); /**Get the number of video channels available on the device. Default behaviour returns 1. */ virtual int GetNumChannels() ; /**Set the video channel to be used on the device. Default behaviour sets the value of the channelNumber variable and then returns the IsOpen() status. */ virtual BOOL SetChannel( int channelNumber /// New channel number for device. ); /**Set the colour format to be used. Default behaviour sets the value of the colourFormat variable and then returns the IsOpen() status. */ virtual BOOL SetColourFormat( const PString & colourFormat // New colour format for device. ); /**Set the video frame rate to be used on the device. Default behaviour sets the value of the frameRate variable and then return the IsOpen() status. */ virtual BOOL SetFrameRate( unsigned rate /// Frames per second ); /**Get the minimum & maximum size of a frame on the device. Default behaviour returns the value 1 to UINT_MAX for both and returns FALSE. */ virtual BOOL GetFrameSizeLimits( unsigned & minWidth, /// Variable to receive minimum width unsigned & minHeight, /// Variable to receive minimum height unsigned & maxWidth, /// Variable to receive maximum width unsigned & maxHeight /// Variable to receive maximum height ) ; /**Set the frame size to be used. Default behaviour sets the frameWidth and frameHeight variables and returns the IsOpen() status. */ virtual BOOL SetFrameSize( unsigned width, /// New width of frame unsigned height /// New height of frame ); void ClearMapping() { return ; } /**Try all known video formats & see which ones are accepted by the video driver */ virtual BOOL TestAllFormats() { return TRUE; } protected: unsigned grabCount; PINDEX videoFrameSize; PINDEX scanLineWidth; PINDEX bytesPerPixel; // 2==YUV420P, 3=RGB24, 4=RGB32 PString textLine[MAX_L_HEIGHT]; }; PCREATE_VIDINPUT_PLUGIN(FakeVideo); /////////////////////////////////////////////////////////////////////////////// // PVideoInputDevice_FakeVideo PVideoInputDevice_FakeVideo::PVideoInputDevice_FakeVideo() { SetColourFormat("RGB24"); channelNumber = 3; // Blank screen grabCount = 0; SetFrameRate(10); } BOOL PVideoInputDevice_FakeVideo::Open(const PString & /*devName*/, BOOL /*startImmediate*/) { deviceName = "fake"; return TRUE; } BOOL PVideoInputDevice_FakeVideo::IsOpen() { return TRUE; } BOOL PVideoInputDevice_FakeVideo::Close() { return TRUE; } BOOL PVideoInputDevice_FakeVideo::Start() { return TRUE; } BOOL PVideoInputDevice_FakeVideo::Stop() { return TRUE; } BOOL PVideoInputDevice_FakeVideo::IsCapturing() { return IsOpen(); } PStringList PVideoInputDevice_FakeVideo::GetInputDeviceNames() { PStringList list; list.AppendString("fake"); return list; } BOOL PVideoInputDevice_FakeVideo::SetVideoFormat(VideoFormat newFormat) { return PVideoDevice::SetVideoFormat(newFormat); } int PVideoInputDevice_FakeVideo::GetNumChannels() { return eNumTestPatterns; } BOOL PVideoInputDevice_FakeVideo::SetChannel(int newChannel) { return PVideoDevice::SetChannel(newChannel); } BOOL PVideoInputDevice_FakeVideo::SetColourFormat(const PString & newFormat) { if (newFormat *= "RGB32") bytesPerPixel = 4; else if (newFormat *= "RGB24") bytesPerPixel = 3; else if (newFormat *= "YUV420P") bytesPerPixel = 2; else return FALSE; if (!PVideoDevice::SetColourFormat(newFormat)) return FALSE; return SetFrameSize(frameWidth, frameHeight); } BOOL PVideoInputDevice_FakeVideo::SetFrameRate(unsigned rate) { if (rate < 1) rate = 1; else if (rate > 50) rate = 50; return PVideoDevice::SetFrameRate(rate); } BOOL PVideoInputDevice_FakeVideo::GetFrameSizeLimits(unsigned & minWidth, unsigned & minHeight, unsigned & maxWidth, unsigned & maxHeight) { minWidth = 16; minHeight = 12; maxWidth = 1024; maxHeight = 768; return TRUE; } BOOL PVideoInputDevice_FakeVideo::SetFrameSize(unsigned width, unsigned height) { if (!PVideoDevice::SetFrameSize(width, height)) return FALSE; videoFrameSize = CalculateFrameBytes(frameWidth, frameHeight, colourFormat); scanLineWidth = videoFrameSize/frameHeight; return videoFrameSize > 0; } PINDEX PVideoInputDevice_FakeVideo::GetMaxFrameBytes() { return GetMaxFrameBytesConverted(videoFrameSize); } BOOL PVideoInputDevice_FakeVideo::GetFrameData(BYTE * buffer, PINDEX * bytesReturned) { frameTimeError += msBetweenFrames; PTime now; PTimeInterval delay = now - previousFrameTime; frameTimeError -= (int)delay.GetMilliSeconds(); previousFrameTime = now; if (frameTimeError > 0) { PTRACE(6, "FakeVideo\t Sleep for " << frameTimeError << " milli seconds"); PThread::Sleep(frameTimeError); } return GetFrameDataNoDelay(buffer, bytesReturned); } BOOL PVideoInputDevice_FakeVideo::GetFrameDataNoDelay(BYTE *destFrame, PINDEX * bytesReturned) { grabCount++; // Make sure are NUM_PATTERNS cases here. switch(channelNumber){ case eMovingBlocks : GrabMovingBlocksTestFrame(destFrame); break; case eMovingLine : GrabMovingLineTestFrame(destFrame); break; case eBouncingBoxes : GrabBouncingBoxes(destFrame); break; case eBlankImage : GrabBlankImage(destFrame); break; case eOriginalMovingBlocks : GrabOriginalMovingBlocksFrame(destFrame); break; case eText : GrabTextVideoFrame(destFrame); break; case eNTSCTest : GrabNTSCTestFrame(destFrame); break; default : return FALSE; } if (NULL != converter) { if (!converter->Convert(destFrame, destFrame, bytesReturned)) return FALSE; } if (bytesReturned != NULL) *bytesReturned = videoFrameSize; return TRUE; } void PVideoInputDevice_FakeVideo::FillRect(BYTE * frame, int xPos, int initialYPos, int rectWidth, int rectHeight, int r, int g, int b) { // PTRACE(0,"x,y is"< 2) { for (int y = 0; y < rectHeight; y++) { BYTE * ptr = frame + (initialYPos+y)*scanLineWidth + xPos*bytesPerPixel; for (int x = 0; x < rectWidth; x++) { *ptr++ = (BYTE)r; *ptr++ = (BYTE)g; *ptr++ = (BYTE)b; if (bytesPerPixel > 3) *ptr++ = 0; } } return; } int yPos = initialYPos; int offset = ( yPos * frameWidth ) + xPos; int colourOffset = ( (yPos * frameWidth) >> 2) + (xPos >> 1); int Y = ( 257 * r + 504 * g + 98 * b)/1000 + 16; int Cb = (-148 * r - 291 * g + 439 * b)/1000 + 128; int Cr = ( 439 * r - 368 * g - 71 * b)/1000 + 128; unsigned char * Yptr = frame + offset; unsigned char * CbPtr = frame + (frameWidth * frameHeight) + colourOffset; unsigned char * CrPtr = frame + (frameWidth * frameHeight) + (frameWidth * frameHeight/4) + colourOffset; int rr ; int halfRectWidth = rectWidth >> 1; int halfWidth = frameWidth >> 1; for (rr = 0; rr < rectHeight;rr+=2) { memset(Yptr, Y, rectWidth); Yptr += frameWidth; memset(Yptr, Y, rectWidth); Yptr += frameWidth; memset(CbPtr, Cb, halfRectWidth); memset(CrPtr, Cr, halfRectWidth); CbPtr += halfWidth; CrPtr += halfWidth; } } void PVideoInputDevice_FakeVideo::GrabBouncingBoxes(BYTE *resFrame) { FillRect(resFrame, 0, 0, frameWidth, frameHeight, //Fill the whole frame with the colour. 200,200,200); //a light grey colour. double t= (grabCount%50) -25 ; double h= t*t*frameHeight*0.85/625; int yBox = (int)h; yBox= (yBox>>1) * 2; //yBox is even. int boxHeight= (int)(frameHeight*0.1); boxHeight= (boxHeight >>1) * 2; int boxWidth = (int)(frameWidth*0.1); boxWidth = (boxWidth >>1) * 2; FillRect(resFrame, frameWidth >> 2, yBox, boxWidth, boxHeight, 255, 0, 0); // Red Box. t= (grabCount%40) -20 ; h= t*t*frameHeight*0.85/400 ; yBox = (int)h; yBox= (yBox>>1) * 2; //yBox is even. FillRect(resFrame, frameWidth>>1, yBox, boxWidth, boxHeight, 0, 255, 0); // Green t= (grabCount%100) -50 ; h= t*t*frameHeight*0.85/2500; yBox = (int)h; yBox= (yBox>>1) * 2; //yBox is even. FillRect(resFrame, (frameWidth>>1) + (frameWidth>>2), yBox, boxWidth, boxHeight, 0, 0, 255); // Blue } void PVideoInputDevice_FakeVideo::GrabNTSCTestFrame(BYTE *resFrame) { // Test image # 1 // A static image is generated, consisting of a series of coloured block. // Sample NTSC test frame is found at http://www.displaymate.com/patterns.html // static int row1[7][3] = { { 204, 204, 204 }, // 80% grey { 255, 255, 0 }, // yellow { 0, 255, 255 }, // cyan { 0, 255, 0 }, // green { 255, 0, 255 }, // magenta { 255, 0, 0 }, // red { 0, 0, 255 }, // blue }; static int row2[7][3] = { { 0, 0, 255 }, // blue { 19, 19, 19 }, // black { 255, 0, 255 }, // magenta { 19, 19, 19 }, // black { 0, 255, 255 }, // cyan { 19, 19, 19 }, // black { 204, 204, 204 }, // grey }; static int row3a[4][3] = { { 8, 62, 89 }, // I { 255, 255, 255 }, // white { 58, 0, 126 }, // +Q { 19, 19, 19 }, // black }; static int row3b[3][3] = { { 0, 0, 0 }, // 3.5 { 19, 19, 19 }, // 7.5 { 38, 38, 38 }, // 11.5 }; static int row3c[3] = { 19, 19, 19 }; int row1Height = (int)(0.66 * frameHeight); int row2Height = (int)((0.75 * frameHeight) - row1Height); row1Height = (row1Height>>1)*2; //Require that height is even. row2Height = (row2Height>>1)*2; int row3Height = frameHeight - row1Height - row2Height; int columns[8]; PINDEX i; for(i=0;i<7;i++) { columns[i]= i*frameWidth/7; columns[i]= (columns[i]>>1)*2; // require that columns[i] is even. } columns[7] = frameWidth; // first row for (i = 0; i < 6; i++) FillRect(resFrame, columns[i], 0, columns[i+1]-columns[i], row1Height, //x,y,w,h row1[i][0], row1[i][1], row1[i][2]); // rgb // second row for (i = 0; i < 7; i++) FillRect(resFrame, columns[i], row1Height, columns[i+1]-columns[i], row2Height, row2[i][0], row2[i][1], row2[i][2]); // third row int columnBot[5]; for (i=0; i<4; i++) { columnBot[i]= i*columns[5]/4; columnBot[i] = 2 * (columnBot[i]>>1); } columnBot[4]= columns[5]; for (i = 0; i < 4; i++) FillRect(resFrame, columnBot[i],row1Height + row2Height, columnBot[i+1]-columnBot[i], row3Height, row3a[i][0], row3a[i][1], row3a[i][2]); for (i=0; i<3; i++) { columnBot[i] = columns[4]+(i*frameWidth)/(7*3); columnBot[i] = 2 * (columnBot[i]>>1); //Force even. } columnBot[3]= columns[5]; for (i = 0; i < 3; i++) FillRect(resFrame, columnBot[i], row1Height + row2Height, columnBot[i+1] - columnBot[i], row3Height, row3b[i][0], row3b[i][1], row3b[i][2]); FillRect(resFrame, columns[6], row1Height + row2Height, columns[7] - columns[6], row3Height, row3c[0], row3c[1], row3c[2]); } void PVideoInputDevice_FakeVideo::GrabMovingBlocksTestFrame(BYTE * resFrame) { // Test image # 2 /*Brightness is set to alter, left to right. Colour component alters top to bottom. Image contains lots of high and low resolution areas. */ unsigned wi,hi, colourIndex,colNo, boxSize; #define COL(b,x,y) ((b+x+y)%7) static int background[7][3] = { { 254, 254, 254 }, // white { 255, 255, 0 }, // yellow { 0, 255, 255 }, // cyan { 0, 255, 0 }, // green { 255, 0, 255 }, // magenta { 255, 0, 0 }, // red { 0, 0, 255 }, // blue }; int columns[9]; int heights[9]; int offset; offset = (frameWidth >> 3) & 0xffe; for(wi = 0; wi < 8; wi++) columns[wi] = wi * offset; columns[8] = frameWidth; offset = (frameHeight >> 3) & 0xffe; for(hi = 0; hi < 9; hi++) heights[hi] = hi * offset; heights[8] = frameHeight; grabCount++; colourIndex = time(NULL);//time in seconds since last epoch. // Provides a difference if run on two ohphone sessions. colNo = (colourIndex / 10) % 7; //Every 10 seconds, coloured background blocks move. for(hi = 0; hi < 8; hi++) //Fill the background in. for(wi = 0 ; wi < 8; wi++) { FillRect(resFrame, columns[wi], heights[hi], columns[wi + 1] - columns[wi], heights[hi + 1] - heights[hi], background[COL(colNo, wi, hi)][0], background[COL(colNo, wi, hi)][1], background[COL(colNo, wi, hi)][2]); } //Draw a black box rapidly moving down the left of the window. boxSize= frameHeight / 10; hi = ((3 * colourIndex) % (frameHeight-boxSize)) & 0xffe; //Make certain hi is even. FillRect(resFrame, 10, hi, boxSize, boxSize, 0, 0, 0); //Black Box. //Draw four parallel black lines, which move up the middle of the window. colourIndex = colourIndex / 3; //Every three seconds, lines move. for(wi = 0; wi < 2; wi++) columns[wi]= (((wi + 1) * frameWidth) / 3) & 0xffe;// Force columns to be even. hi = colourIndex % ((frameHeight - 16) / 2); hi = (frameHeight - 16) - (hi * 2); //hi is even, Lines move in opp. direction to box. unsigned yi; for(yi = 0; yi < 4; yi++) FillRect(resFrame, columns[0], hi+(yi * 4), columns[1] - columns[0], 2, 0, 0, 0); } void PVideoInputDevice_FakeVideo::GrabMovingLineTestFrame(BYTE *resFrame) { // Test image # 3 // Faster image generation. Same every times system runs. // Colours cycle through. Have a vertical lines style of pattern. // There is a horizontal bar which moves down the screen . static int v=0; int r,g,b; v++; r = (200+v) & 255; g = (100+v) & 255; b = (000+v) & 255; FillRect(resFrame, 0, 0,frameWidth, frameHeight, r, g, b); int hi = (v % (frameHeight-2) >> 1) *2; FillRect(resFrame, 0, hi, frameWidth, 2, 0, 0, 0); } void PVideoInputDevice_FakeVideo::GrabBlankImage(BYTE *resFrame) { // Change colour every second, cycle is: // black, red, green, yellow, blue, magenta, cyan, white int mask = grabCount/frameRate; FillRect(resFrame, 0, 0, frameWidth, frameHeight, //Fill the whole frame with the colour. (mask&1) ? 255 : 0, // red (mask&2) ? 255 : 0, // green (mask&4) ? 255 : 0);//blue } void PVideoInputDevice_FakeVideo::GrabOriginalMovingBlocksFrame(BYTE *frame) { unsigned wi,hi,colourIndex,colourNumber; int framesize = frameWidth*frameHeight; static int gCount=0; gCount++; colourIndex = gCount/10; colourNumber= (colourIndex/10)%7; //Every 10 seconds, coloured background blocks move. for(hi=0; hiframeWidth/3)&&(wi= nChars) ld = FindLetter(' '); else ld = FindLetter(message[i]); if (ld == NULL) continue; for (j = 0; j < MAX_L_HEIGHT; j++) textLine[j] += ld->line[j] + PString(" "); } } PINDEX boxSize = (frameHeight / (MAX_L_HEIGHT * 2) ) & 0xffe; int index = (int)((PTime() - startTime).GetMilliSeconds() / 300); PINDEX maxI = (frameWidth / boxSize) - 2; for (i = 0; i < maxI; i++) for (j = 0; j < MAX_L_HEIGHT; j++) { PINDEX ii = (index + i) % textLine[0].GetLength(); if (textLine[j][ii] != ' ') FillRect(resFrame, (i + 1) * boxSize, (frameHeight / 3) + ((j + 1) * boxSize), //x,y start pos boxSize, boxSize, //x,y dimension 250, 00, 00); //red box. } } OneVFakeLetterData *PVideoInputDevice_FakeVideo::FindLetter(char ascii) { int q; int fontNumLetters = sizeof(vFakeLetterData) / sizeof(OneVFakeLetterData); if (ascii == '\t') ascii = ' '; for (q = 0; q < fontNumLetters; q++) if (vFakeLetterData[q].ascii == ascii) return vFakeLetterData + q; return NULL; } ///////////////////////////////////////////////////////////////////////////////////////////////////////// /**This class defines a NULL video output device. This will do precisely nothing with the output. */ class PVideoOutputDevice_NULLOutput : public PVideoOutputDevice { PCLASSINFO(PVideoOutputDevice_NULLOutput, PVideoOutputDevice); public: /** Create a new video output device. */ PVideoOutputDevice_NULLOutput(); /**Get a list of all of the drivers available. */ static PStringList GetOutputDeviceNames(); virtual PStringList GetDeviceNames() const { return GetOutputDeviceNames(); } /**Open the device given the device name. */ virtual BOOL Open( const PString & deviceName, /// Device name to open BOOL startImmediate = TRUE /// Immediately start device ); /**Start the video device I/O. */ BOOL Start(); /**Stop the video device I/O capture. */ BOOL Stop(); /**Close the device. */ virtual BOOL Close(); /**Determine if the device is currently open. */ virtual BOOL IsOpen(); /**Get the maximum frame size in bytes. Note a particular device may be able to provide variable length frames (eg motion JPEG) so will be the maximum size of all frames. */ virtual PINDEX GetMaxFrameBytes(); /**Set a section of the output frame buffer. */ virtual BOOL SetFrameData( unsigned x, unsigned y, unsigned width, unsigned height, const BYTE * data, BOOL endFrame = TRUE ); /**Indicate frame may be displayed. */ virtual BOOL EndFrame(); }; PCREATE_VIDOUTPUT_PLUGIN(NULLOutput); /////////////////////////////////////////////////////////////////////////////// // PVideoOutputDevice_NULLOutput PVideoOutputDevice_NULLOutput::PVideoOutputDevice_NULLOutput() { deviceName = "NULL"; } BOOL PVideoOutputDevice_NULLOutput::Open(const PString & /*deviceName*/, BOOL /*startImmediate*/) { return TRUE; } BOOL PVideoOutputDevice_NULLOutput::Close() { return TRUE; } BOOL PVideoOutputDevice_NULLOutput::Start() { return TRUE; } BOOL PVideoOutputDevice_NULLOutput::Stop() { return TRUE; } BOOL PVideoOutputDevice_NULLOutput::IsOpen() { return TRUE; } PStringList PVideoOutputDevice_NULLOutput::GetOutputDeviceNames() { PStringList list; list += "NULL"; return list; } PINDEX PVideoOutputDevice_NULLOutput::GetMaxFrameBytes() { return GetMaxFrameBytesConverted(CalculateFrameBytes(frameWidth, frameHeight, colourFormat)); } BOOL PVideoOutputDevice_NULLOutput::SetFrameData(unsigned /*x*/, unsigned /*y*/, unsigned /*width*/, unsigned /*height*/, const BYTE * /*data*/, BOOL /*endFrame*/) { return TRUE; } BOOL PVideoOutputDevice_NULLOutput::EndFrame() { return TRUE; } // End Of File ///////////////////////////////////////////////////////////////