Skip to content

Commit 36547a7

Browse files
AlexxITseydx
authored andcommitted
Add support H264, H265, NV12 for V4L2 source AlexxIT#1546
1 parent 3e3988a commit 36547a7

File tree

4 files changed

+80
-17
lines changed

4 files changed

+80
-17
lines changed

pkg/h264/annexb/annexb_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,15 @@ func TestDahua(t *testing.T) {
8383
n := naluTypes(b)
8484
require.Equal(t, []byte{0x40, 0x42, 0x44, 0x26}, n)
8585
}
86+
87+
func TestUSB(t *testing.T) {
88+
s := "00 00 00 01 67 4D 00 1F 8D 8D 40 28 02 DD 37 01 01 01 40 00 01 C2 00 00 57 E4 01 00 00 00 01 68 EE 3C 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 65 88 80 00"
89+
b := EncodeToAVCC(decode(s))
90+
n := naluTypes(b)
91+
require.Equal(t, []byte{0x67, 0x68, 0x65}, n)
92+
93+
s = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 41 9A 00 4C"
94+
b = EncodeToAVCC(decode(s))
95+
n = naluTypes(b)
96+
require.Equal(t, []byte{0x41}, n)
97+
}

pkg/v4l2/device/device.go

+17-9
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import (
1111
)
1212

1313
type Device struct {
14-
fd int
15-
bufs [][]byte
14+
fd int
15+
bufs [][]byte
16+
pixFmt uint32
1617
}
1718

1819
func Open(path string) (*Device, error) {
@@ -119,6 +120,8 @@ func (d *Device) ListFrameRates(pixFmt, width, height uint32) ([]uint32, error)
119120
}
120121

121122
func (d *Device) SetFormat(width, height, pixFmt uint32) error {
123+
d.pixFmt = pixFmt
124+
122125
f := v4l2_format{
123126
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
124127
pix: v4l2_pix_format{
@@ -196,7 +199,7 @@ func (d *Device) StreamOff() (err error) {
196199
return ioctl(d.fd, VIDIOC_REQBUFS, unsafe.Pointer(&rb))
197200
}
198201

199-
func (d *Device) Capture(planarYUV bool) ([]byte, error) {
202+
func (d *Device) Capture() ([]byte, error) {
200203
dec := v4l2_buffer{
201204
typ: V4L2_BUF_TYPE_VIDEO_CAPTURE,
202205
memory: V4L2_MEMORY_MMAP,
@@ -205,11 +208,16 @@ func (d *Device) Capture(planarYUV bool) ([]byte, error) {
205208
return nil, err
206209
}
207210

208-
buf := make([]byte, dec.bytesused)
209-
if planarYUV {
210-
YUYV2YUV(buf, d.bufs[dec.index][:dec.bytesused])
211-
} else {
212-
copy(buf, d.bufs[dec.index][:dec.bytesused])
211+
src := d.bufs[dec.index][:dec.bytesused]
212+
dst := make([]byte, dec.bytesused)
213+
214+
switch d.pixFmt {
215+
case V4L2_PIX_FMT_YUYV:
216+
YUYVtoYUV(dst, src)
217+
case V4L2_PIX_FMT_NV12:
218+
NV12toYUV(dst, src)
219+
default:
220+
copy(dst, d.bufs[dec.index][:dec.bytesused])
213221
}
214222

215223
enc := v4l2_buffer{
@@ -221,7 +229,7 @@ func (d *Device) Capture(planarYUV bool) ([]byte, error) {
221229
return nil, err
222230
}
223231

224-
return buf, nil
232+
return dst, nil
225233
}
226234

227235
func (d *Device) Close() error {

pkg/v4l2/device/formats.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package device
22

33
const (
44
V4L2_PIX_FMT_YUYV = 'Y' | 'U'<<8 | 'Y'<<16 | 'V'<<24
5+
V4L2_PIX_FMT_NV12 = 'N' | 'V'<<8 | '1'<<16 | '2'<<24
56
V4L2_PIX_FMT_MJPEG = 'M' | 'J'<<8 | 'P'<<16 | 'G'<<24
7+
V4L2_PIX_FMT_H264 = 'H' | '2'<<8 | '6'<<16 | '4'<<24
8+
V4L2_PIX_FMT_HEVC = 'H' | 'E'<<8 | 'V'<<16 | 'C'<<24
69
)
710

811
type Format struct {
@@ -13,11 +16,13 @@ type Format struct {
1316

1417
var Formats = []Format{
1518
{V4L2_PIX_FMT_YUYV, "YUV 4:2:2", "yuyv422"},
19+
{V4L2_PIX_FMT_NV12, "Y/UV 4:2:0", "nv12"},
1620
{V4L2_PIX_FMT_MJPEG, "Motion-JPEG", "mjpeg"},
21+
{V4L2_PIX_FMT_H264, "H.264", "h264"},
22+
{V4L2_PIX_FMT_HEVC, "HEVC", "hevc"},
1723
}
1824

19-
// YUYV2YUV convert packed YUV to planar YUV
20-
func YUYV2YUV(dst, src []byte) {
25+
func YUYVtoYUV(dst, src []byte) {
2126
n := len(src)
2227
i0 := 0
2328
iy := 0
@@ -38,3 +43,20 @@ func YUYV2YUV(dst, src []byte) {
3843
iv++
3944
}
4045
}
46+
47+
func NV12toYUV(dst, src []byte) {
48+
n := len(src)
49+
k := n / 6
50+
i0 := k * 4
51+
iu := i0
52+
iv := i0 + k
53+
copy(dst, src[:i0]) // copy Y
54+
for i0 < n {
55+
dst[iu] = src[i0]
56+
i0++
57+
iu++
58+
dst[iv] = src[i0]
59+
i0++
60+
iv++
61+
}
62+
}

pkg/v4l2/producer.go

+27-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99

1010
"github.com/AlexxIT/go2rtc/pkg/core"
11+
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
1112
"github.com/AlexxIT/go2rtc/pkg/v4l2/device"
1213
"github.com/pion/rtp"
1314
)
@@ -46,17 +47,29 @@ func Open(rawURL string) (*Producer, error) {
4647
}
4748

4849
switch query.Get("input_format") {
49-
case "mjpeg":
50-
codec.Name = core.CodecJPEG
51-
pixFmt = device.V4L2_PIX_FMT_MJPEG
5250
case "yuyv422":
5351
if codec.FmtpLine == "" {
5452
return nil, errors.New("v4l2: invalid video_size")
5553
}
56-
5754
codec.Name = core.CodecRAW
5855
codec.FmtpLine += ";colorspace=422"
5956
pixFmt = device.V4L2_PIX_FMT_YUYV
57+
case "nv12":
58+
if codec.FmtpLine == "" {
59+
return nil, errors.New("v4l2: invalid video_size")
60+
}
61+
codec.Name = core.CodecRAW
62+
codec.FmtpLine += ";colorspace=420mpeg2" // maybe 420jpeg
63+
pixFmt = device.V4L2_PIX_FMT_NV12
64+
case "mjpeg":
65+
codec.Name = core.CodecJPEG
66+
pixFmt = device.V4L2_PIX_FMT_MJPEG
67+
case "h264":
68+
codec.Name = core.CodecH264
69+
pixFmt = device.V4L2_PIX_FMT_H264
70+
case "hevc":
71+
codec.Name = core.CodecH265
72+
pixFmt = device.V4L2_PIX_FMT_HEVC
6073
default:
6174
return nil, errors.New("v4l2: invalid input_format")
6275
}
@@ -93,10 +106,14 @@ func (c *Producer) Start() error {
93106
return err
94107
}
95108

96-
planarYUV := c.Medias[0].Codecs[0].Name == core.CodecRAW
109+
var bitstream bool
110+
switch c.Medias[0].Codecs[0].Name {
111+
case core.CodecH264, core.CodecH265:
112+
bitstream = true
113+
}
97114

98115
for {
99-
buf, err := c.dev.Capture(planarYUV)
116+
buf, err := c.dev.Capture()
100117
if err != nil {
101118
return err
102119
}
@@ -107,6 +124,10 @@ func (c *Producer) Start() error {
107124
continue
108125
}
109126

127+
if bitstream {
128+
buf = annexb.EncodeToAVCC(buf)
129+
}
130+
110131
pkt := &rtp.Packet{
111132
Header: rtp.Header{Timestamp: core.Now90000()},
112133
Payload: buf,

0 commit comments

Comments
 (0)