<html><head></head><body>Hi,<br><br>In my experience, duolingual subtitles are stacked together www by default, at the bottom. I think content producers and consumers alike expect the subtitles at the bottom, not the top of the picture.<br><br>And as François hinted, hard-coding an alignment will break existing settings.<br><br><div class="gmail_quote">Le 23 avril 2019 13:43:24 GMT+03:00, Roland Bewick <roland.bewick@gmail.com> a écrit :<blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<pre class="k9mail">Secondary subtitle track will be displayed at the top of the video.<br>Screenshot: <a href="http://prntscr.com/nfok5k">http://prntscr.com/nfok5k</a><br><br>Usage:<br>- Select two subtitle tracks.<br>- If two subtitle tracks are already selected and a third is selected,<br>    the previous secondary subtitle track will be unselected.<br>- To change the primary sub track,<br>    make sure to deselect the secondary one first.<br><br>Limitations:<br>- Only SRT and USF subtitles are supported.<br>    I can update the other codecs if my chosen solution is accepted.<br>- Primary and secondary subtitles share track synchronization.<br>    This must be fixed.<br>- Hotkeys haven't been done yet.<br>A single hotkey could be added to switch state between<br> manipulating the primary or secondary subtitle track.<br>- Legacy / VLC < 4.0 QT GUI isn't supported.<br>    I can add support for this so that people can still use their skins.<br>- Primary and secondary subtitles share the same style (font size, etc).<br>- Remote control is not supported.<br><br>Untested on MAC interface.<hr> modules/codec/subsdec.c             |  4 +-<br> modules/codec/subsusf.c             |  5 +-<br> src/input/es_out.c                  | 84 +++++++++++++++++++++++++++--<br> src/video_output/video_output.c     | 12 +++--<br> src/video_output/vout_subpictures.c |  2 +-<br> 5 files changed, 93 insertions(+), 14 deletions(-)<br><br>diff --git a/modules/codec/subsdec.c b/modules/codec/subsdec.c<br>index 0016332906..a733dffce2 100644<br>--- a/modules/codec/subsdec.c<br>+++ b/modules/codec/subsdec.c<br>@@ -464,9 +464,9 @@ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )<br>     int i_inline_align = -1;<br>     p_spu_sys->region.p_segments = ParseSubtitles( &i_inline_align, psz_subtitle );<br>     free( psz_subtitle );<br>-    if( p_sys->i_align >= 0 ) /* bottom ; left, right or centered */<br>+    if( p_sys->i_align >= 0 ) /* user-set alignment */<br>     {<br>-        p_spu_sys->region.align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;<br>+        p_spu_sys->region.align = p_sys->i_align;<br>         p_spu_sys->region.inner_align = p_sys->i_align;<br>     }<br>     else if( i_inline_align >= 0 )<br>diff --git a/modules/codec/subsusf.c b/modules/codec/subsusf.c<br>index c02c52b52f..faef159576 100644<br>--- a/modules/codec/subsusf.c<br>+++ b/modules/codec/subsusf.c<br>@@ -436,12 +436,11 @@ static subpicture_region_t *CreateTextRegion( decoder_t *p_dec,<br>             }<br>         }<br> <br>-        /* Set default or user align/magin.<br>+        /* Set default or user align/margin.<br>          * Style overriden if no user value. */<br>         p_text_region->i_x = i_sys_align > 0 ? 20 : 0;<br>         p_text_region->i_y = 10;<br>-        p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM |<br>-                                 ((i_sys_align > 0) ? i_sys_align : 0);<br>+        p_text_region->i_align = i_sys_align >= 0 ? i_sys_align : SUBPICTURE_ALIGN_BOTTOM;<br> <br>         if( p_ssa_style )<br>         {<br>diff --git a/src/input/es_out.c b/src/input/es_out.c<br>index 3352602849..835975fa1f 100644<br>--- a/src/input/es_out.c<br>+++ b/src/input/es_out.c<br>@@ -142,6 +142,7 @@ typedef struct<br> {<br>     int         i_count;    /* es count */<br>     es_out_id_t *p_main_es; /* current main es */<br>+    es_out_id_t *p_secondary_es; /* current secondary es (SPU_ES only) */<br>     enum es_out_policy_e e_policy;<br> <br>     /* Parameters used for es selection */<br>@@ -300,6 +301,7 @@ static void EsOutPropsInit( es_out_es_props_t *p_props,<br>     p_props->i_channel = (psz_trackvar) ? var_GetInteger( p_input, psz_trackvar ): -1;<br>     p_props->i_demux_id = -1;<br>     p_props->p_main_es = NULL;<br>+    p_props->p_secondary_es = NULL;<br> <br>     if( !input_priv(p_input)->b_preparsing && psz_langvar )<br>     {<br>@@ -343,7 +345,7 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, float rate )<br>                     "video-track-id", "video-track", NULL, NULL );<br>     EsOutPropsInit( &p_sys->audio, true, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,<br>                     "audio-track-id", "audio-track", "audio-language", "audio" );<br>-    EsOutPropsInit( &p_sys->sub,  false, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,<br>+    EsOutPropsInit( &p_sys->sub,  false, p_input, ES_OUT_ES_POLICY_SIMULTANEOUS,<br>                     "sub-track-id", "sub-track", "sub-language", "sub" );<br> <br>     p_sys->i_group_id = var_GetInteger( p_input, "program" );<br>@@ -1083,6 +1085,7 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )<br>         p_sys->audio.p_main_es = NULL;<br>         p_sys->video.p_main_es = NULL;<br>         p_sys->sub.p_main_es = NULL;<br>+        p_sys->sub.p_secondary_es = NULL;<br>     }<br> <br>     msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );<br>@@ -1805,6 +1808,46 @@ static bool EsIsSelected( es_out_id_t *es )<br>         return es->p_dec != NULL;<br>     }<br> }<br>+<br>+/**<br>+    Set the alignment used by the chosen subtitle codec.<br>+    This solution works for subsdec and subsusf formats. (SRT + MKV Embedded).<br>+<br>+    TODO: It would be nice to have a unified way of setting the subtitle alignment.<br>+    The other codecs could be updated to follow subsusf's example.<br>+*/<br>+static void EsOutSetDecoderSubtitleAlignment( es_out_t *out, es_out_id_t *p_es )<br>+{<br>+    es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);<br>+    es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, p_es->fmt.i_cat );<br>+    input_thread_t *p_input = p_sys->p_input;<br>+<br>+    if( p_es->fmt.i_cat == SPU_ES )<br>+    {<br>+        int i_subtitle_alignment = SUBPICTURE_ALIGN_BOTTOM;<br>+        if( p_esprops->p_main_es != NULL && p_es != p_esprops->p_main_es )<br>+        {<br>+            /* Show secondary sub track on the top of the video */<br>+            i_subtitle_alignment = SUBPICTURE_ALIGN_TOP;<br>+        }<br>+        /* Temporarily create this variable as only the configuration exists */<br>+        var_Create( p_input, "subsdec-align", VLC_VAR_INTEGER);<br>+        var_SetInteger( p_input, "subsdec-align",  i_subtitle_alignment);<br>+    }<br>+}<br>+<br>+/* we don't need these alignment variables anymore (already read by the codec) */<br>+static void EsOutReleaseDecoderAlignmentVariables( es_out_t *out, es_out_id_t *p_es )<br>+{<br>+    es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);<br>+    input_thread_t *p_input = p_sys->p_input;<br>+    <br>+    if( p_es->fmt.i_cat == SPU_ES )<br>+    {<br>+        var_Destroy( p_input, "subsdec-align" );<br>+    }<br>+}<br>+<br> static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )<br> {<br>     es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);<br>@@ -1821,8 +1864,13 @@ static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )<br>     if( !p_es->p_clock )<br>         return;<br> <br>+    EsOutSetDecoderSubtitleAlignment( out, p_es );<br>+<br>     dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_clock,<br>                             input_priv(p_input)->p_sout );<br>+<br>+    EsOutReleaseDecoderAlignmentVariables( out, p_es );<br>+<br>     if( dec != NULL )<br>     {<br>         input_DecoderChangeRate( dec, p_sys->rate );<br>@@ -1998,6 +2046,7 @@ static void EsDeleteCCChannels( es_out_t *out, es_out_id_t *parent )<br> static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )<br> {<br>     es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);<br>+    es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );<br>     input_thread_t *p_input = p_sys->p_input;<br> <br>     if( !EsIsSelected( es ) )<br>@@ -2006,6 +2055,15 @@ static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )<br>         return;<br>     }<br> <br>+    if( es == p_esprops->p_main_es && es->fmt.i_cat == SPU_ES )<br>+    {<br>+        p_esprops->p_main_es = NULL;<br>+    }<br>+    else if( es == p_esprops->p_secondary_es )<br>+    {<br>+        p_esprops->p_secondary_es = NULL;<br>+    }<br>+<br>     if( es->p_master )<br>     {<br>         if( es->p_master->p_dec )<br>@@ -2035,7 +2093,7 @@ static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )<br>  *<br>  * \param out The es_out structure<br>  * \param es es_out_id structure<br>- * \param b_force ...<br>+ * \param b_force force the selection of the ES even if it is lower priority<br>  * \return nothing<br>  */<br> static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )<br>@@ -2049,8 +2107,11 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )<br>         return;<br>     }<br> <br>+    /* Only auto-unselect SPU ES when looking for a higher priority ES */<br>+    bool b_auto_unselect_spu = es->fmt.i_cat == SPU_ES && !b_force;<br>+<br>     bool b_auto_unselect = p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO &&<br>-                           p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE &&<br>+                           ( p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE || b_auto_unselect_spu ) &&<br>                            p_esprops->p_main_es && p_esprops->p_main_es != es;<br> <br>     if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )<br>@@ -2059,6 +2120,11 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )<br>         {<br>             if( b_auto_unselect )<br>                 EsOutUnselectEs( out, p_esprops->p_main_es, true );<br>+            else if( b_force && p_esprops->p_secondary_es && p_esprops->p_secondary_es != es )<br>+            {<br>+                /* Auto unselect secondary subtrack */<br>+                EsOutUnselectEs( out, p_esprops->p_secondary_es, true );<br>+            }<br> <br>             EsOutSelectEs( out, es );<br>         }<br>@@ -2172,7 +2238,17 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )<br> <br>     /* FIXME TODO handle priority here */<br>     if( p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO && EsIsSelected( es ) )<br>-        p_esprops->p_main_es = es;<br>+    {<br>+        if( p_esprops->p_main_es == NULL || es->fmt.i_cat != SPU_ES )<br>+        {<br>+            p_esprops->p_main_es = es;<br>+        }<br>+        else<br>+        {<br>+            /* Only set a secondary SPU if we already have a primary one */<br>+            p_esprops->p_secondary_es = es;<br>+        }<br>+    }<br> }<br> <br> static void EsOutCreateCCChannels( es_out_t *out, vlc_fourcc_t codec, uint64_t i_bitmap,<br>diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c<br>index 9604522825..df5ff3d327 100644<br>--- a/src/video_output/video_output.c<br>+++ b/src/video_output/video_output.c<br>@@ -271,10 +271,14 @@ int vout_RegisterSubpictureChannel( vout_thread_t *vout )<br> <br> void vout_SetSubpictureClock( vout_thread_t *vout, vlc_clock_t *clock )<br> {<br>-    vlc_mutex_lock(&vout->p->spu_lock);<br>-    if (vout->p->spu)<br>-        spu_clock_Set(vout->p->spu, clock);<br>-    vlc_mutex_unlock(&vout->p->spu_lock);<br>+    /* Currently all subtitle tracks share the same clock, so it must be valid. */<br>+    if ( clock != NULL )<br>+    {<br>+        vlc_mutex_lock(&vout->p->spu_lock);<br>+        if (vout->p->spu)<br>+            spu_clock_Set(vout->p->spu, clock);<br>+        vlc_mutex_unlock(&vout->p->spu_lock);<br>+    }<br> }<br> <br> void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel )<br>diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c<br>index 6b106534d3..8b530d3c2c 100644<br>--- a/src/video_output/vout_subpictures.c<br>+++ b/src/video_output/vout_subpictures.c<br>@@ -476,7 +476,7 @@ static void SpuRegionPlace(int *x, int *y,<br>         *y = region->i_y;<br>     } else {<br>         if (region->i_align & SUBPICTURE_ALIGN_TOP)<br>-            *y = region->i_y;<br>+            *y = region->i_y + ( subpic->b_subtitle ? region->fmt.i_visible_height : 0 );<br>         else if (region->i_align & SUBPICTURE_ALIGN_BOTTOM)<br>             *y = subpic->i_original_picture_height - region->fmt.i_visible_height - region->i_y;<br>         else</pre></blockquote></div><br>-- <br>Envoyé de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma brièveté.</body></html>