summaryrefslogtreecommitdiff
path: root/src/render/queue_libplacebo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/queue_libplacebo.c')
-rw-r--r--src/render/queue_libplacebo.c215
1 files changed, 146 insertions, 69 deletions
diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c
index e3540c3..408af51 100644
--- a/src/render/queue_libplacebo.c
+++ b/src/render/queue_libplacebo.c
@@ -13,27 +13,63 @@ static bool queue_lp_configure_subtitles(struct camu_frame_queue *queue, u32 wid
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
#ifdef CAMU_HAVE_SUBTITLES
- if (!lq->ass) return false;
- lq->ass_renderer = ass_renderer_init(lq->ass);
- if (!lq->ass_renderer) {
+ ASS_Library *ass = lq->subs.ass;
+ if (!(lq->subs.renderer = ass_renderer_init(ass))) {
log_error("Failed to initialize ass renderer.");
return false;
}
- ass_set_shaper(lq->ass_renderer, ASS_SHAPING_COMPLEX);
- lq->ass_track = ass_new_track(lq->ass);
- if (!lq->ass_track) {
+ if (!(lq->subs.track = ass_new_track(ass))) {
log_error("Failed to create ass track.");
return false;
}
- 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_set_hinting(lq->ass_renderer, ASS_HINTING_NONE);
+ ASS_Renderer *renderer = lq->subs.renderer;
+ ASS_Track *track = lq->subs.track;
+ ass_set_shaper(renderer, ASS_SHAPING_COMPLEX);
+ ass_set_frame_size(renderer, width, height);
+ ass_set_storage_size(renderer, width, height);
+ ass_set_fonts(renderer, NULL, NULL, ASS_FONTPROVIDER_AUTODETECT, NULL, 0);
+ ass_set_hinting(renderer, ASS_HINTING_NONE);
AVCodecParameters *codecpar = stream->av.stream->codecpar;
- ass_process_codec_private(lq->ass_track, (const char *)codecpar->extradata,
- codecpar->extradata_size);
- nn_mutex_init(&lq->subtitle_lock);
- lq->has_subtitles = true;
+ lq->subs.conversion_needed = codecpar->codec_id != AV_CODEC_ID_ASS;
+ if (lq->subs.conversion_needed) {
+ const AVCodecDescriptor *desc = avcodec_descriptor_get(codecpar->codec_id);
+ if (!(desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+ log_error("Cannot convert non-text (bitmap) subtitles.");
+ return false;
+ }
+ const AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
+ if (!codec) {
+ log_error("Failed to find subtitle decoder.");
+ return false;
+ }
+ if (!(lq->subs.converter = camu_ff_alloc_codec_context(codec, codecpar))) {
+ return false;
+ }
+ AVCodecContext *converter = lq->subs.converter;
+ converter->sub_charenc_mode = FF_SUB_CHARENC_MODE_IGNORE;
+ if (!camu_ff_open_avcodec(converter, codec, NULL)) {
+ return false;
+ }
+ const AVCodecDescriptor *ass_desc = avcodec_descriptor_get(AV_CODEC_ID_ASS);
+ log_info("Subtitle conversion: %s -> %s.", desc->long_name, ass_desc->long_name);
+ // https://github.com/mpv-player/mpv/blob/266cb79f38fd1a5fd448b453dee5971795a145ca/sub/sd_ass.c#L634
+#define MP_ASS_FONT_PLAYRESX 384.0
+ track->PlayResX = track->PlayResY * (f64)width / MAX(height, (u32)1);
+ f64 fix_margins = track->PlayResX / MP_ASS_FONT_PLAYRESX;
+ f64 font_scale = 1.0;
+ for (s32 n = 0; n < track->n_styles; n++) {
+ track->styles[n].MarginL = track->styles[n].MarginL * fix_margins;
+ track->styles[n].MarginR = track->styles[n].MarginR * fix_margins;
+ track->styles[n].MarginV = track->styles[n].MarginV * font_scale;
+ }
+ ass_process_codec_private(track, (const char *)converter->subtitle_header,
+ converter->subtitle_header_size);
+ } else {
+ ass_process_codec_private(track, (const char *)codecpar->extradata,
+ codecpar->extradata_size);
+ }
+ nn_mutex_init(&lq->subs.lock);
+ lq->subs.are_present = true;
return true;
#else
(void)lq;
@@ -65,11 +101,6 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
masks[2] = 0x00ff0000;
pixel_stride = 3;
break;
- case CAMU_PIXEL_FORMAT_GREYA:
- masks[0] = 0x000000ff;
- masks[1] = 0x0000ff00;
- pixel_stride = 2;
- break;
case CAMU_PIXEL_FORMAT_GREY:
masks[0] = 0x000000ff;
pixel_stride = 1;
@@ -79,25 +110,26 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
al_assert_and_return(false);
}
- struct pl_plane_data data = {
- .type = PL_FMT_UNORM,
- .width = frame->video.width,
- .height = frame->video.height,
- .pixel_stride = pixel_stride,
- .row_stride = frame->video.width * pixel_stride,
- .pixels = frame->data
- };
- pl_plane_data_from_mask(&data, masks);
+ if (frame->format == CAMU_PIXEL_FORMAT_GREY) {
+ out_frame->repr.sys = pl_system_from_av(AVCOL_SPC_BT470BG);
+ out_frame->repr.levels = pl_levels_from_av(AVCOL_RANGE_JPEG);
+ } else {
+ out_frame->repr = pl_color_repr_unknown;
+ }
+ out_frame->color = pl_color_space_unknown;
+ out_frame->crop = (pl_rect2df){ 0.f, 0.f, frame->video.width, frame->video.height };
- struct pl_plane plane = { 0 };
- bool ok = pl_upload_plane(gpu, &plane, tex, &data);
+ struct pl_plane_data data[4] = { 0 };
+ data[0].type = PL_FMT_UNORM;
+ data[0].width = frame->video.width;
+ data[0].height = frame->video.height;
+ data[0].pixel_stride = pixel_stride;
+ data[0].row_stride = frame->video.width * pixel_stride;
+ data[0].pixels = frame->data;
+ pl_plane_data_from_mask(&data[0], masks);
- out_frame->num_planes = 1;
- out_frame->planes[0] = plane;
- out_frame->repr = pl_color_repr_unknown;
- out_frame->color = pl_color_space_monitor;
- out_frame->crop = (pl_rect2df){ 0.f, 0.f, frame->video.width, frame->video.height };
- //out_frame->profile =
+ struct pl_plane plane = { 0 };
+ bool ok = pl_upload_plane(gpu, &plane, tex, &data[0]);
camu_codec_frame_discard(frame);
@@ -106,6 +138,9 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
return false;
}
+ out_frame->num_planes = 1;
+ out_frame->planes[0] = plane;
+
return true;
}
@@ -153,7 +188,7 @@ static void free_overlay(pl_gpu gpu, struct camu_overlay *overlay)
static struct camu_overlay *first_nonref_overlay(struct camu_frame_queue_lp *lq, u32 *index)
{
struct camu_overlay *overlay;
- al_array_foreach(lq->subtitles, i, overlay) {
+ al_array_foreach(lq->subs.overlays, i, overlay) {
if (!overlay->ref) {
*index = i;
return overlay;
@@ -224,10 +259,10 @@ static struct camu_overlay *create_subtitle_overlay(struct camu_overlay *prev, p
ass_frame->dst_y + ass_frame->h
};
u32 c = ass_frame->color;
- current_part->color[0] = (c >> 24) / 255.0;
- current_part->color[1] = ((c >> 16) & 0xff) / 255.0;
- current_part->color[2] = ((c >> 8) & 0xff) / 255.0;
- current_part->color[3] = 1.0 - (c & 0xff) / 255.0;
+ current_part->color[0] = (c >> 24) / 255.f;
+ current_part->color[1] = ((c >> 16) & 0xff) / 255.f;
+ current_part->color[2] = ((c >> 8) & 0xff) / 255.f;
+ current_part->color[3] = 1.f - (c & 0xff) / 255.f;
current->parts = current_part;
current->num_parts = 1;
@@ -265,7 +300,7 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
// pl_map_avframe_derived() will fail under Xwayland because of a lack of EGL_EXT_image_dma_buf_import.
if (!ok && frame->hw_frames_ctx) {
if (!lq->copy_frame_fallback) {
- log_warn("Falling back to software copy.");
+ log_warn("Failed to map hwframe, falling back to software copy.");
lq->copy_frame_fallback = true;
lq->swframe = av_frame_alloc();
enum AVPixelFormat *fmts;
@@ -290,27 +325,27 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
((struct pl_source_frame *)src)->frame_data = NULL;
#ifdef CAMU_HAVE_SUBTITLES
- if (ok && lq->has_subtitles) {
+ if (ok && lq->subs.are_present) {
s64 now = av_rescale_q(frame->best_effort_timestamp, stream->time_base, (AVRational){ 1, 1000 });
s32 change = -1; // 1 = different position, 2 = different content.
- nn_mutex_lock(&lq->subtitle_lock);
- ASS_Image *ass_frame = ass_render_frame(lq->ass_renderer, lq->ass_track, now, &change);
+ nn_mutex_lock(&lq->subs.lock);
+ ASS_Image *ass_frame = ass_render_frame(lq->subs.renderer, lq->subs.track, now, &change);
al_assert(change >= 0);
- // ass_frame = NULL and lq->ass_track->n_events = 0 can also mean parsing completely failed.
+ // ass_frame = NULL and lq->subs.track->n_events = 0 can also mean parsing completely failed.
if (ass_frame) {
- struct camu_overlay *overlay = lq->last_subtitle;
+ struct camu_overlay *overlay = lq->subs.last;
if (change != 0) {
u32 reused = AL_ARRAY_NO_INDEX;
overlay = create_subtitle_overlay(first_nonref_overlay(lq, &reused), gpu, ass_frame);
// If all texture uploads failed, overlay->num will be 0.
- lq->last_subtitle = overlay;
+ lq->subs.last = overlay;
if (overlay) {
if (reused != AL_ARRAY_NO_INDEX) {
// Pointer may have changed due to reallocation.
- al_array_at(lq->subtitles, reused) = overlay;
+ al_array_at(lq->subs.overlays, reused) = overlay;
} else { // New overlay object.
- overlay->lock = &lq->subtitle_lock;
- al_array_push(lq->subtitles, overlay);
+ overlay->lock = &lq->subs.lock;
+ al_array_push(lq->subs.overlays, overlay);
}
}
}
@@ -322,7 +357,7 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
out_frame->overlays = NULL;
}
}
- nn_mutex_unlock(&lq->subtitle_lock);
+ nn_mutex_unlock(&lq->subs.lock);
}
#endif
@@ -373,15 +408,40 @@ static void queue_lp_push_av_frame(struct camu_frame_queue *queue, AVFrame *fram
}
#endif
+static bool convert_subtitle(struct camu_frame_queue_lp *lq, AVPacket *pkt)
+{
+ AVSubtitle sub = { 0 };
+ s32 got_sub = 0;
+ s32 ret = avcodec_decode_subtitle2(lq->subs.converter, &sub, &got_sub, pkt);
+ if (ret < 0) {
+ log_error("Failed to convert subtitle (%s).", av_err2str(ret));
+ } else if (got_sub) {
+ for (u32 i = 0; i < sub.num_rects; i++) {
+ AVSubtitleRect *rect = sub.rects[i];
+ size_t size = al_strlen(rect->ass);
+ if (rect->ass[size - 1] == 0x02) size--; // Not sure what this is...
+ ass_process_chunk(lq->subs.track, rect->ass, size, pkt->pts, pkt->duration);
+ }
+ avsubtitle_free(&sub);
+ return true;
+ }
+ return false;
+}
+
static void queue_lp_push_subtitle(struct camu_frame_queue *queue, struct camu_codec_packet *packet)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
#ifdef CAMU_HAVE_SUBTITLES
+ if (!lq->subs.are_present) return;
AVPacket *pkt = packet->av.pkt;
- nn_mutex_lock(&lq->subtitle_lock);
- al_assert(lq->ass_track && lq->ass_renderer);
- ass_process_chunk(lq->ass_track, (const char *)pkt->data, pkt->size, pkt->pts, pkt->duration);
- nn_mutex_unlock(&lq->subtitle_lock);
+ nn_mutex_lock(&lq->subs.lock);
+ al_assert(lq->subs.track && lq->subs.renderer);
+ if (lq->subs.conversion_needed) {
+ convert_subtitle(lq, pkt);
+ } else {
+ ass_process_chunk(lq->subs.track, (const char *)pkt->data, pkt->size, pkt->pts, pkt->duration);
+ }
+ nn_mutex_unlock(&lq->subs.lock);
#else
(void)lq;
(void)packet;
@@ -392,6 +452,16 @@ static void queue_lp_flush(struct camu_frame_queue *queue)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
pl_queue_push(lq->queue, NULL);
+#ifdef CAMU_HAVE_SUBTITLES
+ if (lq->subs.are_present) {
+ if (lq->subs.conversion_needed) {
+ AVPacket *empty_pkt = av_packet_alloc();
+ while (convert_subtitle(lq, empty_pkt)) { }
+ av_packet_free(&empty_pkt);
+ }
+ ass_flush_events(lq->subs.track);
+ }
+#endif
}
static s32 queue_lp_count(struct camu_frame_queue *queue)
@@ -422,7 +492,7 @@ static u8 queue_lp_read(struct camu_frame_queue *queue, f64 pts, void *out)
static void queue_lp_reset(struct camu_frame_queue *queue)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
- pl_queue_push(lq->queue, NULL);
+ queue_lp_flush(&lq->q);
pl_queue_reset(lq->queue);
}
@@ -436,15 +506,22 @@ static void queue_lp_free(struct camu_frame_queue **queue)
}
#endif
#ifdef CAMU_HAVE_SUBTITLES
- if (lq->has_subtitles) {
- struct camu_overlay *overlay;
- al_array_foreach_rev(lq->subtitles, i, overlay) {
- free_overlay(lq->gpu, overlay);
- }
- al_array_free(lq->subtitles);
- ass_free_track(lq->ass_track);
- ass_renderer_done(lq->ass_renderer);
- nn_mutex_destroy(&lq->subtitle_lock);
+ struct camu_overlay *overlay;
+ al_array_foreach_rev(lq->subs.overlays, i, overlay) {
+ free_overlay(lq->gpu, overlay);
+ }
+ al_array_free(lq->subs.overlays);
+ if (lq->subs.are_present) {
+ nn_mutex_destroy(&lq->subs.lock);
+ }
+ if (lq->subs.converter) {
+ avcodec_free_context(&lq->subs.converter);
+ }
+ if (lq->subs.track) {
+ ass_free_track(lq->subs.track);
+ }
+ if (lq->subs.renderer) {
+ ass_renderer_done(lq->subs.renderer);
}
#endif
al_free(lq);
@@ -455,9 +532,9 @@ struct camu_frame_queue *camu_frame_queue_lp_create(void)
{
struct camu_frame_queue_lp *lq = al_alloc_object(struct camu_frame_queue_lp);
#ifdef CAMU_HAVE_SUBTITLES
- lq->last_subtitle = NULL;
- al_array_init(lq->subtitles);
- lq->has_subtitles = false;
+ lq->subs.are_present = false;
+ lq->subs.last = NULL;
+ al_array_init(lq->subs.overlays);
#endif
lq->q.configure_subtitles = queue_lp_configure_subtitles;
lq->q.push = queue_lp_push;