vlc | branch: master | Hugo Beauzée-Luyssen <[email protected]> | 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; +} _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
