vlc | branch: master | Francois Cartegnie <[email protected]> | Thu Jul 25 15:58:33 2019 +0200| [f501c7dc21215b840c106842e8ed54479c047104] | committer: Francois Cartegnie
vout: spu: prerender text Should avoid frame delay in most cases. Will still re-render at blending time if dst has changed in between. > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=f501c7dc21215b840c106842e8ed54479c047104 --- src/video_output/vout_subpictures.c | 406 +++++++++++++++++++++++++++++------- 1 file changed, 332 insertions(+), 74 deletions(-) diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c index 53daf00827..c2b0d50ace 100644 --- a/src/video_output/vout_subpictures.c +++ b/src/video_output/vout_subpictures.c @@ -69,6 +69,8 @@ struct spu_channel { }; typedef struct VLC_VECTOR(struct spu_channel) spu_channel_vector; +typedef struct VLC_VECTOR(subpicture_t *) spu_prerender_vector; +#define SPU_CHROMALIST_COUNT 8 struct spu_private_t { vlc_mutex_t lock; /* lock to protect all followings fields */ @@ -78,6 +80,7 @@ struct spu_private_t { int channel; /**< number of subpicture channels registered */ filter_t *text; /**< text renderer module */ + vlc_mutex_t textlock; filter_t *scale_yuvp; /**< scaling module for YUVP */ filter_t *scale; /**< scaling module (all but YUVP) */ bool force_crop; /**< force cropping of subpicture */ @@ -106,12 +109,28 @@ struct spu_private_t { char *filter_chain_update; vlc_mutex_t filter_chain_lock; filter_chain_t *filter_chain; + /**/ + struct + { + vlc_thread_t thread; + vlc_mutex_t lock; + vlc_cond_t cond; + vlc_cond_t output_cond; + spu_prerender_vector vector; + subpicture_t *p_processed; + video_format_t fmtsrc; + video_format_t fmtdst; + vlc_fourcc_t chroma_list[SPU_CHROMALIST_COUNT+1]; + } prerender; /* */ vlc_tick_t last_sort_date; vout_thread_t *vout; }; +static void spu_PrerenderSync(spu_private_t *, const subpicture_t *); +static void spu_PrerenderCancel(spu_private_t *, const subpicture_t *); + static void spu_channel_Init(struct spu_channel *channel, size_t id, enum vlc_vout_order order, vlc_clock_t *clock) { @@ -144,11 +163,12 @@ static void spu_channel_DeleteAt(struct spu_channel *channel, size_t index) vlc_vector_remove(&channel->entries, index); } -static void spu_channel_Clean(struct spu_channel *channel) +static void spu_channel_Clean(spu_private_t *sys, struct spu_channel *channel) { for (size_t i = 0; i < channel->entries.size; i++) { assert(channel->entries.data[i].subpic); + spu_PrerenderCancel(sys, channel->entries.data[i].subpic); subpicture_Delete(channel->entries.data[i].subpic); } vlc_vector_destroy(&channel->entries); @@ -295,14 +315,38 @@ static filter_t *SpuRenderCreateAndLoadScale(vlc_object_t *object, static void SpuRenderText(spu_t *spu, subpicture_region_t *region, + int i_original_width, + int i_original_height, const vlc_fourcc_t *chroma_list) { - filter_t *text = spu->p->text; - + spu_private_t *sys = spu->p; + filter_t *text = sys->text; assert(region->fmt.i_chroma == VLC_CODEC_TEXT); - if ( region->p_text ) - text->pf_render(text, region, region, chroma_list); + vlc_mutex_lock(&sys->textlock); + if(text) + { + // assume rendered text is in sRGB if nothing is set + if (region->fmt.transfer == TRANSFER_FUNC_UNDEF) + region->fmt.transfer = TRANSFER_FUNC_SRGB; + if (region->fmt.primaries == COLOR_PRIMARIES_UNDEF) + region->fmt.primaries = COLOR_PRIMARIES_SRGB; + if (region->fmt.space == COLOR_SPACE_UNDEF) + region->fmt.space = COLOR_SPACE_SRGB; + if (region->fmt.color_range == COLOR_RANGE_UNDEF) + region->fmt.color_range = COLOR_RANGE_FULL; + + /* FIXME aspect ratio ? */ + text->fmt_out.video.i_width = + text->fmt_out.video.i_visible_width = i_original_width; + + text->fmt_out.video.i_height = + text->fmt_out.video.i_visible_height = i_original_height; + + if ( region->p_text ) + text->pf_render(text, region, region, chroma_list); + } + vlc_mutex_unlock(&sys->textlock); } /** @@ -719,6 +763,7 @@ spu_SelectSubpictures(spu_t *spu, vlc_tick_t system_now, if (is_rejeted) { + spu_PrerenderCancel(sys, current); subpicture_Delete(current); vlc_vector_remove(&channel->entries, index); } @@ -746,6 +791,7 @@ static void SpuRenderRegion(spu_t *spu, const spu_scale_t scale_size, const vlc_fourcc_t *chroma_list, const video_format_t *fmt, + int i_original_width, int i_original_height, const spu_area_t *subtitle_area, size_t subtitle_area_count, vlc_tick_t render_date) { @@ -763,19 +809,11 @@ static void SpuRenderRegion(spu_t *spu, *dst_ptr = NULL; /* Render text region */ - if (region->fmt.i_chroma == VLC_CODEC_TEXT) { - // assume rendered text is in sRGB if nothing is set - if (region->fmt.transfer == TRANSFER_FUNC_UNDEF) - region->fmt.transfer = TRANSFER_FUNC_SRGB; - if (region->fmt.primaries == COLOR_PRIMARIES_UNDEF) - region->fmt.primaries = COLOR_PRIMARIES_SRGB; - if (region->fmt.space == COLOR_SPACE_UNDEF) - region->fmt.space = COLOR_SPACE_SRGB; - if (region->fmt.color_range == COLOR_RANGE_UNDEF) - region->fmt.color_range = COLOR_RANGE_FULL; - - SpuRenderText(spu, region, chroma_list); - + if (region->fmt.i_chroma == VLC_CODEC_TEXT) + { + SpuRenderText(spu, region, + i_original_width, i_original_height, + chroma_list); /* Check if the rendering has failed ... */ if (region->fmt.i_chroma == VLC_CODEC_TEXT) return; @@ -1101,8 +1139,6 @@ static subpicture_t *SpuRenderSubpictures(spu_t *spu, vlc_tick_t render_subtitle_date, bool external_scale) { - spu_private_t *sys = spu->p; - /* Count the number of regions and subtitle regions */ unsigned int subtitle_region_count = 0; unsigned int region_count = 0; @@ -1161,12 +1197,8 @@ static subpicture_t *SpuRenderSubpictures(spu_t *spu, subpic->i_original_picture_height = fmt_src->i_visible_height; } - /* FIXME aspect ratio ? */ - sys->text->fmt_out.video.i_width = - sys->text->fmt_out.video.i_visible_width = subpic->i_original_picture_width; - - sys->text->fmt_out.video.i_height = - sys->text->fmt_out.video.i_visible_height = subpic->i_original_picture_height; + const int i_original_width = subpic->i_original_picture_width; + const int i_original_height = subpic->i_original_picture_height; /* Render all regions * We always transform non absolute subtitle into absolute one on the @@ -1208,6 +1240,7 @@ static subpicture_t *SpuRenderSubpictures(spu_t *spu, SpuRenderRegion(spu, output_last_ptr, &area, entry, region, virtual_scale, chroma_list, fmt_dst, + i_original_width, i_original_height, subtitle_area, subtitle_area_count, subpic->b_subtitle ? render_subtitle_date : system_now); if (*output_last_ptr) @@ -1373,10 +1406,247 @@ static int SubSourceDelProxyCallbacks(filter_t *filter, void *opaque) return VLC_SUCCESS; } +static void spu_PrerenderWake(spu_private_t *sys, + const video_format_t *fmt_dst, + const video_format_t *fmt_src, + const vlc_fourcc_t *chroma_list) +{ + vlc_mutex_lock(&sys->prerender.lock); + if(!video_format_IsSimilar(fmt_dst, &sys->prerender.fmtdst)) + { + video_format_Clean(&sys->prerender.fmtdst); + video_format_Copy(&sys->prerender.fmtdst, fmt_dst); + } + if(!video_format_IsSimilar(fmt_src, &sys->prerender.fmtsrc)) + { + video_format_Clean(&sys->prerender.fmtsrc); + video_format_Copy(&sys->prerender.fmtsrc, fmt_src); + } + + for(size_t i=0; i<SPU_CHROMALIST_COUNT; i++) + { + sys->prerender.chroma_list[i] = chroma_list[i]; + if(!chroma_list[i]) + break; + } + + vlc_cond_signal(&sys->prerender.cond); + vlc_mutex_unlock(&sys->prerender.lock); +} + +static void spu_PrerenderEnqueue(spu_private_t *sys, subpicture_t *p_subpic) +{ + vlc_mutex_lock(&sys->prerender.lock); + vlc_vector_push(&sys->prerender.vector, p_subpic); + vlc_cond_signal(&sys->prerender.cond); + vlc_mutex_unlock(&sys->prerender.lock); +} + +static void spu_PrerenderCancel(spu_private_t *sys, const subpicture_t *p_subpic) +{ + vlc_mutex_lock(&sys->prerender.lock); + ssize_t i_idx; + vlc_vector_index_of(&sys->prerender.vector, p_subpic, &i_idx); + if(i_idx >= 0) + vlc_vector_remove(&sys->prerender.vector, i_idx); + else while(sys->prerender.p_processed == p_subpic) + vlc_cond_wait(&sys->prerender.output_cond, &sys->prerender.lock); + vlc_mutex_unlock(&sys->prerender.lock); +} + +static void spu_PrerenderPause(spu_private_t *sys) +{ + vlc_mutex_lock(&sys->prerender.lock); + while(sys->prerender.p_processed) + vlc_cond_wait(&sys->prerender.output_cond, &sys->prerender.lock); + sys->prerender.chroma_list[0] = 0; + vlc_mutex_unlock(&sys->prerender.lock); +} + +static void spu_PrerenderSync(spu_private_t *sys, const subpicture_t *p_subpic) +{ + vlc_mutex_lock(&sys->prerender.lock); + ssize_t i_idx; + vlc_vector_index_of(&sys->prerender.vector, p_subpic, &i_idx); + while(i_idx >= 0 || sys->prerender.p_processed == p_subpic) + { + vlc_cond_wait(&sys->prerender.output_cond, &sys->prerender.lock); + vlc_vector_index_of(&sys->prerender.vector, p_subpic, &i_idx); + } + vlc_mutex_unlock(&sys->prerender.lock); +} + +static void spu_PrerenderText(spu_t *spu, subpicture_t *p_subpic, + video_format_t *fmtsrc, video_format_t *fmtdst, + vlc_fourcc_t *chroma_list) +{ + if (p_subpic->i_original_picture_width <= 0 || + p_subpic->i_original_picture_height <= 0) { + if (p_subpic->i_original_picture_width > 0 || + p_subpic->i_original_picture_height > 0) + msg_Err(spu, "original picture size %dx%d is unsupported", + p_subpic->i_original_picture_width, + p_subpic->i_original_picture_height); + else + msg_Warn(spu, "original picture size is undefined"); + + p_subpic->i_original_picture_width = fmtsrc->i_visible_width; + p_subpic->i_original_picture_height = fmtsrc->i_visible_height; + } + + + subpicture_Update(p_subpic, fmtsrc, fmtdst, + p_subpic->b_subtitle ? p_subpic->i_start : vlc_tick_now()); + + const int i_original_picture_width = p_subpic->i_original_picture_width; + const int i_original_picture_height = p_subpic->i_original_picture_height; + + subpicture_region_t *region; + for (region = p_subpic->p_region; region != NULL; region = region->p_next) + { + if(region->fmt.i_chroma != VLC_CODEC_TEXT) + continue; + SpuRenderText(spu, region, + i_original_picture_width, i_original_picture_height, + chroma_list); + } +} + +struct spu_prerender_ctx_s +{ + video_format_t fmtsrc; + video_format_t fmtdst; + vlc_fourcc_t chroma_list[SPU_CHROMALIST_COUNT+1]; + vlc_mutex_t *cleanuplock; + subpicture_t **pp_processed; +}; + +static void spu_prerender_cleanup_routine(void *priv) +{ + struct spu_prerender_ctx_s *ctx = priv; + video_format_Clean(&ctx->fmtdst); + video_format_Clean(&ctx->fmtsrc); + *ctx->pp_processed = NULL; + vlc_mutex_unlock(ctx->cleanuplock); +} + +static void * spu_PrerenderThread(void *priv) +{ + spu_t *spu = priv; + spu_private_t *sys = spu->p; + + struct spu_prerender_ctx_s ctx; + ctx.cleanuplock = &sys->prerender.lock; + ctx.chroma_list[SPU_CHROMALIST_COUNT] = 0; + video_format_Init(&ctx.fmtsrc, 0); + video_format_Init(&ctx.fmtdst, 0); + ctx.pp_processed = &sys->prerender.p_processed; + + vlc_mutex_lock(&sys->prerender.lock); + for( ;; ) + { + vlc_cleanup_push(spu_prerender_cleanup_routine, &ctx); + while(!sys->prerender.vector.size || !sys->prerender.chroma_list[0]) + vlc_cond_wait(&sys->prerender.cond, &sys->prerender.lock); + + size_t i_idx = 0; + sys->prerender.p_processed = sys->prerender.vector.data[0]; + for(size_t i=1; i<sys->prerender.vector.size; i++) + { + if(sys->prerender.p_processed->i_start > sys->prerender.vector.data[i]->i_start) + { + sys->prerender.p_processed = sys->prerender.vector.data[i]; + i_idx = i; + } + } + vlc_vector_remove(&sys->prerender.vector, i_idx); + memcpy(&ctx.chroma_list, sys->prerender.chroma_list, SPU_CHROMALIST_COUNT); + video_format_Clean(&ctx.fmtdst); + video_format_Clean(&ctx.fmtsrc); + video_format_Copy(&ctx.fmtdst, &sys->prerender.fmtdst); + video_format_Copy(&ctx.fmtsrc, &sys->prerender.fmtsrc); + + vlc_mutex_unlock(&sys->prerender.lock); + vlc_cleanup_pop(); + + int canc = vlc_savecancel(); + spu_PrerenderText(spu, sys->prerender.p_processed, + &ctx.fmtsrc, &ctx.fmtdst, ctx.chroma_list); + vlc_restorecancel(canc); + + vlc_mutex_lock(&sys->prerender.lock); + sys->prerender.p_processed = NULL; + vlc_cond_signal(&sys->prerender.output_cond); + } + + return NULL; +} + /***************************************************************************** * Public API *****************************************************************************/ +static void spu_Cleanup(spu_t *spu) +{ + spu_private_t *sys = spu->p; + + if (sys->text) + FilterRelease(sys->text); + vlc_mutex_destroy(&sys->textlock); + + if (sys->scale_yuvp) + FilterRelease(sys->scale_yuvp); + + if (sys->scale) + FilterRelease(sys->scale); + + filter_chain_ForEach(sys->source_chain, SubSourceClean, spu); + if (sys->vout) + filter_chain_ForEach(sys->source_chain, + SubSourceDelProxyCallbacks, sys->vout); + filter_chain_Delete(sys->source_chain); + free(sys->source_chain_current); + if (sys->vout) + filter_chain_ForEach(sys->filter_chain, + SubFilterDelProxyCallbacks, sys->vout); + filter_chain_Delete(sys->filter_chain); + free(sys->filter_chain_current); + vlc_mutex_destroy(&sys->filter_chain_lock); + free(sys->source_chain_update); + free(sys->filter_chain_update); + + /* Destroy all remaining subpictures */ + for (size_t i = 0; i < sys->channels.size; ++i) + spu_channel_Clean(sys, &sys->channels.data[i]); + + vlc_vector_destroy(&sys->channels); + + vlc_mutex_destroy(&sys->lock); + + vlc_mutex_destroy(&sys->prerender.lock); + vlc_cond_destroy(&sys->prerender.cond); + vlc_cond_destroy(&sys->prerender.output_cond); + vlc_vector_clear(&sys->prerender.vector); + video_format_Clean(&sys->prerender.fmtdst); + video_format_Clean(&sys->prerender.fmtsrc); +} + +/** + * Destroy the subpicture unit + * + * \param p_this the parent object which destroys the subpicture unit + */ +void spu_Destroy(spu_t *spu) +{ + spu_private_t *sys = spu->p; + /* stop prerendering */ + vlc_cancel(sys->prerender.thread); + vlc_join(sys->prerender.thread, NULL); + /* delete filters and free resources */ + spu_Cleanup(spu); + vlc_object_delete(spu); +} + #undef spu_Create /** * Creates the subpicture unit @@ -1424,6 +1694,7 @@ spu_t *spu_Create(vlc_object_t *object, vout_thread_t *vout) /* Load text and scale module */ sys->text = SpuRenderCreateAndLoadText(spu); + vlc_mutex_init(&sys->textlock); /* XXX spu->p_scale is used for all conversion/scaling except yuvp to * yuva/rgba */ @@ -1440,58 +1711,31 @@ spu_t *spu_Create(vlc_object_t *object, vout_thread_t *vout) || !sys->scale_yuvp) { sys->vout = NULL; - spu_Destroy(spu); + spu_Cleanup(spu); + vlc_object_delete(spu); return NULL; } /* */ sys->last_sort_date = -1; sys->vout = vout; - return spu; -} - -/** - * Destroy the subpicture unit - * - * \param p_this the parent object which destroys the subpicture unit - */ -void spu_Destroy(spu_t *spu) -{ - spu_private_t *sys = spu->p; - - if (sys->text) - FilterRelease(sys->text); - - if (sys->scale_yuvp) - FilterRelease(sys->scale_yuvp); - - if (sys->scale) - FilterRelease(sys->scale); - - filter_chain_ForEach(sys->source_chain, SubSourceClean, spu); - if (sys->vout) - filter_chain_ForEach(sys->source_chain, - SubSourceDelProxyCallbacks, sys->vout); - filter_chain_Delete(sys->source_chain); - free(sys->source_chain_current); - if (sys->vout) - filter_chain_ForEach(sys->filter_chain, - SubFilterDelProxyCallbacks, sys->vout); - filter_chain_Delete(sys->filter_chain); - free(sys->filter_chain_current); - vlc_mutex_destroy(&sys->filter_chain_lock); - free(sys->source_chain_update); - free(sys->filter_chain_update); - - /* Destroy all remaining subpictures */ - for (size_t i = 0; i < sys->channels.size; ++i) - spu_channel_Clean(&sys->channels.data[i]); - - vlc_vector_destroy(&sys->channels); - - vlc_mutex_destroy(&sys->lock); + vlc_mutex_init(&sys->prerender.lock); + vlc_cond_init(&sys->prerender.cond); + vlc_cond_init(&sys->prerender.output_cond); + vlc_vector_init(&sys->prerender.vector); + video_format_Init(&sys->prerender.fmtdst, 0); + video_format_Init(&sys->prerender.fmtsrc, 0); + sys->prerender.p_processed = NULL; + sys->prerender.chroma_list[0] = 0; + sys->prerender.chroma_list[SPU_CHROMALIST_COUNT] = 0; + if(vlc_clone(&sys->prerender.thread, spu_PrerenderThread, spu, VLC_THREAD_PRIORITY_VIDEO)) + { + spu_Cleanup(spu); + vlc_object_delete(spu); + spu = NULL; + } - vlc_object_delete(spu); + return spu; } /** @@ -1505,9 +1749,11 @@ void spu_Attach(spu_t *spu, input_thread_t *input) spu->p->input = input; + vlc_mutex_lock(&spu->p->textlock); if (spu->p->text) FilterRelease(spu->p->text); spu->p->text = SpuRenderCreateAndLoadText(spu); + vlc_mutex_unlock(&spu->p->textlock); } vlc_mutex_unlock(&spu->p->lock); } @@ -1518,6 +1764,7 @@ void spu_Attach(spu_t *spu, input_thread_t *input) void spu_Detach(spu_t *spu) { vlc_mutex_lock(&spu->p->lock); + spu_PrerenderPause(spu->p); spu->p->input = NULL; vlc_mutex_unlock(&spu->p->lock); } @@ -1652,6 +1899,7 @@ void spu_PutSubpicture(spu_t *spu, subpicture_t *subpic) subpicture_Delete(subpic); return; } + spu_PrerenderEnqueue(sys, subpic); vlc_mutex_unlock(&sys->lock); } @@ -1712,6 +1960,9 @@ subpicture_t *spu_Render(spu_t *spu, chroma_list = vlc_fourcc_IsYUV(fmt_dst->i_chroma) ? chroma_list_default_yuv : chroma_list_default_rgb; + /* wake up prerenderer, we have some video size and chroma */ + spu_PrerenderWake(sys, fmt_dst, fmt_src, chroma_list); + vlc_mutex_lock(&sys->lock); size_t subpicture_count; @@ -1730,6 +1981,9 @@ subpicture_t *spu_Render(spu_t *spu, for (size_t i = 0; i < subpicture_count; i++) { spu_render_entry_t *entry = &subpicture_array[i]; subpicture_t *subpic = entry->subpic; + + spu_PrerenderSync(sys, entry->subpic); + if (!subpic->updater.pf_validate) continue; @@ -1789,10 +2043,14 @@ ssize_t spu_RegisterChannel(spu_t *spu) return spu_RegisterChannelInternal(spu, NULL, NULL); } -static void spu_channel_Clear(struct spu_channel *channel) +static void spu_channel_Clear(spu_private_t *sys, + struct spu_channel *channel) { for (size_t i = 0; i < channel->entries.size; i++) + { + spu_PrerenderCancel(sys, channel->entries.data[i].subpic); spu_channel_DeleteAt(channel, i); + } } void spu_ClearChannel(spu_t *spu, size_t channel_id) @@ -1800,7 +2058,7 @@ void spu_ClearChannel(spu_t *spu, size_t channel_id) spu_private_t *sys = spu->p; vlc_mutex_lock(&sys->lock); struct spu_channel *channel = spu_GetChannel(spu, channel_id); - spu_channel_Clear(channel); + spu_channel_Clear(sys, channel); if (channel->clock) { vlc_clock_Reset(channel->clock); @@ -1815,7 +2073,7 @@ void spu_UnregisterChannel(spu_t *spu, size_t channel_id) vlc_mutex_lock(&sys->lock); struct spu_channel *channel = spu_GetChannel(spu, channel_id); - spu_channel_Clean(channel); + spu_channel_Clean(sys, channel); vlc_vector_remove(&sys->channels, channel_id); vlc_mutex_unlock(&sys->lock); } _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
