diff options
Diffstat (limited to 'src/render')
| -rw-r--r-- | src/render/meson.build | 7 | ||||
| -rw-r--r-- | src/render/queue_libplacebo.c | 150 | ||||
| -rw-r--r-- | src/render/queue_libplacebo.h | 12 | ||||
| -rw-r--r-- | src/render/renderer.h | 2 | ||||
| -rw-r--r-- | src/render/renderer_libplacebo.c | 34 | ||||
| -rw-r--r-- | src/render/renderer_libplacebo.h | 3 |
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 *)¤t->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(¤t->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, ¤t->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); |