summaryrefslogtreecommitdiff
path: root/src/render
diff options
context:
space:
mode:
Diffstat (limited to 'src/render')
-rw-r--r--src/render/meson.build7
-rw-r--r--src/render/queue_libplacebo.c150
-rw-r--r--src/render/queue_libplacebo.h12
-rw-r--r--src/render/renderer.h2
-rw-r--r--src/render/renderer_libplacebo.c34
-rw-r--r--src/render/renderer_libplacebo.h3
6 files changed, 187 insertions, 21 deletions
diff --git a/src/render/meson.build b/src/render/meson.build
index 74d323e..c2bcfd7 100644
--- a/src/render/meson.build
+++ b/src/render/meson.build
@@ -59,4 +59,11 @@ elif get_option('renderer') == 'libplacebo'
render_args += ['-DCAMU_RENDERER_LIBPLACEBO']
endif
+libass_opts = []
+if not is_windows
+ libass_opts += ['fontconfig=enabled']
+endif
+libass = dependency('libass', default_options: libass_opts)
+render_deps += [libass]
+
render = declare_dependency(sources: render_src, dependencies: render_deps, compile_args: render_args)
diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c
index bd5ed7b..a95afc4 100644
--- a/src/render/queue_libplacebo.c
+++ b/src/render/queue_libplacebo.c
@@ -101,12 +101,84 @@ static void queue_lp_push(struct camu_frame_queue *queue, struct camu_codec_fram
}
#ifdef CAMU_HAVE_FFMPEG
+static struct camu_overlay_lp *create_subtitle_overlay(pl_gpu gpu, ASS_Image *ass_frame)
+{
+ struct camu_overlay_lp *overlay = al_alloc_object(struct camu_overlay_lp);
+ for (; ass_frame; ass_frame = ass_frame->next) {
+ pl_tex tex = pl_tex_create(gpu, pl_tex_params(
+ .w = ass_frame->w,
+ .h = ass_frame->h,
+ .format = pl_find_named_fmt(gpu, "r8"),
+ .sampleable = true,
+ .host_writable = true
+ ));
+
+ bool ok = pl_tex_upload(gpu, pl_tex_transfer_params(
+ .tex = tex,
+ .row_pitch = ass_frame->stride,
+ .ptr = ass_frame->bitmap
+ ));
+ if (!ok) {
+ al_log_error("frame_queue_libplacebo", "Failed to upload subtitle bitmap.");
+ continue;
+ }
+
+ struct pl_overlay *current = NULL;
+ if (overlay->num >= overlay->alloc) {
+ s32 old_alloc = overlay->alloc;
+ overlay->alloc = old_alloc ? al_next_power_of_two(overlay->alloc + 1) : 8;
+ if (!overlay->overlays) {
+ overlay->overlays = al_malloc(sizeof(struct pl_overlay) * overlay->alloc);
+ } else {
+ overlay->overlays = al_realloc(overlay->overlays, sizeof(struct pl_overlay) * overlay->alloc);
+ }
+ for (s32 i = old_alloc; i < overlay->alloc; i++) {
+ current = (struct pl_overlay *)&overlay->overlays[i];
+ current->parts = al_malloc(sizeof(struct pl_overlay_part) * 1);
+ current->num_parts = 0;
+ }
+ }
+
+ // https://github.com/mpv-player/mpv/blob/70aaba71d6e3071a732069a1d222d1eb4293faf2/video/out/vo_gpu_next.c#L289
+ // https://github.com/mpv-player/mpv/blob/70aaba71d6e3071a732069a1d222d1eb4293faf2/sub/ass_mp.c#L195
+ current = (struct pl_overlay *)&overlay->overlays[overlay->num++];
+ struct pl_overlay_part *part = (struct pl_overlay_part *)&current->parts[0];
+ part->src = (pl_rect2df){ 0, 0, ass_frame->w, ass_frame->h };
+ part->dst = (pl_rect2df){
+ ass_frame->dst_x,
+ ass_frame->dst_y,
+ ass_frame->dst_x + ass_frame->w,
+ ass_frame->dst_y + ass_frame->h
+ };
+ u32 c = ass_frame->color;
+ part->color[0] = (c >> 24) / 255.0;
+ part->color[1] = ((c >> 16) & 0xFF) / 255.0;
+ part->color[2] = ((c >> 8) & 0xFF) / 255.0;
+ part->color[3] = 1.0 - (c & 0xFF) / 255.0;
+
+ current->tex = tex;
+ //pl_color_space_from_avframe(&current->color, frame);
+ current->color = *pl_color_space(
+ .primaries = PL_COLOR_PRIM_BT_709,
+ .transfer = PL_COLOR_TRC_SRGB
+ );
+ current->mode = PL_OVERLAY_MONOCHROME;
+ current->repr = pl_color_repr_unknown;
+ //current->repr.sys = pl_system_from_av(frame->colorspace);
+ //current->repr.levels = pl_levels_from_av(frame->color_range);
+ current->repr.alpha = PL_ALPHA_INDEPENDENT;
+ current->coords = PL_OVERLAY_COORDS_SRC_FRAME;
+ current->num_parts = 1;
+ }
+ return overlay;
+}
+
static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src,
struct pl_frame *out_frame)
{
AVFrame *frame = src->frame_data;
- struct camu_video_buffer *buf = frame->opaque;
- AVStream *stream = buf->stream->av.stream;
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)frame->opaque;
+ AVStream *stream = lq->q.buf->stream->av.stream;
bool ok = pl_map_avframe_ex(gpu, out_frame, pl_avframe_params(
.frame = frame,
@@ -114,6 +186,27 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
.map_dovi = false
));
+ s64 now = av_rescale_q(frame->best_effort_timestamp, stream->time_base, (AVRational){ 1, 1000 });
+ s32 change;
+ ASS_Image *ass_frame = ass_render_frame(lq->ass_renderer, lq->ass_track, now, &change);
+ if (ass_frame) {
+ struct camu_overlay_lp *overlay = NULL;
+ if (lq->overlays.size > 0) {
+ overlay = al_array_last(lq->overlays);
+ }
+ if (!overlay || change > 0) {
+ overlay = create_subtitle_overlay(gpu, ass_frame);
+ overlay->lq = lq;
+ al_array_push(lq->overlays, overlay);
+ }
+ overlay->ref++;
+ out_frame->overlays = overlay->overlays;
+ out_frame->num_overlays = overlay->num;
+ ((struct pl_source_frame *)src)->frame_data = overlay;
+ } else {
+ ((struct pl_source_frame *)src)->frame_data = NULL;
+ }
+
av_frame_free(&frame);
if (!ok) {
@@ -128,7 +221,27 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
static void unmap_av_frame(pl_gpu gpu, struct pl_frame *frame, const struct pl_source_frame *src)
{
- (void)src;
+ if (src->frame_data) {
+ struct camu_overlay_lp *overlay = (struct camu_overlay_lp *)src->frame_data;
+ struct camu_frame_queue_lp *lq = overlay->lq;
+ if (!--overlay->ref) {
+ for (s32 i = 0; i < overlay->alloc; i++) {
+ struct pl_overlay *current = &overlay->overlays[i];
+ if (current->num_parts > 0) {
+ pl_tex_destroy(gpu, &current->tex);
+ }
+ al_free((struct pl_overlay_part *)current->parts);
+ }
+ struct camu_overlay_lp *roverlay;
+ al_array_foreach(lq->overlays, i, roverlay) {
+ if (roverlay == overlay) {
+ al_array_remove_at(lq->overlays, i);
+ break;
+ }
+ }
+ al_free(overlay);
+ }
+ }
pl_unmap_avframe(gpu, frame);
}
@@ -142,10 +255,10 @@ static void discard_av_frame(const struct pl_source_frame *src)
static void queue_lp_push_av_frame(struct camu_frame_queue *queue, AVFrame *frame, f64 pts)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
- struct camu_video_buffer *buf = frame->opaque;
+ frame->opaque = lq;
pl_queue_push(lq->queue, &(struct pl_source_frame){
.pts = pts,
- .duration = buf->avg_frame_duration,
+ .duration = lq->q.buf->avg_frame_duration,
.map = map_av_frame,
.unmap = unmap_av_frame,
.discard = discard_av_frame,
@@ -153,6 +266,22 @@ static void queue_lp_push_av_frame(struct camu_frame_queue *queue, AVFrame *fram
.first_field = pl_field_from_avframe(frame)
});
}
+
+static void queue_lp_configure_subtitiles(struct camu_frame_queue *queue, s32 width, s32 height, AVCodecParameters *codecpar)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ ass_set_frame_size(lq->ass_renderer, width, height);
+ ass_set_storage_size(lq->ass_renderer, width, height);
+ ass_set_fonts(lq->ass_renderer, NULL, NULL, ASS_FONTPROVIDER_AUTODETECT, NULL, 0);
+ ass_process_codec_private(lq->ass_track, (const char *)codecpar->extradata,
+ codecpar->extradata_size);
+}
+
+static void queue_lp_push_subtitle(struct camu_frame_queue *queue, AVPacket *pkt)
+{
+ struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ ass_process_chunk(lq->ass_track, (const char *)pkt->data, pkt->size, pkt->pts, pkt->duration);
+}
#endif
static void queue_lp_flush(struct camu_frame_queue *queue)
@@ -170,10 +299,11 @@ static s32 queue_lp_count(struct camu_frame_queue *queue)
static u8 queue_lp_read(struct camu_frame_queue *queue, f64 pts, void *out)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
+ struct pl_frame_mix *mix = (struct pl_frame_mix *)out;
lq->params.pts = pts;
- switch (pl_queue_update(lq->queue, (struct pl_frame_mix *)out, &lq->params)) {
+ switch (pl_queue_update(lq->queue, mix, &lq->params)) {
case PL_QUEUE_OK:
- return CAMU_QUEUE_OK;
+ break;
case PL_QUEUE_MORE:
return CAMU_QUEUE_MORE;
case PL_QUEUE_EOF:
@@ -182,6 +312,7 @@ static u8 queue_lp_read(struct camu_frame_queue *queue, f64 pts, void *out)
default:
return CAMU_QUEUE_ERR;
}
+ return CAMU_QUEUE_OK;
}
static void queue_lp_reset(struct camu_frame_queue *queue)
@@ -194,6 +325,8 @@ static void queue_lp_reset(struct camu_frame_queue *queue)
static void queue_lp_free(struct camu_frame_queue **queue)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)*queue;
+ ass_free_track(lq->ass_track);
+ ass_renderer_done(lq->ass_renderer);
pl_queue_destroy(&lq->queue);
al_free(lq);
*queue = NULL;
@@ -202,9 +335,12 @@ static void queue_lp_free(struct camu_frame_queue **queue)
struct camu_frame_queue *camu_frame_queue_lp_create(void)
{
struct camu_frame_queue_lp *lq = al_alloc_object(struct camu_frame_queue_lp);
+ al_array_init(lq->overlays);
lq->q.push = queue_lp_push;
#ifdef CAMU_HAVE_FFMPEG
lq->q.push_av_frame = queue_lp_push_av_frame;
+ lq->q.configure_subtitles = queue_lp_configure_subtitiles;
+ lq->q.push_subtitle = queue_lp_push_subtitle;
#endif
lq->q.flush = queue_lp_flush;
lq->q.count = queue_lp_count;
diff --git a/src/render/queue_libplacebo.h b/src/render/queue_libplacebo.h
index 829ef87..3b403e5 100644
--- a/src/render/queue_libplacebo.h
+++ b/src/render/queue_libplacebo.h
@@ -4,11 +4,23 @@
#include <libplacebo/utils/frame_queue.h>
#include <libplacebo/utils/upload.h>
+#include <libass/ass.h>
+
+struct camu_overlay_lp {
+ struct pl_overlay *overlays;
+ s32 alloc;
+ s32 num;
+ s16 ref;
+ struct camu_frame_queue_lp *lq;
+};
struct camu_frame_queue_lp {
struct camu_frame_queue q;
pl_queue queue;
struct pl_queue_params params;
+ ASS_Renderer *ass_renderer;
+ ASS_Track *ass_track;
+ array(struct camu_overlay_lp *) overlays;
};
struct camu_frame_queue *camu_frame_queue_lp_create(void);
diff --git a/src/render/renderer.h b/src/render/renderer.h
index dcd74c1..b304fdf 100644
--- a/src/render/renderer.h
+++ b/src/render/renderer.h
@@ -12,8 +12,8 @@
#define CAMU_RENDERER_VULKAN
#endif
-struct camu_screen;
struct camu_frame_queue;
+struct camu_screen;
struct camu_renderer {
bool (*create_renderer)(
struct camu_renderer *, s32 *, s32 *,
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index ae341f3..480da0b 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -161,6 +161,8 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, s32 *wid
lr->r.opaque = &lr->gpu;
#endif
+ lr->ass = ass_library_init();
+
return true;
err:
lr->r.free((struct camu_renderer **)&lr);
@@ -177,6 +179,8 @@ static struct camu_frame_queue *renderer_lp_create_queue(struct camu_renderer *r
lq->params.interpolation_threshold = 0.01f;
lq->params.radius = pl_frame_mix_radius(&lr->params);
lq->params.timeout = 0uL; // vsync
+ lq->ass_renderer = ass_renderer_init(lr->ass);
+ lq->ass_track = ass_new_track(lr->ass);
return queue;
}
@@ -208,19 +212,22 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
while (scr->videos.size > 0) {
bool any_eof = false;
al_array_foreach_ptr(scr->videos, i, video) {
- if (camu_video_buffer_read(video->buf, &mix) && mix.frames) {
- target.crop = mix.frames[0]->crop;
- target.crop.x1 *= video->view.zoom / video->view.stretch;
- target.crop.y1 *= video->view.zoom * video->view.stretch;
- target.crop.x0 += video->view.x_offset;
- target.crop.y0 += video->view.y_offset;
- target.crop.x1 += video->view.x_offset;
- target.crop.y1 += video->view.y_offset;
- target.rotation = video->view.rotation;
- //lr->params.color_adjustment = pl_color_adjustment(
- // .saturation = 0.0
- //);
- pl_render_image_mix(lr->renderer, &mix, &target, &lr->params);
+ if (camu_video_buffer_read(video->buf, &mix)) {
+ // QUEUE_MORE doesn't return a frame obviously but don't treat it like an EOF.
+ if (mix.frames) {
+ target.crop = mix.frames[0]->crop;
+ target.crop.x1 *= video->view.zoom / video->view.stretch;
+ target.crop.y1 *= video->view.zoom * video->view.stretch;
+ target.crop.x0 += video->view.x_offset;
+ target.crop.y0 += video->view.y_offset;
+ target.crop.x1 += video->view.x_offset;
+ target.crop.y1 += video->view.y_offset;
+ target.rotation = video->view.rotation;
+ //lr->params.color_adjustment = pl_color_adjustment(
+ // .saturation = 0.0
+ //);
+ pl_render_image_mix(lr->renderer, &mix, &target, &lr->params);
+ }
} else {
any_eof = true;
}
@@ -250,6 +257,7 @@ void renderer_lp_free(struct camu_renderer **renderer)
if (lr->gl) pl_opengl_destroy(&lr->gl);
#endif
if (lr->logger) pl_log_destroy(&lr->logger);
+ if (lr->ass) ass_library_done(lr->ass);
al_free(lr);
*renderer = NULL;
}
diff --git a/src/render/renderer_libplacebo.h b/src/render/renderer_libplacebo.h
index 80f4688..5b206c6 100644
--- a/src/render/renderer_libplacebo.h
+++ b/src/render/renderer_libplacebo.h
@@ -4,6 +4,7 @@
#include <libplacebo/renderer.h>
#include <libplacebo/shaders/lut.h>
#include <libplacebo/colorspace.h>
+#include <libass/ass.h>
#include "renderer.h"
@@ -27,6 +28,8 @@ struct camu_renderer_lp {
pl_swapchain swapchain;
pl_renderer renderer;
struct pl_render_params params;
+ ASS_Library *ass;
+ ASS_Renderer *ass_renderer;
};
struct camu_renderer *camu_renderer_lp_create(void);