📄 vidinput_v4l.cxx
字号:
tempList.SetDataAt(j, revisedUserName);
}
}
}
//At this stage, we have correctly modified the temp list of names.
for (j = 0; j < tempList.GetSize(); j++)
AddUserDeviceName(tempList.GetDataAt(j), tempList.GetKeyAt(j));
}
PString V4LNames::GetUserFriendly(PString devName)
{
PWaitAndSignal m(mutex);
PString result= deviceKey(devName);
if (result.IsEmpty())
return devName;
return result;
}
PString V4LNames::GetDeviceName(PString userName)
{
PWaitAndSignal m(mutex);
for (PINDEX i = 0; i < userKey.GetSize(); i++)
if (userKey.GetKeyAt(i).Find(userName) != P_MAX_INDEX)
return userKey.GetDataAt(i);
return userName;
}
void V4LNames::AddUserDeviceName(PString userName, PString devName)
{
if (userName != devName) { // must be a real userName!
userKey.SetAt(userName, devName);
deviceKey.SetAt(devName, userName);
} else { // we didn't find a good userName
if (!deviceKey.Contains (devName)) { // never met before: fallback
userKey.SetAt(userName, devName);
deviceKey.SetAt(devName, userName);
} // no else: we already know the pair
}
}
PString V4LNames::BuildUserFriendly(PString devname)
{
PString Result;
int fd = ::open((const char *)devname, O_RDONLY);
if(fd < 0) {
return devname;
}
struct video_capability videocap;
if (::ioctl(fd, VIDIOCGCAP, &videocap) < 0) {
::close(fd);
return devname;
}
::close(fd);
PString ufname(videocap.name);
return ufname;
}
/*
There is a duplication in the list of names.
Consequently, opening the device as "ov511++" or "/dev/video0" will work.
*/
PStringList V4LNames::GetInputDeviceNames()
{
PWaitAndSignal m(mutex);
PStringList result;
for (PINDEX i = 0; i < inputDeviceNames.GetSize(); i++) {
result += GetUserFriendly (inputDeviceNames[i]);
//result += inputDeviceNames[i];
}
return result;
}
static
V4LNames & GetNames()
{
static V4LNames names;
names.Update();
return names;
}
///////////////////////////////////////////////////////////////////////////////
// PVideoInputDevice_V4L
PVideoInputDevice_V4L::PVideoInputDevice_V4L()
{
videoFd = -1;
hint_index = PARRAYSIZE(driver_hints) - 1;
canMap = -1;
for (int i=0; i<2; i++)
pendingSync[i] = FALSE;
}
PVideoInputDevice_V4L::~PVideoInputDevice_V4L()
{
Close();
}
static struct {
const char * colourFormat;
int code;
} colourFormatTab[] = {
{ "Grey", VIDEO_PALETTE_GREY }, //Entries in this table correspond
{ "BGR32", VIDEO_PALETTE_RGB32 }, //(line by line) to those in the
{ "BGR24", VIDEO_PALETTE_RGB24 }, // PVideoDevice ColourFormat table.
{ "RGB565", VIDEO_PALETTE_RGB565 },
{ "RGB555", VIDEO_PALETTE_RGB555 },
{ "YUV422", VIDEO_PALETTE_YUV422 },
{ "YUV422P", VIDEO_PALETTE_YUV422P },
{ "YUV411", VIDEO_PALETTE_YUV411 },
{ "YUV411P", VIDEO_PALETTE_YUV411P },
{ "YUV420", VIDEO_PALETTE_YUV420 },
{ "YUV420P", VIDEO_PALETTE_YUV420P },
{ "YUV410P", VIDEO_PALETTE_YUV410P },
{ "UYVY422", VIDEO_PALETTE_UYVY }
};
BOOL PVideoInputDevice_V4L::Open(const PString & devName, BOOL startImmediate)
{
struct utsname buf;
PString version;
uname (&buf);
if (buf.release)
version = PString (buf.release);
Close();
PTRACE(1,"PVideoInputDevice_V4L: trying to open "<< devName);
// check if it is a userfriendly name, and if so, get the real device name
PString deviceName = GetNames().GetDeviceName(devName);
videoFd = ::open((const char *)deviceName, O_RDWR);
if (videoFd < 0) {
PTRACE(1,"PVideoInputDevice_V4L::Open failed : "<< ::strerror(errno));
return FALSE;
}
// get the device capabilities
if (::ioctl(videoFd, VIDIOCGCAP, &videoCapability) < 0) {
PTRACE(1,"PVideoInputDevice_V4L:: get device capablilities failed : "<< ::strerror(errno));
::close (videoFd);
videoFd = -1;
return FALSE;
}
if ((videoCapability.type & VID_TYPE_CAPTURE) == 0) {
PTRACE(1,"PVideoInputDevice_V4L:: device capablilities reports cannot capture");
::close (videoFd);
videoFd = -1;
return FALSE;
}
hint_index = PARRAYSIZE(driver_hints) - 1;
PString driver_name(videoCapability.name);
// Scan the hint table, looking for regular expression matches with
// drivers we hold hints for.
PINDEX tbl;
for (tbl = 0; tbl < PARRAYSIZE(driver_hints); tbl ++) {
PRegularExpression regexp;
regexp.Compile(driver_hints[tbl].name_regexp, PRegularExpression::Extended);
if (driver_name.FindRegEx(regexp) != P_MAX_INDEX) {
PTRACE(1,"PVideoInputDevice_V4L::Open: Found driver hints: " << driver_hints[tbl].name);
PTRACE(1,"PVideoInputDevice_V4L::Open: format: " << driver_hints[tbl].pref_palette);
if (driver_hints[tbl].version && !version.IsEmpty ()) {
if (PString (version) < PString (driver_hints[tbl].version)) {
PTRACE(1,"PVideoInputDevice_V4L::Open: Hints applied because kernel version less than " << driver_hints[tbl].version);
hint_index = tbl;
break;
}
else {
PTRACE(1,"PVideoInputDevice_V4L::Open: Hints not applied because kernel version is not less than " << driver_hints[tbl].version);
}
}
else {
hint_index = tbl;
break;
}
}
}
// Force double-buffering with buggy Quickcam driver.
if (HINT (HINT_FORCE_DBLBUF)) {
#define QC_IOCTLBASE 220
#define VIDIOCQCGCOMPATIBLE _IOR ('v',QC_IOCTLBASE+10,int) /* Get enable workaround for bugs, bitfield */
#define VIDIOCQCSCOMPATIBLE _IOWR('v',QC_IOCTLBASE+10,int) /* Set enable workaround for bugs, bitfield */
int reg = 2; /* enable double buffering */
::ioctl (videoFd, VIDIOCQCSCOMPATIBLE, ®);
}
// set height and width
frameHeight = PMIN (videoCapability.maxheight, QCIFHeight);
frameWidth = PMIN (videoCapability.maxwidth, QCIFWidth);
// Init audio
struct video_audio videoAudio;
if (::ioctl(videoFd, VIDIOCGAUDIO, &videoAudio) >= 0 &&
(videoAudio.flags & VIDEO_AUDIO_MUTABLE) != 0) {
videoAudio.flags &= ~VIDEO_AUDIO_MUTE;
videoAudio.mode = VIDEO_SOUND_MONO;
::ioctl(videoFd, VIDIOCSAUDIO, &videoAudio);
}
return TRUE;
}
BOOL PVideoInputDevice_V4L::IsOpen()
{
return videoFd >= 0;
}
BOOL PVideoInputDevice_V4L::Close()
{
if (!IsOpen())
return FALSE;
// Mute audio
struct video_audio videoAudio;
if (::ioctl(videoFd, VIDIOCGAUDIO, &videoAudio) >= 0 &&
(videoAudio.flags & VIDEO_AUDIO_MUTABLE) != 0) {
videoAudio.flags |= VIDEO_AUDIO_MUTE;
::ioctl(videoFd, VIDIOCSAUDIO, &videoAudio);
}
ClearMapping();
::close(videoFd);
videoFd = -1;
canMap = -1;
return TRUE;
}
BOOL PVideoInputDevice_V4L::Start()
{
return TRUE;
}
BOOL PVideoInputDevice_V4L::Stop()
{
return TRUE;
}
BOOL PVideoInputDevice_V4L::IsCapturing()
{
return IsOpen();
}
PStringList PVideoInputDevice_V4L::GetInputDeviceNames()
{
return GetNames().GetInputDeviceNames();
}
BOOL PVideoInputDevice_V4L::SetVideoFormat(VideoFormat newFormat)
{
if (!PVideoDevice::SetVideoFormat(newFormat)) {
PTRACE(1,"PVideoDevice::SetVideoFormat\t failed");
return FALSE;
}
// The channel and format are both set at the same time with one ioctl().
// Get the channel information (to check if channel is valid)
// Note: If the channel is -1, we need to search for the first valid channel
if (channelNumber == -1) {
if (!SetChannel(channelNumber)){
PTRACE(1,"PVideoDevice::Cannot set default channel in SetVideoFormat");
return FALSE;
}
}
struct video_channel channel;
channel.channel = channelNumber;
if (::ioctl(videoFd, VIDIOCGCHAN, &channel) < 0) {
PTRACE(1,"VideoInputDevice Get Channel info failed : "<< ::strerror(errno));
return FALSE;
}
// set channel information
static int fmt[4] = { VIDEO_MODE_PAL, VIDEO_MODE_NTSC,
VIDEO_MODE_SECAM, VIDEO_MODE_AUTO };
channel.norm = fmt[newFormat];
// set the information
if (::ioctl(videoFd, VIDIOCSCHAN, &channel) >= 0)
return TRUE;
PTRACE(1,"VideoInputDevice SetChannel failed : "<< ::strerror(errno));
if (newFormat != Auto)
return FALSE;
if (SetVideoFormat(PAL))
return TRUE;
if (SetVideoFormat(NTSC))
return TRUE;
if (SetVideoFormat(SECAM))
return TRUE;
return FALSE;
}
int PVideoInputDevice_V4L::GetNumChannels()
{
/* If Opened, return the capability value, else 1 as in videoio.cxx */
if (IsOpen ())
return videoCapability.channels;
else
return 1;
}
BOOL PVideoInputDevice_V4L::SetChannel(int newChannel)
{
if (!PVideoDevice::SetChannel(newChannel))
return FALSE;
// get channel information (to check if channel is valid)
struct video_channel channel;
channel.channel = channelNumber;
if (::ioctl(videoFd, VIDIOCGCHAN, &channel) < 0) {
PTRACE(1,"VideoInputDevice:: Get info on channel " << channelNumber << " failed : "<< ::strerror(errno));
return FALSE;
}
// set channel information
channel.channel = channelNumber;
// set the information
if (::ioctl(videoFd, VIDIOCSCHAN, &channel) < 0) {
PTRACE(1,"VideoInputDevice:: Set info on channel " << channelNumber << " failed : "<< ::strerror(errno));
return FALSE;
}
return TRUE;
}
BOOL PVideoInputDevice_V4L::SetVideoChannelFormat (int newNumber, VideoFormat videoFormat)
{
if (!PVideoDevice::SetChannel(newNumber))
return FALSE;
if (!PVideoDevice::SetVideoFormat(videoFormat)) {
PTRACE(1,"PVideoDevice::SetVideoFormat\t failed");
return FALSE;
}
static int fmt[4] = { VIDEO_MODE_PAL, VIDEO_MODE_NTSC,
VIDEO_MODE_SECAM, VIDEO_MODE_AUTO };
// select the specified input and video format
// get channel information (to check if channel is valid)
struct video_channel channel;
channel.channel = channelNumber;
if (::ioctl(videoFd, VIDIOCGCHAN, &channel) < 0) {
PTRACE(1,"VideoInputDevice Get Channel info failed : "<< ::strerror(errno));
return FALSE;
}
// set channel information
channel.norm = fmt[videoFormat];
channel.channel = channelNumber;
// set the information
if (::ioctl(videoFd, VIDIOCSCHAN, &channel) < 0) {
PTRACE(1,"VideoInputDevice SetChannel failed : "<< ::strerror(errno));
return FALSE;
}
return TRUE;
}
BOOL PVideoInputDevice_V4L::SetColourFormat(const PString & newFormat)
{
PINDEX colourFormatIndex = 0;
while (newFormat != colourFormatTab[colourFormatIndex].colourFormat) {
colourFormatIndex++;
if (colourFormatIndex >= PARRAYSIZE(colourFormatTab))
return FALSE;
}
if (!PVideoDevice::SetColourFormat(newFormat))
return FALSE;
ClearMapping();
// get current picture information
struct video_picture pictureInfo;
if (::ioctl(videoFd, VIDIOCGPICT, &pictureInfo) < 0) {
PTRACE(1,"PVideoInputDevice_V4L::Get pict info failed : "<< ::strerror(errno));
return FALSE;
}
// set colour format
colourFormatCode = colourFormatTab[colourFormatIndex].code;
pictureInfo.palette = colourFormatCode;
if (HINT (HINT_FORCE_DEPTH_16))
pictureInfo.depth = 16;
// set the information
if (::ioctl(videoFd, VIDIOCSPICT, &pictureInfo) < 0) {
PTRACE(1,"PVideoInputDevice_V4L::Set pict info failed : "<< ::strerror(errno));
PTRACE(1,"PVideoInputDevice_V4L:: used code of "<<colourFormatCode);
PTRACE(1,"PVideoInputDevice_V4L:: palette: "<<colourFormatTab[colourFormatIndex].colourFormat);
return FALSE;
}
// Driver only (and always) manages to set the colour format with call to VIDIOCSPICT.
if( (HINT(HINT_ONLY_WORKS_PREF_PALETTE) ) &&
( colourFormatCode == driver_hints[hint_index].pref_palette) ) {
PTRACE(3,"PVideoInputDevice_V4L:: SetColourFormat succeeded with "<<newFormat);
return TRUE;
}
// Some drivers always return success for CSPICT, and can't
// read the current palette back in CGPICT. We can't do much
// more than just check to see if there is a preferred palette,
// and fail if the request isn't the preferred palette.
if (HINT(HINT_CSPICT_ALWAYS_WORKS) &&
HINT(HINT_CGPICT_DOESNT_SET_PALETTE) &&
HINT(HINT_HAS_PREF_PALETTE)) {
if (colourFormatCode != driver_hints[hint_index].pref_palette)
return FALSE;
}
// Some V4L drivers can't use CGPICT to check for errors.
if (!HINT(HINT_CGPICT_DOESNT_SET_PALETTE)) {
if (::ioctl(videoFd, VIDIOCGPICT, &pictureInfo) < 0) {
PTRACE(1,"PVideoInputDevice_V4L::Get pict info failed : "<< ::strerror(errno));
return FALSE;
}
if (pictureInfo.palette != colourFormatCode)
return FALSE;
}
// set the new information
return SetFrameSizeConverter(frameWidth, frameHeight, FALSE);
}
BOOL PVideoInputDevice_V4L::SetFrameRate(unsigned rate)
{
if (!PVideoDevice::SetFrameRate(rate))
return FALSE;
return TRUE;
}
BOOL PVideoInputDevice_V4L::GetFrameSizeLimits(unsigned & minWidth,
unsigned & minHeight,
unsigned & maxWidth,
unsigned & maxHeight)
{
if (!IsOpen())
return FALSE;
if(HINT(HINT_FORCE_LARGE_SIZE)) {
videoCapability.maxheight = 288;
videoCapability.maxwidth = 352;
videoCapability.minheight = 288;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -