Changed PVideoOutputDevice::CreateDeviceByName() to include driverName parameter...
[pwlib.git] / src / ptlib / common / videoio.cxx
bloba671d2e6c18f9ccabeb4e56a2fb831baca933dcc
1 /*
2 * videoio.cxx
4 * Classes to support streaming video input (grabbing) and output.
6 * Portable Windows Library
8 * Copyright (c) 1993-2000 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): Mark Cooke (mpc@star.sr.bham.ac.uk)
26 * $Log$
27 * Revision 1.67 2007/04/05 01:53:00 rjongbloed
28 * Changed PVideoOutputDevice::CreateDeviceByName() to include driverName parameter so symmetric with PVideoInputDevice.
30 * Revision 1.66 2007/04/03 12:09:38 rjongbloed
31 * Fixed various "file video device" issues:
32 * Remove filename from PVideoDevice::OpenArgs (use deviceName)
33 * Added driverName to PVideoDevice::OpenArgs (so can select YUVFile)
34 * Added new statics to create correct video input/output device object
35 * given a PVideoDevice::OpenArgs structure.
36 * Fixed begin able to write to YUVFile when YUV420P colour format
37 * is not actually selected.
38 * Fixed truncating output video file if overwriting.
40 * Revision 1.65 2006/12/07 21:33:24 dominance
41 * make sure we support also webcams that do *only* do 640x480 properly.
42 * Thanks goes to Luc Saillard for this patch.
44 * Revision 1.64 2006/10/31 04:10:40 csoutheren
45 * Make sure PVidFileDev class is loaded, and make it work with OPAL
47 * Revision 1.63 2006/10/25 11:04:38 shorne
48 * fix for devices having same name for different drivers.
50 * Revision 1.62 2006/06/21 03:28:44 csoutheren
51 * Various cleanups thanks for Frederic Heem
53 * Revision 1.61 2006/04/19 04:10:27 csoutheren
54 * Fix problem when using converter when frame size not supported
56 * Revision 1.60 2006/03/07 20:53:51 dsandras
57 * Added support for JPEG based webcams, thanks to Luc Saillard <luc saillard org>.
59 * Revision 1.59 2006/01/29 22:46:39 csoutheren
60 * Added support for cameras that return MJPEG streams
61 * Thanks to Luc Saillard and Damien Sandras
63 * Revision 1.58 2006/01/09 18:19:13 dsandras
64 * Add YUY2 (or YUV420) format. Resizing to YUV420P is done, but it's not very
65 * efficient.
66 * Fix the gray border when doing padding for YUV420P (change it to black).
67 * Logitech webcam fusion export only big format in yuy2.
68 * Patches provided by Luc Saillard <luc _AT____ saillard.org>. Many thanks!
70 * Revision 1.57 2005/11/30 12:47:42 csoutheren
71 * Removed tabs, reformatted some code, and changed tags for Doxygen
73 * Revision 1.56 2005/09/06 12:41:22 rjongbloed
74 * Fixed correct creation (or non creation) of converter when using SetFrameSizeConverter()
76 * Revision 1.55 2005/08/23 12:42:23 rjongbloed
77 * Fixed problems with negative hight native flipping not working with all sizes.
79 * Revision 1.54 2005/08/09 09:08:12 rjongbloed
80 * Merged new video code from branch back to the trunk.
82 * Revision 1.53.6.2 2005/07/17 12:17:41 rjongbloed
83 * Fixed deadlock changing size
85 * Revision 1.53.6.1 2005/07/17 09:27:08 rjongbloed
86 * Major revisions of the PWLib video subsystem including:
87 * removal of F suffix on colour formats for vertical flipping, all done with existing bool
88 * working through use of RGB and BGR formats so now consistent
89 * cleaning up the plug in system to use virtuals instead of pointers to functions.
90 * rewrite of SDL to be a plug in compatible video output device.
91 * extensive enhancement of video test program
93 * Revision 1.53 2005/01/04 08:09:42 csoutheren
94 * Fixed Linux configure problems
96 * Revision 1.52 2004/11/17 10:13:14 csoutheren
97 * Fixed compilation with gcc 4.0.0
99 * Revision 1.51 2004/10/27 09:24:18 dsandras
100 * Added patch from Nicola Orru' to convert from SBGGR8 to YUV420P. Thanks!
102 * Revision 1.50 2004/10/23 10:54:39 ykiryanov
103 * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
105 * Revision 1.49 2004/08/16 06:40:59 csoutheren
106 * Added adapters template to make device plugins available via the abstract factory interface
108 * Revision 1.48 2004/04/18 12:49:22 csoutheren
109 * Patches to video code thanks to Guilhem Tardy (hope I get it right this time :)
111 * Revision 1.47 2004/04/03 23:53:10 csoutheren
112 * Added various changes to improce compatibility with the Sun Forte compiler
113 * Thanks to Brian Cameron
114 * Added detection of readdir_r version
116 * Revision 1.46 2004/01/18 14:25:58 dereksmithies
117 * New methods to make the opening of video input devices easier.
119 * Revision 1.45 2004/01/17 17:41:50 csoutheren
120 * Changed to use PString::MakeEmpty
122 * Revision 1.44 2003/12/14 10:01:02 rjongbloed
123 * Resolved issue with name space conflict os static and virtual forms of GetDeviceNames() function.
125 * Revision 1.43 2003/11/23 22:17:35 dsandras
126 * Added YUV420P to BGR24 and BGR32 conversion.
128 * Revision 1.42 2003/11/19 04:30:15 csoutheren
129 * Changed to support video output plugins
131 * Revision 1.41 2003/11/18 10:40:51 csoutheren
132 * Added pragma implementation to fix vtable link problems
134 * Revision 1.40 2003/11/18 06:46:38 csoutheren
135 * Changed to support video input plugins
137 * Revision 1.39 2003/05/14 07:51:23 rjongbloed
138 * Changed SetColourFormatConverter so if converter already in place no
139 * change is made.
140 * Fixed some trace logs.
142 * Revision 1.38 2003/04/03 23:21:34 robertj
143 * Added reversed RGB byte order versions (BGR24), thanks Damien Sandras
145 * Revision 1.37 2003/03/21 04:09:33 robertj
146 * Changed PPM video output device so you can specify the full format of the
147 * output file uinng printf command for the frame number eg %u or %03i or
148 * something. If there is no %u in the Opan() argument, a %u is added after
149 * the filename.
150 * Fixed video output RGB SetFrameData so abide by correct semantics. The input
151 * is aways to be what was set using SetColourFormat() or
152 * SetColourFormatConverter().
154 * Revision 1.36 2003/03/20 23:42:01 dereks
155 * Make PPM video output device work correctly.
157 * Revision 1.35 2003/03/17 07:50:41 robertj
158 * Added OpenFull() function to open with all video parameters in one go.
159 * Made sure vflip variable is set in converter even if converter has not
160 * been set yet, should not depend on the order of functions!
161 * Removed canCaptureVideo variable as this is really a virtual function to
162 * distinguish PVideoOutputDevice from PVideoInputDevice, it is not dynamic.
163 * Made significant enhancements to PVideoOutputDevice class.
164 * Added PVideoOutputDevice descendants for NULL and PPM files.
166 * Revision 1.34 2002/09/01 23:25:26 dereks
167 * Documentation fix from Walter Whitlock. Many thanks.
169 * Revision 1.33 2002/09/01 22:38:21 dereks
170 * Remove previous clarification, cause it breaks openphone code.
172 * Revision 1.32 2002/08/30 02:31:43 dereks
173 * Make operation of the code more clear. Thanks Diego Tartara
175 * Revision 1.31 2002/04/12 08:24:53 robertj
176 * Added text string output for tracing video format.
178 * Revision 1.30 2002/04/07 22:49:32 rogerh
179 * Add some comments
181 * Revision 1.29 2002/04/05 06:41:54 rogerh
182 * Apply video changes from Damien Sandras <dsandras@seconix.com>.
183 * The Video Channel and Format are no longer set in Open(). Instead
184 * call the new SetVideoChannelFormat() method. This makes video capture
185 * and GnomeMeeting more stable with certain Linux video capture devices.
187 * Revision 1.28 2002/02/20 02:37:26 dereks
188 * Initial release of Firewire camera support for linux.
189 * Many thanks to Ryutaroh Matsumoto <ryutaroh@rmatsumoto.org>.
191 * Revision 1.27 2002/01/17 20:20:46 dereks
192 * Adjust PVideoInputDevice::SetVFlipState to cope better when a converter class is
193 * not attached. Thanks Walter Whitlock.
195 * Revision 1.26 2002/01/16 07:51:16 robertj
196 * MSVC compatibilty changes
198 * Revision 1.25 2002/01/14 02:59:41 robertj
199 * Added preferred colour format selection, thanks Walter Whitlock
201 * Revision 1.24 2002/01/08 01:32:20 robertj
202 * Tidied up some PTRACE debug output.
204 * Revision 1.23 2002/01/04 04:11:45 dereks
205 * Add video flip code from Walter Whitlock, which flips code at the grabber.
207 * Revision 1.22 2001/12/10 22:23:43 dereks
208 * Enable resize of CIF source image into QCIF size.
210 * Revision 1.21 2001/12/06 22:15:09 dereks
211 * Additional debugging lines
213 * Revision 1.20 2001/11/28 04:45:14 robertj
214 * Added Win32 flipped RGB colour formats.
216 * Revision 1.19 2001/11/28 00:07:32 dereks
217 * Locking added to PVideoChannel, allowing reader/writer to be changed mid call
218 * Enabled adjustment of the video frame rate
219 * New fictitous image, a blank grey area
221 * Revision 1.18 2001/09/25 04:25:38 dereks
222 * Add fix from Stelian Pop to improve the field of view for
223 * the small image with a Sony Vaio Motion Eye. Many thanks.
225 * Revision 1.17 2001/08/03 04:21:51 dereks
226 * Add colour/size conversion for YUV422->YUV411P
227 * Add Get/Set Brightness,Contrast,Hue,Colour for PVideoDevice, and
228 * Linux PVideoInputDevice.
229 * Add lots of PTRACE statement for debugging colour conversion.
230 * Add support for Sony Vaio laptop under linux. Requires 2.4.7 kernel.
232 * Revision 1.16 2001/06/27 17:23:33 rogerh
233 * Back out my previous change
235 * Revision 1.15 2001/06/26 15:48:31 rogerh
236 * Do not call GetInputDeviceName if there is no video grabber code
238 * Revision 1.14 2001/05/22 23:38:45 robertj
239 * Fixed bug in PVideoOutputDevice, removed redundent SetFrameSize.
241 * Revision 1.13 2001/03/20 02:21:57 robertj
242 * More enhancements from Mark Cooke
244 * Revision 1.12 2001/03/08 08:31:34 robertj
245 * Numerous enhancements to the video grabbing code including resizing
246 * infrastructure to converters. Thanks a LOT, Mark Cooke.
248 * Revision 1.11 2001/03/08 02:18:45 robertj
249 * Added improved defaulting of video formats so Open() does not fail.
250 * Removed the requirement that the device be open before you can set
251 * formats such as colour, video, channel number etc.
253 * Revision 1.10 2001/03/07 01:41:03 dereks
254 * Fix memory leak, on destroying PVideoDevice
255 * Ensure converter class is resized correctly.
257 * Revision 1.9 2001/03/06 23:34:20 robertj
258 * Added static function to get input device names.
259 * Moved some inline virtuals to non-inline.
261 * Revision 1.8 2001/03/05 01:12:41 robertj
262 * Added more source formats for conversion, use list. Thanks Mark Cooke.
264 * Revision 1.7 2001/03/03 05:06:31 robertj
265 * Major upgrade of video conversion and grabbing classes.
267 * Revision 1.6 2000/12/19 22:20:26 dereks
268 * Add video channel classes to connect to the PwLib PVideoInputDevice class.
269 * Add PFakeVideoInput class to generate test images for video.
271 * Revision 1.5 2000/11/09 00:20:58 robertj
272 * Added qcif size constants
274 * Revision 1.4 2000/07/26 03:50:50 robertj
275 * Added last error variable to video device.
277 * Revision 1.3 2000/07/26 02:13:48 robertj
278 * Added some more "common" bounds checking to video device.
280 * Revision 1.2 2000/07/25 13:38:26 robertj
281 * Added frame rate parameter to video frame grabber.
283 * Revision 1.1 2000/07/15 09:47:35 robertj
284 * Added video I/O device classes.
288 #ifndef _WIN32_WCE
289 #pragma implementation "videoio.h"
290 #endif // !_WIN32_WCE
292 #include <ptlib.h>
293 #include <ptlib/pluginmgr.h>
294 #include <ptlib/videoio.h>
295 #include <ptlib/vconvert.h>
297 namespace PWLibStupidWindowsHacks {
298 int loadVideoStuff;
301 namespace PWLib {
302 PFactory<PDevicePluginAdapterBase>::Worker< PDevicePluginAdapter<PVideoInputDevice> > vidinChannelFactoryAdapter("PVideoInputDevice", TRUE);
303 PFactory<PDevicePluginAdapterBase>::Worker< PDevicePluginAdapter<PVideoOutputDevice> > vidoutChannelFactoryAdapter("PVideoOutputDevice", TRUE);
306 template <> PVideoInputDevice * PDevicePluginFactory<PVideoInputDevice>::Worker::Create(const PString & type) const
308 return PVideoInputDevice::CreateDevice(type);
311 template <> PVideoOutputDevice * PDevicePluginFactory<PVideoOutputDevice>::Worker::Create(const PString & type) const
313 return PVideoOutputDevice::CreateDevice(type);
316 ///////////////////////////////////////////////////////////////////////////////
318 #if PTRACING
319 ostream & operator<<(ostream & strm, PVideoDevice::VideoFormat fmt)
321 static const char * const VideoFormatNames[PVideoDevice::NumVideoFormats] = {
322 "PAL",
323 "NTSC",
324 "SECAM",
325 "Auto"
328 if (fmt < PVideoDevice::NumVideoFormats && VideoFormatNames[fmt] != NULL)
329 strm << VideoFormatNames[fmt];
330 else
331 strm << "VideoFormat<" << (unsigned)fmt << '>';
333 return strm;
335 #endif
337 ///////////////////////////////////////////////////////////////////////////////
338 // PVideoDevice
340 PVideoDevice::PVideoDevice()
342 lastError = 0;
344 videoFormat = Auto;
345 channelNumber = -1; // -1 will find the first working channel number.
346 frameWidth = CIFWidth;
347 frameHeight = CIFHeight;
348 nativeVerticalFlip = FALSE;
350 converter = NULL;
352 SetFrameRate(0);
355 PVideoDevice::~PVideoDevice()
357 if (converter)
358 delete converter;
362 PVideoDevice::OpenArgs::OpenArgs()
363 : pluginMgr(NULL),
364 deviceName("#1"),
365 videoFormat(Auto),
366 channelNumber(0),
367 colourFormat("YUV420P"),
368 convertFormat(TRUE),
369 rate(0),
370 width(CIFWidth),
371 height(CIFHeight),
372 convertSize(TRUE),
373 scaleSize(FALSE),
374 flip(FALSE),
375 brightness(-1),
376 whiteness(-1),
377 contrast(-1),
378 colour(-1),
379 hue(-1)
384 BOOL PVideoDevice::OpenFull(const OpenArgs & args, BOOL startImmediate)
386 if (args.deviceName[0] == '#') {
387 PStringArray devices = GetDeviceNames();
388 PINDEX id = args.deviceName.Mid(1).AsUnsigned();
389 if (id == 0 || id > devices.GetSize())
390 return FALSE;
392 if (!Open(devices[id-1], FALSE))
393 return FALSE;
395 else {
396 if (!Open(args.deviceName, FALSE))
397 return FALSE;
400 if (!SetVideoFormat(args.videoFormat))
401 return FALSE;
403 if (!SetChannel(args.channelNumber))
404 return FALSE;
406 if (args.convertFormat) {
407 if (!SetColourFormatConverter(args.colourFormat))
408 return FALSE;
410 else {
411 if (!SetColourFormat(args.colourFormat))
412 return FALSE;
415 if (args.rate > 0) {
416 if (!SetFrameRate(args.rate))
417 return FALSE;
420 if (args.convertSize) {
421 if (!SetFrameSizeConverter(args.width, args.height, args.scaleSize))
422 return FALSE;
424 else {
425 if (!SetFrameSize(args.width, args.height))
426 return FALSE;
429 if (!SetVFlipState(args.flip))
430 return FALSE;
432 if (args.brightness >= 0) {
433 if (!SetBrightness(args.brightness))
434 return FALSE;
437 if (args.whiteness >= 0) {
438 if (!SetWhiteness(args.whiteness))
439 return FALSE;
442 if (args.contrast >= 0) {
443 if (!SetContrast(args.contrast))
444 return FALSE;
447 if (args.colour >= 0) {
448 if (!SetColour(args.colour))
449 return FALSE;
452 if (args.hue >= 0) {
453 if (!SetColour(args.hue))
454 return FALSE;
457 if (startImmediate)
458 return Start();
460 return TRUE;
464 BOOL PVideoDevice::Close()
466 return TRUE;
470 BOOL PVideoDevice::Start()
472 return TRUE;
476 BOOL PVideoDevice::Stop()
478 return TRUE;
482 BOOL PVideoDevice::SetVideoFormat(VideoFormat videoFmt)
484 videoFormat = videoFmt;
485 return TRUE;
489 PVideoDevice::VideoFormat PVideoDevice::GetVideoFormat() const
491 return videoFormat;
495 int PVideoDevice::GetNumChannels()
497 return 1;
501 BOOL PVideoDevice::SetChannel(int channelNum)
503 if (channelNum < 0) { // Seek out the first available channel
504 for (int c = 0; c < GetNumChannels(); c++) {
505 if (SetChannel(c))
506 return TRUE;
508 return FALSE;
511 if (channelNum >= GetNumChannels())
512 return FALSE;
514 channelNumber = channelNum;
515 return TRUE;
519 int PVideoDevice::GetChannel() const
521 return channelNumber;
525 //Colour format bit per pixel table.
526 // These are in rough order of colour gamut size
527 static struct {
528 const char * colourFormat;
529 unsigned bitsPerPixel;
530 } colourFormatBPPTab[] = {
531 { "RGB32", 32 },
532 { "BGR32", 32 },
533 { "RGB24", 24 },
534 { "BGR24", 24 },
535 { "MJPEG", 16 },
536 { "JPEG", 16 },
537 { "YUY2", 16 },
538 { "YUV422", 16 },
539 { "YUV422P", 16 },
540 { "YUV411", 12 },
541 { "YUV411P", 12 },
542 { "RGB565", 16 },
543 { "RGB555", 16 },
544 { "YUV420", 12 },
545 { "YUV420P", 12 },
546 { "IYUV", 12 },
547 { "I420", 12 },
548 { "YUV410", 10 },
549 { "YUV410P", 10 },
550 { "Grey", 8 },
551 { "GreyF", 8 },
552 { "UYVY422", 16 },
553 { "UYV444", 24 },
554 { "SBGGR8", 8 }
558 BOOL PVideoDevice::SetColourFormatConverter(const PString & colourFmt)
560 if (converter != NULL) {
561 if (CanCaptureVideo()) {
562 if (converter->GetDstColourFormat() == colourFmt)
563 return TRUE;
565 else {
566 if (converter->GetSrcColourFormat() == colourFmt)
567 return TRUE;
569 delete converter;
570 converter = NULL;
573 if (!preferredColourFormat.IsEmpty()) {
574 PTRACE(4,"PVidDev\tSetColourFormatConverter, want " << colourFmt << " trying " << preferredColourFormat);
575 if (SetColourFormat(preferredColourFormat)) {
576 if (CanCaptureVideo()) {
577 PTRACE(4,"PVidDev\tSetColourFormatConverter set camera to native "<< preferredColourFormat);
578 if (preferredColourFormat != colourFmt)
579 converter = PColourConverter::Create(preferredColourFormat, colourFmt, frameWidth, frameHeight);
581 else {
582 PTRACE(4,"PVidDev\tSetColourFormatConverter set renderer to "<< preferredColourFormat);
583 if (preferredColourFormat != colourFmt)
584 converter = PColourConverter::Create(colourFmt, preferredColourFormat, frameWidth, frameHeight);
587 if (nativeVerticalFlip && converter == NULL)
588 converter = PColourConverter::Create(colourFmt, colourFmt, frameWidth, frameHeight);
590 if (converter != NULL) {
591 converter->SetVFlipState(nativeVerticalFlip);
592 return TRUE;
597 if (SetColourFormat(colourFmt)) {
598 if (nativeVerticalFlip) {
599 converter = PColourConverter::Create(colourFmt, colourFmt, frameWidth, frameHeight);
600 if (PAssertNULL(converter) == NULL)
601 return FALSE;
602 converter->SetVFlipState(nativeVerticalFlip);
605 PTRACE(3, "PVidDev\tSetColourFormatConverter success for native " << colourFmt);
606 return TRUE;
609 /************************
610 Eventually, need something more sophisticated than this, but for the
611 moment pick the known colour formats that the device is very likely to
612 support and then look for a conversion routine from that to the
613 destination format.
615 What we really want is some sort of better heuristic that looks at
616 computational requirements of each converter and picks a pair of formats
617 that the hardware supports and uses the least CPU.
620 PINDEX knownFormatIdx = 0;
621 while (knownFormatIdx < PARRAYSIZE(colourFormatBPPTab)) {
622 PString formatToTry = colourFormatBPPTab[knownFormatIdx].colourFormat;
623 PTRACE(4,"PVidDev\tSetColourFormatConverter, want " << colourFmt << " trying " << formatToTry);
624 if (SetColourFormat(formatToTry)) {
625 if (CanCaptureVideo()) {
626 PTRACE(4,"PVidDev\tSetColourFormatConverter set camera to "<< formatToTry);
627 converter = PColourConverter::Create(formatToTry, colourFmt, frameWidth, frameHeight);
629 else {
630 PTRACE(4,"PVidDev\tSetColourFormatConverter set renderer to "<< formatToTry);
631 converter = PColourConverter::Create(colourFmt, formatToTry, frameWidth, frameHeight);
633 if (converter != NULL) {
634 // set converter properties that depend on this color format
635 PTRACE(3, "PVidDev\tSetColourFormatConverter succeeded for " << colourFmt << " and device using " << formatToTry);
636 converter->SetVFlipState(nativeVerticalFlip);
637 return TRUE;
640 knownFormatIdx++;
643 PTRACE(2, "PVidDev\tSetColourFormatConverter FAILED for " << colourFmt);
644 return FALSE;
648 BOOL PVideoDevice::SetColourFormat(const PString & colourFmt)
650 if (!colourFmt) {
651 colourFormat = colourFmt.ToUpper();
652 return TRUE;
655 for (PINDEX i = 0; i < PARRAYSIZE(colourFormatBPPTab); i++) {
656 if (SetColourFormat(colourFormatBPPTab[i].colourFormat))
657 return TRUE;
660 return FALSE;
664 const PString & PVideoDevice::GetColourFormat() const
666 return colourFormat;
670 BOOL PVideoDevice::SetFrameRate(unsigned rate)
673 if (rate < 1) {
674 frameRate = 0;
675 return TRUE;
678 frameRate = rate;
679 previousFrameTime = PTime();
680 msBetweenFrames = 1000/rate;
681 frameTimeError = 0;
683 return TRUE;
687 BOOL PVideoDevice::GetVFlipState()
689 if (converter != NULL)
690 return converter->GetVFlipState() ^ nativeVerticalFlip;
692 return nativeVerticalFlip;
696 BOOL PVideoDevice::SetVFlipState(BOOL newVFlip)
698 if (newVFlip && converter == NULL)
699 converter = PColourConverter::Create(colourFormat, colourFormat, frameWidth, frameHeight);
701 if (converter != NULL)
702 converter->SetVFlipState(newVFlip ^ nativeVerticalFlip);
704 return TRUE;
708 unsigned PVideoDevice::GetFrameRate() const
710 return frameRate;
714 BOOL PVideoDevice::GetFrameSizeLimits(unsigned & minWidth,
715 unsigned & minHeight,
716 unsigned & maxWidth,
717 unsigned & maxHeight)
719 minWidth = minHeight = 1;
720 maxWidth = maxHeight = UINT_MAX;
721 return FALSE;
725 static struct {
726 unsigned asked_width, asked_height, device_width, device_height;
727 } framesizeTab[] = {
728 { 704, 576, 640, 480 },
729 { 640, 480, 704, 576 },
730 { 640, 480, 352, 288 },
732 { 352, 288, 704, 576 },
733 { 352, 288, 640, 480 },
734 { 352, 288, 352, 240 },
735 { 352, 288, 320, 240 },
736 { 352, 288, 176, 144 },
737 { 352, 288, 1024, 576 }, /* High resolution need to be set at the end */
738 { 352, 288, 1280, 960 },
740 { 352, 240, 352, 288 },
741 { 352, 240, 320, 240 },
742 { 352, 240, 640, 480 },
744 { 320, 240, 352, 288 },
745 { 320, 240, 352, 240 },
746 { 320, 240, 640, 480 },
748 { 176, 144, 352, 288 },
749 { 176, 144, 352, 240 },
750 { 176, 144, 320, 240 },
751 { 176, 144, 176, 120 },
752 { 176, 144, 160, 120 },
753 { 176, 144, 640, 480 },
754 { 176, 144, 1024, 576 },
755 { 176, 144, 1280, 960 }, /* High resolution need to be set at the end */
757 { 176, 120, 352, 288 },
758 { 176, 120, 352, 240 },
759 { 176, 120, 320, 240 },
760 { 176, 120, 176, 144 },
761 { 176, 120, 160, 120 },
762 { 176, 120, 640, 480 },
764 { 160, 120, 352, 288 },
765 { 160, 120, 352, 240 },
766 { 160, 120, 320, 240 },
767 { 160, 120, 176, 144 },
768 { 160, 120, 176, 120 },
769 { 160, 120, 640, 480 },
772 BOOL PVideoDevice::SetFrameSizeConverter(unsigned width, unsigned height,
773 BOOL bScaleNotCrop)
775 if (SetFrameSize(width, height)) {
776 if (nativeVerticalFlip && converter == NULL) {
777 converter = PColourConverter::Create(colourFormat, colourFormat, frameWidth, frameHeight);
778 if (PAssertNULL(converter) == NULL)
779 return FALSE;
781 if (converter != NULL) {
782 converter->SetFrameSize(frameWidth, frameHeight);
783 converter->SetVFlipState(nativeVerticalFlip);
785 return TRUE;
788 if (converter == NULL) {
789 converter = PColourConverter::Create(colourFormat, colourFormat, frameWidth, frameHeight);
790 if (converter == NULL) {
791 PTRACE(1, "PVidDev\tSetFrameSizeConverter Colour converter creation failed");
792 return FALSE;
796 PTRACE(3,"PVidDev\tColour converter used for " << width << 'x' << height);
798 unsigned minWidth, minHeight, maxWidth, maxHeight;
799 BOOL limits = GetFrameSizeLimits(minWidth, minHeight, maxWidth, maxHeight);
801 for (PINDEX i = 0; i < PARRAYSIZE(framesizeTab); i++) {
802 if (framesizeTab[i].asked_width == width &&
803 framesizeTab[i].asked_height == height &&
804 (!limits ||
805 (framesizeTab[i].device_width >= minWidth &&
806 framesizeTab[i].device_width <= maxWidth &&
807 framesizeTab[i].device_height >= minHeight &&
808 framesizeTab[i].device_height <= maxHeight)) &&
809 SetFrameSize(framesizeTab[i].device_width,
810 framesizeTab[i].device_height)) {
811 if (CanCaptureVideo() ?
812 converter->SetDstFrameSize(width, height, bScaleNotCrop)
814 converter->SetSrcFrameSize(width, height) &&
815 converter->SetDstFrameSize(framesizeTab[i].device_width,
816 framesizeTab[i].device_height,
817 bScaleNotCrop)) {
818 PTRACE(4,"PVideoDevice\tSetFrameSizeConverter succeeded for converting from "
819 << framesizeTab[i].device_width << 'x' << framesizeTab[i].device_height
820 << " to " << width << 'x' << height);
822 converter->SetVFlipState(converter->GetVFlipState() ^ nativeVerticalFlip);
823 return TRUE;
828 if (CanCaptureVideo()) {
829 // Failed to find a resolution the device can do so far, so try
830 // using the maximum width and height it claims it can do.
831 if (limits &&
832 SetFrameSize(maxWidth, maxHeight)) {
833 if (converter->SetDstFrameSize(width, height, bScaleNotCrop)) {
834 PTRACE(4,"PVideoDevice\tSetFrameSizeConverter succeeded for converting from "
835 << maxWidth << 'x' << maxHeight
836 << " to " << width << 'x' << height);
838 converter->SetVFlipState(converter->GetVFlipState() ^ nativeVerticalFlip);
839 return TRUE;
844 PTRACE(2,"PVideoDevice\tSetFrameSizeConverter failed for " << width << 'x' << height);
846 return FALSE;
850 BOOL PVideoDevice::SetFrameSize(unsigned width, unsigned height)
852 #if PTRACING
853 unsigned oldWidth = frameWidth;
854 unsigned oldHeight = frameHeight;
855 #endif
857 unsigned minWidth, minHeight, maxWidth, maxHeight;
858 GetFrameSizeLimits(minWidth, minHeight, maxWidth, maxHeight);
860 if (width < minWidth)
861 frameWidth = minWidth;
862 else if (width > maxWidth)
863 frameWidth = maxWidth;
864 else
865 frameWidth = width;
867 if (height < minHeight)
868 frameHeight = minHeight;
869 else if (height > maxHeight)
870 frameHeight = maxHeight;
871 else
872 frameHeight = height;
874 if (converter != NULL) {
875 if (!converter->SetSrcFrameSize(width, height) ||
876 !converter->SetDstFrameSize(width, height, FALSE)) {
877 PTRACE(1, "PVidDev\tSetFrameSize with converter failed with " << width << 'x' << height);
878 return FALSE;
882 PTRACE_IF(2, oldWidth != frameWidth || oldHeight != frameHeight,
883 "PVidDev\tSetFrameSize to " << frameWidth << 'x' << frameHeight);
884 return TRUE;
888 BOOL PVideoDevice::GetFrameSize(unsigned & width, unsigned & height)
890 #if 1
891 // Channels get very upset at this not returning the output size.
892 if (converter)
893 return converter->GetDstFrameSize(width, height);
894 #endif
895 width = frameWidth;
896 height = frameHeight;
897 return TRUE;
901 unsigned PVideoDevice::GetFrameWidth() const
903 #if 1
904 unsigned w,h;
906 // Channels get very upset at this not returning the output size.
907 if (converter) {
908 converter->GetDstFrameSize(w, h);
909 return w;
911 #endif
912 return frameWidth;
916 unsigned PVideoDevice::GetFrameHeight() const
918 #if 1
919 unsigned w,h;
921 // Channels get very upset at this not returning the output size.
922 if (converter) {
923 converter->GetDstFrameSize(w, h);
924 return h;
926 #endif
927 return frameHeight;
931 unsigned PVideoDevice::CalculateFrameBytes(unsigned width, unsigned height,
932 const PString & colourFormat)
934 for (PINDEX i = 0; i < PARRAYSIZE(colourFormatBPPTab); i++) {
935 if (colourFormat *= colourFormatBPPTab[i].colourFormat)
936 return width * height * colourFormatBPPTab[i].bitsPerPixel/8;
938 return 0;
942 PINDEX PVideoDevice::GetMaxFrameBytesConverted(PINDEX rawFrameBytes) const
944 if (converter == NULL)
945 return rawFrameBytes;
947 PINDEX srcFrameBytes = converter->GetMaxSrcFrameBytes();
948 PINDEX dstFrameBytes = converter->GetMaxDstFrameBytes();
949 PINDEX convertedFrameBytes = PMAX(srcFrameBytes, dstFrameBytes);
950 return PMAX(rawFrameBytes, convertedFrameBytes);
954 int PVideoDevice::GetBrightness()
956 return frameBrightness;
960 BOOL PVideoDevice::SetBrightness(unsigned newBrightness)
962 frameBrightness = newBrightness;
963 return TRUE;
967 int PVideoDevice::GetWhiteness()
969 return frameWhiteness;
973 BOOL PVideoDevice::SetWhiteness(unsigned newWhiteness)
975 frameWhiteness = newWhiteness;
976 return TRUE;
980 int PVideoDevice::GetColour()
982 return frameColour;
986 BOOL PVideoDevice::SetColour(unsigned newColour)
988 frameColour=newColour;
989 return TRUE;
993 int PVideoDevice::GetContrast()
995 return frameContrast;
999 BOOL PVideoDevice::SetContrast(unsigned newContrast)
1001 frameContrast=newContrast;
1002 return TRUE;
1006 int PVideoDevice::GetHue()
1008 return frameHue;
1012 BOOL PVideoDevice::SetHue(unsigned newHue)
1014 frameHue=newHue;
1015 return TRUE;
1019 BOOL PVideoDevice::GetParameters (int *whiteness,
1020 int *brightness,
1021 int *colour,
1022 int *contrast,
1023 int *hue)
1025 if (!IsOpen())
1026 return FALSE;
1028 *brightness = frameBrightness;
1029 *colour = frameColour;
1030 *contrast = frameContrast;
1031 *hue = frameHue;
1032 *whiteness = frameWhiteness;
1034 return TRUE;
1037 BOOL PVideoDevice::SetVideoChannelFormat (int newNumber, VideoFormat newFormat)
1039 BOOL err1, err2;
1041 err1 = SetChannel (newNumber);
1042 err2 = SetVideoFormat (newFormat);
1044 return (err1 && err2);
1047 PStringList PVideoDevice::GetDeviceNames() const
1049 return PStringList();
1053 ///////////////////////////////////////////////////////////////////////////////
1054 // PVideoOutputDevice
1056 PVideoOutputDevice::PVideoOutputDevice()
1061 BOOL PVideoOutputDevice::CanCaptureVideo() const
1063 return FALSE;
1067 ///////////////////////////////////////////////////////////////////////////////
1068 // PVideoOutputDeviceRGB
1070 PVideoOutputDeviceRGB::PVideoOutputDeviceRGB()
1072 PTRACE(6, "RGB\t Constructor of PVideoOutputDeviceRGB");
1074 colourFormat = "RGB24";
1075 bytesPerPixel = 3;
1076 swappedRedAndBlue = false;
1077 SetFrameSize(frameWidth, frameHeight);
1081 BOOL PVideoOutputDeviceRGB::SetColourFormat(const PString & colourFormat)
1083 PWaitAndSignal m(mutex);
1085 PINDEX newBytesPerPixel;
1086 bool newSwappedRedAndBlue;
1087 if (colourFormat *= "RGB32") {
1088 newBytesPerPixel = 4;
1089 newSwappedRedAndBlue = false;
1091 else if (colourFormat *= "RGB24") {
1092 newBytesPerPixel = 3;
1093 newSwappedRedAndBlue = false;
1095 else if (colourFormat *= "BGR32") {
1096 newBytesPerPixel = 4;
1097 newSwappedRedAndBlue = true;
1099 else if (colourFormat *= "BGR24") {
1100 newBytesPerPixel = 3;
1101 newSwappedRedAndBlue = true;
1103 else
1104 return FALSE;
1106 if (!PVideoOutputDevice::SetColourFormat(colourFormat))
1107 return FALSE;
1109 bytesPerPixel = newBytesPerPixel;
1110 scanLineWidth = ((frameWidth*bytesPerPixel+3)/4)*4;
1111 return frameStore.SetSize(frameHeight*scanLineWidth);
1115 BOOL PVideoOutputDeviceRGB::SetFrameSize(unsigned width, unsigned height)
1117 PWaitAndSignal m(mutex);
1119 if (!PVideoOutputDevice::SetFrameSize(width, height))
1120 return FALSE;
1122 scanLineWidth = ((frameWidth*bytesPerPixel+3)/4)*4;
1123 return frameStore.SetSize(frameHeight*scanLineWidth);
1127 PINDEX PVideoOutputDeviceRGB::GetMaxFrameBytes()
1129 PWaitAndSignal m(mutex);
1130 return GetMaxFrameBytesConverted(frameStore.GetSize());
1134 BOOL PVideoOutputDeviceRGB::SetFrameData(unsigned x, unsigned y,
1135 unsigned width, unsigned height,
1136 const BYTE * data,
1137 BOOL endFrame)
1139 PWaitAndSignal m(mutex);
1141 if (x+width > frameWidth || y+height > frameHeight)
1142 return FALSE;
1144 if (x == 0 && width == frameWidth && y == 0 && height == frameHeight) {
1145 if (converter != NULL)
1146 converter->Convert(data, frameStore.GetPointer());
1147 else
1148 memcpy(frameStore.GetPointer(), data, height*scanLineWidth);
1150 else {
1151 if (converter != NULL) {
1152 PAssertAlways("Converted output of partial RGB frame not supported");
1153 return FALSE;
1156 if (x == 0 && width == frameWidth)
1157 memcpy(frameStore.GetPointer() + y*scanLineWidth, data, height*scanLineWidth);
1158 else {
1159 for (unsigned dy = 0; dy < height; dy++)
1160 memcpy(frameStore.GetPointer() + (y+dy)*scanLineWidth + x*bytesPerPixel,
1161 data + dy*width*bytesPerPixel, width*bytesPerPixel);
1165 if (endFrame)
1166 return FrameComplete();
1168 return TRUE;
1172 ///////////////////////////////////////////////////////////////////////////////
1173 // PVideoOutputDevicePPM
1175 #ifdef SHOULD_BE_MOVED_TO_PLUGIN
1177 PVideoOutputDevicePPM::PVideoOutputDevicePPM()
1179 PTRACE(6, "PPM\t Constructor of PVideoOutputDevicePPM");
1180 frameNumber = 0;
1184 BOOL PVideoOutputDevicePPM::Open(const PString & name,
1185 BOOL /*startImmediate*/)
1187 Close();
1189 PFilePath path = name;
1190 if (!PDirectory::Exists(path.GetDirectory()))
1191 return FALSE;
1193 if (path != psprintf(path, 12345))
1194 deviceName = path;
1195 else
1196 deviceName = path.GetDirectory() + path.GetTitle() + "%u" + path.GetType();
1198 return TRUE;
1202 BOOL PVideoOutputDevicePPM::IsOpen()
1204 return !deviceName;
1208 BOOL PVideoOutputDevicePPM::Close()
1210 deviceName.MakeEmpty();
1211 return TRUE;
1215 PStringList PVideoOutputDevicePPM::GetDeviceNames() const
1217 PStringList list;
1218 list += PDirectory();
1219 return list;
1223 BOOL PVideoOutputDevicePPM::EndFrame()
1225 PFile file;
1226 if (!file.Open(psprintf(deviceName, frameNumber++), PFile::WriteOnly)) {
1227 PTRACE(1, "PPMVid\tFailed to open PPM output file \""
1228 << file.GetName() << "\": " << file.GetErrorText());
1229 return FALSE;
1232 file << "P6 " << frameWidth << " " << frameHeight << " " << 255 << "\n";
1234 if (!file.Write(frameStore, frameStore.GetSize())) {
1235 PTRACE(1, "PPMVid\tFailed to write frame data to PPM output file " << file.GetName());
1236 return FALSE;
1239 PTRACE(6, "PPMVid\tFinished writing PPM file " << file.GetName());
1240 return file.Close();
1243 #endif // SHOULD_BE_MOVED_TO_PLUGIN
1246 ///////////////////////////////////////////////////////////////////////////////
1247 // PVideoInputDevice
1249 BOOL PVideoInputDevice::CanCaptureVideo() const
1251 return TRUE;
1254 static const char videoInputPluginBaseClass[] = "PVideoInputDevice";
1257 PStringList PVideoInputDevice::GetDriverNames(PPluginManager * pluginMgr)
1259 if (pluginMgr == NULL)
1260 pluginMgr = &PPluginManager::GetPluginManager();
1262 return pluginMgr->GetPluginsProviding(videoInputPluginBaseClass);
1266 PStringList PVideoInputDevice::GetDriversDeviceNames(const PString & driverName, PPluginManager * pluginMgr)
1268 if (pluginMgr == NULL)
1269 pluginMgr = &PPluginManager::GetPluginManager();
1271 return pluginMgr->GetPluginsDeviceNames(driverName, videoInputPluginBaseClass);
1275 PVideoInputDevice * PVideoInputDevice::CreateDevice(const PString &driverName, PPluginManager * pluginMgr)
1277 if (pluginMgr == NULL)
1278 pluginMgr = &PPluginManager::GetPluginManager();
1280 return (PVideoInputDevice *)pluginMgr->CreatePluginsDevice(driverName, videoInputPluginBaseClass);
1284 PVideoInputDevice * PVideoInputDevice::CreateDeviceByName(const PString & deviceName, const PString & driverName, PPluginManager * pluginMgr)
1286 if (pluginMgr == NULL)
1287 pluginMgr = &PPluginManager::GetPluginManager();
1289 return (PVideoInputDevice *)pluginMgr->CreatePluginsDeviceByName(deviceName, videoInputPluginBaseClass,0,driverName);
1293 PVideoInputDevice * PVideoInputDevice::CreateOpenedDevice(const PString & driverName,
1294 const PString & deviceName,
1295 BOOL startImmediate,
1296 PPluginManager * pluginMgr)
1298 PVideoInputDevice * device = CreateDeviceByName(deviceName, driverName, pluginMgr);
1300 if (device != NULL && device->Open(deviceName, startImmediate))
1301 return device;
1303 delete device;
1304 return NULL;
1308 PVideoInputDevice * PVideoInputDevice::CreateOpenedDevice(const OpenArgs & args,
1309 BOOL startImmediate)
1311 PVideoInputDevice * device = CreateDeviceByName(args.deviceName, args.driverName, args.pluginMgr);
1313 if (device != NULL && device->OpenFull(args, startImmediate))
1314 return device;
1316 delete device;
1317 return NULL;
1321 BOOL PVideoInputDevice::GetFrame(PBYTEArray & frame)
1323 PINDEX returned;
1324 if (!GetFrameData(frame.GetPointer(GetMaxFrameBytes()), &returned))
1325 return FALSE;
1327 frame.SetSize(returned);
1328 return TRUE;
1332 ////////////////////////////////////////////////////////////////////////////////////////////
1334 static const char videoOutputPluginBaseClass[] = "PVideoOutputDevice";
1337 PStringList PVideoOutputDevice::GetDriverNames(PPluginManager * pluginMgr)
1339 if (pluginMgr == NULL)
1340 pluginMgr = &PPluginManager::GetPluginManager();
1342 return pluginMgr->GetPluginsProviding(videoOutputPluginBaseClass);
1346 PStringList PVideoOutputDevice::GetDriversDeviceNames(const PString & driverName, PPluginManager * pluginMgr)
1348 if (pluginMgr == NULL)
1349 pluginMgr = &PPluginManager::GetPluginManager();
1351 return pluginMgr->GetPluginsDeviceNames(driverName, videoOutputPluginBaseClass);
1355 PVideoOutputDevice * PVideoOutputDevice::CreateDevice(const PString & driverName, PPluginManager * pluginMgr)
1357 if (pluginMgr == NULL)
1358 pluginMgr = &PPluginManager::GetPluginManager();
1360 return (PVideoOutputDevice *)pluginMgr->CreatePluginsDevice(driverName, videoOutputPluginBaseClass);
1364 PVideoOutputDevice * PVideoOutputDevice::CreateDeviceByName(const PString & deviceName, const PString & driverName, PPluginManager * pluginMgr)
1366 if (pluginMgr == NULL)
1367 pluginMgr = &PPluginManager::GetPluginManager();
1369 return (PVideoOutputDevice *)pluginMgr->CreatePluginsDeviceByName(deviceName, videoOutputPluginBaseClass, 0, driverName);
1373 PVideoOutputDevice * PVideoOutputDevice::CreateOpenedDevice(const PString &driverName,
1374 const PString &deviceName,
1375 BOOL startImmediate,
1376 PPluginManager * pluginMgr)
1378 PVideoOutputDevice * device;
1379 if (driverName.IsEmpty() || driverName == "*")
1380 device = CreateDeviceByName(deviceName, driverName, pluginMgr);
1381 else
1382 device = CreateDevice(driverName, pluginMgr);
1384 if (device != NULL && device->Open(deviceName, startImmediate))
1385 return device;
1387 delete device;
1388 return NULL;
1392 PVideoOutputDevice * PVideoOutputDevice::CreateOpenedDevice(const OpenArgs & args,
1393 BOOL startImmediate)
1395 PVideoOutputDevice * device;
1396 if (args.driverName.IsEmpty() || args.driverName == "*")
1397 device = CreateDeviceByName(args.deviceName, args.driverName, args.pluginMgr);
1398 else
1399 device = CreateDevice(args.driverName, args.pluginMgr);
1401 if (device != NULL && device->OpenFull(args, startImmediate))
1402 return device;
1404 delete device;
1405 return NULL;
1409 // End Of File ///////////////////////////////////////////////////////////////