[vlc-commits] player: Save & restore playback states
Hugo Beauzée-Luyssen
git at videolan.org
Tue Sep 24 14:34:28 CEST 2019
vlc | branch: master | Hugo Beauzée-Luyssen <hugo at beauzee.fr> | Tue Jul 30 15:59:05 2019 +0200| [4431d8920326147b4e4e4cfddeb6681bb6967ba5] | committer: Hugo Beauzée-Luyssen
player: Save & restore playback states
Refs #22524
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=4431d8920326147b4e4e4cfddeb6681bb6967ba5
---
src/Makefile.am | 1 +
src/player/input.c | 76 +++++++++++++++
src/player/medialib.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/player/player.c | 3 +-
src/player/player.h | 23 +++++
src/player/track.c | 13 +++
6 files changed, 366 insertions(+), 1 deletion(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index e91fff6a3f..b6c5996837 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -271,6 +271,7 @@ libvlccore_la_SOURCES = \
player/aout.c \
player/vout.c \
player/osd.c \
+ player/medialib.c \
clock/input_clock.h \
clock/clock.h \
clock/clock_internal.h \
diff --git a/src/player/input.c b/src/player/input.c
index 06f75fac9b..2ea68d0de5 100644
--- a/src/player/input.c
+++ b/src/player/input.c
@@ -437,6 +437,32 @@ vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
}
vlc_player_SendEvent(player, on_track_list_changed,
VLC_PLAYER_LIST_ADDED, &trackpriv->t);
+ switch (ev->fmt->i_cat)
+ {
+ case VIDEO_ES:
+ /* If we need to restore a specific track, let's do it upon
+ * insertion. The initialization of the default track when
+ * we don't have a value will be done when the first track
+ * gets selected */
+ if (input->ml.states.current_video_track != -2 &&
+ input->ml.states.current_video_track == ev->fmt->i_id)
+ vlc_player_SelectTrack(input->player, &trackpriv->t,
+ VLC_PLAYER_SELECT_EXCLUSIVE);
+ break;
+ case AUDIO_ES:
+ if (input->ml.states.current_audio_track != -2 &&
+ input->ml.states.current_audio_track == ev->fmt->i_id)
+ vlc_player_SelectTrack(input->player, &trackpriv->t,
+ VLC_PLAYER_SELECT_EXCLUSIVE);
+ break;
+ case SPU_ES:
+ if (input->ml.states.current_subtitle_track != -2 &&
+ input->ml.states.current_subtitle_track == ev->fmt->i_id)
+ vlc_player_SelectTrack(input->player, &trackpriv->t,
+ VLC_PLAYER_SELECT_EXCLUSIVE);
+ default:
+ break;
+ }
break;
case VLC_INPUT_ES_DELETED:
{
@@ -468,6 +494,26 @@ vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
vlc_player_SendEvent(player, on_track_selection_changed,
NULL, trackpriv->t.es_id);
}
+ switch (ev->fmt->i_cat)
+ {
+ /* Save the default selected track to know if it changed
+ * when the playback stops, in order to save the user's
+ * explicitely selected track */
+ case VIDEO_ES:
+ if (input->ml.default_video_track == -2)
+ input->ml.default_video_track = ev->fmt->i_id;
+ break;
+ case AUDIO_ES:
+ if (input->ml.default_audio_track == -2)
+ input->ml.default_audio_track = ev->fmt->i_id;
+ break;
+ case SPU_ES:
+ if (input->ml.default_subtitle_track == -2)
+ input->ml.default_subtitle_track = ev->fmt->i_id;
+ break;
+ default:
+ break;
+ }
break;
case VLC_INPUT_ES_UNSELECTED:
trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
@@ -505,8 +551,15 @@ vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
title_offset, chapter_offset);
vlc_player_SendEvent(player, on_titles_changed, input->titles);
if (input->titles)
+ {
vlc_player_SendEvent(player, on_title_selection_changed,
&input->titles->array[0], 0);
+ if (input->ml.states.current_title >= 0 &&
+ (size_t)input->ml.states.current_title < ev->list.count)
+ {
+ vlc_player_SelectTitleIdx(player, input->ml.states.current_title);
+ }
+ }
break;
}
case VLC_INPUT_TITLE_SELECTED:
@@ -517,6 +570,16 @@ vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
vlc_player_SendEvent(player, on_title_selection_changed,
&input->titles->array[input->title_selected],
input->title_selected);
+ if (input->ml.states.current_title >= 0 &&
+ (size_t)input->ml.states.current_title == ev->selected_idx &&
+ input->ml.states.progress > .0f)
+ {
+ input_SetPosition(input->thread, input->ml.states.progress, false);
+ /* Reset the wanted title to avoid forcing it or the position
+ * again during the next title change
+ */
+ input->ml.states.current_title = 0;
+ }
break;
default:
vlc_assert_unreachable();
@@ -802,6 +865,17 @@ vlc_player_input_New(vlc_player_t *player, input_item_t *item)
input->abloop_state[0].set = input->abloop_state[1].set = false;
+ memset(&input->ml.states, 0, sizeof(input->ml.states));
+ input->ml.states.aspect_ratio = input->ml.states.crop =
+ input->ml.states.deinterlace = input->ml.states.video_filter = NULL;
+ input->ml.states.current_title = -1;
+ input->ml.states.current_video_track =
+ input->ml.states.current_audio_track =
+ input->ml.states.current_subtitle_track =
+ input->ml.default_video_track = input->ml.default_audio_track =
+ input->ml.default_subtitle_track = -2;
+ input->ml.states.progress = -1.f;
+
input->thread = input_Create(player, input_thread_Events, input, item,
player->resource, player->renderer);
if (!input->thread)
@@ -810,6 +884,8 @@ vlc_player_input_New(vlc_player_t *player, input_item_t *item)
return NULL;
}
+ vlc_player_input_RestoreMlStates(input, item);
+
/* Initial sub/audio delay */
const vlc_tick_t cat_delays[DATA_ES] = {
[AUDIO_ES] =
diff --git a/src/player/medialib.c b/src/player/medialib.c
new file mode 100644
index 0000000000..5c06f1ec22
--- /dev/null
+++ b/src/player/medialib.c
@@ -0,0 +1,251 @@
+/*****************************************************************************
+ * medialib.c: Player/Media Library interractions
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include "player.h"
+#include "misc/variables.h"
+
+void
+vlc_player_input_RestoreMlStates(struct vlc_player_input* input,
+ const input_item_t* item)
+{
+ vlc_player_t* player = input->player;
+ vlc_player_assert_locked(player);
+
+ vlc_medialibrary_t* ml = vlc_ml_instance_get(input->player);
+ if (!ml)
+ return;
+ vlc_ml_media_t* media = vlc_ml_get_media_by_mrl( ml, item->psz_uri);
+ if (!media)
+ return;
+ if (vlc_ml_media_get_all_playback_pref(ml, media->i_id, &input->ml.states) != VLC_SUCCESS)
+ return;
+ /* If we are aiming at a specific title, wait for it to be added, and
+ * only then select it & set the position.
+ * If we're not aiming at a specific title, just set the position now.
+ */
+ if (input->ml.states.current_title == -1 && input->ml.states.progress > .0f)
+ input_SetPosition(input->thread, input->ml.states.progress, false);
+ if (input->ml.states.rate != .0f)
+ vlc_player_ChangeRate(player, input->ml.states.rate);
+
+ /* Tracks are restored upon insertion, except when explicitely disabled */
+ if (input->ml.states.current_video_track == -1)
+ {
+ input->ml.default_video_track = -1;
+ input_ControlSync(input->thread, INPUT_CONTROL_SET_ES_AUTOSELECT,
+ &(input_control_param_t) {
+ .es_autoselect.cat = VIDEO_ES,
+ .es_autoselect.enabled = false,
+ });
+ }
+ if (input->ml.states.current_audio_track == -1)
+ {
+ input->ml.default_audio_track = -1;
+ input_ControlSync(input->thread, INPUT_CONTROL_SET_ES_AUTOSELECT,
+ &(input_control_param_t) {
+ .es_autoselect.cat = AUDIO_ES,
+ .es_autoselect.enabled = false,
+ });
+ }
+ if (input->ml.states.current_subtitle_track == -1)
+ {
+ input->ml.default_subtitle_track = -1;
+ input_ControlSync(input->thread, INPUT_CONTROL_SET_ES_AUTOSELECT,
+ &(input_control_param_t) {
+ .es_autoselect.cat = SPU_ES,
+ .es_autoselect.enabled = false,
+ });
+ }
+
+ vout_thread_t* vout = vlc_player_vout_Hold(player);
+ if (vout != NULL)
+ {
+ if (input->ml.states.zoom >= .0f)
+ var_SetFloat(vout, "zoom", input->ml.states.zoom);
+ else
+ var_SetFloat(vout, "zoom", 1.f);
+ if (input->ml.states.aspect_ratio)
+ var_SetString(vout, "aspect-ratio", input->ml.states.aspect_ratio);
+ else
+ var_SetString(vout, "aspect-ratio", NULL);
+ if (input->ml.states.deinterlace)
+ {
+ var_SetString(vout, "deinterlace-mode", input->ml.states.deinterlace);
+ var_SetInteger(vout, "deinterlace", 1);
+ }
+ else
+ {
+ var_SetString(vout, "deinterlace-mode", NULL);
+ var_SetInteger(vout, "deinterlace", 0);
+ }
+ if (input->ml.states.video_filter)
+ var_SetString(vout, "video-filter", input->ml.states.video_filter);
+ else
+ var_SetString(vout, "video-filter", NULL);
+ vout_Release(vout);
+ }
+ vlc_ml_release(media);
+}
+
+void
+vlc_player_UpdateMLStates(vlc_player_t *player, struct vlc_player_input* input)
+{
+ /* Do not save states for any secondary player. If this player's parent is
+ the main vlc object, then it's the main player */
+ if (player->obj.priv->parent != (vlc_object_t*)vlc_object_instance(player))
+ return;
+
+ vlc_medialibrary_t* ml = vlc_ml_instance_get(player);
+ if (!ml)
+ return;
+ input_item_t* item = input_GetItem(input->thread);
+ if (!item)
+ return;
+ vlc_ml_media_t* media = vlc_ml_get_media_by_mrl(ml, item->psz_uri);
+ if (!media)
+ {
+ /* We don't know this media yet, let's add it as an external media so
+ * we can still store its playback preferences
+ */
+ media = vlc_ml_new_external_media(ml, item->psz_uri);
+ if (media == NULL)
+ return;
+ }
+
+ input->ml.states.progress = input->position;
+
+ /* If the value changed during the playback, update it in the medialibrary.
+ * If not, set each state to their "unset" values, so that they aren't saved
+ * in database */
+ if ((input->ml.states.current_title == -1 && input->title_selected != 0) ||
+ (input->ml.states.current_title != -1 &&
+ input->ml.states.current_title != (int)input->title_selected))
+ {
+ input->ml.states.current_title = input->title_selected;
+ }
+ else
+ input->ml.states.current_title = -1;
+
+ /* We use .0f to signal an unsaved rate. We want to save it to the ml if it
+ * changed, and if it's not the player's default value when the value was
+ * never saved in the ML */
+ if (input->rate != input->ml.states.rate &&
+ (input->rate != 1.f || input->ml.states.rate != .0f))
+ input->ml.states.rate = input->rate;
+ else
+ input->ml.states.rate = -1.f;
+
+ struct vlc_player_track_priv* t;
+ vout_thread_t* vout = NULL;
+
+ vlc_vector_foreach(t, &input->video_track_vector)
+ {
+ if (!t->t.selected)
+ continue;
+ enum vlc_vout_order order;
+ vout = vlc_player_GetEsIdVout(player, t->t.es_id, &order);
+ if (vout != NULL && order == VLC_VOUT_ORDER_PRIMARY)
+ break;
+ vout = NULL;
+ }
+ if (vout != NULL)
+ {
+ /* We only want to save these states if they are different, and not the
+ * default values (NULL), so this means that either one is NULL and the
+ * other isn't, or they are both non null and differ lexicographically */
+#define COMPARE_ASSIGN_STR(field, var) \
+ char* field = var_GetNonEmptyString(vout, var); \
+ if ( ( field != NULL && input->ml.states.field != NULL && \
+ strcmp(field, input->ml.states.field) ) || \
+ ( field == NULL && input->ml.states.field != NULL ) || \
+ ( field != NULL && input->ml.states.field == NULL ) ) \
+ { \
+ free(input->ml.states.field); \
+ input->ml.states.field = field; \
+ field = NULL; \
+ } \
+ else \
+ { \
+ free(input->ml.states.field); \
+ input->ml.states.field = NULL; \
+ }
+
+ COMPARE_ASSIGN_STR(aspect_ratio, "aspect-ratio" );
+ COMPARE_ASSIGN_STR(crop, "crop");
+ COMPARE_ASSIGN_STR(deinterlace, "deinterlace-mode");
+ COMPARE_ASSIGN_STR(video_filter, "video-filter");
+
+ if (input->ml.states.deinterlace != NULL &&
+ !strcmp(input->ml.states.deinterlace, "auto"))
+ {
+ free(input->ml.states.deinterlace);
+ input->ml.states.deinterlace = NULL;
+ }
+
+ float zoom = var_GetFloat(vout, "zoom");
+ if (zoom != input->ml.states.zoom &&
+ (zoom != 1.f && input->ml.states.zoom >= .0f))
+ input->ml.states.zoom = zoom;
+ else
+ input->ml.states.zoom = -1.f;
+
+#undef COMPARE_ASSIGN_STR
+ free(video_filter);
+ free(deinterlace);
+ free(crop);
+ free(aspect_ratio);
+ }
+
+ if (input->ml.default_video_track != -2)
+ {
+ int current_video_track = vlc_player_GetFirstSelectedTrackId(&input->video_track_vector);
+ if (input->ml.default_video_track != current_video_track)
+ input->ml.states.current_video_track = current_video_track;
+ else
+ input->ml.states.current_video_track = -2;
+ }
+
+ if (input->ml.default_audio_track != -2)
+ {
+ int current_audio_track = vlc_player_GetFirstSelectedTrackId(&input->audio_track_vector);
+ if (input->ml.default_audio_track != current_audio_track)
+ input->ml.states.current_audio_track = current_audio_track;
+ else
+ input->ml.states.current_audio_track = -2;
+ }
+
+ if (input->ml.default_subtitle_track != -2)
+ {
+ int current_subtitle_track = vlc_player_GetFirstSelectedTrackId(&input->spu_track_vector);
+ if (input->ml.default_subtitle_track != current_subtitle_track)
+ input->ml.states.current_subtitle_track = current_subtitle_track;
+ else
+ input->ml.states.current_subtitle_track = -2;
+ }
+
+ vlc_ml_media_set_all_playback_states(ml, media->i_id, &input->ml.states);
+ vlc_ml_release(&input->ml.states);
+ vlc_ml_release(media);
+}
diff --git a/src/player/player.c b/src/player/player.c
index 76e9f1d5d8..22dad216cc 100644
--- a/src/player/player.c
+++ b/src/player/player.c
@@ -54,7 +54,7 @@ vlc_player_PrepareNextMedia(vlc_player_t *player)
{
vlc_player_assert_locked(player);
- if (!player->media_provider
+ if (!player->media_provider
|| player->media_stopped_action != VLC_PLAYER_MEDIA_STOPPED_CONTINUE
|| player->next_media_requested)
return;
@@ -207,6 +207,7 @@ vlc_player_destructor_Thread(void *data)
VLC_TICK_INVALID);
vlc_player_destructor_AddStoppingInput(player, input);
+ vlc_player_UpdateMLStates(player, input);
input_Stop(input->thread);
}
diff --git a/src/player/player.h b/src/player/player.h
index b83ba55b4c..aea4337e0a 100644
--- a/src/player/player.h
+++ b/src/player/player.h
@@ -25,6 +25,7 @@
#include <vlc_list.h>
#include <vlc_vector.h>
#include <vlc_atomic.h>
+#include <vlc_media_library.h>
#include "input/input_internal.h"
@@ -101,6 +102,14 @@ struct vlc_player_input
float pos;
bool set;
} abloop_state[2];
+
+ struct
+ {
+ vlc_ml_playback_states_all states;
+ int default_video_track;
+ int default_audio_track;
+ int default_subtitle_track;
+ } ml;
};
struct vlc_player_listener_id
@@ -334,6 +343,9 @@ struct vlc_player_track_priv *
vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t *id,
size_t *idx);
+int
+vlc_player_GetFirstSelectedTrackId(const vlc_player_track_vector* tracks);
+
/*
* player_title.c
*/
@@ -468,4 +480,15 @@ vlc_player_osd_Track(vlc_player_t *player, vlc_es_id_t *id, bool select);
void
vlc_player_osd_Program(vlc_player_t *player, const char *name);
+/*
+ * player/medialib.c
+ */
+
+void
+vlc_player_input_RestoreMlStates(struct vlc_player_input* input,
+ const input_item_t* item);
+
+void
+vlc_player_UpdateMLStates(vlc_player_t *player, struct vlc_player_input* input);
+
#endif
diff --git a/src/player/track.c b/src/player/track.c
index 4438ce8031..b02f8d5881 100644
--- a/src/player/track.c
+++ b/src/player/track.c
@@ -205,3 +205,16 @@ vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t *id,
}
return NULL;
}
+
+
+int
+vlc_player_GetFirstSelectedTrackId(const vlc_player_track_vector* tracks)
+{
+ struct vlc_player_track_priv* t;
+ vlc_vector_foreach(t, tracks)
+ {
+ if (t->t.selected)
+ return t->t.fmt.i_id;
+ }
+ return -1;
+}
More information about the vlc-commits
mailing list