Create abstract h264 encoder interface

pull/61/merge
Andri Yngvason 2024-03-10 14:05:54 +00:00
parent b043f004a8
commit 0bf53a4843
4 changed files with 111 additions and 44 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 - 2022 Andri Yngvason * Copyright (c) 2021 - 2024 Andri Yngvason
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -17,13 +17,28 @@
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <stdbool.h>
struct h264_encoder;
struct nvnc_fb; struct nvnc_fb;
struct h264_encoder;
typedef void (*h264_encoder_packet_handler_fn)(const void* payload, size_t size, typedef void (*h264_encoder_packet_handler_fn)(const void* payload, size_t size,
uint64_t pts, void* userdata); uint64_t pts, void* userdata);
struct h264_encoder_impl {
struct h264_encoder* (*create)(uint32_t width, uint32_t height,
uint32_t format, int quality);
void (*destroy)(struct h264_encoder*);
void (*feed)(struct h264_encoder*, struct nvnc_fb*);
};
struct h264_encoder {
struct h264_encoder_impl *impl;
h264_encoder_packet_handler_fn on_packet_ready;
void* userdata;
bool next_frame_should_be_keyframe;
};
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);

View File

@ -139,7 +139,11 @@ if gbm.found()
endif endif
if gbm.found() and libdrm.found() and libavcodec.found() and libavfilter.found() and libavutil.found() if gbm.found() and libdrm.found() and libavcodec.found() and libavfilter.found() and libavutil.found()
sources += [ 'src/h264-encoder-ffmpeg-impl.c', 'src/open-h264.c' ] sources += [
'src/h264-encoder.c',
'src/h264-encoder-ffmpeg-impl.c',
'src/open-h264.c'
]
dependencies += [libdrm, libavcodec, libavfilter, libavutil] dependencies += [libdrm, libavcodec, libavfilter, libavutil]
config.set('ENABLE_OPEN_H264', true) config.set('ENABLE_OPEN_H264', true)
config.set('HAVE_LIBAVUTIL', true) config.set('HAVE_LIBAVUTIL', true)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 - 2022 Andri Yngvason * Copyright (c) 2021 - 2024 Andri Yngvason
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -50,9 +50,8 @@ struct fb_queue_entry {
TAILQ_HEAD(fb_queue, fb_queue_entry); TAILQ_HEAD(fb_queue, fb_queue_entry);
struct h264_encoder { struct h264_encoder_ffmpeg {
h264_encoder_packet_handler_fn on_packet_ready; struct h264_encoder base;
void* userdata;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
@ -74,7 +73,6 @@ struct h264_encoder {
AVFilterContext* filter_in; AVFilterContext* filter_in;
AVFilterContext* filter_out; AVFilterContext* filter_out;
bool next_frame_should_be_keyframe;
struct fb_queue fb_queue; struct fb_queue fb_queue;
struct aml_work* work; struct aml_work* work;
@ -85,6 +83,8 @@ struct h264_encoder {
bool please_destroy; bool please_destroy;
}; };
struct h264_encoder_impl h264_encoder_ffmpeg_impl;
static enum AVPixelFormat drm_to_av_pixel_format(uint32_t format) static enum AVPixelFormat drm_to_av_pixel_format(uint32_t format)
{ {
switch (format) { switch (format) {
@ -197,7 +197,7 @@ static int fb_queue_enqueue(struct fb_queue* queue, struct nvnc_fb* fb)
return 0; return 0;
} }
static int h264_encoder__init_buffersrc(struct h264_encoder* self) static int h264_encoder__init_buffersrc(struct h264_encoder_ffmpeg* self)
{ {
int rc; int rc;
@ -229,7 +229,7 @@ static int h264_encoder__init_buffersrc(struct h264_encoder* self)
return 0; return 0;
} }
static int h264_encoder__init_filters(struct h264_encoder* self) static int h264_encoder__init_filters(struct h264_encoder_ffmpeg* self)
{ {
int rc; int rc;
@ -292,7 +292,7 @@ failure:
return -1; return -1;
} }
static int h264_encoder__init_codec_context(struct h264_encoder* self, static int h264_encoder__init_codec_context(struct h264_encoder_ffmpeg* self,
const AVCodec* codec, int quality) const AVCodec* codec, int quality)
{ {
self->codec_ctx = avcodec_alloc_context3(codec); self->codec_ctx = avcodec_alloc_context3(codec);
@ -317,7 +317,7 @@ static int h264_encoder__init_codec_context(struct h264_encoder* self,
return 0; return 0;
} }
static int h264_encoder__init_hw_frames_context(struct h264_encoder* self) static int h264_encoder__init_hw_frames_context(struct h264_encoder_ffmpeg* self)
{ {
self->hw_frames_ctx = av_hwframe_ctx_alloc(self->hw_device_ctx); self->hw_frames_ctx = av_hwframe_ctx_alloc(self->hw_device_ctx);
if (!self->hw_frames_ctx) if (!self->hw_frames_ctx)
@ -335,7 +335,7 @@ static int h264_encoder__init_hw_frames_context(struct h264_encoder* self)
return 0; return 0;
} }
static int h264_encoder__schedule_work(struct h264_encoder* self) static int h264_encoder__schedule_work(struct h264_encoder_ffmpeg* self)
{ {
if (self->current_fb) if (self->current_fb)
return 0; return 0;
@ -346,13 +346,14 @@ static int h264_encoder__schedule_work(struct h264_encoder* self)
DTRACE_PROBE1(neatvnc, h264_encode_frame_begin, self->current_fb->pts); DTRACE_PROBE1(neatvnc, h264_encode_frame_begin, self->current_fb->pts);
self->current_frame_is_keyframe = self->next_frame_should_be_keyframe; self->current_frame_is_keyframe = self->base.next_frame_should_be_keyframe;
self->next_frame_should_be_keyframe = false; self->base.next_frame_should_be_keyframe = false;
return aml_start(aml_get_default(), self->work); return aml_start(aml_get_default(), self->work);
} }
static int h264_encoder__encode(struct h264_encoder* self, AVFrame* frame_in) static int h264_encoder__encode(struct h264_encoder_ffmpeg* self,
AVFrame* frame_in)
{ {
int rc; int rc;
@ -401,7 +402,7 @@ get_frame_failure:
static void h264_encoder__do_work(void* handle) static void h264_encoder__do_work(void* handle)
{ {
struct h264_encoder* self = aml_get_userdata(handle); struct h264_encoder_ffmpeg* self = aml_get_userdata(handle);
AVFrame* frame = fb_to_avframe(self->current_fb); AVFrame* frame = fb_to_avframe(self->current_fb);
assert(frame); // TODO assert(frame); // TODO
@ -439,7 +440,7 @@ failure:
static void h264_encoder__on_work_done(void* handle) static void h264_encoder__on_work_done(void* handle)
{ {
struct h264_encoder* self = aml_get_userdata(handle); struct h264_encoder_ffmpeg* self = aml_get_userdata(handle);
uint64_t pts = nvnc_fb_get_pts(self->current_fb); uint64_t pts = nvnc_fb_get_pts(self->current_fb);
nvnc_fb_release(self->current_fb); nvnc_fb_release(self->current_fb);
@ -450,7 +451,7 @@ static void h264_encoder__on_work_done(void* handle)
if (self->please_destroy) { if (self->please_destroy) {
vec_destroy(&self->current_packet); vec_destroy(&self->current_packet);
h264_encoder_destroy(self); h264_encoder_destroy(&self->base);
return; return;
} }
@ -459,7 +460,7 @@ static void h264_encoder__on_work_done(void* handle)
return; return;
} }
void* userdata = self->userdata; void* userdata = self->base.userdata;
// Must make a copy of packet because the callback might destroy the // Must make a copy of packet because the callback might destroy the
// encoder object. // encoder object.
@ -471,7 +472,7 @@ static void h264_encoder__on_work_done(void* handle)
vec_clear(&self->current_packet); vec_clear(&self->current_packet);
h264_encoder__schedule_work(self); h264_encoder__schedule_work(self);
self->on_packet_ready(packet.data, packet.len, pts, userdata); self->base.on_packet_ready(packet.data, packet.len, pts, userdata);
vec_destroy(&packet); vec_destroy(&packet);
} }
@ -495,15 +496,17 @@ static int find_render_node(char *node, size_t maxlen) {
return r; return r;
} }
struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height, static struct h264_encoder* h264_encoder_ffmpeg_create(uint32_t width,
uint32_t format, int quality) uint32_t height, uint32_t format, int quality)
{ {
int rc; int rc;
struct h264_encoder* self = calloc(1, sizeof(*self)); struct h264_encoder_ffmpeg* self = calloc(1, sizeof(*self));
if (!self) if (!self)
return NULL; return NULL;
self->base.impl = &h264_encoder_ffmpeg_impl;
if (vec_init(&self->current_packet, 65536) < 0) if (vec_init(&self->current_packet, 65536) < 0)
goto packet_failure; goto packet_failure;
@ -521,7 +524,7 @@ struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height,
if (rc != 0) if (rc != 0)
goto hwdevice_ctx_failure; goto hwdevice_ctx_failure;
self->next_frame_should_be_keyframe = true; self->base.next_frame_should_be_keyframe = true;
TAILQ_INIT(&self->fb_queue); TAILQ_INIT(&self->fb_queue);
self->width = width; self->width = width;
@ -558,7 +561,7 @@ struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height,
if (rc != 0) if (rc != 0)
goto avcodec_open_failure; goto avcodec_open_failure;
return self; return &self->base;
avcodec_open_failure: avcodec_open_failure:
avcodec_free_context(&self->codec_ctx); avcodec_free_context(&self->codec_ctx);
@ -579,8 +582,10 @@ packet_failure:
return NULL; return NULL;
} }
void h264_encoder_destroy(struct h264_encoder* self) static void h264_encoder_ffmpeg_destroy(struct h264_encoder* base)
{ {
struct h264_encoder_ffmpeg* self = (struct h264_encoder_ffmpeg*)base;
if (self->current_fb) { if (self->current_fb) {
self->please_destroy = true; self->please_destroy = true;
return; return;
@ -595,24 +600,10 @@ void h264_encoder_destroy(struct h264_encoder* self)
free(self); free(self);
} }
void h264_encoder_set_packet_handler_fn(struct h264_encoder* self, static void h264_encoder_ffmpeg_feed(struct h264_encoder* base,
h264_encoder_packet_handler_fn value) struct nvnc_fb* fb)
{
self->on_packet_ready = value;
}
void h264_encoder_set_userdata(struct h264_encoder* self, void* value)
{
self->userdata = value;
}
void h264_encoder_request_keyframe(struct h264_encoder* self)
{
self->next_frame_should_be_keyframe = true;
}
void h264_encoder_feed(struct h264_encoder* self, struct nvnc_fb* fb)
{ {
struct h264_encoder_ffmpeg* self = (struct h264_encoder_ffmpeg*)base;
assert(fb->type == NVNC_FB_GBM_BO); assert(fb->type == NVNC_FB_GBM_BO);
// TODO: Add transform filter // TODO: Add transform filter
@ -626,3 +617,9 @@ void h264_encoder_feed(struct h264_encoder* self, struct nvnc_fb* fb)
rc = h264_encoder__schedule_work(self); rc = h264_encoder__schedule_work(self);
assert(rc == 0); // TODO assert(rc == 0); // TODO
} }
struct h264_encoder_impl h264_encoder_ffmpeg_impl = {
.create = h264_encoder_ffmpeg_create,
.destroy = h264_encoder_ffmpeg_destroy,
.feed = h264_encoder_ffmpeg_feed,
};

51
src/h264-encoder.c 100644
View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "h264-encoder.h"
extern struct h264_encoder_impl h264_encoder_ffmpeg_impl;
struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height,
uint32_t format, int quality)
{
return h264_encoder_ffmpeg_impl.create(width, height, format, quality);
}
void h264_encoder_destroy(struct h264_encoder* self)
{
self->impl->destroy(self);
}
void h264_encoder_set_packet_handler_fn(struct h264_encoder* self,
h264_encoder_packet_handler_fn fn)
{
self->on_packet_ready = fn;
}
void h264_encoder_set_userdata(struct h264_encoder* self, void* userdata)
{
self->userdata = userdata;
}
void h264_encoder_feed(struct h264_encoder* self, struct nvnc_fb* fb)
{
self->impl->feed(self, fb);
}
void h264_encoder_request_keyframe(struct h264_encoder* self)
{
self->next_frame_should_be_keyframe = true;
}