]> git.sesse.net Git - vlc/blobdiff - src/video_output/video_output.c
Gives the input_thread_t to use to vout_Request().
[vlc] / src / video_output / video_output.c
index 5b06f51b5add848718bfe0ededa329d66ec43d7a..d827b41db8816593d6fbed133a6c463cdfc116fd 100644 (file)
@@ -92,56 +92,11 @@ static int VoutValidateFormat(video_format_t *dst,
     return VLC_SUCCESS;
 }
 
-/*****************************************************************************
- * vout_Request: find a video output thread, create one, or destroy one.
- *****************************************************************************
- * This function looks for a video output thread matching the current
- * properties. If not found, it spawns a new one.
- *****************************************************************************/
-vout_thread_t *(vout_Request)(vlc_object_t *object, vout_thread_t *vout,
-                              const video_format_t *fmt)
-{
-    if (!fmt) {
-        if (vout)
-            vout_CloseAndRelease(vout);
-        return NULL;
-    }
-
-    /* If a vout is provided, try reusing it */
-    if (vout) {
-        spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
-        vlc_object_detach(vout);
-
-        vout_control_cmd_t cmd;
-        vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT);
-        cmd.u.reinit.fmt = fmt;
-
-        vout_control_Push(&vout->p->control, &cmd);
-        vout_control_WaitEmpty(&vout->p->control);
-        if (!vout->p->dead) {
-            vlc_object_attach(vout, object);
-            spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), true);
-
-            msg_Dbg(object, "reusing provided vout");
-            return vout;
-        }
-        vout_CloseAndRelease(vout);
-
-        msg_Warn(object, "cannot reuse provided vout");
-    }
-    return vout_Create(object, fmt);
-}
-
-/*****************************************************************************
- * vout_Create: creates a new video output thread
- *****************************************************************************
- * This function creates a new video output thread, and returns a pointer
- * to its description. On error, it returns NULL.
- *****************************************************************************/
-vout_thread_t *(vout_Create)(vlc_object_t *object, const video_format_t *fmt)
+static vout_thread_t *VoutCreate(vlc_object_t *object,
+                                 const vout_configuration_t *cfg)
 {
     video_format_t original;
-    if (VoutValidateFormat(&original, fmt))
+    if (VoutValidateFormat(&original, cfg->fmt))
         return NULL;
 
     /* Allocate descriptor */
@@ -202,7 +157,6 @@ vout_thread_t *(vout_Create)(vlc_object_t *object, const video_format_t *fmt)
         vlc_object_release(vout);
         return NULL;
     }
-    spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), true);
 
     vout_control_WaitEmpty(&vout->p->control);
 
@@ -212,14 +166,58 @@ vout_thread_t *(vout_Create)(vlc_object_t *object, const video_format_t *fmt)
         return NULL;
     }
 
+    vout->p->input = cfg->input;
+    if (vout->p->input)
+        spu_Attach(vout->p->p_spu, vout->p->input, true);
+
     return vout;
 }
 
+vout_thread_t *(vout_Request)(vlc_object_t *object,
+                              const vout_configuration_t *cfg)
+{
+    vout_thread_t *vout = cfg->vout;
+    if (!cfg->fmt) {
+        if (vout)
+            vout_CloseAndRelease(vout);
+        return NULL;
+    }
+
+    /* If a vout is provided, try reusing it */
+    if (vout) {
+        vlc_object_detach(vout);
+        vlc_object_attach(vout, object);
+
+        if (vout->p->input != cfg->input) {
+            if (vout->p->input)
+                spu_Attach(vout->p->p_spu, vout->p->input, false);
+            vout->p->input = cfg->input;
+            if (vout->p->input)
+                spu_Attach(vout->p->p_spu, vout->p->input, true);
+        }
+
+        vout_control_cmd_t cmd;
+        vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT);
+        cmd.u.cfg = cfg;
+
+        vout_control_Push(&vout->p->control, &cmd);
+        vout_control_WaitEmpty(&vout->p->control);
+        if (!vout->p->dead) {
+            msg_Dbg(object, "reusing provided vout");
+            return vout;
+        }
+        vout_CloseAndRelease(vout);
+
+        msg_Warn(object, "cannot reuse provided vout");
+    }
+    return VoutCreate(object, cfg);
+}
+
 /*****************************************************************************
- * vout_Close: Close a vout created by vout_Create.
+ * vout_Close: Close a vout created by VoutCreate.
  *****************************************************************************
- * You HAVE to call it on vout created by vout_Create before vlc_object_release.
- * You should NEVER call it on vout not obtained through vout_Create
+ * You HAVE to call it on vout created by VoutCreate before vlc_object_release.
+ * You should NEVER call it on vout not obtained through VoutCreate
  * (like with vout_Request or vlc_object_find.)
  * You can use vout_CloseAndRelease() as a convenience method.
  *****************************************************************************/
@@ -227,7 +225,9 @@ void vout_Close(vout_thread_t *vout)
 {
     assert(vout);
 
-    spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
+    if (vout->p->input)
+        spu_Attach(vout->p->p_spu, vout->p->input, false);
+    vlc_object_detach(vout->p->p_spu);
 
     vout_snapshot_End(&vout->p->snapshot);
 
@@ -427,6 +427,94 @@ void vout_ControlChangeSubFilters(vout_thread_t *vout, const char *filters)
                             filters);
 }
 
+/* */
+static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg, const char *title)
+{
+    /* Load configuration */
+    cfg->is_fullscreen = var_CreateGetBool(vout, "fullscreen");
+    cfg->display.title = title;
+    const int display_width = var_CreateGetInteger(vout, "width");
+    const int display_height = var_CreateGetInteger(vout, "height");
+    cfg->display.width   = display_width > 0  ? display_width  : 0;
+    cfg->display.height  = display_height > 0 ? display_height : 0;
+    cfg->is_display_filled  = var_CreateGetBool(vout, "autoscale");
+    cfg->display.sar.num = 1; /* TODO monitor AR */
+    cfg->display.sar.den = 1;
+    unsigned zoom_den = 1000;
+    unsigned zoom_num = zoom_den * var_CreateGetFloat(vout, "scale");
+    vlc_ureduce(&zoom_num, &zoom_den, zoom_num, zoom_den, 0);
+    cfg->zoom.num = zoom_num;
+    cfg->zoom.den = zoom_den;
+    cfg->align.vertical = VOUT_DISPLAY_ALIGN_CENTER;
+    cfg->align.horizontal = VOUT_DISPLAY_ALIGN_CENTER;
+    const int align_mask = var_CreateGetInteger(vout, "align");
+    if (align_mask & 0x1)
+        cfg->align.horizontal = VOUT_DISPLAY_ALIGN_LEFT;
+    else if (align_mask & 0x2)
+        cfg->align.horizontal = VOUT_DISPLAY_ALIGN_RIGHT;
+    if (align_mask & 0x4)
+        cfg->align.horizontal = VOUT_DISPLAY_ALIGN_TOP;
+    else if (align_mask & 0x8)
+        cfg->align.horizontal = VOUT_DISPLAY_ALIGN_BOTTOM;
+}
+
+vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd,
+                                      const vout_window_cfg_t *cfg)
+{
+    VLC_UNUSED(vd);
+    vout_window_cfg_t cfg_override = *cfg;
+
+    if (!var_InheritBool( vout, "embedded-video"))
+        cfg_override.is_standalone = true;
+
+    if (vout->p->window.is_unused && vout->p->window.object) {
+        assert(!vout->p->splitter_name);
+        if (!cfg_override.is_standalone == !vout->p->window.cfg.is_standalone &&
+            cfg_override.type           == vout->p->window.cfg.type) {
+            /* Reuse the stored window */
+            msg_Dbg(vout, "Reusing previous vout window");
+            vout_window_t *window = vout->p->window.object;
+            if (cfg_override.width  != vout->p->window.cfg.width ||
+                cfg_override.height != vout->p->window.cfg.height)
+                vout_window_SetSize(window,
+                                    cfg_override.width, cfg_override.height);
+            vout->p->window.is_unused = false;
+            vout->p->window.cfg       = cfg_override;
+            return window;
+        }
+
+        vout_window_Delete(vout->p->window.object);
+        vout->p->window.is_unused = true;
+        vout->p->window.object    = NULL;
+    }
+
+    vout_window_t *window = vout_window_New(VLC_OBJECT(vout), NULL,
+                                            &cfg_override);
+    if (!window)
+        return NULL;
+    if (!vout->p->splitter_name) {
+        vout->p->window.is_unused = false;
+        vout->p->window.cfg       = cfg_override;
+        vout->p->window.object    = window;
+    }
+    return window;
+}
+
+void vout_DeleteDisplayWindow(vout_thread_t *vout, vout_display_t *vd,
+                              vout_window_t *window)
+{
+    VLC_UNUSED(vd);
+    if (!vout->p->window.is_unused && vout->p->window.object == window) {
+        vout->p->window.is_unused = true;
+    } else if (vout->p->window.is_unused && vout->p->window.object && !window) {
+        vout_window_Delete(vout->p->window.object);
+        vout->p->window.is_unused = true;
+        vout->p->window.object    = NULL;
+    } else if (window) {
+        vout_window_Delete(window);
+    }
+}
+
 /* */
 static picture_t *VoutVideoFilterNewPicture(filter_t *filter)
 {
@@ -839,39 +927,14 @@ static void ThreadExecuteCropRatio(vout_thread_t *vout,
                                    unsigned num, unsigned den)
 {
     const video_format_t *source = &vout->p->original;
-
-    int x, y;
-    int width, height;
-    if (num <= 0 || den <= 0) {
-        num = 0;
-        den = 0;
-        x   = 0;
-        y   = 0;
-        width  = source->i_visible_width;
-        height = source->i_visible_height;
-    } else {
-        unsigned scaled_width  = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
-        unsigned scaled_height = (uint64_t)source->i_visible_width  * den * source->i_sar_num / num / source->i_sar_den;
-
-        if (scaled_width < source->i_visible_width) {
-            x      = (source->i_visible_width - scaled_width) / 2;
-            y      = 0;
-            width  = scaled_width;
-            height = source->i_visible_height;
-        } else {
-            x      = 0;
-            y      = (source->i_visible_height - scaled_height) / 2;
-            width  = source->i_visible_width;
-            height = scaled_height;
-        }
-    }
-    ThreadExecuteCropWindow(vout, num, den, x, y, width, height);
+    ThreadExecuteCropWindow(vout, num, den,
+                            0, 0,
+                            source->i_visible_width,
+                            source->i_visible_height);
 }
 
-static int ThreadInit(vout_thread_t *vout)
+static int ThreadStart(vout_thread_t *vout, const vout_display_state_t *state)
 {
-    vout->p->dead = false;
-    vout->p->is_late_dropped = var_InheritBool(vout, "drop-late-frames");
     vlc_mouse_Init(&vout->p->mouse);
     vout->p->decoder_fifo = picture_fifo_New();
     vout->p->decoder_pool = NULL;
@@ -882,14 +945,23 @@ static int ThreadInit(vout_thread_t *vout)
         filter_chain_New( vout, "video filter2", false,
                           VoutVideoFilterAllocationSetup, NULL, vout);
 
-    if (vout_OpenWrapper(vout, vout->p->splitter_name))
+    vout_display_state_t state_default;
+    if (!state) {
+        VoutGetDisplayCfg(vout, &state_default.cfg, vout->p->display.title);
+        state_default.wm_state = var_CreateGetBool(vout, "video-on-top") ? VOUT_WINDOW_STATE_ABOVE :
+                                                                           VOUT_WINDOW_STATE_NORMAL;
+        state_default.sar.num = 0;
+        state_default.sar.den = 0;
+
+        state = &state_default;
+    }
+
+    if (vout_OpenWrapper(vout, vout->p->splitter_name, state))
         return VLC_EGENERIC;
     if (vout_InitWrapper(vout))
         return VLC_EGENERIC;
     assert(vout->p->decoder_pool);
 
-    vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */
-
     vout->p->displayed.decoded       = NULL;
     vout->p->displayed.date          = VLC_TS_INVALID;
     vout->p->displayed.decoded       = NULL;
@@ -900,14 +972,11 @@ static int ThreadInit(vout_thread_t *vout)
     vout->p->step.last               = VLC_TS_INVALID;
     vout->p->step.timestamp          = VLC_TS_INVALID;
 
-    vout->p->pause.is_on             = false;
-    vout->p->pause.date              = VLC_TS_INVALID;
-
     video_format_Print(VLC_OBJECT(vout), "original format", &vout->p->original);
     return VLC_SUCCESS;
 }
 
-static void ThreadClean(vout_thread_t *vout)
+static void ThreadStop(vout_thread_t *vout, vout_display_state_t *state)
 {
     /* Destroy the video filters2 */
     filter_chain_Delete(vout->p->vfilter_chain);
@@ -918,31 +987,69 @@ static void ThreadClean(vout_thread_t *vout)
             ThreadFlush(vout, true, INT64_MAX);
             vout_EndWrapper(vout);
         }
-        vout_CloseWrapper(vout);
+        vout_CloseWrapper(vout, state);
     }
 
-    /* Detach subpicture unit from vout */
-    vlc_object_detach(vout->p->p_spu);
-
     if (vout->p->decoder_fifo)
         picture_fifo_Delete(vout->p->decoder_fifo);
     assert(!vout->p->decoder_pool);
-    vout_chrono_Clean(&vout->p->render);
+}
 
+static void ThreadInit(vout_thread_t *vout)
+{
+    vout->p->window.is_unused = true;
+    vout->p->window.object    = NULL;
+    vout->p->dead             = false;
+    vout->p->is_late_dropped  = var_InheritBool(vout, "drop-late-frames");
+    vout->p->pause.is_on      = false;
+    vout->p->pause.date       = VLC_TS_INVALID;
+
+    vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */
+}
+
+static void ThreadClean(vout_thread_t *vout)
+{
+    if (vout->p->window.object) {
+        assert(vout->p->window.is_unused);
+        vout_window_Delete(vout->p->window.object);
+    }
+    vout_chrono_Clean(&vout->p->render);
     vout->p->dead = true;
     vout_control_Dead(&vout->p->control);
 }
+
 static int ThreadReinit(vout_thread_t *vout,
-                        const video_format_t *fmt)
+                        const vout_configuration_t *cfg)
 {
     video_format_t original;
-    if (VoutValidateFormat(&original, fmt))
+    if (VoutValidateFormat(&original, cfg->fmt)) {
+        ThreadStop(vout, NULL);
+        ThreadClean(vout);
         return VLC_EGENERIC;
+    }
     if (video_format_IsSimilar(&original, &vout->p->original))
         return VLC_SUCCESS;
 
-    /* TODO */
-    return VLC_EGENERIC;
+    vout_display_state_t state;
+    memset(&state, 0, sizeof(state));
+
+    ThreadStop(vout, &state);
+
+    if (!state.cfg.is_fullscreen) {
+        state.cfg.display.width  = 0;
+        state.cfg.display.height = 0;
+    }
+    state.sar.num = 0;
+    state.sar.den = 0;
+    /* FIXME current vout "variables" are not in sync here anymore
+     * and I am not sure what to do */
+
+    vout->p->original = original;
+    if (ThreadStart(vout, &state)) {
+        ThreadClean(vout);
+        return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
 }
 
 /*****************************************************************************
@@ -973,19 +1080,20 @@ static void *Thread(void *object)
         while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) {
             switch(cmd.type) {
             case VOUT_CONTROL_INIT:
-                if (ThreadInit(vout)) {
+                ThreadInit(vout);
+                if (ThreadStart(vout, NULL)) {
+                    ThreadStop(vout, NULL);
                     ThreadClean(vout);
                     return NULL;
                 }
                 break;
             case VOUT_CONTROL_CLEAN:
+                ThreadStop(vout, NULL);
                 ThreadClean(vout);
                 return NULL;
             case VOUT_CONTROL_REINIT:
-                if (ThreadReinit(vout, cmd.u.reinit.fmt)) {
-                    ThreadClean(vout);
+                if (ThreadReinit(vout, cmd.u.cfg))
                     return NULL;
-                }
                 break;
             case VOUT_CONTROL_OSD_TITLE:
                 ThreadDisplayOsdTitle(vout, cmd.u.string);