📄 vidinput_v4l2.cxx
字号:
/* * vidinput_v4l2.cxx * * Classes to support streaming video input (grabbing) and output. * * Portable Windows Library * * Copyright (c) 1998-2000 Equivalence Pty. Ltd. * Copyright (c) 2003 March Networks * * 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. * * First V4L2 capture code written by March Networks * (http://www.marchnetworks.com) * * This code is based on the Video4Linux 1 code. * * Contributor(s): Guilhem Tardy (gtardy@salyens.com) * Nicola Orru' <nigu@itadinanta.it> * * TODO: * - fix the devices detection code using the new code from the V4L1 plugin * - make that code work * * $Log: vidinput_v4l2.cxx,v $ * Revision 1.4 2004/11/07 22:48:47 dominance * fixed copyright of v4l2 plugin. Last commit's credits go to Nicola Orru' <nigu@itadinanta.it> ... * * Revision 1.3 2004/11/07 21:34:21 dominance * v4l2 patch to add verbose device names detection. * * Revision 1.2 2004/10/27 09:22:59 dsandras * Added patch from Nicola Orru' to make things work better. * * Revision 1.1 2004/09/21 12:54:23 dsandras * Added initial port to the new pwlib API/V4L2 API for the video4linux 2 code of Guilhem Tardy. Thanks! * * Revision 1.0 2003/03/03 12:27:00 guilhem * First build. * */#pragma implementation "vidinput_v4l2.h"#include "vidinput_v4l2.h"#include <sys/utsname.h>PCREATE_VIDINPUT_PLUGIN(V4L2, PVideoInputV4l2Device);#include "vidinput_names.h" class V4L2Names : public V4LXNames{ PCLASSINFO(V4L2Names, V4LXNames);public: V4L2Names() { kernelVersion=KUNKNOWN; }; virtual void Update (); protected: virtual PString V4L2Names::BuildUserFriendly(PString devname); enum KernelVersionEnum { K2_4, K2_6, KUNKNOWN, } kernelVersion;};static V4L2Names & GetNames(){ static V4L2Names names; names.Update(); return names;}///////////////////////////////////////////////////////////////////////////////// PVideoInputV4l2DevicePVideoInputV4l2Device::PVideoInputV4l2Device(){ videoFd = -1; canRead = FALSE; canStream = FALSE; canSelect = FALSE; canSetFrameRate = FALSE; isMapped = FALSE;}PVideoInputV4l2Device::~PVideoInputV4l2Device(){ Close();}#ifndef V4L2_PIX_FMT_H263#define V4L2_PIX_FMT_H263 v4l2_fourcc('H','2','6','3')#endifstatic struct { const char * colourFormat; __u32 code;} colourFormatTab[] = { { "Grey", V4L2_PIX_FMT_GREY }, //Entries in this table correspond { "RGB32", V4L2_PIX_FMT_RGB32 }, //(line by line) to those in the { "RGB24", V4L2_PIX_FMT_RGB24 }, // PVideoDevice ColourFormat table. { "RGB565", V4L2_PIX_FMT_RGB565 }, { "RGB555", V4L2_PIX_FMT_RGB555 }, { "YUV411", V4L2_PIX_FMT_Y41P }, { "YUV411P", V4L2_PIX_FMT_YUV411P }, { "YUV420", V4L2_PIX_FMT_NV21 }, { "YUV420P", V4L2_PIX_FMT_YUV420 }, { "YUV422", V4L2_PIX_FMT_YYUV }, { "YUV422P", V4L2_PIX_FMT_YUV422P }, { "JPEG", V4L2_PIX_FMT_JPEG }, { "H263", V4L2_PIX_FMT_H263 }, { "SBGGR8", V4L2_PIX_FMT_SBGGR8 }};BOOL PVideoInputV4l2Device::Open(const PString & devName, BOOL startImmediate){ struct utsname buf; PString version; uname (&buf); if (buf.release) version = PString (buf.release); PTRACE(1,"PVidInDev\tOpen()\tvideoFd:" << videoFd); Close(); PString name = GetNames().GetDeviceName(devName); PTRACE(1,"PVidInDev\tOpen()\tdevName:" << name << " videoFd:" << videoFd); videoFd = ::open((const char *)name, O_RDWR); if (videoFd < 0) { PTRACE(1,"PVidInDev\topen failed : " << ::strerror(errno)); return FALSE; } PTRACE(6,"PVidInDev\topen, fd=" << videoFd); deviceName=name; // get the device capabilities if (::ioctl(videoFd, VIDIOC_QUERYCAP, &videoCapability) < 0) { PTRACE(1,"PVidInDev\tQUERYCAP failed : " << ::strerror(errno)); ::close (videoFd); videoFd = -1; return FALSE; } canRead = videoCapability.capabilities & V4L2_CAP_READWRITE; canStream = videoCapability.capabilities & V4L2_CAP_STREAMING; canSelect = videoCapability.capabilities & V4L2_CAP_ASYNCIO; // set height and width frameHeight = QCIFHeight; frameWidth = QCIFWidth; // get the capture parameters videoStreamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (::ioctl(videoFd, VIDIOC_G_PARM, &videoStreamParm) < 0) { PTRACE(1,"PVidInDev\tG_PARM failed : " << ::strerror(errno)); canSetFrameRate = FALSE; } else { canSetFrameRate = videoStreamParm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME; if (canSetFrameRate) PVideoDevice::SetFrameRate (10000000 * videoStreamParm.parm.capture.timeperframe.numerator / videoStreamParm.parm.capture.timeperframe.denominator); } return TRUE;}BOOL PVideoInputV4l2Device::IsOpen() { return videoFd >= 0;}BOOL PVideoInputV4l2Device::Close(){ PTRACE(1,"PVidInDev\tClose()\tvideoFd:" << videoFd << " started:" << started); if (!IsOpen()) return FALSE; Stop(); ClearMapping(); ::close(videoFd); PTRACE(6,"PVidInDev\tclose, fd=" << videoFd); videoFd = -1; canRead = FALSE; canStream = FALSE; canSelect = FALSE; canSetFrameRate = FALSE; isMapped = FALSE; PTRACE(1,"PVidInDev\tClose()\tvideoFd:" << videoFd << " started:" << started); return TRUE;}BOOL PVideoInputV4l2Device::Start(){ // automatically set mapping if (!isMapped && !SetMapping()) { ClearMapping(); canStream = FALSE; // don't try again return FALSE; } if (!started) { PTRACE(6,"PVidInDev\tstart streaming, fd=" << videoFd); struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.index = 0; if (::ioctl(videoFd, VIDIOC_QBUF, &buf) < 0) { PTRACE(3,"PVidInDev\tVIDIOC_QBUF failed : " << ::strerror(errno)); return FALSE; } buf.index = 0; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (::ioctl(videoFd, VIDIOC_STREAMON, &buf.type) < 0) { PTRACE(3,"PVidInDev\tSTREAMON failed : " << ::strerror(errno)); return FALSE; } started = TRUE; // requeue all buffers for (buf.index = 0; buf.index < videoBufferCount; buf.index++) { PTRACE(3,"PVidInDev\tQBUF for index:" << buf.index); if (::ioctl(videoFd, VIDIOC_QBUF, &buf) < 0) { PTRACE(3,"PVidInDev\tQBUF failed : " << ::strerror(errno)); return FALSE; } } } return TRUE;}BOOL PVideoInputV4l2Device::Stop(){ if (started) { PTRACE(6,"PVidInDev\tstop streaming, fd=" << videoFd); int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; started = FALSE; if (::ioctl(videoFd, VIDIOC_STREAMOFF, &type) < 0) { PTRACE(3,"PVidInDev\tSTREAMOFF failed : " << ::strerror(errno)); return FALSE; } // no need to dequeue filled buffers, as this is handled by V4L2 at the next VIDIOC_STREAMON } return TRUE;}BOOL PVideoInputV4l2Device::IsCapturing(){ return started;}PStringList PVideoInputV4l2Device::GetInputDeviceNames(){ return GetNames().GetInputDeviceNames(); }BOOL PVideoInputV4l2Device::SetVideoFormat(VideoFormat newFormat){ if (newFormat == Auto) { if (SetVideoFormat(PAL) || SetVideoFormat(NTSC) || SetVideoFormat(SECAM)) return TRUE; else return FALSE; } if (!PVideoDevice::SetVideoFormat(newFormat)) { PTRACE(1,"PVideoDevice::SetVideoFormat failed for format " << newFormat); return FALSE; } struct { __u32 code; const char * name; } static const fmt[3] = { {V4L2_STD_PAL, "PAL"}, {V4L2_STD_NTSC, "NTSC"}, {V4L2_STD_SECAM, "SECAM"} }; struct v4l2_standard videoEnumStd; videoEnumStd.index = 0; while (1) { if (::ioctl(videoFd, VIDIOC_ENUMSTD, &videoEnumStd) < 0) { PTRACE(1,"VideoInputDevice\tEnumStd failed : " << ::strerror(errno)); videoEnumStd.id = V4L2_STD_PAL; break; } if (videoEnumStd.id == fmt[newFormat].code) { break; } videoEnumStd.index++; } // set the video standard if (::ioctl(videoFd, VIDIOC_S_STD, &videoEnumStd.id) < 0) { PTRACE(1,"VideoInputDevice\tS_STD failed : " << ::strerror(errno)); } PTRACE(6,"PVidInDev\tset video format \"" << fmt[newFormat].name << "\", fd=" << videoFd); return TRUE;}int PVideoInputV4l2Device::GetNumChannels() { // if opened, return the capability value, else 1 as in videoio.cxx if (IsOpen ()) { struct v4l2_input videoEnumInput; videoEnumInput.index = 0; while (1) { if (::ioctl(videoFd, VIDIOC_ENUMINPUT, &videoEnumInput) < 0) { PTRACE(1,"VideoInputDevice\tEnumInput failed : " << ::strerror(errno)); break; } else videoEnumInput.index++; } return videoEnumInput.index; } else return 1;}BOOL PVideoInputV4l2Device::SetChannel(int newChannel){ if (!PVideoDevice::SetChannel(newChannel)) { PTRACE(1,"PVideoDevice::SetChannel failed for channel " << newChannel); return FALSE; } // set the channel if (::ioctl(videoFd, VIDIOC_S_INPUT, &channelNumber) < 0) { PTRACE(1,"VideoInputDevice\tS_INPUT failed : " << ::strerror(errno)); return FALSE; } PTRACE(6,"PVidInDev\tset channel " << newChannel << ", fd=" << videoFd); return TRUE;}BOOL PVideoInputV4l2Device::SetVideoChannelFormat (int newChannel, VideoFormat videoFormat) { if (!SetChannel(newChannel) || !SetVideoFormat(videoFormat)) return FALSE; return TRUE;}BOOL PVideoInputV4l2Device::SetColourFormat(const PString & newFormat){ PINDEX colourFormatIndex = 0; while (newFormat != colourFormatTab[colourFormatIndex].colourFormat) { colourFormatIndex++; if (colourFormatIndex >= PARRAYSIZE(colourFormatTab)) return FALSE; } if (!PVideoDevice::SetColourFormat(newFormat)) { PTRACE(3,"PVidInDev\tSetColourFormat failed for colour format " << newFormat); return FALSE; } BOOL resume = started; Stop(); ClearMapping(); struct v4l2_format videoFormat; videoFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // get the colour format if (::ioctl(videoFd, VIDIOC_G_FMT, &videoFormat) < 0) { PTRACE(1,"PVidInDev\tG_FMT failed : " << ::strerror(errno)); return FALSE; } videoFormat.fmt.pix.pixelformat = colourFormatTab[colourFormatIndex].code; // set the colour format if (::ioctl(videoFd, VIDIOC_S_FMT, &videoFormat) < 0) { PTRACE(1,"PVidInDev\tS_FMT failed : " << ::strerror(errno)); PTRACE(1,"\tused code of " << videoFormat.fmt.pix.pixelformat << " for palette: " << colourFormatTab[colourFormatIndex].colourFormat); return FALSE; } // get the colour format again to be careful about broken drivers if (::ioctl(videoFd, VIDIOC_G_FMT, &videoFormat) < 0) { PTRACE(1,"PVidInDev\tG_FMT failed : " << ::strerror(errno)); return FALSE; } if (videoFormat.fmt.pix.pixelformat != colourFormatTab[colourFormatIndex].code) { PTRACE(3,"PVidInDev\tcolour format mismatch."); return FALSE; } frameBytes = videoFormat.fmt.pix.sizeimage; PTRACE(6,"PVidInDev\tset colour format \"" << newFormat << "\", fd=" << videoFd); if (resume) return Start(); return TRUE;}BOOL PVideoInputV4l2Device::SetFrameRate(unsigned rate){ if (!PVideoDevice::SetFrameRate(rate)) { PTRACE(3,"PVidInDev\tSetFrameRate failed for rate " << rate); return TRUE; // Ignore } if (canSetFrameRate) { videoStreamParm.parm.capture.timeperframe.numerator = 10000000; videoStreamParm.parm.capture.timeperframe.denominator = (rate ? rate : 1); // set the stream parameters if (::ioctl(videoFd, VIDIOC_S_PARM, &videoStreamParm) < 0) { PTRACE(1,"PVidInDev\tS_PARM failed : "<< ::strerror(errno)); return TRUE; } PTRACE(6,"PVidInDev\tset frame rate " << rate << "fps, fd=" << videoFd); } return TRUE;}BOOL PVideoInputV4l2Device::GetFrameSizeLimits(unsigned & minWidth, unsigned & minHeight, unsigned & maxWidth, unsigned & maxHeight) { /* Not used in V4L2 */ minWidth=0; maxWidth=65535; minHeight=0; maxHeight=65535; return FALSE;}BOOL PVideoInputV4l2Device::SetFrameSize(unsigned width, unsigned height){ if (!PVideoDevice::SetFrameSize(width, height)) { PTRACE(3,"PVidInDev\tSetFrameSize failed for size " << width << "x" << height); return FALSE; } BOOL resume = started; Stop(); ClearMapping(); if (!VerifyHardwareFrameSize(width, height)) { PTRACE(3,"PVidInDev\tVerifyHardwareFrameSize failed for size " << width << "x" << height); return FALSE; } PTRACE(6,"PVidInDev\tset frame size " << width << "x" << height << ", fd=" << videoFd); if (resume) return Start(); return TRUE;}PINDEX PVideoInputV4l2Device::GetMaxFrameBytes(){ if (converter != NULL) { PINDEX bytes = converter->GetMaxDstFrameBytes(); if (bytes > frameBytes) return bytes; } return frameBytes;}BOOL PVideoInputV4l2Device::SetMapping(){ if (!canStream) return FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -