diff --git a/include/h264-encoder.h b/include/h264-encoder.h index 6466a46..62a4fa9 100644 --- a/include/h264-encoder.h +++ b/include/h264-encoder.h @@ -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 * purpose with or without fee is hereby granted, provided that the above @@ -17,13 +17,28 @@ #include #include +#include -struct h264_encoder; struct nvnc_fb; +struct h264_encoder; typedef void (*h264_encoder_packet_handler_fn)(const void* payload, size_t size, 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, uint32_t format, int quality); diff --git a/meson.build b/meson.build index dbed125..b49102a 100644 --- a/meson.build +++ b/meson.build @@ -139,7 +139,11 @@ if gbm.found() endif 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] config.set('ENABLE_OPEN_H264', true) config.set('HAVE_LIBAVUTIL', true) diff --git a/src/h264-encoder-ffmpeg-impl.c b/src/h264-encoder-ffmpeg-impl.c index 6ad0262..8f082bb 100644 --- a/src/h264-encoder-ffmpeg-impl.c +++ b/src/h264-encoder-ffmpeg-impl.c @@ -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 * 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); -struct h264_encoder { - h264_encoder_packet_handler_fn on_packet_ready; - void* userdata; +struct h264_encoder_ffmpeg { + struct h264_encoder base; uint32_t width; uint32_t height; @@ -74,7 +73,6 @@ struct h264_encoder { AVFilterContext* filter_in; AVFilterContext* filter_out; - bool next_frame_should_be_keyframe; struct fb_queue fb_queue; struct aml_work* work; @@ -85,6 +83,8 @@ struct h264_encoder { bool please_destroy; }; +struct h264_encoder_impl h264_encoder_ffmpeg_impl; + static enum AVPixelFormat drm_to_av_pixel_format(uint32_t format) { switch (format) { @@ -197,7 +197,7 @@ static int fb_queue_enqueue(struct fb_queue* queue, struct nvnc_fb* fb) 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; @@ -229,7 +229,7 @@ static int h264_encoder__init_buffersrc(struct h264_encoder* self) 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; @@ -292,7 +292,7 @@ failure: 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) { self->codec_ctx = avcodec_alloc_context3(codec); @@ -317,7 +317,7 @@ static int h264_encoder__init_codec_context(struct h264_encoder* self, 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); if (!self->hw_frames_ctx) @@ -335,7 +335,7 @@ static int h264_encoder__init_hw_frames_context(struct h264_encoder* self) 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) 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); - self->current_frame_is_keyframe = self->next_frame_should_be_keyframe; - self->next_frame_should_be_keyframe = false; + self->current_frame_is_keyframe = self->base.next_frame_should_be_keyframe; + self->base.next_frame_should_be_keyframe = false; 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; @@ -401,7 +402,7 @@ get_frame_failure: 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); assert(frame); // TODO @@ -439,7 +440,7 @@ failure: 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); nvnc_fb_release(self->current_fb); @@ -450,7 +451,7 @@ static void h264_encoder__on_work_done(void* handle) if (self->please_destroy) { vec_destroy(&self->current_packet); - h264_encoder_destroy(self); + h264_encoder_destroy(&self->base); return; } @@ -459,7 +460,7 @@ static void h264_encoder__on_work_done(void* handle) return; } - void* userdata = self->userdata; + void* userdata = self->base.userdata; // Must make a copy of packet because the callback might destroy the // encoder object. @@ -471,7 +472,7 @@ static void h264_encoder__on_work_done(void* handle) vec_clear(&self->current_packet); 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); } @@ -495,15 +496,17 @@ static int find_render_node(char *node, size_t maxlen) { return r; } -struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height, - uint32_t format, int quality) +static struct h264_encoder* h264_encoder_ffmpeg_create(uint32_t width, + uint32_t height, uint32_t format, int quality) { int rc; - struct h264_encoder* self = calloc(1, sizeof(*self)); + struct h264_encoder_ffmpeg* self = calloc(1, sizeof(*self)); if (!self) return NULL; + self->base.impl = &h264_encoder_ffmpeg_impl; + if (vec_init(&self->current_packet, 65536) < 0) goto packet_failure; @@ -521,7 +524,7 @@ struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height, if (rc != 0) goto hwdevice_ctx_failure; - self->next_frame_should_be_keyframe = true; + self->base.next_frame_should_be_keyframe = true; TAILQ_INIT(&self->fb_queue); self->width = width; @@ -558,7 +561,7 @@ struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height, if (rc != 0) goto avcodec_open_failure; - return self; + return &self->base; avcodec_open_failure: avcodec_free_context(&self->codec_ctx); @@ -579,8 +582,10 @@ packet_failure: 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) { self->please_destroy = true; return; @@ -595,24 +600,10 @@ void h264_encoder_destroy(struct h264_encoder* self) free(self); } -void h264_encoder_set_packet_handler_fn(struct h264_encoder* self, - h264_encoder_packet_handler_fn value) -{ - 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) +static void h264_encoder_ffmpeg_feed(struct h264_encoder* base, + struct nvnc_fb* fb) { + struct h264_encoder_ffmpeg* self = (struct h264_encoder_ffmpeg*)base; assert(fb->type == NVNC_FB_GBM_BO); // 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); 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, +}; diff --git a/src/h264-encoder.c b/src/h264-encoder.c new file mode 100644 index 0000000..2b0cf09 --- /dev/null +++ b/src/h264-encoder.c @@ -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; +}