WiP: Implement v4l2m2m h264 encoder
parent
2bfa86a24c
commit
8c4c2cfa5c
23
meson.build
23
meson.build
|
@ -138,17 +138,26 @@ if gbm.found()
|
||||||
config.set('HAVE_GBM', true)
|
config.set('HAVE_GBM', true)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if gbm.found() and libdrm.found() and libavcodec.found() and libavfilter.found() and libavutil.found()
|
have_ffmpeg = gbm.found() and libdrm.found() and libavcodec.found() and libavfilter.found() and libavutil.found()
|
||||||
sources += [
|
have_v4l2 = gbm.found() and libdrm.found() and cc.check_header('linux/videodev2.h')
|
||||||
'src/h264-encoder.c',
|
|
||||||
'src/h264-encoder-ffmpeg-impl.c',
|
if have_ffmpeg
|
||||||
'src/open-h264.c'
|
sources += [ 'src/h264-encoder-ffmpeg-impl.c' ]
|
||||||
]
|
|
||||||
dependencies += [libdrm, libavcodec, libavfilter, libavutil]
|
dependencies += [libdrm, libavcodec, libavfilter, libavutil]
|
||||||
config.set('ENABLE_OPEN_H264', true)
|
config.set('HAVE_FFMPEG', true)
|
||||||
config.set('HAVE_LIBAVUTIL', true)
|
config.set('HAVE_LIBAVUTIL', true)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if have_v4l2
|
||||||
|
sources += [ 'src/h264-encoder-v4l2m2m-impl.c' ]
|
||||||
|
config.set('HAVE_V4L2', true)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if have_ffmpeg or have_v4l2
|
||||||
|
sources += [ 'src/h264-encoder.c', 'src/open-h264.c' ]
|
||||||
|
config.set('ENABLE_OPEN_H264', true)
|
||||||
|
endif
|
||||||
|
|
||||||
if enable_websocket
|
if enable_websocket
|
||||||
sources += [
|
sources += [
|
||||||
'src/ws-handshake.c',
|
'src/ws-handshake.c',
|
||||||
|
|
|
@ -0,0 +1,502 @@
|
||||||
|
#include "h264-encoder.h"
|
||||||
|
#include "neatvnc.h"
|
||||||
|
#include "fb.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/videodev2.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <aml.h>
|
||||||
|
|
||||||
|
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||||
|
#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0]))
|
||||||
|
|
||||||
|
#define N_SRC_BUFS 3
|
||||||
|
#define N_DST_BUFS 3
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m_dst_buf {
|
||||||
|
struct v4l2_buffer buffer;
|
||||||
|
struct v4l2_plane plane;
|
||||||
|
void* payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf {
|
||||||
|
struct v4l2_buffer buffer;
|
||||||
|
struct v4l2_plane planes[4];
|
||||||
|
int fd;
|
||||||
|
bool is_taken;
|
||||||
|
struct nvnc_fb* fb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m {
|
||||||
|
struct h264_encoder base;
|
||||||
|
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t format;
|
||||||
|
int quality; // TODO: Can we affect the quality?
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
struct aml_handler* handler;
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf src_bufs[N_SRC_BUFS];
|
||||||
|
int src_buf_index;
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m_dst_buf dst_bufs[N_DST_BUFS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct h264_encoder_impl h264_encoder_v4l2m2m_impl;
|
||||||
|
|
||||||
|
static int v4l2_qbuf(int fd, const struct v4l2_buffer* inbuf)
|
||||||
|
{
|
||||||
|
assert(inbuf->length <= 4);
|
||||||
|
struct v4l2_plane planes[4];
|
||||||
|
struct v4l2_buffer outbuf;
|
||||||
|
outbuf = *inbuf;
|
||||||
|
memcpy(&planes, inbuf->m.planes, inbuf->length * sizeof(planes[0]));
|
||||||
|
outbuf.m.planes = planes;
|
||||||
|
return ioctl(fd, VIDIOC_QBUF, &outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int v4l2_dqbuf(int fd, struct v4l2_buffer* buf)
|
||||||
|
{
|
||||||
|
return ioctl(fd, VIDIOC_DQBUF, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct h264_encoder_v4l2m2m_src_buf* take_src_buffer(
|
||||||
|
struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
unsigned int count = 0;
|
||||||
|
int i = self->src_buf_index;
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf* buffer;
|
||||||
|
do {
|
||||||
|
buffer = &self->src_bufs[i++];
|
||||||
|
i %= ARRAY_LENGTH(self->src_bufs);
|
||||||
|
} while (++count < ARRAY_LENGTH(self->src_bufs) && buffer->is_taken);
|
||||||
|
|
||||||
|
if (buffer->is_taken)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->src_buf_index = i;
|
||||||
|
buffer->is_taken = true;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool any_src_buf_is_taken(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
for (unsigned int i = 0; i < ARRAY_LENGTH(self->src_bufs); ++i)
|
||||||
|
if (self->src_bufs[i].is_taken)
|
||||||
|
result = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_src_fmt(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// TODO: Derive the correct format
|
||||||
|
uint32_t format = V4L2_PIX_FMT_RGBA32;
|
||||||
|
|
||||||
|
struct v4l2_format fmt = {
|
||||||
|
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||||
|
};
|
||||||
|
rc = ioctl(self->fd, VIDIOC_G_FMT, &fmt);
|
||||||
|
if (rc < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct v4l2_pix_format_mplane* pix_fmt = &fmt.fmt.pix_mp;
|
||||||
|
pix_fmt->pixelformat = format;
|
||||||
|
pix_fmt->width = self->width;
|
||||||
|
pix_fmt->height = self->height;
|
||||||
|
|
||||||
|
rc = ioctl(self->fd, VIDIOC_S_FMT, &fmt);
|
||||||
|
if (rc < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_dst_fmt(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
struct v4l2_format fmt = {
|
||||||
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||||
|
};
|
||||||
|
rc = ioctl(self->fd, VIDIOC_G_FMT, &fmt);
|
||||||
|
if (rc < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct v4l2_pix_format_mplane* pix_fmt = &fmt.fmt.pix_mp;
|
||||||
|
pix_fmt->pixelformat = V4L2_PIX_FMT_H264;
|
||||||
|
pix_fmt->width = self->width;
|
||||||
|
pix_fmt->height = self->height;
|
||||||
|
|
||||||
|
rc = ioctl(self->fd, VIDIOC_S_FMT, &fmt);
|
||||||
|
if (rc < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_dst_buffers(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int n_bufs = ARRAY_LENGTH(self->dst_bufs);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
struct v4l2_requestbuffers req = {
|
||||||
|
.memory = V4L2_MEMORY_MMAP,
|
||||||
|
.count = n_bufs,
|
||||||
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||||
|
};
|
||||||
|
rc = ioctl(self->fd, VIDIOC_REQBUFS, &req);
|
||||||
|
if (rc < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < req.count; ++i) {
|
||||||
|
struct h264_encoder_v4l2m2m_dst_buf* buffer = &self->dst_bufs[i];
|
||||||
|
struct v4l2_buffer* buf = &buffer->buffer;
|
||||||
|
|
||||||
|
buf->index = i;
|
||||||
|
buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||||
|
buf->memory = V4L2_MEMORY_MMAP;
|
||||||
|
buf->length = 1;
|
||||||
|
buf->m.planes = &buffer->plane;
|
||||||
|
|
||||||
|
rc = ioctl(self->fd, VIDIOC_QUERYBUF, buf);
|
||||||
|
if (rc < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buffer->payload = mmap(0, buffer->plane.length,
|
||||||
|
PROT_READ | PROT_WRITE, MAP_SHARED, self->fd,
|
||||||
|
buffer->plane.m.mem_offset);
|
||||||
|
if (buffer->payload == MAP_FAILED) {
|
||||||
|
nvnc_log(NVNC_LOG_ERROR, "Whoops, mapping failed: %m");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enqueue_dst_buffers(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < ARRAY_LENGTH(self->dst_bufs); ++i) {
|
||||||
|
int rc = v4l2_qbuf(self->fd, &self->dst_bufs[i].buffer);
|
||||||
|
assert(rc >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_dst_bufs(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct v4l2_plane plane = { 0 };
|
||||||
|
struct v4l2_buffer buf = {
|
||||||
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||||
|
.memory = V4L2_MEMORY_MMAP,
|
||||||
|
.length = 1,
|
||||||
|
.m.planes = &plane,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
rc = v4l2_dqbuf(self->fd, &buf);
|
||||||
|
if (rc < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint64_t pts = buf.timestamp.tv_sec * UINT64_C(1000000) +
|
||||||
|
buf.timestamp.tv_usec;
|
||||||
|
struct h264_encoder_v4l2m2m_dst_buf* dstbuf =
|
||||||
|
&self->dst_bufs[buf.index];
|
||||||
|
size_t size = buf.m.planes[0].bytesused;
|
||||||
|
|
||||||
|
static uint64_t last_pts;
|
||||||
|
if (last_pts && last_pts > pts) {
|
||||||
|
nvnc_log(NVNC_LOG_ERROR, "pts - last_pts = %"PRIi64,
|
||||||
|
(int64_t)pts - (int64_t)last_pts);
|
||||||
|
}
|
||||||
|
last_pts = pts;
|
||||||
|
|
||||||
|
nvnc_trace("Encoded frame (index %d) at %"PRIu64" µs with size: %zu",
|
||||||
|
buf.index, pts, size);
|
||||||
|
|
||||||
|
self->base.on_packet_ready(dstbuf->payload, size, pts,
|
||||||
|
self->base.userdata);
|
||||||
|
|
||||||
|
v4l2_qbuf(self->fd, &buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_src_bufs(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct v4l2_plane planes[4] = { 0 };
|
||||||
|
struct v4l2_buffer buf = {
|
||||||
|
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||||
|
.memory = V4L2_MEMORY_DMABUF,
|
||||||
|
.length = 1,
|
||||||
|
.m.planes = planes,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
rc = v4l2_dqbuf(self->fd, &buf);
|
||||||
|
if (rc < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf* srcbuf =
|
||||||
|
&self->src_bufs[buf.index];
|
||||||
|
srcbuf->is_taken = false;
|
||||||
|
|
||||||
|
// TODO: This assumes that there's only one fd
|
||||||
|
close(srcbuf->planes[0].m.fd);
|
||||||
|
|
||||||
|
nvnc_fb_unmap(srcbuf->fb);
|
||||||
|
nvnc_fb_release(srcbuf->fb);
|
||||||
|
nvnc_fb_unref(srcbuf->fb);
|
||||||
|
srcbuf->fb = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stream_off(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||||
|
ioctl(self->fd, VIDIOC_STREAMOFF, &type);
|
||||||
|
|
||||||
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||||
|
ioctl(self->fd, VIDIOC_STREAMOFF, &type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_src_buffers(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < ARRAY_LENGTH(self->src_bufs); ++i) {
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf* buf = &self->src_bufs[i];
|
||||||
|
assert(!buf->is_taken);
|
||||||
|
close(buf->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_dst_buffers(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < ARRAY_LENGTH(self->dst_bufs); ++i) {
|
||||||
|
struct h264_encoder_v4l2m2m_dst_buf* buf = &self->dst_bufs[i];
|
||||||
|
munmap(buf->payload, buf->plane.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stream_on(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||||
|
ioctl(self->fd, VIDIOC_STREAMON, &type);
|
||||||
|
|
||||||
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||||
|
return ioctl(self->fd, VIDIOC_STREAMON, &type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_src_buffers(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
struct v4l2_requestbuffers req = {
|
||||||
|
.memory = V4L2_MEMORY_DMABUF,
|
||||||
|
.count = N_SRC_BUFS,
|
||||||
|
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||||
|
};
|
||||||
|
rc = ioctl(self->fd, VIDIOC_REQBUFS, &req);
|
||||||
|
if (rc < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < N_SRC_BUFS; ++i) {
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf* buffer = &self->src_bufs[i];
|
||||||
|
struct v4l2_buffer* buf = &buffer->buffer;
|
||||||
|
|
||||||
|
buf->index = i;
|
||||||
|
buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||||
|
buf->memory = V4L2_MEMORY_DMABUF;
|
||||||
|
buf->length = 1;
|
||||||
|
buf->m.planes = buffer->planes;
|
||||||
|
|
||||||
|
rc = ioctl(self->fd, VIDIOC_QUERYBUF, buf);
|
||||||
|
if (rc < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void encode_buffer(struct h264_encoder_v4l2m2m* self,
|
||||||
|
struct nvnc_fb* fb)
|
||||||
|
{
|
||||||
|
struct h264_encoder_v4l2m2m_src_buf* srcbuf = take_src_buffer(self);
|
||||||
|
if (!srcbuf) {
|
||||||
|
nvnc_log(NVNC_LOG_ERROR, "Out of source buffers. Dropping frame...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!srcbuf->fb);
|
||||||
|
|
||||||
|
nvnc_fb_ref(fb);
|
||||||
|
nvnc_fb_hold(fb);
|
||||||
|
|
||||||
|
/* For some reason the v4l2m2m h264 encoder in the Rapberry Pi 4 gets
|
||||||
|
* really glitchy unless the buffer is mapped first.
|
||||||
|
* This should probably be handled by the driver, but it's not.
|
||||||
|
*/
|
||||||
|
nvnc_fb_map(fb);
|
||||||
|
|
||||||
|
srcbuf->fb = fb;
|
||||||
|
|
||||||
|
struct gbm_bo* bo = nvnc_fb_get_gbm_bo(fb);
|
||||||
|
|
||||||
|
int n_planes = gbm_bo_get_plane_count(bo);
|
||||||
|
int fd = gbm_bo_get_fd(bo);
|
||||||
|
uint32_t height = gbm_bo_get_height(bo);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_planes; ++i) {
|
||||||
|
uint32_t stride = gbm_bo_get_stride_for_plane(bo, i);
|
||||||
|
uint32_t offset = gbm_bo_get_offset(bo, i);
|
||||||
|
uint32_t size = stride * height;
|
||||||
|
|
||||||
|
srcbuf->buffer.m.planes[i].m.fd = fd;
|
||||||
|
srcbuf->buffer.m.planes[i].bytesused = size;
|
||||||
|
srcbuf->buffer.m.planes[i].length = size;
|
||||||
|
srcbuf->buffer.m.planes[i].data_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = v4l2_qbuf(self->fd, &srcbuf->buffer);
|
||||||
|
if (rc < 0) {
|
||||||
|
nvnc_log(NVNC_LOG_PANIC, "Failed to enqueue buffer: %m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_fd_events(void* handle)
|
||||||
|
{
|
||||||
|
struct h264_encoder_v4l2m2m* self = aml_get_userdata(handle);
|
||||||
|
process_dst_bufs(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void h264_encoder_v4l2m2m_configure(struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
struct v4l2_control ctrl = { 0 };
|
||||||
|
|
||||||
|
ctrl.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
|
||||||
|
ctrl.value = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE;
|
||||||
|
ioctl(self->fd, VIDIOC_S_CTRL, &ctrl);
|
||||||
|
|
||||||
|
ctrl.id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD;
|
||||||
|
ctrl.value = INT_MAX;
|
||||||
|
ioctl(self->fd, VIDIOC_S_CTRL, &ctrl);
|
||||||
|
|
||||||
|
ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
|
||||||
|
ctrl.value = V4L2_MPEG_VIDEO_BITRATE_MODE_CQ;
|
||||||
|
ioctl(self->fd, VIDIOC_S_CTRL, &ctrl);
|
||||||
|
|
||||||
|
ctrl.id = V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY;
|
||||||
|
ctrl.value = self->quality;
|
||||||
|
ioctl(self->fd, VIDIOC_S_CTRL, &ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct h264_encoder* h264_encoder_v4l2m2m_create(uint32_t width,
|
||||||
|
uint32_t height, uint32_t format, int quality)
|
||||||
|
{
|
||||||
|
struct h264_encoder_v4l2m2m* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->base.impl = &h264_encoder_v4l2m2m_impl;
|
||||||
|
self->fd = -1;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->format = format;
|
||||||
|
self->quality = quality;
|
||||||
|
|
||||||
|
// TODO: Find a device that supports the given config
|
||||||
|
self->fd = open("/dev/video11", O_RDWR | O_CLOEXEC);
|
||||||
|
if (self->fd < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (set_src_fmt(self) < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (set_dst_fmt(self) < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
h264_encoder_v4l2m2m_configure(self);
|
||||||
|
|
||||||
|
if (alloc_dst_buffers(self) < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (alloc_src_buffers(self) < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
enqueue_dst_buffers(self);
|
||||||
|
|
||||||
|
if (stream_on(self) < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
int flags = fcntl(self->fd, F_GETFL);
|
||||||
|
fcntl(self->fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
self->handler = aml_handler_new(self->fd, process_fd_events, self, NULL);
|
||||||
|
aml_set_event_mask(self->handler, AML_EVENT_READ);
|
||||||
|
|
||||||
|
if (aml_start(aml_get_default(), self->handler) < 0) {
|
||||||
|
aml_unref(self->handler);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &self->base;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
if (self->fd >= 0)
|
||||||
|
close(self->fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void claim_all_src_bufs(
|
||||||
|
struct h264_encoder_v4l2m2m* self)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
process_src_bufs(self);
|
||||||
|
if (!any_src_buf_is_taken(self))
|
||||||
|
break;
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void h264_encoder_v4l2m2m_destroy(struct h264_encoder* base)
|
||||||
|
{
|
||||||
|
struct h264_encoder_v4l2m2m* self = (struct h264_encoder_v4l2m2m*)base;
|
||||||
|
claim_all_src_bufs(self);
|
||||||
|
aml_stop(aml_get_default(), self->handler);
|
||||||
|
aml_unref(self->handler);
|
||||||
|
stream_off(self);
|
||||||
|
free_dst_buffers(self);
|
||||||
|
free_src_buffers(self);
|
||||||
|
if (self->fd >= 0)
|
||||||
|
close(self->fd);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void h264_encoder_v4l2m2m_feed(struct h264_encoder* base,
|
||||||
|
struct nvnc_fb* fb)
|
||||||
|
{
|
||||||
|
struct h264_encoder_v4l2m2m* self = (struct h264_encoder_v4l2m2m*)base;
|
||||||
|
process_src_bufs(self);
|
||||||
|
encode_buffer(self, fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct h264_encoder_impl h264_encoder_v4l2m2m_impl = {
|
||||||
|
.create = h264_encoder_v4l2m2m_create,
|
||||||
|
.destroy = h264_encoder_v4l2m2m_destroy,
|
||||||
|
.feed = h264_encoder_v4l2m2m_feed,
|
||||||
|
};
|
|
@ -15,13 +15,36 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "h264-encoder.h"
|
#include "h264-encoder.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_FFMPEG
|
||||||
extern struct h264_encoder_impl h264_encoder_ffmpeg_impl;
|
extern struct h264_encoder_impl h264_encoder_ffmpeg_impl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_V4L2
|
||||||
|
extern struct h264_encoder_impl h264_encoder_v4l2m2m_impl;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height,
|
struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height,
|
||||||
uint32_t format, int quality)
|
uint32_t format, int quality)
|
||||||
{
|
{
|
||||||
return h264_encoder_ffmpeg_impl.create(width, height, format, quality);
|
struct h264_encoder* encoder = NULL;
|
||||||
|
|
||||||
|
#ifdef HAVE_FFMPEG
|
||||||
|
encoder = h264_encoder_ffmpeg_impl.create(width, height, format, quality);
|
||||||
|
if (encoder) {
|
||||||
|
return encoder;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_V4L2
|
||||||
|
encoder = h264_encoder_v4l2m2m_impl.create(width, height, format, quality);
|
||||||
|
if (encoder) {
|
||||||
|
return encoder;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return encoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void h264_encoder_destroy(struct h264_encoder* self)
|
void h264_encoder_destroy(struct h264_encoder* self)
|
||||||
|
|
Loading…
Reference in New Issue