[vlc-devel] [PATCH 08/20] vout: spu: handle secondary channel order

Thomas Guillem thomas at gllm.fr
Thu Jun 20 17:23:51 CEST 2019


From: Roland Bewick <roland.bewick at gmail.com>

Signed-off-by: Thomas Guillem <thomas at gllm.fr>
---
 include/vlc_vout.h                  | 10 ++++
 src/input/decoder.c                 |  3 +-
 src/video_output/video_output.c     |  5 +-
 src/video_output/vout_internal.h    |  6 ++-
 src/video_output/vout_subpictures.c | 83 +++++++++++++++++++++++------
 5 files changed, 86 insertions(+), 21 deletions(-)

diff --git a/include/vlc_vout.h b/include/vlc_vout.h
index da1a3a836c..42fdec9c26 100644
--- a/include/vlc_vout.h
+++ b/include/vlc_vout.h
@@ -71,6 +71,16 @@ struct vout_thread_t {
 #define VOUT_ALIGN_BOTTOM       0x0008
 #define VOUT_ALIGN_VMASK        0x000C
 
+/**
+ * SPU/Subtitle channel order
+ */
+enum vlc_spu_channel_order
+{
+    VLC_SPU_CHANNEL_ORDER_NONE,
+    VLC_SPU_CHANNEL_ORDER_PRIMARY,
+    VLC_SPU_CHANNEL_ORDER_SECONDARY,
+};
+
 /*****************************************************************************
  * Prototypes
  *****************************************************************************/
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 3b19b51d66..3a47c01ddf 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -630,7 +630,8 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec,
     {
         ssize_t old_spu_channel = p_owner->i_spu_channel;
         p_owner->i_spu_channel =
-            vout_RegisterSubpictureChannelInternal(p_vout, p_owner->p_clock);
+            vout_RegisterSubpictureChannelInternal(p_vout, p_owner->p_clock,
+                                                   NULL);
         p_owner->i_spu_order = 0;
 
         if (p_owner->i_spu_channel != -1)
diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c
index a3ef144da1..ab3666da81 100644
--- a/src/video_output/video_output.c
+++ b/src/video_output/video_output.c
@@ -276,13 +276,14 @@ ssize_t vout_RegisterSubpictureChannel( vout_thread_t *vout )
 }
 
 ssize_t vout_RegisterSubpictureChannelInternal(vout_thread_t *vout,
-                                               vlc_clock_t *clock)
+                                               vlc_clock_t *clock,
+                                               enum vlc_spu_channel_order *out_order)
 {
     assert(!vout->p->dummy);
     ssize_t channel = VOUT_SPU_CHANNEL_INVALID;
 
     if (vout->p->spu)
-        channel = spu_RegisterChannelInternal(vout->p->spu, clock);
+        channel = spu_RegisterChannelInternal(vout->p->spu, clock, out_order);
 
     return channel;
 }
diff --git a/src/video_output/vout_internal.h b/src/video_output/vout_internal.h
index 5490b2b12c..699ade93ae 100644
--- a/src/video_output/vout_internal.h
+++ b/src/video_output/vout_internal.h
@@ -261,8 +261,10 @@ vout_display_t *vout_OpenWrapper(vout_thread_t *, const char *,
 void vout_CloseWrapper(vout_thread_t *, vout_display_t *vd);
 
 /* */
-ssize_t vout_RegisterSubpictureChannelInternal( vout_thread_t *, vlc_clock_t *clock );
-ssize_t spu_RegisterChannelInternal( spu_t *, vlc_clock_t * );
+ssize_t vout_RegisterSubpictureChannelInternal( vout_thread_t *,
+                                                vlc_clock_t *clock,
+                                                enum vlc_spu_channel_order *out_order );
+ssize_t spu_RegisterChannelInternal( spu_t *, vlc_clock_t *, enum vlc_spu_channel_order * );
 void spu_Attach( spu_t *, input_thread_t *input );
 void spu_Detach( spu_t * );
 void spu_SetClockDelay(spu_t *spu, size_t channel_id, vlc_tick_t delay);
diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c
index 18a40f51a8..8d209bbbde 100644
--- a/src/video_output/vout_subpictures.c
+++ b/src/video_output/vout_subpictures.c
@@ -54,6 +54,7 @@ typedef struct {
     vlc_tick_t start;
     vlc_tick_t stop;
     bool is_late;
+    enum vlc_spu_channel_order channel_order;
 } spu_render_entry_t;
 
 typedef struct VLC_VECTOR(spu_render_entry_t) spu_render_vector;
@@ -61,6 +62,7 @@ typedef struct VLC_VECTOR(spu_render_entry_t) spu_render_vector;
 struct spu_channel {
     spu_render_vector entries;
     size_t id;
+    enum vlc_spu_channel_order order;
     vlc_clock_t *clock;
     vlc_tick_t delay;
     float rate;
@@ -87,6 +89,13 @@ struct spu_private_t {
     } crop;                                                  /**< cropping */
 
     int margin;                        /**< force position of a subpicture */
+    /**
+     * Move the secondary subtites vertically.
+     * Note: Primary sub margin is applied to all sub tracks and is absolute.
+     * Secondary sub margin is not absolute to enable overlap detection.
+     */
+    int secondary_margin;
+    int secondary_alignment;       /**< Force alignment for secondary subs */
     video_palette_t palette;              /**< force palette of subpicture */
 
     /* Subpiture filters */
@@ -104,12 +113,13 @@ struct spu_private_t {
 };
 
 static void spu_channel_Init(struct spu_channel *channel, size_t id,
-                             vlc_clock_t *clock)
+                             enum vlc_spu_channel_order order, vlc_clock_t *clock)
 {
     channel->id = id;
     channel->clock = clock;
     channel->delay = 0;
     channel->rate = 1.f;
+    channel->order = order;
 
     vlc_vector_init(&channel->entries);
 }
@@ -167,7 +177,7 @@ static struct spu_channel *spu_GetChannel(spu_t *spu, size_t channel_id)
     vlc_assert_unreachable();
 }
 
-static ssize_t spu_GetFreeChannelId(spu_t *spu)
+static ssize_t spu_GetFreeChannelId(spu_t *spu, enum vlc_spu_channel_order *order)
 {
     spu_private_t *sys = spu->p;
 
@@ -175,6 +185,8 @@ static ssize_t spu_GetFreeChannelId(spu_t *spu)
         return VOUT_SPU_CHANNEL_INVALID;
 
     size_t id;
+    if (order)
+        *order = VLC_SPU_CHANNEL_ORDER_PRIMARY;
     for (id = VOUT_SPU_CHANNEL_OSD_COUNT; id < sys->channels.size + 1; ++id)
     {
         bool used = false;
@@ -183,6 +195,8 @@ static ssize_t spu_GetFreeChannelId(spu_t *spu)
             if (sys->channels.data[i].id == id)
             {
                 used = true;
+                if (order)
+                    *order = VLC_SPU_CHANNEL_ORDER_SECONDARY;
                 break;
             }
         }
@@ -500,23 +514,24 @@ static void SpuAreaFitInside(spu_area_t *area, const spu_area_t *boundary)
  */
 static void SpuRegionPlace(int *x, int *y,
                            const subpicture_t *subpic,
-                           const subpicture_region_t *region)
+                           const subpicture_region_t *region,
+                           int i_align)
 {
     assert(region->i_x != INT_MAX && region->i_y != INT_MAX);
     if (subpic->b_absolute) {
         *x = region->i_x;
         *y = region->i_y;
     } else {
-        if (region->i_align & SUBPICTURE_ALIGN_TOP)
-            *y = region->i_y;
-        else if (region->i_align & SUBPICTURE_ALIGN_BOTTOM)
+        if (i_align & SUBPICTURE_ALIGN_TOP)
+            *y = region->i_y + ( subpic->b_subtitle ? region->fmt.i_visible_height : 0 );
+        else if (i_align & SUBPICTURE_ALIGN_BOTTOM)
             *y = subpic->i_original_picture_height - region->fmt.i_visible_height - region->i_y;
         else
             *y = subpic->i_original_picture_height / 2 - region->fmt.i_visible_height / 2;
 
-        if (region->i_align & SUBPICTURE_ALIGN_LEFT)
+        if (i_align & SUBPICTURE_ALIGN_LEFT)
             *x = region->i_x;
-        else if (region->i_align & SUBPICTURE_ALIGN_RIGHT)
+        else if (i_align & SUBPICTURE_ALIGN_RIGHT)
             *x = subpic->i_original_picture_width - region->fmt.i_visible_width - region->i_x;
         else
             *x = subpic->i_original_picture_width / 2 - region->fmt.i_visible_width / 2;
@@ -743,7 +758,10 @@ spu_SelectSubpictures(spu_t *spu, vlc_tick_t system_now,
             if (is_rejeted)
                 spu_channel_DeleteSubpicture(channel, current);
             else
+            {
+                render_entry->channel_order = channel->order;
                 subpicture_array[(*subpicture_count)++] = *render_entry;
+            }
         }
     }
 
@@ -815,15 +833,41 @@ static void SpuRenderRegion(spu_t *spu,
 
     /* Compute the margin which is expressed in destination pixel unit
      * The margin is applied only to subtitle and when no forced crop is
-     * requested (dvd menu) */
+     * requested (dvd menu).
+     * Note: Margin will also be applied to secondary subtitles if they exist
+     * to ensure that overlap does not occur. */
     int y_margin = 0;
     if (!crop_requested && subpic->b_subtitle)
         y_margin = spu_invscale_h(sys->margin, scale_size);
 
     /* Place the picture
      * We compute the position in the rendered size */
+
+    int i_align = region->i_align;
+    if (entry->channel_order == VLC_SPU_CHANNEL_ORDER_SECONDARY)
+        i_align = sys->secondary_alignment >= 0 ? sys->secondary_alignment : i_align;
+
     SpuRegionPlace(&x_offset, &y_offset,
-                   subpic, region);
+                   subpic, region, i_align);
+
+    if (entry->channel_order == VLC_SPU_CHANNEL_ORDER_SECONDARY)
+    {
+        int secondary_margin =
+            spu_invscale_h(sys->secondary_margin, scale_size);
+        if (!subpic->b_absolute)
+        {
+            /* Move the secondary subtitles by the secondary margin before
+             * overlap detection. This way, overlaps will be resolved if they
+             * still exist.  */
+            y_offset -= secondary_margin;
+        }
+        else
+        {
+            /* Use an absolute margin for secondary subpictures that have
+             * already been placed but have been moved by the user */
+            y_margin += secondary_margin;
+        }
+    }
 
     /* Save this position for subtitle overlap support
      * it is really important that there are given without scale_size applied */
@@ -835,7 +879,7 @@ static void SpuRenderRegion(spu_t *spu,
     /* Handle overlapping subtitles when possible */
     if (subpic->b_subtitle && !subpic->b_absolute)
         SpuAreaFixOverlap(dst_area, subtitle_area, subtitle_area_count,
-                          region->i_align);
+                          i_align);
 
     /* we copy the area: for the subtitle overlap support we want
      * to only save the area without margin applied */
@@ -1415,7 +1459,7 @@ spu_t *spu_Create(vlc_object_t *object, vout_thread_t *vout)
     for (size_t i = 0; i < VOUT_SPU_CHANNEL_OSD_COUNT; ++i)
     {
         struct spu_channel channel;
-        spu_channel_Init(&channel, i, NULL);
+        spu_channel_Init(&channel, i, VLC_SPU_CHANNEL_ORDER_PRIMARY, NULL);
         vlc_vector_push(&sys->channels, channel);
     }
 
@@ -1423,6 +1467,10 @@ spu_t *spu_Create(vlc_object_t *object, vout_thread_t *vout)
     vlc_mutex_init(&sys->lock);
 
     sys->margin = var_InheritInteger(spu, "sub-margin");
+    sys->secondary_margin = var_InheritInteger(spu, "secondary-sub-margin");
+
+    sys->secondary_alignment = var_InheritInteger(spu,
+                                                  "secondary-spu-alignment");
 
     sys->source_chain_update = NULL;
     sys->filter_chain_update = NULL;
@@ -1761,18 +1809,20 @@ subpicture_t *spu_Render(spu_t *spu,
     return render;
 }
 
-ssize_t spu_RegisterChannelInternal(spu_t *spu, vlc_clock_t *clock)
+ssize_t spu_RegisterChannelInternal(spu_t *spu, vlc_clock_t *clock,
+                                    enum vlc_spu_channel_order *order)
 {
     spu_private_t *sys = spu->p;
 
     vlc_mutex_lock(&sys->lock);
 
-    ssize_t channel_id = spu_GetFreeChannelId(spu);
+    ssize_t channel_id = spu_GetFreeChannelId(spu, order);
 
     if (channel_id != VOUT_SPU_CHANNEL_INVALID)
     {
         struct spu_channel channel;
-        spu_channel_Init(&channel, channel_id, clock);
+        spu_channel_Init(&channel, channel_id,
+                         order ? *order : VLC_SPU_CHANNEL_ORDER_PRIMARY, clock);
         if (vlc_vector_push(&sys->channels, channel))
         {
             vlc_mutex_unlock(&sys->lock);
@@ -1787,7 +1837,8 @@ ssize_t spu_RegisterChannelInternal(spu_t *spu, vlc_clock_t *clock)
 
 ssize_t spu_RegisterChannel(spu_t *spu)
 {
-    return spu_RegisterChannelInternal(spu, NULL);
+    /* Public call, order is always primary (used for OSD or dvd/bluray spus) */
+    return spu_RegisterChannelInternal(spu, NULL, NULL);
 }
 
 static void spu_channel_Clear(struct spu_channel *channel)
-- 
2.20.1



More information about the vlc-devel mailing list