4 * Video file declaration
6 * Portable Windows Library
8 * Copyright (C) 2004 Post Increment
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
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is
23 * Craig Southeren <craigs@postincrement.com>
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
30 * Revision 1.12 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.11 2007/04/02 05:29:54 rjongbloed
41 * Tidied some trace logs to assure all have a category (bit before a tab character) set.
43 * Revision 1.10 2006/11/01 00:46:01 csoutheren
44 * Fix problem in video output file device
46 * Revision 1.9 2006/10/31 04:10:40 csoutheren
47 * Make sure PVidFileDev class is loaded, and make it work with OPAL
49 * Revision 1.8 2006/06/21 03:28:44 csoutheren
50 * Various cleanups thanks for Frederic Heem
52 * Revision 1.7 2006/04/19 04:09:04 csoutheren
53 * Allow frame size conversions
55 * Revision 1.6 2006/03/17 06:55:33 csoutheren
56 * Removed unused member variable
58 * Revision 1.5 2006/03/06 06:04:13 csoutheren
59 * Added YUVFile video output device
61 * Revision 1.4 2006/02/24 04:51:26 csoutheren
62 * Fixed problem with using CIF from video files
63 * Added support for video files in y4m format
65 * Revision 1.3 2006/02/20 09:31:58 csoutheren
66 * Fixed link problem on Linux
68 * Revision 1.2 2006/02/20 06:49:45 csoutheren
69 * Added video file and video file input device code
71 * Revision 1.1 2006/02/20 06:17:28 csoutheren
72 * Added ability to read video from a file
77 #pragma implementation "pvfiledev.h"
80 #define P_FORCE_STATIC_PLUGIN
86 namespace PWLibStupidHacks
{
87 int loadVideoFileStuff
;
90 #include <ptlib/vconvert.h>
91 #include <ptclib/pvfiledev.h>
92 #include <ptlib/pfactory.h>
93 #include <ptlib/pluginmgr.h>
94 #include <ptlib/videoio.h>
96 ///////////////////////////////////////////////////////////////////////////////
97 // PVideoInputDevice_YUVFile
99 PINSTANTIATE_FACTORY(PVideoInputDevice
, YUVFile
)
101 class PVideoInputDevice_YUVFile_PluginServiceDescriptor
: public PDevicePluginServiceDescriptor
104 virtual PObject
* CreateInstance(int /*userData*/) const { return new PVideoInputDevice_YUVFile
; }
105 virtual PStringList
GetDeviceNames(int /*userData*/) const { return PVideoInputDevice_YUVFile::GetInputDeviceNames(); }
106 virtual bool ValidateDeviceName(const PString
& deviceName
, int /*userData*/) const { return PFile::Access(deviceName
, PFile::ReadOnly
); }
107 } PVideoInputDevice_YUVFile_descriptor
;
109 PCREATE_PLUGIN(YUVFile
, PVideoInputDevice
, &PVideoInputDevice_YUVFile_descriptor
);
113 PVideoInputDevice_YUVFile::PVideoInputDevice_YUVFile()
115 SetColourFormat("YUV420P");
122 BOOL
PVideoInputDevice_YUVFile::Open(const PString
& devName
, BOOL
/*startImmediate*/)
124 PFilePath
fn(devName
);
126 if (!file
.Open(fn
, PFile::ReadOnly
, PFile::MustExist
))
129 deviceName
= fn
.GetTitle();
135 BOOL
PVideoInputDevice_YUVFile::IsOpen()
137 return file
.IsOpen();
141 BOOL
PVideoInputDevice_YUVFile::Close()
147 BOOL
PVideoInputDevice_YUVFile::Start()
153 BOOL
PVideoInputDevice_YUVFile::Stop()
159 BOOL
PVideoInputDevice_YUVFile::IsCapturing()
165 PStringList
PVideoInputDevice_YUVFile::GetInputDeviceNames()
168 list
.AppendString("*.yuv");
173 BOOL
PVideoInputDevice_YUVFile::SetVideoFormat(VideoFormat newFormat
)
175 return PVideoDevice::SetVideoFormat(newFormat
);
179 int PVideoInputDevice_YUVFile::GetNumChannels()
185 BOOL
PVideoInputDevice_YUVFile::SetChannel(int newChannel
)
187 return PVideoDevice::SetChannel(newChannel
);
190 BOOL
PVideoInputDevice_YUVFile::SetColourFormat(const PString
& newFormat
)
192 if (!(newFormat
*= "YUV420P"))
195 return PVideoDevice::SetColourFormat(newFormat
);
199 BOOL
PVideoInputDevice_YUVFile::SetFrameRate(unsigned rate
)
206 return PVideoDevice::SetFrameRate(rate
);
210 BOOL
PVideoInputDevice_YUVFile::GetFrameSizeLimits(unsigned & minWidth
,
211 unsigned & minHeight
,
213 unsigned & maxHeight
)
215 if (file
.GetWidth() != 0 && file
.GetHeight() != 0) {
216 minWidth
= maxWidth
= file
.GetWidth();
217 minHeight
= maxHeight
= file
.GetHeight();
230 BOOL
PVideoInputDevice_YUVFile::SetFrameSizeConverter(
231 unsigned width
, ///< New width of frame
232 unsigned height
, ///< New height of frame
233 BOOL bScaleNotCrop
///< Scale or crop/pad preference
236 // if the file does not know what size it is, then set it
237 if (file
.GetWidth() == 0 && file
.GetHeight() == 0) {
238 file
.SetWidth(width
);
239 file
.SetHeight(height
);
242 return PVideoInputDevice::SetFrameSizeConverter(width
, height
, bScaleNotCrop
);
245 BOOL
PVideoInputDevice_YUVFile::SetFrameSize(unsigned width
, unsigned height
)
247 // if the file does not know what size it is, then set it
248 if (file
.GetWidth() == 0 && file
.GetHeight() == 0) {
249 file
.SetWidth(width
);
250 file
.SetHeight(height
);
253 if (width
!= (unsigned)file
.GetWidth() || height
!= (unsigned)file
.GetHeight())
257 frameHeight
= height
;
259 videoFrameSize
= CalculateFrameBytes(frameWidth
, frameHeight
, colourFormat
);
260 scanLineWidth
= videoFrameSize
/frameHeight
;
261 return videoFrameSize
> 0;
265 PINDEX
PVideoInputDevice_YUVFile::GetMaxFrameBytes()
267 return GetMaxFrameBytesConverted(videoFrameSize
);
271 BOOL
PVideoInputDevice_YUVFile::GetFrameData(BYTE
* buffer
, PINDEX
* bytesReturned
)
273 frameTimeError
+= msBetweenFrames
;
276 PTimeInterval delay
= now
- previousFrameTime
;
277 frameTimeError
-= (int)delay
.GetMilliSeconds();
278 previousFrameTime
= now
;
280 if (frameTimeError
> 0) {
281 PTRACE(6, "YUVFile\tSleep for " << frameTimeError
<< " milli seconds");
282 PThread::Sleep(frameTimeError
);
285 return GetFrameDataNoDelay(buffer
, bytesReturned
);
289 BOOL
PVideoInputDevice_YUVFile::GetFrameDataNoDelay(BYTE
*destFrame
, PINDEX
* bytesReturned
)
293 BYTE
* readBuffer
= destFrame
;
295 if (converter
!= NULL
)
296 readBuffer
= frameStore
.GetPointer(videoFrameSize
);
299 if (!file
.ReadFrame(readBuffer
))
303 if (!file
.IsOpen()) {
304 switch (channelNumber
) {
305 case Channel_PlayAndClose
:
309 case Channel_PlayAndRepeat
:
310 if (!file
.Open() || !file
.ReadFrame(readBuffer
))
314 case Channel_PlayAndKeepLast
:
317 case Channel_PlayAndShowBlack
:
318 FillRect(readBuffer
, 0, 0, frameWidth
, frameHeight
, 0, 0, 0);
323 if (converter
== NULL
) {
324 if (bytesReturned
!= NULL
)
325 *bytesReturned
= videoFrameSize
;
327 converter
->SetSrcFrameSize(frameWidth
, frameHeight
);
328 if (!converter
->Convert(readBuffer
, destFrame
, bytesReturned
))
330 if (bytesReturned
!= NULL
)
331 *bytesReturned
= converter
->GetMaxDstFrameBytes();
338 void PVideoInputDevice_YUVFile::GrabBlankImage(BYTE
*resFrame
)
340 // Change colour every second, cycle is:
341 // black, red, green, yellow, blue, magenta, cyan, white
342 int mask
= grabCount
/frameRate
;
344 0, 0, frameWidth
, frameHeight
, //Fill the whole frame with the colour.
345 (mask
&1) ? 255 : 0, // red
346 (mask
&2) ? 255 : 0, // green
347 (mask
&4) ? 255 : 0);//blue
350 void PVideoInputDevice_YUVFile::FillRect(BYTE
* frame
,
351 int xPos
, int initialYPos
,
352 int rectWidth
, int rectHeight
,
355 // PTRACE(0,"x,y is"<<xPos<<" "<<yPos<<" and size is "<<rectWidth<<" "<<rectHeight);
357 //This routine fills a region of the video image with data. It is used as the central
358 //point because one only has to add other image formats here.
360 int yPos
= initialYPos
;
362 int offset
= ( yPos
* frameWidth
) + xPos
;
363 int colourOffset
= ( (yPos
* frameWidth
) >> 2) + (xPos
>> 1);
365 int Y
= ( 257 * r
+ 504 * g
+ 98 * b
)/1000 + 16;
366 int Cb
= (-148 * r
- 291 * g
+ 439 * b
)/1000 + 128;
367 int Cr
= ( 439 * r
- 368 * g
- 71 * b
)/1000 + 128;
369 unsigned char * Yptr
= frame
+ offset
;
370 unsigned char * CbPtr
= frame
+ (frameWidth
* frameHeight
) + colourOffset
;
371 unsigned char * CrPtr
= frame
+ (frameWidth
* frameHeight
) + (frameWidth
* frameHeight
/4) + colourOffset
;
374 int halfRectWidth
= rectWidth
>> 1;
375 int halfWidth
= frameWidth
>> 1;
377 for (rr
= 0; rr
< rectHeight
;rr
+=2) {
378 memset(Yptr
, Y
, rectWidth
);
380 memset(Yptr
, Y
, rectWidth
);
383 memset(CbPtr
, Cb
, halfRectWidth
);
384 memset(CrPtr
, Cr
, halfRectWidth
);
391 ///////////////////////////////////////////////////////////////////////////////
392 // PVideoOutputDevice_YUVFile
394 PINSTANTIATE_FACTORY(PVideoOutputDevice
, YUVFile
)
396 class PVideoOutputDevice_YUVFile_PluginServiceDescriptor
: public PDevicePluginServiceDescriptor
399 virtual PObject
* CreateInstance(int /*userData*/) const { return new PVideoOutputDevice_YUVFile
; }
400 virtual PStringList
GetDeviceNames(int /*userData*/) const { return PVideoOutputDevice_YUVFile::GetOutputDeviceNames(); }
401 virtual bool ValidateDeviceName(const PString
& deviceName
, int /*userData*/) const { return PFile::Access(deviceName
, PFile::WriteOnly
); }
402 } PVideoOutputDevice_YUVFile_descriptor
;
404 PCREATE_PLUGIN(YUVFile
, PVideoOutputDevice
, &PVideoOutputDevice_YUVFile_descriptor
);
407 PVideoOutputDevice_YUVFile::PVideoOutputDevice_YUVFile()
412 BOOL
PVideoOutputDevice_YUVFile::Open(const PString
& _deviceName
, BOOL
/*startImmediate*/)
414 deviceName
= _deviceName
;
415 if (!file
.Open(deviceName
, PFile::WriteOnly
, PFile::Create
|PFile::Truncate
)) {
416 PTRACE(1, "YUVFile\tCannot create file " << deviceName
<< " as video output device");
423 BOOL
PVideoOutputDevice_YUVFile::Close()
428 BOOL
PVideoOutputDevice_YUVFile::Start()
430 file
.SetHeight(frameHeight
);
431 file
.SetWidth(frameWidth
);
435 BOOL
PVideoOutputDevice_YUVFile::Stop()
440 BOOL
PVideoOutputDevice_YUVFile::IsOpen()
442 return file
.IsOpen();
446 PStringList
PVideoOutputDevice_YUVFile::GetOutputDeviceNames()
449 list
.AppendString("*.yuv");
454 BOOL
PVideoOutputDevice_YUVFile::SetColourFormat(const PString
& newFormat
)
456 if (!(newFormat
*= "YUV420P"))
459 return PVideoDevice::SetColourFormat(newFormat
);
463 PINDEX
PVideoOutputDevice_YUVFile::GetMaxFrameBytes()
465 return GetMaxFrameBytesConverted(CalculateFrameBytes(frameWidth
, frameHeight
, colourFormat
));
469 BOOL
PVideoOutputDevice_YUVFile::SetFrameData(unsigned x
, unsigned y
,
470 unsigned width
, unsigned height
,
474 if (x
!= 0 || y
!= 0 || width
!= frameWidth
|| height
!= frameHeight
) {
475 PTRACE(1, "YUVFile\tOutput device only supports full frame writes");
479 if ((file
.GetWidth() == 0) && (file
.GetHeight() == 0)) {
480 file
.SetWidth(width
);
481 file
.SetHeight(height
);
483 else if (((unsigned)file
.GetWidth() != width
) || ((unsigned)file
.GetHeight() != height
))
486 if (converter
== NULL
)
487 return file
.WriteFrame(data
);
489 PBYTEArray tempStore
;
490 converter
->Convert(data
, tempStore
.GetPointer(GetMaxFrameBytes()));
491 return file
.WriteFrame(tempStore
);
495 BOOL
PVideoOutputDevice_YUVFile::EndFrame()