├── LICENSE ├── README.md ├── buftype ├── consts.go ├── doc.go └── type.go ├── cap.go ├── dev_video.go ├── device.go ├── device_businfo.go ├── device_cap.go ├── device_card.go ├── device_driver.go ├── device_formatfamilies.go ├── device_setformat.go ├── device_unfit.go ├── device_version.go ├── doc.go ├── errors.go ├── format ├── cast.go ├── doc.go ├── pix.go └── type.go ├── formatfamily.go ├── formatfamily_description.go ├── formatfamily_flags.go ├── formatfamily_framesizes.go ├── formatfamily_pixelformat.go ├── framesize ├── cast.go ├── continuous.go ├── discrete.go ├── doc.go ├── stepwise.go └── type.go ├── internal_capabilities.go ├── ioc.go ├── pixelformat ├── doc.go ├── type.go └── type_test.go └── vidioc.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Charles Iliya Krempeaux 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-v4l2 2 | 3 | Package v4l2 exposes the V4L2 (Video4Linux version 2) API to Golang. 4 | 5 | 6 | ## Documention 7 | 8 | Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-v4l2 9 | 10 | [![GoDoc](https://godoc.org/github.com/reiver/go-v4l2?status.svg)](https://godoc.org/github.com/reiver/go-v4l2) 11 | 12 | 13 | ## Example 14 | ```go 15 | device, err := v4l2.Open(v4l2.Video0) 16 | if nil != err { 17 | //@TODO 18 | return err 19 | } 20 | defer device.Close() 21 | 22 | fmt.Printf("Driver: %q\n", device.MustDriver()) 23 | fmt.Printf("Card: %q\n", device.MustCard()) 24 | fmt.Printf("BusInfo: %q\n", device.MustBusInfo()) 25 | fmt.Printf("Version: %q\n", device.MustVersion()) 26 | fmt.Println() 27 | fmt.Printf("Has Video Capture: %v\n", device.MustHasCapability(v4l2.CapabilityVideoCapture)) 28 | fmt.Printf("Has Streaming I/O: %v\n", device.MustHasCapability(v4l2.CapabilityStreaming)) 29 | ``` 30 | -------------------------------------------------------------------------------- /buftype/consts.go: -------------------------------------------------------------------------------- 1 | package v4l2_buftype 2 | 3 | // These were mimicked from: 4 | // api/linux/videodev2.h 5 | const ( 6 | VideoCapture = 1 // (V4L2_BUF_TYPE_VIDEO_CAPTURE) 7 | VideoOutput = 2 // (V4L2_BUF_TYPE_VIDEO_OUTPUT) 8 | VideoOverlay = 3 // (V4L2_BUF_TYPE_VIDEO_OVERLAY) 9 | VBICapture = 4 // (V4L2_BUF_TYPE_VBI_CAPTURE) 10 | VBIOutput = 5 // (V4L2_BUF_TYPE_VBI_OUTPUT) 11 | SlicedVBICapture = 6 // (V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) 12 | SlicedVBIOutput = 7 // (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) 13 | VideoOutputOverlay = 8 // (V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) Experimental 14 | VideoCaptureMultiplanar = 9 // (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 15 | VideoOutputMultiplanar = 10 // (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 16 | SDRCapture = 11 // (V4L2_BUF_TYPE_SDR_CAPTURE) 17 | 18 | Private = 0x80 // (V4L2_BUF_TYPE_PRIVATE) Deprecated, do not use 19 | ) 20 | -------------------------------------------------------------------------------- /buftype/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package v4l2_buftype provides a type used to represents a V4L2 (Video4Linux version 2) buffer type. 3 | */ 4 | package v4l2_buftype 5 | -------------------------------------------------------------------------------- /buftype/type.go: -------------------------------------------------------------------------------- 1 | package v4l2_buftype 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Type represents a V4L2 (Video4Linux version 2) buffer type. 8 | type Type struct { 9 | 10 | // NOTE THAT THIS STRUCT MUST BE EXACLTLY 4 BYTES IN SIZE. 11 | // 12 | // THIS IS WHAT THE V4L2 (Video4Linux version 2) DRIVERS EXPECT. 13 | // 14 | // THUS WE CANNOT PUT EXTRA (USEFUL) HIDDEN FIELDS IN HERE. 15 | // 16 | // THERE MUST ONLY BE A SINGLE uint32 FIELD IN THIS STRUCT! 17 | 18 | value uint32 19 | } 20 | 21 | // Datum returns a v4l2_buftype.Type from a uint32. 22 | // 23 | // Example 24 | // 25 | // var buftype v4l2_buftype.Type 26 | // 27 | // buftype = v4l2_buftype.Datum(v4l2_buftype.VideoCapture) 28 | func Datum(value uint32) Type { 29 | return Type{value} 30 | } 31 | 32 | // String returns a human-readable version of the V4L2 (Video4Linux version 2) buffer type. 33 | // 34 | // So, rather than returning the uint32 1, it returns the string "V4L2_BUF_TYPE_VIDEO_CAPTURE". 35 | func (receiver Type) String() string { 36 | 37 | ui32 := receiver.value 38 | 39 | switch ui32 { 40 | case VideoCapture: 41 | return "V4L2_BUF_TYPE_VIDEO_CAPTURE" 42 | case VideoOutput: 43 | return "V4L2_BUF_TYPE_VIDEO_OUTPUT" 44 | case VideoOverlay: 45 | return "V4L2_BUF_TYPE_VIDEO_OVERLAY" 46 | case VBICapture: 47 | return "V4L2_BUF_TYPE_VBI_CAPTURE" 48 | case VBIOutput: 49 | return "V4L2_BUF_TYPE_VBI_OUTPUT" 50 | case SlicedVBICapture: 51 | return "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE" 52 | case SlicedVBIOutput: 53 | return "V4L2_BUF_TYPE_SLICED_VBI_OUTPUT" 54 | case VideoOutputOverlay: 55 | return "V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY" 56 | case VideoCaptureMultiplanar: 57 | return "V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE" 58 | case VideoOutputMultiplanar: 59 | return "V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE" 60 | case SDRCapture: 61 | return "V4L2_BUF_TYPE_SDR_CAPTURE" 62 | 63 | case Private: 64 | return "V4L2_BUF_TYPE_PRIVATE" 65 | 66 | default: 67 | return fmt.Sprintf("[UNKNOWN: %d]", ui32) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cap.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // V4L2 (Video4Linux version 2) capability constants. 4 | // 5 | // Use these with the v4l2.Device.HasCapability() method. 6 | // 7 | // For example: 8 | // 9 | // var device v4l2.Device 10 | // 11 | // // ... 12 | // 13 | // if ! device.HasCapability(v4l2.CapabilityVideoCapture) { 14 | // //@TODO 15 | // } 16 | // 17 | // These are mimicked from: 18 | // /usr/include/linux/videodev2.h 19 | const ( 20 | 21 | CapabilityVideoCapture = 0x00000001 // (V4L2_CAP_VIDEO_CAPTURE) Is a video capture device 22 | CapabilityVideoOutput = 0x00000002 // (V4L2_CAP_VIDEO_OUTPUT) Is a video output device 23 | CapabilityVideoOverlay = 0x00000004 // (V4L2_CAP_VIDEO_OVERLAY) Can do video overlay 24 | CapabilityVBICapture = 0x00000010 // (V4L2_CAP_VBI_CAPTURE) Is a raw VBI capture device 25 | CapabilityVBIOutput = 0x00000020 // (V4L2_CAP_VBI_OUTPUT) Is a raw VBI output device 26 | CapabilitySlicedVBICapture = 0x00000040 // (V4L2_CAP_SLICED_VBI_CAPTURE) Is a sliced VBI capture device 27 | CapabilitySlicedVBIOutput = 0x00000080 // (V4L2_CAP_SLICED_VBI_OUTPUT) Is a sliced VBI output device 28 | CapabilityRDSCapture = 0x00000100 // (V4L2_CAP_RDS_CAPTURE) RDS data capture 29 | CapabilityVideoOutputOverlay = 0x00000200 // (V4L2_CAP_VIDEO_OUTPUT_OVERLAY) Can do video output overlay 30 | CapabilityHardwareFrequencySeek = 0x00000400 // (V4L2_CAP_HW_FREQ_SEEK) Can do hardware frequency seek 31 | CapabilityRDSOutput = 0x00000800 // (V4L2_CAP_RDS_OUTPUT) Is an RDS encoder 32 | 33 | CapabilityVideoCaptureMultiplanar = 0x00001000 // (V4L2_CAP_VIDEO_CAPTURE_MPLANE) Is a video capture device that supports multiplanar formats 34 | CapabilityVideoOutputMultiplanar = 0x00002000 // (V4L2_CAP_VIDEO_OUTPUT_MPLANE) Is a video output device that supports multiplanar formats 35 | CapabilityVideoM2MMultiplanar = 0x00004000 // (V4L2_CAP_VIDEO_M2M_MPLANE) Is a video mem-to-mem device that supports multiplanar formats 36 | CapabilityVideoM2M = 0x00008000 // (V4L2_CAP_VIDEO_M2M) Is a video mem-to-mem device 37 | 38 | CapabilityTuner = 0x00010000 // (V4L2_CAP_TUNER) has a tuner 39 | CapabilityAudio = 0x00020000 // (V4L2_CAP_AUDIO) has audio support 40 | CapabilityRadio = 0x00040000 // (V4L2_CAP_RADIO) is a radio device 41 | CapabilityModulator = 0x00080000 // (V4L2_CAP_MODULATOR) has a modulator 42 | 43 | CapabilityReadWrite = 0x01000000 // (V4L2_CAP_READWRITE) read/write systemcalls 44 | CapabilityAsyncIO = 0x02000000 // (V4L2_CAP_ASYNCIO) async I/O 45 | CapabilityStreaming = 0x04000000 // (V4L2_CAP_STREAMING) streaming I/O ioctls 46 | 47 | CapabilityDeviceCaps = 0x80000000 // (V4L2_CAP_DEVICE_CAPS) sets device capabilities field 48 | ) 49 | -------------------------------------------------------------------------------- /dev_video.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // Someone claimed that the V4L2 documentation says that there can only be (a maximum of) 64 allowed devices with the prefix "/dev/video", 4 | // and starting with "/dev/video0" 5 | // 6 | // Thus, these constants represent the paths to these devices. Such that: 7 | // 8 | // • v4l2.Video0 = "/dev/video0" 9 | // 10 | // • v4l2.Video1 = "/dev/video1" 11 | // 12 | // • v4l2.Video2 = "/dev/video2" 13 | // 14 | // … 15 | // 16 | // • v4l2.Video63 = "/dev/video63" 17 | // 18 | // Use these with the v4l2.Device.Open() method or the v4l2.Open() func. 19 | // 20 | // For example: 21 | // 22 | // device, err := v4l2.Open(v4l2.Video0) 23 | // 24 | // Or, alternatively, for example: 25 | // 26 | // var device v4l2.Device 27 | // 28 | // err := device.Open(v4l2.Video0) 29 | const ( 30 | Video0 = "/dev/video0" 31 | Video1 = "/dev/video1" 32 | Video2 = "/dev/video2" 33 | Video3 = "/dev/video3" 34 | Video4 = "/dev/video4" 35 | Video5 = "/dev/video5" 36 | Video6 = "/dev/video6" 37 | Video7 = "/dev/video7" 38 | Video8 = "/dev/video8" 39 | Video9 = "/dev/video9" 40 | 41 | Video10 = "/dev/video10" 42 | Video11 = "/dev/video11" 43 | Video12 = "/dev/video12" 44 | Video13 = "/dev/video13" 45 | Video14 = "/dev/video14" 46 | Video15 = "/dev/video15" 47 | Video16 = "/dev/video16" 48 | Video17 = "/dev/video17" 49 | Video18 = "/dev/video18" 50 | Video19 = "/dev/video19" 51 | 52 | Video20 = "/dev/video20" 53 | Video21 = "/dev/video21" 54 | Video22 = "/dev/video22" 55 | Video23 = "/dev/video23" 56 | Video24 = "/dev/video24" 57 | Video25 = "/dev/video25" 58 | Video26 = "/dev/video26" 59 | Video27 = "/dev/video27" 60 | Video28 = "/dev/video28" 61 | Video29 = "/dev/video29" 62 | 63 | Video30 = "/dev/video30" 64 | Video31 = "/dev/video31" 65 | Video32 = "/dev/video32" 66 | Video33 = "/dev/video33" 67 | Video34 = "/dev/video34" 68 | Video35 = "/dev/video35" 69 | Video36 = "/dev/video36" 70 | Video37 = "/dev/video37" 71 | Video38 = "/dev/video38" 72 | Video39 = "/dev/video39" 73 | 74 | Video40 = "/dev/video40" 75 | Video41 = "/dev/video41" 76 | Video42 = "/dev/video42" 77 | Video43 = "/dev/video43" 78 | Video44 = "/dev/video44" 79 | Video45 = "/dev/video45" 80 | Video46 = "/dev/video46" 81 | Video47 = "/dev/video47" 82 | Video48 = "/dev/video48" 83 | Video49 = "/dev/video49" 84 | 85 | Video50 = "/dev/video50" 86 | Video51 = "/dev/video51" 87 | Video52 = "/dev/video52" 88 | Video53 = "/dev/video53" 89 | Video54 = "/dev/video54" 90 | Video55 = "/dev/video55" 91 | Video56 = "/dev/video56" 92 | Video57 = "/dev/video57" 93 | Video58 = "/dev/video58" 94 | Video59 = "/dev/video59" 95 | 96 | Video60 = "/dev/video60" 97 | Video61 = "/dev/video61" 98 | Video62 = "/dev/video62" 99 | Video63 = "/dev/video63" 100 | ) 101 | -------------------------------------------------------------------------------- /device.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "golang.org/x/sys/unix" 5 | ) 6 | 7 | // Device represents a V4L2 (Video4Linux version 2) device. 8 | type Device struct { 9 | opened bool 10 | capQueried bool 11 | fileDescriptor int 12 | cap internalCapability 13 | } 14 | 15 | // Open opens a V4L2 device. 16 | // 17 | // Example: 18 | // 19 | // device, err := v4l2.Open(v4l2.Video0) 20 | // if nil != err { 21 | // return err 22 | // }) 23 | // defer device.Close() 24 | func Open(name string) (*Device, error) { 25 | var device Device 26 | 27 | err := device.Open(name) 28 | if nil != err { 29 | return nil, err 30 | } 31 | 32 | return &device, nil 33 | } 34 | 35 | // MustOpen is like Open, except it panic()s if there is an error. 36 | func MustOpen(name string) *Device { 37 | datum, err := Open(name) 38 | if nil != err { 39 | panic(err) 40 | } 41 | 42 | return datum 43 | } 44 | 45 | // Open opens a V4L2 device. 46 | // 47 | // (This method exist so as not to create GC pressure.) 48 | // 49 | // Example: 50 | // 51 | // var device Device 52 | // 53 | // err := device.Open(v4l2.Video0) 54 | // if nil != err { 55 | // return err 56 | // }) 57 | // defer device.Close() 58 | func (receiver *Device) Open(name string) error { 59 | if nil == receiver { 60 | return errNilReceiver 61 | } 62 | 63 | if receiver.opened { 64 | return errOpen 65 | } 66 | 67 | { 68 | var err error 69 | 70 | receiver.fileDescriptor, err = unix.Open(name, unix.O_RDWR|unix.O_NONBLOCK, 0666) 71 | if nil != err { 72 | return err 73 | } 74 | receiver.opened = true 75 | } 76 | 77 | if err := receiver.cap.QueryFd(receiver.fileDescriptor) ; nil != err { 78 | return err 79 | } 80 | receiver.capQueried = true 81 | 82 | return nil 83 | } 84 | 85 | // MustOpen is like Open, except it panic()s if there is an error. 86 | func (receiver *Device) MustOpen(name string) { 87 | if nil == receiver { 88 | panic(errNilReceiver) 89 | } 90 | 91 | err := receiver.Open(name) 92 | if nil != err { 93 | panic(err) 94 | } 95 | } 96 | 97 | // Close closes the device. 98 | // 99 | // You should always close a device after you finished with it, so that you do not create a resource leak. 100 | func (receiver *Device) Close() error { 101 | if nil == receiver { 102 | return nil 103 | } 104 | 105 | if !receiver.opened { 106 | return nil 107 | } 108 | 109 | err := unix.Close(receiver.fileDescriptor) 110 | if nil != err { 111 | return err 112 | } 113 | 114 | receiver.opened = false 115 | 116 | return nil 117 | } 118 | 119 | // MustClose is like Close, except it panic()s if there is an error. 120 | // 121 | // Example: 122 | // 123 | // device := v4l2.MustOpen(v4l2.Video0) 124 | // defer device.MustClose() 125 | func (receiver *Device) MustClose() { 126 | if nil == receiver { 127 | panic(errNilReceiver) 128 | } 129 | 130 | err := receiver.Close() 131 | if nil != err { 132 | panic(err) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /device_businfo.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // BusInfo returns the location of the V4L2 device in the system. 4 | // 5 | // Example values that BusInfo might return are: 6 | // 7 | // • "usb-0000:00:1a.0-1.5" 8 | // 9 | // • "PCI:0000:05:06.0" 10 | // 11 | // The information returned by BusInfo is intended for users to be able to distinguish between multiple identical devices. 12 | // 13 | // For example, if you have 2 of the exact same (hardware) devices that plug into the USB ports of your computer, 14 | // and capture HDMI video, then the driver, the card, all the capabilities, etc will be the same for these 2 devices. 15 | // However, what BusInfo returns will be different. 16 | // 17 | // In the cases wwere no such information is available for the BusInfo (at the Linux or driver level) then BusInfo will 18 | // be the count of the devices controlled by the driver ("platform:vivi-000"). 19 | // 20 | // What is returned from BusInfo will start with: 21 | // 22 | // • "PCI:" for PCI boards, 23 | // • "PCIe:" for PCI Express boards, 24 | // • "usb-" for USB devices, 25 | // • "I2C:" for i2c devices, 26 | // • "ISA:" for ISA devices, 27 | // • "parport" for parallel port devices, and 28 | // • "platform:" for platform devices. 29 | // 30 | // What BusInfo returns is called "bus_info" in the Linux V4L2 documention, which is found in the Linux "v4l2_capability" data structure. 31 | func (receiver Device) BusInfo() (string, error) { 32 | if err := receiver.unfit(); nil != err { 33 | return "", err 34 | } 35 | 36 | return receiver.cap.BusInfo(), nil 37 | } 38 | 39 | // MustBusInfo is like BusInfo, except it panic()s if there is an error. 40 | // 41 | // device := v4l2.MustOpen(v4l2.Video0) 42 | // defer device.MustClose() 43 | // 44 | // fmt.Printf("Bus Info: %q \n", device.MustBusInfo()) 45 | func (receiver Device) MustBusInfo() string { 46 | datum, err := receiver.BusInfo() 47 | if nil != err { 48 | panic(err) 49 | } 50 | 51 | return datum 52 | } 53 | -------------------------------------------------------------------------------- /device_cap.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // HasCapability return whether the device has a capability a particular capability. 4 | // 5 | // Example: 6 | // 7 | // device, err := v4l2.Open(v4l2.Video17) 8 | // 9 | // // ... 10 | // 11 | // hasVideoCaptureCapability, err := device.HasCapability(CapabilityVideoCapture) 12 | func (receiver Device) HasCapability(cap uint32) (bool, error) { 13 | if err := receiver.unfit(); nil != err { 14 | return false, err 15 | } 16 | 17 | has := 0 != (receiver.cap.capabilities & cap) 18 | 19 | return has, nil 20 | } 21 | 22 | // MustHasCapability is like HasCapability, except it panic()s if there is an error. 23 | // 24 | // device := v4l2.MustOpen(v4l2.Video0) 25 | // defer device.MustClose() 26 | // 27 | // fmt.Printf("Has Streaming Capability: %t \n", device.MustHasCapability(v4l2.CapabilityStreaming)) 28 | func (receiver Device) MustHasCapability(cap uint32) bool { 29 | datum, err := receiver.HasCapability(cap) 30 | if nil != err { 31 | panic(err) 32 | } 33 | 34 | return datum 35 | } 36 | -------------------------------------------------------------------------------- /device_card.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // Card returns the name of the device. 4 | // 5 | // Example values that Card might return are: 6 | // 7 | // • "Laptop_Integrated_Webcam_HD" 8 | // 9 | // • "Yoyodyne TV/FM" 10 | // 11 | // The information that Card returns is intended for users. 12 | // I.e., intended to be human readable) 13 | // And can be used for things such as being use in a menu of available devices. 14 | // 15 | // Note that it is possible for a system to have multiple cards from the same brand. 16 | // 17 | // And thus it is possible for multiple devices to return the exact same value from Card. 18 | // 19 | // To make the name, shown to the user, unique, it can be combined with either the file name path, 20 | // or what BusInfo returns. 21 | func (receiver Device) Card() (string, error) { 22 | if err := receiver.unfit(); nil != err { 23 | return "", err 24 | } 25 | 26 | return receiver.cap.Card(), nil 27 | } 28 | 29 | // MustCard is like BusCard, except it panic()s if there is an error. 30 | // 31 | // device := v4l2.MustOpen(v4l2.Video0) 32 | // defer device.MustClose() 33 | // 34 | // fmt.Printf("Card: %q \n", device.MustCard()) 35 | func (receiver Device) MustCard() string { 36 | datum, err := receiver.Card() 37 | if nil != err { 38 | panic(err) 39 | } 40 | 41 | return datum 42 | } 43 | -------------------------------------------------------------------------------- /device_driver.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // Driver returns the name of the driver. 4 | // 5 | // Example values that Driver might return are: 6 | // 7 | // • "uvcvideo" 8 | // 9 | // • "bttv" 10 | // 11 | // If an application can only work with specific drivers, then the information returned 12 | // from Driver can be used to detect which driver is behind this device. 13 | // 14 | // Also, if certain drivers have bugs, then this can (in part) be used to create driver 15 | // specific work-arounds. 16 | func (receiver Device) Driver() (string, error) { 17 | if err := receiver.unfit(); nil != err { 18 | return "", err 19 | } 20 | 21 | return receiver.cap.Driver(), nil 22 | } 23 | 24 | // MustDriver is like Driver, except it panic()s if there is an error. 25 | // 26 | // Example: 27 | // 28 | // device := v4l2.MustOpen(v4l2.Video0) 29 | // defer device.MustClose() 30 | // 31 | // fmt.Printf("Driver: %q \n", device.MustDriver()) 32 | func (receiver Device) MustDriver() string { 33 | datum, err := receiver.Driver() 34 | if nil != err { 35 | panic(err) 36 | } 37 | 38 | return datum 39 | } 40 | -------------------------------------------------------------------------------- /device_formatfamilies.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/buftype" 5 | 6 | "golang.org/x/sys/unix" 7 | 8 | "unsafe" 9 | ) 10 | 11 | // FormatFamilies returns an iterator that enables you to list out all the supported formats by the device. 12 | // 13 | // Example: 14 | // 15 | // var device v4l2.Device 16 | // 17 | // // ... 18 | // 19 | // formats, err := device.FormatFamilies() // <---- NOTE THAT THIS IS WHERE THE v4l2.Device.FormatFamilies() METHOD IS CALLED. 20 | // if nil != err { 21 | // return err 22 | // } 23 | // defer formats.Close() 24 | // 25 | // var formatFamily v4l2.FormatFamily 26 | // forformats.Next() { 27 | // 28 | // err := formats.Decode(&formatFamily) 29 | // if nil != err { 30 | // fmt.Fprintf(os.Stderr, "ERROR: Problem decoding format: (%T) %v \n", err, err) 31 | // return err 32 | // } 33 | // 34 | // fmt.Printf("[format] %q (%q) {compressed=%t} {emulated=%t} \n", 35 | // formatFamily.Description(), 36 | // formatFamily.PixelFormat(), 37 | // formatFamily.HasFlags(v4l2.FormatFamilyFlagCompressed), 38 | // formatFamily.HasFlags(v4l2.FormatFamilyFlagEmulated), 39 | // ) 40 | // } 41 | // if err := formats.Err(); nil != err { 42 | // return err 43 | // } 44 | func (receiver *Device) FormatFamilies() (FormatFamilies, error) { 45 | if err := receiver.unfit(); nil != err { 46 | return FormatFamilies{}, err 47 | } 48 | 49 | return FormatFamilies{ 50 | device: receiver, 51 | }, nil 52 | } 53 | 54 | // FormatFamilies is an interator that enables you to list out all the supported formats by the device. 55 | type FormatFamilies struct { 56 | device *Device 57 | err error 58 | datum internalFormatFamily 59 | } 60 | 61 | // Close closes the FormatFamilies iterator. 62 | func (receiver *FormatFamilies) Close() error { 63 | if nil == receiver { 64 | return nil 65 | } 66 | 67 | receiver.device = nil 68 | receiver.err = nil 69 | receiver.datum.index = 0 70 | 71 | return nil 72 | } 73 | 74 | // Decode loads the next format (previously obtained by calling Next). 75 | func (receiver FormatFamilies) Decode(x interface{}) error { 76 | if nil != receiver.err { 77 | return receiver.err 78 | } 79 | 80 | p, ok := x.(*FormatFamily) 81 | if !ok { 82 | return errUnsupportedType 83 | } 84 | 85 | p.device = receiver.device 86 | p.internal = receiver.datum 87 | 88 | return nil 89 | } 90 | 91 | // Err returns any errors that occurred when Next was called. 92 | func (receiver *FormatFamilies) Err() error { 93 | if nil == receiver { 94 | return errNilReceiver 95 | } 96 | 97 | return receiver.err 98 | } 99 | 100 | // Next fetches the next format. 101 | // 102 | // If there is a next format, it returns true. 103 | // And the next format get be obtained by calling Decode. 104 | // 105 | // If there is not next format, then it returns false. 106 | func (receiver *FormatFamilies) Next() bool { 107 | if nil == receiver { 108 | return false 109 | } 110 | 111 | device := receiver.device 112 | if nil == device { 113 | receiver.err = errInternalError 114 | return false 115 | } 116 | 117 | receiver.datum.typ = v4l2_buftype.Datum(v4l2_buftype.VideoCapture) 118 | 119 | _, _, errorNumber := unix.Syscall( 120 | unix.SYS_IOCTL, 121 | uintptr(device.fileDescriptor), 122 | const_VIDIOC_ENUM_FMT, 123 | uintptr(unsafe.Pointer(&receiver.datum)), 124 | ) 125 | if unix.EINVAL == errorNumber { 126 | return false 127 | } 128 | if 0 != errorNumber { 129 | receiver.err = errorNumber 130 | return false 131 | } 132 | 133 | receiver.datum.index++ 134 | 135 | return true 136 | } 137 | -------------------------------------------------------------------------------- /device_setformat.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/format" 5 | 6 | "golang.org/x/sys/unix" 7 | 8 | "unsafe" 9 | ) 10 | 11 | func (receiver *Device) SetFormat(formatcaster v4l2_format.Caster) error { 12 | if err := receiver.unfit(); nil != err { 13 | return err 14 | } 15 | 16 | format := formatcaster.CastFormat() 17 | 18 | _, _, errorNumber := unix.Syscall( 19 | unix.SYS_IOCTL, 20 | uintptr(receiver.fileDescriptor), 21 | const_VIDIOC_S_FMT, 22 | uintptr(unsafe.Pointer(&format)), 23 | ) 24 | if 0 != errorNumber { 25 | return errorNumber 26 | } 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /device_unfit.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // safe returns an error if 4 | func (receiver *Device) unfit() error { 5 | if nil == receiver { 6 | return errNilReceiver 7 | } 8 | 9 | if !receiver.opened { 10 | return errNotOpen 11 | } 12 | if !receiver.capQueried { 13 | return errInternalError 14 | } 15 | 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /device_version.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // Version returns the version number of the driver. 4 | // 5 | // Example values that Version might return are: 6 | // 7 | // • "4.4.98" 8 | // 9 | // • "4.14.0" 10 | func (receiver Device) Version() (string, error) { 11 | if err := receiver.unfit(); nil != err { 12 | return "", err 13 | } 14 | 15 | return receiver.cap.Version(), nil 16 | } 17 | 18 | // MustVersion is like Version, except it panic()s if there is an error. 19 | // 20 | // Example: 21 | // 22 | // device := v4l2.MustOpen(v4l2.Video0) 23 | // defer device.MustClose() 24 | // 25 | // fmt.Printf("Version: %q \n", device.MustVersion()) 26 | func (receiver Device) MustVersion() string { 27 | datum, err := receiver.Version() 28 | if nil != err { 29 | panic(err) 30 | } 31 | 32 | return datum 33 | } 34 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package v4l2 exposes the V4L2 (Video4Linux version 2) API to Golang. 3 | 4 | Example: 5 | 6 | device, err := v4l2.Open(v4l2.Video0) 7 | if nil != err { 8 | //@TODO 9 | return err 10 | } 11 | defer device.Close() 12 | 13 | fmt.Printf("Driver: %q\n", device.MustDriver()) 14 | fmt.Printf("Card: %q\n", device.MustCard()) 15 | fmt.Printf("BusInfo: %q\n", device.MustBusInfo()) 16 | fmt.Printf("Version: %q\n", device.MustVersion()) 17 | fmt.Println() 18 | fmt.Printf("Has Video Capture: %v\n", device.MustHasCapability(v4l2.CapabilityVideoCapture)) 19 | fmt.Printf("Has Streaming I/O: %v\n", device.MustHasCapability(v4l2.CapabilityStreaming)) 20 | 21 | That example opens up the V4L2 device at "/dev/video0" on the file system, and displays some basic information about the device. 22 | 23 | (Of course, we could have opened one of the other V4L2 devices. Such as: v4l2.Video1, v4l2.Video2, ..., or v4l2.Video63.) 24 | 25 | Continuing this same example: 26 | 27 | formatFamilies, err := device.FormatFamilies() 28 | if nil != err { 29 | return err 30 | } 31 | defer formatFamilies.Close() 32 | 33 | var formatFamily v4l2.FormatFamily 34 | for formatFamilies.Next() { 35 | 36 | if err := formatFamilies.Decode(&formatFamily); nil != err { 37 | return err 38 | } 39 | 40 | fmt.Printf("[format] %q (%q) {compressed=%t} {emulated=%t} \n", 41 | formatFamily.Description(), 42 | formatFamily.PixelFormat(), 43 | formatFamily.HasFlags(v4l2.FormatFamilyFlagCompressed), 44 | formatFamily.HasFlags(v4l2.FormatFamilyFlagEmulated), 45 | ) 46 | 47 | 48 | //@TODO 49 | } 50 | if err := formatFamilies.Err(); nil != err { 51 | return err 52 | } 53 | 54 | Here we have iterating through the formats that are supported for this device. 55 | 56 | Extending that last code block, to fill in that "//@TODO", we can iterate through the frame sizes, for each format, we the following: 57 | 58 | frameSizes, err := formatFamily.FrameSizes() 59 | if nil != err { 60 | return return 61 | } 62 | defer frameSizes.Close() 63 | 64 | var frameSize v4l2.FrameSize 65 | for frameSizes.Next() { 66 | 67 | if err := frameSizes.Decode(&frameSize); nil != err { 68 | return err 69 | } 70 | 71 | casted, err := frameSize.Cast() 72 | if nil != err { 73 | return err 74 | } 75 | 76 | switch t := casted.(type) { 77 | case v4l2.FrameSizeDiscrete: 78 | fmt.Printf("\t [frame size discrete] pixel_format=%q, width=%d, height=%d \n", 79 | t.PixelFormat, 80 | t.Width, 81 | t.Height, 82 | ) 83 | case v4l2.FrameSizeContinuous: 84 | fmt.Printf("\t [frame size continuous] pixel_format=%q, min_width=%d, max_width=%d, min_height=%d, max_height=% \n", 85 | t.PixelFormat, 86 | t.MinWidth, 87 | t.MaxWidth, 88 | t.MinHeight, 89 | t.MaxHeight, 90 | ) 91 | case v4l2.FrameSizeStepwise: 92 | fmt.Printf("\t [frame size stepwise] pixel_format=%q, min_width=%d, max_width=%d, min_height=%d, max_height=% \n", 93 | t.PixelFormat, 94 | t.MinWidth, 95 | t.MaxWidth, 96 | t.MinHeight, 97 | t.MaxHeight, 98 | ) 99 | default: 100 | return err 101 | } 102 | 103 | } 104 | if err := frameSizes.Err(); nil != err { 105 | return err 106 | } 107 | 108 | 109 | Device Paths 110 | 111 | One thing to be cognisant of, is that on Linux systems, V4L2 (Video4Linux version 2) will create a (special) file in the 112 | /dev/ directory for every V4L2 device. 113 | 114 | These devices will name path names such as: 115 | 116 | • /dev/video0 117 | 118 | • /dev/video1 119 | 120 | • /dev/video2 121 | 122 | … 123 | 124 | • /dev/video63 125 | 126 | You would call Open using these names directly. For example: 127 | 128 | device, err := v4l2.Open("/dev/video3") 129 | 130 | However, this package also provides constants that can be used. For example: 131 | 132 | device, err := v4l2.Open(v4l2.Video3) 133 | 134 | */ 135 | package v4l2 136 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | errInternalError = errors.New("Internal Error") 9 | errNilReceiver = errors.New("Nil Receiver") 10 | errNotOpen = errors.New("Not Open") 11 | errOpen = errors.New("Open") 12 | errUnsupportedType = errors.New("Unsupported Type") 13 | ) 14 | -------------------------------------------------------------------------------- /format/cast.go: -------------------------------------------------------------------------------- 1 | package v4l2_format 2 | 3 | type Caster interface { 4 | CastFormat() Type 5 | } 6 | -------------------------------------------------------------------------------- /format/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package v4l2_format provides a type used to represents a V4L2 (Video4Linux version 2) format. 3 | */ 4 | package v4l2_format 5 | -------------------------------------------------------------------------------- /format/pix.go: -------------------------------------------------------------------------------- 1 | package v4l2_format 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/buftype" 5 | "github.com/reiver/go-v4l2/pixelformat" 6 | 7 | "unsafe" 8 | ) 9 | 10 | // This is more or less equivalent to the Linux V4L2 (Video4Linux version 2) 11 | // v4l2_format.type with the v4l2_format.pix (v4l2_pix_format). 12 | // 13 | // And thus, where v4l2_format.type is V4L2_BUF_TYPE_VIDEO_CAPTURE. 14 | type Pix struct { 15 | buftype v4l2_buftype.Type 16 | 17 | Width uint32 18 | Height uint32 19 | PixelFormat v4l2_pixelformat.Type 20 | field uint32 // enum v4l2_field 21 | bytesperline uint32 // for padding, zero if unused 22 | sizeimage uint32 23 | colorspace uint32 // enum v4l2_colorspace 24 | priv uint32 // private data, depends on pixelformat 25 | flags uint32 // format flags (V4L2_PIX_FMT_FLAG_*) 26 | ycbcrEnc uint32 // enum v4l2_ycbcr_encoding 27 | quantization uint32 // enum v4l2_quantization 28 | 29 | padding [200-(11*4)]byte // This struct MUST be 200 bytes long, so we add padding to make it that. 30 | } 31 | 32 | // CastFormat returns the v4l2_format.Pix as a v4l2_format.Type. 33 | // 34 | // Basically, doing something similar to a type cast. 35 | // Although there is copying involved. 36 | func (receiver Pix) CastFormat() Type { 37 | 38 | receiver.buftype = v4l2_buftype.Datum(v4l2_buftype.VideoCapture) 39 | 40 | return *(*Type)(unsafe.Pointer(&receiver)) 41 | } 42 | -------------------------------------------------------------------------------- /format/type.go: -------------------------------------------------------------------------------- 1 | package v4l2_format 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/buftype" 5 | ) 6 | 7 | // This is more or less equivalent to the Linux V4L2 (Video4Linux version 2) 8 | // v4l2_format.type with the v4l2_format.raw_data. 9 | type Type struct { 10 | buftype v4l2_buftype.Type 11 | rawData [200]byte 12 | } 13 | 14 | // BufferType returns the buffer type of the format. 15 | func (receiver Type) BufferType() v4l2_buftype.Type { 16 | return receiver.buftype 17 | } 18 | -------------------------------------------------------------------------------- /formatfamily.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/buftype" 5 | "github.com/reiver/go-v4l2/pixelformat" 6 | ) 7 | 8 | // FormatFamily is a format family. 9 | // 10 | // A format family contains: 11 | // 12 | // • a human-readable description, 13 | // 14 | // • a pixel format (as a FOURCC code), and 15 | // 16 | // • flags. 17 | // 18 | // As well as logicall "containing" a list of frame sizes. 19 | // 20 | // The possible flags are given by the constants: 21 | // 22 | // • v4l2.FormatFamilyFlagCompressed, and 23 | // 24 | // • v4l2.FormatFamilyFlagEmulated. 25 | // 26 | // Samples: 27 | // 28 | // An example format migh have have values such as: 29 | // 30 | // • description: "YUYV 4:2:2" 31 | // 32 | // • pixel format: "YUYV" 33 | // 34 | // • (flag) compressed: false 35 | // 36 | // • (flag) emulated: false 37 | // 38 | // Or an example format migh have have values such as: 39 | // 40 | // • description: "Motion-JPEG" 41 | // 42 | // • pixel format: "MJPG" 43 | // 44 | // • (flag) compressed: true 45 | // 46 | // • (flag) emulated: false 47 | // 48 | // Etc. 49 | // 50 | // Usually, one would get a series of formats by iterating through all the supported formats 51 | // that are supported by a device. 52 | // 53 | // Example: 54 | // 55 | // var device v4l2.Device 56 | // 57 | // // ... 58 | // 59 | // formats, err := device.Formats() 60 | // if nil != err { 61 | // return err 62 | // } 63 | // defer formats.Close() 64 | // 65 | // var formatFamily v4l2.FormatFamily // <---- NOTE THAT THIS IS THE v4l2.FormatFamily TYPE. 66 | // forformats.Next() { 67 | // 68 | // err := formats.Decode(&formatFamily) // <---- NOTE THAT WE ARE PUTTING A NEW VALUE INTO THE v4l2.FormatFamily HERE. 69 | // if nil != err { 70 | // fmt.Fprintf(os.Stderr, "ERROR: Problem decoding format family: (%T) %v \n", err, err) 71 | // return err 72 | // } 73 | // 74 | // fmt.Printf("[format family] %q (%q) {compressed=%t} {emulated=%t} \n", 75 | // formatFamily.Description(), 76 | // formatFamily.PixelFormat(), 77 | // formatFamily.HasFlags(v4l2.FormatFamilyFlagCompressed), 78 | // formatFamily.HasFlags(v4l2.FormatFamilyFlagEmulated), 79 | // ) 80 | // } 81 | // if err := formats.Err(); nil != err { 82 | // return err 83 | // } 84 | // 85 | // v4l2.FormatFamily is the same as the V4L2 (Video4Linux version 2) type v4l2_fmtdesc. 86 | type FormatFamily struct { 87 | device *Device 88 | internal internalFormatFamily 89 | } 90 | 91 | type internalFormatFamily struct { 92 | index uint32 // Format number 93 | typ v4l2_buftype.Type // enum v4l2_buf_type 94 | flags uint32 95 | description [32]byte // Description string 96 | pixelFormat v4l2_pixelformat.Type // Format fourcc 97 | reserved [4]uint32 98 | } 99 | -------------------------------------------------------------------------------- /formatfamily_description.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // Description returns a human-readable description for the format family. 4 | // 5 | // Some examples of what might be returned by Description are; 6 | // 7 | // • "YUYV 4:2:2", 8 | // 9 | // • "Motion-JPEG", 10 | // 11 | // • etc. 12 | func (receiver FormatFamily) Description() string { 13 | 14 | max := len(receiver.internal.description) 15 | 16 | index := 0 17 | 18 | for ; index < max; index++ { 19 | if 0 == receiver.internal.description[index] { 20 | break 21 | } 22 | } 23 | 24 | return string(receiver.internal.description[:index]) 25 | } 26 | 27 | -------------------------------------------------------------------------------- /formatfamily_flags.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | const ( 4 | FormatFamilyFlagCompressed = 0x0001 // V4L2_FMT_FLAG_COMPRESSED 5 | FormatFamilyFlagEmulated = 0x0002 // V4L2_FMT_FLAG_EMULATED 6 | ) 7 | 8 | // HasFlags returns true if the format family has all the flags inquired about, 9 | // and returns false otherwise. 10 | // 11 | // Example: 12 | // 13 | // var formatFamily v4l2.FormatFamily 14 | // 15 | // // ... 16 | // 17 | // if formatFamily.HasFlags(v4l2.FormatFamilyFlagCompressed) { 18 | // //@TODO 19 | // } 20 | // 21 | // Example: 22 | // 23 | // var formatFamily v4l2.FormatFamily 24 | // 25 | // // ... 26 | // 27 | // if formatFamily.HasFlags(v4l2.FormatFamilyFlagEmulated) { 28 | // //@TODO 29 | // } 30 | // 31 | // Example: 32 | // 33 | // var formatFamily v4l2.FormatFamily 34 | // 35 | // // ... 36 | // 37 | // if formatFamily.HasFlags(v4l2.FormatFamilyFlagCompressed | v4l2.FormatFamilyFlagEmulated) { 38 | // //@TODO 39 | // } 40 | func (receiver FormatFamily) HasFlags(flags uint32) bool { 41 | return 0 != (receiver.internal.flags & flags) 42 | } 43 | -------------------------------------------------------------------------------- /formatfamily_framesizes.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/framesize" 5 | "github.com/reiver/go-v4l2/pixelformat" 6 | 7 | "golang.org/x/sys/unix" 8 | 9 | "unsafe" 10 | ) 11 | 12 | // FrameSizes returns an iterator that enables you to list out all the supported frame sizes by the format family. 13 | func (receiver *FormatFamily) FrameSizes() (FrameSizes, error) { 14 | if nil == receiver { 15 | return FrameSizes{}, errNilReceiver 16 | } 17 | 18 | device := receiver.device 19 | if nil == device { 20 | return FrameSizes{}, errInternalError 21 | } 22 | 23 | if err := device.unfit(); nil != err { 24 | return FrameSizes{}, err 25 | } 26 | 27 | return FrameSizes{ 28 | device: receiver.device, 29 | pixelFormat: receiver.internal.pixelFormat, 30 | }, nil 31 | } 32 | 33 | // FrameSizes is an interator that enables you to list out all the supported formats by the format family. 34 | type FrameSizes struct { 35 | device *Device 36 | pixelFormat v4l2_pixelformat.Type 37 | err error 38 | datum v4l2_framesize.Type 39 | } 40 | 41 | // Close closes the FrameSizes iterator. 42 | func (receiver *FrameSizes) Close() error { 43 | if nil == receiver { 44 | return nil 45 | } 46 | 47 | receiver.device = nil 48 | receiver.err = nil 49 | receiver.datum.Index = 0 50 | 51 | return nil 52 | } 53 | 54 | // Decode loads the next frame size (previously obtained by calling Next). 55 | func (receiver FrameSizes) Decode(x interface{}) error { 56 | if nil != receiver.err { 57 | return receiver.err 58 | } 59 | 60 | p, ok := x.(*v4l2_framesize.Type) 61 | if !ok { 62 | return errUnsupportedType 63 | } 64 | 65 | *p = receiver.datum 66 | 67 | return nil 68 | } 69 | 70 | // Err returns any errors that occurred when Next was called. 71 | func (receiver *FrameSizes) Err() error { 72 | if nil == receiver { 73 | return errNilReceiver 74 | } 75 | 76 | return receiver.err 77 | } 78 | 79 | // Next fetches the next frame size. 80 | // 81 | // If there is a next frame size, it returns true. 82 | // And the next frame size get be obtained by calling Decode. 83 | // 84 | // If there is not next frame size, then it returns false. 85 | func (receiver *FrameSizes) Next() bool { 86 | if nil == receiver { 87 | return false 88 | } 89 | 90 | device := receiver.device 91 | if nil == device { 92 | receiver.err = errInternalError 93 | return false 94 | } 95 | 96 | receiver.datum.PixelFormat = receiver.pixelFormat 97 | 98 | _, _, errorNumber := unix.Syscall( 99 | unix.SYS_IOCTL, 100 | uintptr(device.fileDescriptor), 101 | const_VIDIOC_ENUM_FRAMESIZES, 102 | uintptr(unsafe.Pointer(&receiver.datum)), 103 | ) 104 | if unix.EINVAL == errorNumber { 105 | return false 106 | } 107 | if 0 != errorNumber { 108 | receiver.err = errorNumber 109 | return false 110 | } 111 | 112 | receiver.datum.Index++ 113 | 114 | return true 115 | } 116 | -------------------------------------------------------------------------------- /formatfamily_pixelformat.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/pixelformat" 5 | ) 6 | 7 | // PixelFormat returns the formal family's pixel format. 8 | // 9 | // Some examples of what might be returned by PixelFormat are; 10 | // 11 | // • v4l2_pixelformat.FourCC("YUYV"), 12 | // 13 | // • v4l2_pixelformat.FourCC("MJPG"), 14 | // 15 | // • etc, 16 | func (receiver FormatFamily) PixelFormat() v4l2_pixelformat.Type { 17 | return receiver.internal.pixelFormat 18 | } 19 | -------------------------------------------------------------------------------- /framesize/cast.go: -------------------------------------------------------------------------------- 1 | package v4l2_framesize 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | ) 7 | 8 | const ( 9 | const_TYPE_DISCRETE = 1 // (V4L2_FRMSIZE_TYPE_DISCRETE) 10 | const_TYPE_CONTINUOUS = 2 // (V4L2_FRMSIZE_TYPE_CONTINUOUS) 11 | const_TYPE_STEPWISE = 3 // (V4L2_FRMSIZE_TYPE_STEPWISE) 12 | ) 13 | 14 | // Cast tries to turn a v4l2_framesize.Type into a: 15 | // v4l2_framesize.Discrete, 16 | // v4l2_framesize.Continuous, or 17 | // v4l2_framesize.Stepwise. 18 | // 19 | // Example: 20 | // 21 | // switch casted := frameSize.Cast().(type) { 22 | // case v4l2_framesize.Continuous 23 | // //@TODO 24 | // case v4l2_framesize.Discrete: 25 | // //@TODO 26 | // case v4l2_framesize.Stepwise 27 | // //@TODO 28 | // default: 29 | // //@TODO: Error, unknown framesize type. 30 | // } 31 | func (receiver Type) Cast() (interface{}, error) { 32 | switch receiver.typ { 33 | case const_TYPE_DISCRETE: 34 | return *(*Discrete)(unsafe.Pointer(&receiver)), nil 35 | case const_TYPE_CONTINUOUS: 36 | return *(*Continuous)(unsafe.Pointer(&receiver)), nil 37 | case const_TYPE_STEPWISE: 38 | return *(*Stepwise)(unsafe.Pointer(&receiver)), nil 39 | default: 40 | return nil, fmt.Errorf("Unexpected frame size type: %d", receiver.typ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /framesize/continuous.go: -------------------------------------------------------------------------------- 1 | package v4l2_framesize 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/pixelformat" 5 | ) 6 | 7 | type Continuous struct { 8 | index uint32 // Frame size number 9 | PixelFormat v4l2_pixelformat.Type // Pixel format 10 | typ uint32 // Frame size type the device supports. 11 | 12 | MinWidth uint32 // Minimum frame width [pixel] 13 | MaxWidth uint32 // Maximum frame width [pixel] 14 | StepWidth uint32 // Frame width step size [pixel] 15 | MinHeight uint32 // Minimum frame height [pixel] 16 | MaxHeight uint32 // Maximum frame height [pixel] 17 | StepHeight uint32 // Frame height step size [pixel] 18 | 19 | reserved [2]uint32 // Reserved space for future use 20 | } 21 | -------------------------------------------------------------------------------- /framesize/discrete.go: -------------------------------------------------------------------------------- 1 | package v4l2_framesize 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/pixelformat" 5 | ) 6 | 7 | type Discrete struct { 8 | index uint32 // Frame size number 9 | PixelFormat v4l2_pixelformat.Type // Pixel format 10 | typ uint32 // Frame size type the device supports. 11 | 12 | Width uint32 13 | Height uint32 14 | restOfFrameSize [4]uint32 15 | 16 | reserved [2]uint32 // Reserved space for future use 17 | } 18 | -------------------------------------------------------------------------------- /framesize/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package v4l2_framesize provides a type used to represents a V4L2 (Video4Linux version 2) frame size. 3 | */ 4 | package v4l2_framesize 5 | -------------------------------------------------------------------------------- /framesize/stepwise.go: -------------------------------------------------------------------------------- 1 | package v4l2_framesize 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/pixelformat" 5 | ) 6 | 7 | type Stepwise struct { 8 | index uint32 // Frame size number 9 | PixelFormat v4l2_pixelformat.Type // Pixel format 10 | typ uint32 // Frame size type the device supports. 11 | 12 | MinWidth uint32 // Minimum frame width [pixel] 13 | MaxWidth uint32 // Maximum frame width [pixel] 14 | StepWidth uint32 // Frame width step size [pixel] 15 | MinHeight uint32 // Minimum frame height [pixel] 16 | MaxHeight uint32 // Maximum frame height [pixel] 17 | StepHeight uint32 // Frame height step size [pixel] 18 | 19 | reserved [2]uint32 // Reserved space for future use 20 | } 21 | 22 | -------------------------------------------------------------------------------- /framesize/type.go: -------------------------------------------------------------------------------- 1 | package v4l2_framesize 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/pixelformat" 5 | ) 6 | 7 | type Type struct { 8 | Index uint32 // Frame size number 9 | PixelFormat v4l2_pixelformat.Type // Pixel format 10 | typ uint32 // Frame size type the device supports. 11 | 12 | frameSize [6]uint32 // Frame size 13 | 14 | reserved [2]uint32 // Reserved space for future use 15 | } 16 | -------------------------------------------------------------------------------- /internal_capabilities.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "golang.org/x/sys/unix" 5 | 6 | "fmt" 7 | "unsafe" 8 | ) 9 | 10 | // internalCapability is used to receive the capabilities of a V4L2 (Video4Linux video 2) device. 11 | // 12 | // Example: 13 | // 14 | // device, err := v4l2.Open(v4l2.Video0) 15 | // 16 | // //... 17 | // 18 | // var cap internalCapability 19 | // 20 | // err := cap.Query(videoDevice) 21 | // 22 | // This is used internally of v4l2.Device 23 | type internalCapability struct { 24 | driver [16]uint8 25 | card [32]uint8 26 | busInfo [32]uint8 27 | version uint32 28 | capabilities uint32 29 | deviceCaps uint32 30 | reserved [3]uint32 31 | } 32 | 33 | func (receiver *internalCapability) QueryFd(fileDesciptor int) error { 34 | if nil == receiver { 35 | return errNilReceiver 36 | } 37 | 38 | _, _, errorNumber := unix.Syscall( 39 | unix.SYS_IOCTL, 40 | uintptr(fileDesciptor), 41 | const_VIDIOC_QUERYCAP, 42 | uintptr(unsafe.Pointer(receiver)), 43 | ) 44 | if 0 != errorNumber { 45 | return errorNumber 46 | } 47 | 48 | return nil 49 | } 50 | 51 | func (receiver internalCapability) BusInfo() string { 52 | 53 | max := len(receiver.busInfo) 54 | 55 | index := 0 56 | 57 | for ; index < max; index++ { 58 | if 0 == receiver.busInfo[index] { 59 | break 60 | } 61 | } 62 | 63 | return string(receiver.busInfo[:index]) 64 | } 65 | 66 | func (receiver internalCapability) Card() string { 67 | 68 | max := len(receiver.card) 69 | 70 | index := 0 71 | 72 | for ; index < max; index++ { 73 | if 0 == receiver.card[index] { 74 | break 75 | } 76 | } 77 | 78 | return string(receiver.card[:index]) 79 | } 80 | 81 | func (receiver internalCapability) Driver() string { 82 | 83 | max := len(receiver.driver) 84 | 85 | index := 0 86 | 87 | for ; index < max; index++ { 88 | if 0 == receiver.driver[index] { 89 | break 90 | } 91 | } 92 | 93 | return string(receiver.driver[:index]) 94 | } 95 | 96 | func (receiver internalCapability) Version() string { 97 | version := receiver.version 98 | 99 | major := (version >> 16) & 0xFF 100 | minor := (version >> 8) & 0xFF 101 | patch := (version ) & 0xFF 102 | 103 | return fmt.Sprintf("%d.%d.%d", major, minor, patch) 104 | } 105 | -------------------------------------------------------------------------------- /ioc.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | // These constants were mimicked from: 4 | // /include/uapi/asm-generic/ioctl.h 5 | const ( 6 | const_IOC_NRBITS = 8 7 | const_IOC_TYPEBITS = 8 8 | 9 | const_IOC_SIZEBITS = 14 10 | const_IOC_DIRBITS = 2 11 | 12 | const_IOC_NRMASK = ((1 << const_IOC_NRBITS)-1) 13 | const_IOC_TYPEMASK = ((1 << const_IOC_TYPEBITS)-1) 14 | const_IOC_SIZEMASK = ((1 << const_IOC_SIZEBITS)-1) 15 | const_IOC_DIRMASK = ((1 << const_IOC_DIRBITS)-1) 16 | 17 | const_IOC_NONE = 0 18 | const_IOC_WRITE = 1 19 | const_IOC_READ = 2 20 | 21 | const_IOC_NRSHIFT = 0 22 | const_IOC_TYPESHIFT = (const_IOC_NRSHIFT + const_IOC_NRBITS) 23 | const_IOC_SIZESHIFT = (const_IOC_TYPESHIFT + const_IOC_TYPEBITS) 24 | const_IOC_DIRSHIFT = (const_IOC_SIZESHIFT + const_IOC_SIZEBITS) 25 | ) 26 | 27 | -------------------------------------------------------------------------------- /pixelformat/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package v4l2_pixelformat provides a type used to represents a V4L2 (Video4Linux version 2) pixel format. 3 | 4 | Example 5 | 6 | var pixelFormat v4l2_pixelformat.Type 7 | 8 | pixelFormat = v4l2_pixelformat.FourCC("MJPG") 9 | 10 | Example 11 | 12 | var pixelFormat 13 | 14 | // ... 15 | 16 | var humanReadable string = pixelFormat.String() 17 | */ 18 | package v4l2_pixelformat 19 | -------------------------------------------------------------------------------- /pixelformat/type.go: -------------------------------------------------------------------------------- 1 | package v4l2_pixelformat 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Type represents a V4L2 (Video4Linux version 2) pixel format. 8 | type Type struct { 9 | 10 | // NOTE THAT THIS STRUCT MUST BE EXACLTLY 4 BYTES IN SIZE. 11 | // 12 | // THIS IS WHAT THE V4L2 (Video4Linux version 2) DRIVERS EXPECT. 13 | // 14 | // THUS WE CANNOT PUT EXTRA (USEFUL) HIDDEN FIELDS IN HERE. 15 | // 16 | // THERE MUST ONLY BE A SINGLE uint32 FIELD IN THIS STRUCT! 17 | 18 | value uint32 19 | } 20 | 21 | // FourCC returns a pixelformat.Type from a FOURCC code. 22 | // 23 | // Example: 24 | // 25 | // var pixelFormat v4l2_pixelformat.Type 26 | // 27 | // pixelFormat = v4l2_pixelformat.FourCC("YUYV") 28 | func FourCC(value string) Type { 29 | 30 | var a byte = ' ' 31 | var b byte = ' ' 32 | var c byte = ' ' 33 | var d byte = ' ' 34 | 35 | { 36 | length := len(value) 37 | 38 | if 1 <= length { 39 | a = byte(value[0]) 40 | } 41 | if 2 <= length { 42 | b = byte(value[1]) 43 | } 44 | if 3 <= length { 45 | c = byte(value[2]) 46 | } 47 | if 4 <= length { 48 | d = byte(value[3]) 49 | } 50 | } 51 | 52 | var code uint32 53 | 54 | code = uint32(a) | 55 | (uint32(b) << 8) | 56 | (uint32(c) << 16) | 57 | (uint32(d) << 24) 58 | 59 | return Type{code} 60 | } 61 | 62 | // String returns a human-readable version of v4l2_pixelformat.Type. 63 | // 64 | // So, rather than returning a machine-readable binary uint32, 65 | // it returns a human-readable FOURCC code string. 66 | // 67 | // So, for example, even if the value stored as the machine-readable uint32 0x47504a4d, 68 | // String will return this as "MJPG". 69 | // 70 | // Or also, for example, even if the value stored as the machine-readable uint32 0x30385056, 71 | // String will return this as "VP80". 72 | // 73 | // Example 74 | // 75 | // pixelFormat := v4l2_pixelformat.FourCC("PAL8") 76 | // 77 | // fmt.Printf("Pixel Format: %q \n", pixelFormat) 78 | // // Output: Pixel Format: "PAL8" 79 | func (receiver Type) String() string { 80 | a := byte( receiver.value & 0xff) 81 | b := byte((receiver.value >> 8) & 0xff) 82 | c := byte((receiver.value >> 16) & 0xff) 83 | d := byte((receiver.value >> 24) & 0xff) 84 | 85 | return fmt.Sprintf("%c%c%c%c", a, b, c, d) 86 | } 87 | -------------------------------------------------------------------------------- /pixelformat/type_test.go: -------------------------------------------------------------------------------- 1 | package v4l2_pixelformat 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFourCC(t *testing.T) { 8 | 9 | tests := []struct{ 10 | Value string 11 | Expected uint32 12 | }{ 13 | { 14 | Value: "", 15 | Expected: 0x20202020, 16 | }, 17 | 18 | 19 | 20 | { 21 | Value: " ", 22 | Expected: 0x20202020, 23 | }, 24 | { 25 | Value: " ", 26 | Expected: 0x20202020, 27 | }, 28 | { 29 | Value: " ", 30 | Expected: 0x20202020, 31 | }, 32 | { 33 | Value: " ", 34 | Expected: 0x20202020, 35 | }, 36 | 37 | 38 | 39 | { 40 | Value: "a", 41 | Expected: 0x20202061, 42 | }, 43 | { 44 | Value: "ab", 45 | Expected: 0x20206261, 46 | }, 47 | { 48 | Value: "abc", 49 | Expected: 0x20636261, 50 | }, 51 | { 52 | Value: "abcd", 53 | Expected: 0x64636261, 54 | }, 55 | { 56 | Value: "abcde", 57 | Expected: 0x64636261, 58 | }, 59 | { 60 | Value: "abcdef", 61 | Expected: 0x64636261, 62 | }, 63 | 64 | 65 | 66 | { 67 | Value: "A", 68 | Expected: 0x20202041, 69 | }, 70 | { 71 | Value: "AB", 72 | Expected: 0x20204241, 73 | }, 74 | { 75 | Value: "ABC", 76 | Expected: 0x20434241, 77 | }, 78 | { 79 | Value: "ABCD", 80 | Expected: 0x44434241, 81 | }, 82 | { 83 | Value: "ABCDE", 84 | Expected: 0x44434241, 85 | }, 86 | { 87 | Value: "ABCDEF", 88 | Expected: 0x44434241, 89 | }, 90 | { 91 | Value: "ABCDEFG", 92 | Expected: 0x44434241, 93 | }, 94 | 95 | 96 | 97 | { 98 | Value: "A", 99 | Expected: 0x20202041, 100 | }, 101 | 102 | 103 | 104 | { 105 | Value: "GREY", 106 | Expected: 0x59455247, 107 | }, 108 | { 109 | Value: "RGB1", 110 | Expected: 0x31424752, 111 | }, 112 | { 113 | Value: "H264", 114 | Expected: 0x34363248, 115 | }, 116 | { 117 | Value: "MJPG", 118 | Expected: 0x47504a4d, 119 | }, 120 | { 121 | Value: "MPEG", 122 | Expected: 0x4745504d, 123 | }, 124 | { 125 | Value: "PAL8", 126 | Expected: 0x384c4150, 127 | }, 128 | { 129 | Value: "VP80", 130 | Expected: 0x30385056, 131 | }, 132 | { 133 | Value: "XVID", 134 | Expected: 0x44495658, 135 | }, 136 | 137 | 138 | 139 | { 140 | Value: "UV8", 141 | Expected: 0x20385655, 142 | }, 143 | { 144 | Value: "UV8 ", 145 | Expected: 0x20385655, 146 | }, 147 | 148 | 149 | 150 | { 151 | Value: "Y04", 152 | Expected: 0x20343059, 153 | }, 154 | { 155 | Value: "Y04 ", 156 | Expected: 0x20343059, 157 | }, 158 | } 159 | 160 | 161 | for testNumber, test := range tests { 162 | 163 | pixelFormat := FourCC(test.Value) 164 | 165 | if actual, expected := pixelFormat.value, test.Expected; expected != actual { 166 | t.Errorf("For test #%d, expected %#x (%q), but actually got %#x (%q).", testNumber, expected, Type{expected}.String(), actual, Type{actual}.String()) 167 | continue 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /vidioc.go: -------------------------------------------------------------------------------- 1 | package v4l2 2 | 3 | import ( 4 | "github.com/reiver/go-v4l2/framesize" 5 | "github.com/reiver/go-v4l2/format" 6 | 7 | "unsafe" 8 | ) 9 | 10 | // ioctl codes for V4L2 (Video4Linux version 2) video devices. 11 | const ( 12 | // A Golang conversion of the following C code: 13 | // 14 | // #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) 15 | const_VIDIOC_QUERYCAP = (const_IOC_READ << const_IOC_DIRSHIFT) | 16 | (uintptr('V') << const_IOC_TYPESHIFT) | 17 | (0 << const_IOC_NRSHIFT) | 18 | (unsafe.Sizeof(internalCapability{}) << const_IOC_SIZESHIFT) 19 | 20 | // A Golang conversion of the following C code: 21 | // 22 | // #define VIDIOC_RESERVED _IO('V', 1) 23 | const_VIDIOC_RESERVED = (const_IOC_NONE << const_IOC_DIRSHIFT) | 24 | (uintptr('V') << const_IOC_TYPESHIFT) | 25 | (1 << const_IOC_NRSHIFT) | 26 | (0 << const_IOC_SIZESHIFT) 27 | 28 | // A Golang conversion of the following C code: 29 | // 30 | // #define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc) 31 | const_VIDIOC_ENUM_FMT = ((const_IOC_READ | const_IOC_WRITE) << const_IOC_DIRSHIFT) | 32 | (uintptr('V') << const_IOC_TYPESHIFT) | 33 | (2 << const_IOC_NRSHIFT) | 34 | (unsafe.Sizeof(internalFormatFamily{}) << const_IOC_SIZESHIFT) 35 | 36 | // A Golang conversion of the following C code: 37 | // 38 | // #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) 39 | const_VIDIOC_S_FMT = ((const_IOC_READ | const_IOC_WRITE) << const_IOC_DIRSHIFT) | 40 | (uintptr('V') << const_IOC_TYPESHIFT) | 41 | (5 << const_IOC_NRSHIFT) | 42 | (unsafe.Sizeof(v4l2_format.Type{}) << const_IOC_SIZESHIFT) 43 | 44 | // A Golang conversion of the following C code: 45 | // 46 | // #define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) 47 | const_VIDIOC_ENUM_FRAMESIZES = ((const_IOC_READ | const_IOC_WRITE) << const_IOC_DIRSHIFT) | 48 | (uintptr('V') << const_IOC_TYPESHIFT) | 49 | (74 << const_IOC_NRSHIFT) | 50 | (unsafe.Sizeof(v4l2_framesize.Type{}) << const_IOC_SIZESHIFT) 51 | 52 | ) 53 | 54 | --------------------------------------------------------------------------------