Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
O
obs-studio
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Yunxiang Li
obs-studio
Commits
32911f77
Commit
32911f77
authored
10 years ago
by
Azat Khasanshin
Browse files
Options
Downloads
Patches
Plain Diff
v4l2 plugin properties
added ability to choose device, pixel format, resolution and frame rate
parent
5f8a6db8
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
plugins/linux-v4l2/v4l2-input.c
+346
-43
346 additions, 43 deletions
plugins/linux-v4l2/v4l2-input.c
with
346 additions
and
43 deletions
plugins/linux-v4l2/v4l2-input.c
+
346
−
43
View file @
32911f77
...
...
@@ -15,10 +15,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include
<stdio.h>
#include
<stdlib.h>
#include
<unistd.h>
#include
<string.h>
#include
<inttypes.h>
#include
<fcntl.h>
#include
<dirent.h>
#include
<sys/mman.h>
#include
<sys/time.h>
#include
<sys/types.h>
...
...
@@ -29,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include
<util/threading.h>
#include
<util/bmem.h>
#include
<util/dstr.h>
#include
<obs.h>
#define V4L2_DATA(voidptr) struct v4l2_data *data = voidptr;
...
...
@@ -36,14 +39,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define timeval2ns(tv) \
(((uint64_t) tv.tv_sec * 1000000000) + ((uint64_t) tv.tv_usec * 1000))
static
const
char
video
[]
=
"/dev/video0"
;
struct
v4l2_buffer_data
{
size_t
length
;
void
*
start
;
};
struct
v4l2_data
{
char
*
device
;
pthread_t
thread
;
os_event_t
event
;
obs_source_t
source
;
...
...
@@ -52,10 +55,41 @@ struct v4l2_data {
uint64_t
frames
;
int_fast32_t
dev
;
int_fast32_t
pixelformat
;
int_fast32_t
width
;
int_fast32_t
height
;
int_fast32_t
fps_numerator
;
int_fast32_t
fps_denominator
;
uint_fast32_t
buf_count
;
struct
v4l2_buffer_data
*
buf
;
};
static
enum
video_format
v4l2_to_obs_video_format
(
uint_fast32_t
format
)
{
switch
(
format
)
{
case
V4L2_PIX_FMT_YVYU
:
return
VIDEO_FORMAT_YVYU
;
case
V4L2_PIX_FMT_YUYV
:
return
VIDEO_FORMAT_YUY2
;
case
V4L2_PIX_FMT_UYVY
:
return
VIDEO_FORMAT_UYVY
;
case
V4L2_PIX_FMT_NV12
:
return
VIDEO_FORMAT_NV12
;
case
V4L2_PIX_FMT_YUV420
:
return
VIDEO_FORMAT_I420
;
default:
return
VIDEO_FORMAT_NONE
;
}
}
/*
* used to store framerate and resolution values
*/
static
int
pack_tuple
(
int
a
,
int
b
)
{
return
(
a
<<
16
)
|
(
b
&
0xffff
);
}
static
void
unpack_tuple
(
int
*
a
,
int
*
b
,
int
packed
)
{
*
a
=
packed
>>
16
;
*
b
=
packed
&
0xffff
;
}
/*
* start capture
*/
...
...
@@ -212,10 +246,10 @@ static void *v4l2_thread(void *vptr)
out
.
color_range_max
);
out
.
data
[
0
]
=
(
uint8_t
*
)
data
->
buf
[
buf
.
index
].
start
;
out
.
linesize
[
0
]
=
data
->
linesize
;
out
.
width
=
640
;
out
.
height
=
480
;
out
.
width
=
data
->
width
;
out
.
height
=
data
->
height
;
out
.
timestamp
=
timeval2ns
(
buf
.
timestamp
);
out
.
format
=
VIDEO_FORMAT_YUY2
;
out
.
format
=
v4l2_to_obs_video_format
(
data
->
pixelformat
)
;
obs_source_output_video
(
data
->
source
,
&
out
);
...
...
@@ -238,74 +272,288 @@ static const char* v4l2_getname(const char* locale)
return
"V4L2 Capture Input"
;
}
static
void
v4l2_destroy
(
void
*
vptr
)
static
void
v4l2_defaults
(
obs_data_t
settings
)
{
obs_data_set_default_int
(
settings
,
"pixelformat"
,
V4L2_PIX_FMT_YUYV
);
obs_data_set_default_int
(
settings
,
"resolution"
,
pack_tuple
(
640
,
480
));
obs_data_set_default_int
(
settings
,
"framerate"
,
pack_tuple
(
1
,
30
));
}
/*
* List available devices
*/
static
void
v4l2_device_list
(
obs_property_t
prop
,
obs_data_t
settings
)
{
DIR
*
dirp
;
struct
dirent
*
dp
;
int
fd
;
struct
v4l2_capability
video_cap
;
struct
dstr
device
;
bool
first
=
true
;
obs_property_list_clear
(
prop
);
dstr_init_copy
(
&
device
,
"/dev/"
);
dirp
=
opendir
(
"/sys/class/video4linux"
);
if
(
dirp
)
{
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
dstr_resize
(
&
device
,
5
);
dstr_cat
(
&
device
,
dp
->
d_name
);
if
((
fd
=
open
(
device
.
array
,
O_RDWR
|
O_NONBLOCK
))
==
-
1
)
{
continue
;
}
if
(
ioctl
(
fd
,
VIDIOC_QUERYCAP
,
&
video_cap
)
==
-
1
)
{
continue
;
}
else
if
(
video_cap
.
capabilities
&
V4L2_CAP_VIDEO_CAPTURE
)
{
obs_property_list_add_string
(
prop
,
(
char
*
)
video_cap
.
card
,
device
.
array
);
if
(
first
)
{
obs_data_setstring
(
settings
,
"device_id"
,
device
.
array
);
first
=
false
;
}
}
close
(
fd
);
}
closedir
(
dirp
);
}
dstr_free
(
&
device
);
}
/*
* List formats for device
*/
static
void
v4l2_format_list
(
int
dev
,
obs_property_t
prop
)
{
struct
v4l2_fmtdesc
fmt
;
fmt
.
type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE
;
fmt
.
index
=
0
;
obs_property_list_clear
(
prop
);
while
(
ioctl
(
dev
,
VIDIOC_ENUM_FMT
,
&
fmt
)
==
0
)
{
if
(
v4l2_to_obs_video_format
(
fmt
.
pixelformat
)
!=
VIDEO_FORMAT_NONE
)
{
obs_property_list_add_int
(
prop
,
(
char
*
)
fmt
.
description
,
fmt
.
pixelformat
);
}
fmt
.
index
++
;
}
}
/*
* List resolutions for device and format
*/
static
void
v4l2_resolution_list
(
int
dev
,
uint_fast32_t
pixelformat
,
obs_property_t
prop
)
{
struct
v4l2_frmsizeenum
frmsize
;
frmsize
.
pixel_format
=
pixelformat
;
frmsize
.
index
=
0
;
struct
dstr
buffer
;
dstr_init
(
&
buffer
);
obs_property_list_clear
(
prop
);
while
(
ioctl
(
dev
,
VIDIOC_ENUM_FRAMESIZES
,
&
frmsize
)
==
0
)
{
dstr_printf
(
&
buffer
,
"%dx%d"
,
frmsize
.
discrete
.
width
,
frmsize
.
discrete
.
height
);
obs_property_list_add_int
(
prop
,
buffer
.
array
,
pack_tuple
(
frmsize
.
discrete
.
width
,
frmsize
.
discrete
.
height
));
frmsize
.
index
++
;
}
dstr_free
(
&
buffer
);
}
/*
* List framerates for device and resolution
*/
static
void
v4l2_framerate_list
(
int
dev
,
uint_fast32_t
pixelformat
,
uint_fast32_t
width
,
uint_fast32_t
height
,
obs_property_t
prop
)
{
struct
v4l2_frmivalenum
frmival
;
frmival
.
pixel_format
=
pixelformat
;
frmival
.
width
=
width
;
frmival
.
height
=
height
;
frmival
.
index
=
0
;
struct
dstr
buffer
;
dstr_init
(
&
buffer
);
obs_property_list_clear
(
prop
);
while
(
ioctl
(
dev
,
VIDIOC_ENUM_FRAMEINTERVALS
,
&
frmival
)
==
0
)
{
float
fps
=
(
float
)
frmival
.
discrete
.
denominator
/
frmival
.
discrete
.
numerator
;
int
pack
=
pack_tuple
(
frmival
.
discrete
.
numerator
,
frmival
.
discrete
.
denominator
);
dstr_printf
(
&
buffer
,
"%.2f"
,
fps
);
obs_property_list_add_int
(
prop
,
buffer
.
array
,
pack
);
frmival
.
index
++
;
}
dstr_free
(
&
buffer
);
}
/*
* Device selected callback
*/
static
bool
device_selected
(
obs_properties_t
props
,
obs_property_t
p
,
obs_data_t
settings
)
{
UNUSED_PARAMETER
(
p
);
int
dev
=
open
(
obs_data_getstring
(
settings
,
"device_id"
),
O_RDWR
|
O_NONBLOCK
);
obs_property_t
prop
=
obs_properties_get
(
props
,
"pixelformat"
);
v4l2_format_list
(
dev
,
prop
);
obs_property_modified
(
prop
,
settings
);
close
(
dev
);
return
true
;
}
/*
* Format selected callback
*/
static
bool
format_selected
(
obs_properties_t
props
,
obs_property_t
p
,
obs_data_t
settings
)
{
UNUSED_PARAMETER
(
p
);
int
dev
=
open
(
obs_data_getstring
(
settings
,
"device_id"
),
O_RDWR
|
O_NONBLOCK
);
obs_property_t
prop
=
obs_properties_get
(
props
,
"resolution"
);
v4l2_resolution_list
(
dev
,
obs_data_getint
(
settings
,
"pixelformat"
),
prop
);
obs_property_modified
(
prop
,
settings
);
close
(
dev
);
return
true
;
}
/*
* Resolution selected callback
*/
static
bool
resolution_selected
(
obs_properties_t
props
,
obs_property_t
p
,
obs_data_t
settings
)
{
UNUSED_PARAMETER
(
p
);
int
width
,
height
;
int
dev
=
open
(
obs_data_getstring
(
settings
,
"device_id"
),
O_RDWR
|
O_NONBLOCK
);
obs_property_t
prop
=
obs_properties_get
(
props
,
"framerate"
);
unpack_tuple
(
&
width
,
&
height
,
obs_data_getint
(
settings
,
"resolution"
));
v4l2_framerate_list
(
dev
,
obs_data_getint
(
settings
,
"pixelformat"
),
width
,
height
,
prop
);
obs_property_modified
(
prop
,
settings
);
close
(
dev
);
return
true
;
}
static
obs_properties_t
v4l2_properties
(
const
char
*
locale
)
{
obs_properties_t
props
=
obs_properties_create
(
locale
);
obs_property_t
device_list
=
obs_properties_add_list
(
props
,
"device_id"
,
"Device"
,
OBS_COMBO_TYPE_LIST
,
OBS_COMBO_FORMAT_STRING
);
obs_property_t
format_list
=
obs_properties_add_list
(
props
,
"pixelformat"
,
"Image Format"
,
OBS_COMBO_TYPE_LIST
,
OBS_COMBO_FORMAT_INT
);
obs_property_t
resolution_list
=
obs_properties_add_list
(
props
,
"resolution"
,
"Resolution"
,
OBS_COMBO_TYPE_LIST
,
OBS_COMBO_FORMAT_INT
);
obs_properties_add_list
(
props
,
"framerate"
,
"Frame Rate"
,
OBS_COMBO_TYPE_LIST
,
OBS_COMBO_FORMAT_INT
);
v4l2_device_list
(
device_list
,
NULL
);
obs_property_set_modified_callback
(
device_list
,
device_selected
);
obs_property_set_modified_callback
(
format_list
,
format_selected
);
obs_property_set_modified_callback
(
resolution_list
,
resolution_selected
);
return
props
;
}
static
uint32_t
v4l2_getwidth
(
void
*
vptr
)
{
V4L2_DATA
(
vptr
);
if
(
!
data
)
return
;
return
data
->
width
;
}
static
uint32_t
v4l2_getheight
(
void
*
vptr
)
{
V4L2_DATA
(
vptr
);
return
data
->
height
;
}
static
void
v4l2_terminate
(
struct
v4l2_data
*
data
)
{
if
(
data
->
thread
)
{
os_event_signal
(
data
->
event
);
pthread_join
(
data
->
thread
,
NULL
);
os_event_destroy
(
data
->
event
);
}
if
(
data
->
buf_count
)
v4l2_destroy_mmap
(
data
);
if
(
data
->
dev
!=
-
1
)
if
(
data
->
dev
!=
-
1
)
{
close
(
data
->
dev
);
data
->
dev
=
-
1
;
}
}
static
void
v4l2_destroy
(
void
*
vptr
)
{
V4L2_DATA
(
vptr
);
if
(
!
data
)
return
;
v4l2_terminate
(
data
);
if
(
data
->
device
)
bfree
(
data
->
device
);
bfree
(
data
);
}
static
void
*
v4l2_
create
(
obs_data_t
settings
,
obs_source_t
source
)
static
void
v4l2_
init
(
struct
v4l2_data
*
data
)
{
UNUSED_PARAMETER
(
settings
);
struct
v4l2_capability
cap
;
struct
v4l2_format
fmt
;
struct
v4l2_streamparm
par
;
struct
v4l2_data
*
data
=
bzalloc
(
sizeof
(
struct
v4l2_data
));
data
->
source
=
source
;
blog
(
LOG_DEBUG
,
"v4l2-input: New input created"
);
data
->
dev
=
open
(
video
,
O_RDWR
|
O_NONBLOCK
);
data
->
dev
=
open
(
data
->
device
,
O_RDWR
|
O_NONBLOCK
);
if
(
data
->
dev
==
-
1
)
{
blog
(
LOG_ERROR
,
"v4l2-input: Unable to open device: %s"
,
video
);
blog
(
LOG_ERROR
,
"v4l2-input: Unable to open device: %s"
,
data
->
device
);
goto
fail
;
}
if
(
ioctl
(
data
->
dev
,
VIDIOC_QUERYCAP
,
&
cap
)
<
0
)
{
blog
(
LOG_ERROR
,
"v4l2-input: Unable to get capabilities !"
);
goto
fail
;
}
blog
(
LOG_DEBUG
,
"v4l2-input: Got capabilities for '%s'"
,
cap
.
card
);
/* TODO: check if device supports needed capabilities */
fmt
.
type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE
;
fmt
.
fmt
.
pix
.
width
=
640
;
fmt
.
fmt
.
pix
.
height
=
480
;
fmt
.
fmt
.
pix
.
pixelformat
=
V4L2_PIX_FMT_YUYV
;
fmt
.
fmt
.
pix
.
width
=
data
->
width
;
fmt
.
fmt
.
pix
.
height
=
data
->
height
;
fmt
.
fmt
.
pix
.
pixelformat
=
data
->
pixelformat
;
fmt
.
fmt
.
pix
.
field
=
V4L2_FIELD_INTERLACED
;
if
(
ioctl
(
data
->
dev
,
VIDIOC_S_FMT
,
&
fmt
)
<
0
)
{
blog
(
LOG_DEBUG
,
"v4l2-input: unable to set format"
);
goto
fail
;
}
data
->
pixelformat
=
fmt
.
fmt
.
pix
.
pixelformat
;
data
->
width
=
fmt
.
fmt
.
pix
.
width
;
data
->
height
=
fmt
.
fmt
.
pix
.
height
;
par
.
type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE
;
par
.
parm
.
capture
.
timeperframe
.
numerator
=
1
;
par
.
parm
.
capture
.
timeperframe
.
denominator
=
30
;
par
.
parm
.
capture
.
timeperframe
.
numerator
=
data
->
fps_numerator
;
par
.
parm
.
capture
.
timeperframe
.
denominator
=
data
->
fps_denominator
;
if
(
ioctl
(
data
->
dev
,
VIDIOC_S_PARM
,
&
par
)
<
0
)
{
blog
(
LOG_DEBUG
,
"v4l2-input: unable to set params"
);
goto
fail
;
}
data
->
fps_numerator
=
par
.
parm
.
capture
.
timeperframe
.
numerator
;
data
->
fps_denominator
=
par
.
parm
.
capture
.
timeperframe
.
denominator
;
data
->
linesize
=
fmt
.
fmt
.
pix
.
bytesperline
;
blog
(
LOG_DEBUG
,
"v4l2-input: Linesize: %"
PRIuFAST32
,
data
->
linesize
);
...
...
@@ -319,25 +567,77 @@ static void *v4l2_create(obs_data_t settings, obs_source_t source)
goto
fail
;
if
(
pthread_create
(
&
data
->
thread
,
NULL
,
v4l2_thread
,
data
)
!=
0
)
goto
fail
;
return
data
;
return
;
fail:
v4l2_destroy
(
data
);
return
NULL
;
blog
(
LOG_DEBUG
,
"v4l2-input: initialization failed"
);
v4l2_terminate
(
data
)
;
}
static
uint32_t
v4l2_getwidth
(
void
*
vptr
)
static
void
v4l2_update
(
void
*
vptr
,
obs_data_t
settings
)
{
V4L2_DATA
(
vptr
);
bool
restart
=
false
;
const
char
*
new_device
;
int
width
,
height
,
fps_num
,
fps_denom
;
new_device
=
obs_data_getstring
(
settings
,
"device_id"
);
if
(
strlen
(
new_device
)
==
0
)
{
v4l2_device_list
(
NULL
,
settings
);
new_device
=
obs_data_getstring
(
settings
,
"device_id"
);
}
if
(
!
data
->
device
||
strcmp
(
data
->
device
,
new_device
)
!=
0
)
{
if
(
data
->
device
)
bfree
(
data
->
device
);
data
->
device
=
bstrdup
(
new_device
);
restart
=
true
;
}
if
(
data
->
pixelformat
!=
obs_data_getint
(
settings
,
"pixelformat"
))
{
data
->
pixelformat
=
obs_data_getint
(
settings
,
"pixelformat"
);
restart
=
true
;
}
unpack_tuple
(
&
width
,
&
height
,
obs_data_getint
(
settings
,
"resolution"
));
if
(
width
!=
data
->
width
||
height
!=
data
->
height
)
{
restart
=
true
;
}
return
640
;
unpack_tuple
(
&
fps_num
,
&
fps_denom
,
obs_data_getint
(
settings
,
"framerate"
));
if
(
fps_num
!=
data
->
fps_numerator
||
fps_denom
!=
data
->
fps_denominator
)
{
data
->
fps_numerator
=
fps_num
;
data
->
fps_denominator
=
fps_denom
;
restart
=
true
;
}
if
(
restart
)
{
v4l2_terminate
(
data
);
/* Wait for v4l2_thread to finish before
* updating width and height */
data
->
width
=
width
;
data
->
height
=
height
;
v4l2_init
(
data
);
}
}
static
uint32_t
v4l2_getheight
(
void
*
vptr
)
static
void
*
v4l2_create
(
obs_data_t
settings
,
obs_source_t
source
)
{
V4L2_DATA
(
vptr
);
UNUSED_PARAMETER
(
settings
);
struct
v4l2_data
*
data
=
bzalloc
(
sizeof
(
struct
v4l2_data
));
data
->
dev
=
-
1
;
data
->
source
=
source
;
v4l2_update
(
data
,
settings
);
blog
(
LOG_DEBUG
,
"v4l2-input: New input created"
);
return
480
;
return
data
;
}
struct
obs_source_info
v4l2_input
=
{
...
...
@@ -347,6 +647,9 @@ struct obs_source_info v4l2_input = {
.
getname
=
v4l2_getname
,
.
create
=
v4l2_create
,
.
destroy
=
v4l2_destroy
,
.
update
=
v4l2_update
,
.
defaults
=
v4l2_defaults
,
.
properties
=
v4l2_properties
,
.
getwidth
=
v4l2_getwidth
,
.
getheight
=
v4l2_getheight
};
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment