gui: macos: use float for rate
[vlc.git] / src / input / player.c
blob63265a753579ee6bf13ba5c5c12def78ebd4d14c
1 /*****************************************************************************
2 * player.c: Player interface
3 *****************************************************************************
4 * Copyright © 2018 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include "player.h"
27 #include <vlc_aout.h>
28 #include <vlc_interface.h>
29 #include <vlc_renderer_discovery.h>
30 #include <vlc_list.h>
31 #include <vlc_vector.h>
32 #include <vlc_atomic.h>
33 #include <vlc_tick.h>
35 #include "libvlc.h"
36 #include "input_internal.h"
37 #include "resource.h"
38 #include "../audio_output/aout_internal.h"
40 #define RETRY_TIMEOUT_BASE VLC_TICK_FROM_MS(100)
41 #define RETRY_TIMEOUT_MAX VLC_TICK_FROM_MS(3200)
43 static_assert(VLC_PLAYER_CAP_SEEK == VLC_INPUT_CAPABILITIES_SEEKABLE &&
44 VLC_PLAYER_CAP_PAUSE == VLC_INPUT_CAPABILITIES_PAUSEABLE &&
45 VLC_PLAYER_CAP_CHANGE_RATE == VLC_INPUT_CAPABILITIES_CHANGE_RATE &&
46 VLC_PLAYER_CAP_REWIND == VLC_INPUT_CAPABILITIES_REWINDABLE,
47 "player/input capabilities mismatch");
49 static_assert(VLC_PLAYER_TITLE_MENU == INPUT_TITLE_MENU &&
50 VLC_PLAYER_TITLE_INTERACTIVE == INPUT_TITLE_INTERACTIVE,
51 "player/input title flag mismatch");
53 #define GAPLESS 0 /* TODO */
55 typedef struct VLC_VECTOR(struct vlc_player_program *)
56 vlc_player_program_vector;
58 typedef struct VLC_VECTOR(struct vlc_player_track *)
59 vlc_player_track_vector;
61 struct vlc_player_listener_id
63 const struct vlc_player_cbs *cbs;
64 void *cbs_data;
65 struct vlc_list node;
68 struct vlc_player_vout_listener_id
70 const struct vlc_player_vout_cbs *cbs;
71 void *cbs_data;
72 struct vlc_list node;
75 struct vlc_player_aout_listener_id
77 const struct vlc_player_aout_cbs *cbs;
78 void *cbs_data;
79 struct vlc_list node;
82 struct vlc_player_title_list
84 vlc_atomic_rc_t rc;
85 size_t count;
86 struct vlc_player_title array[];
89 struct vlc_player_input
91 input_thread_t *thread;
92 vlc_player_t *player;
93 bool started;
95 enum vlc_player_state state;
96 enum vlc_player_error error;
97 float rate;
98 int capabilities;
99 vlc_tick_t length;
101 vlc_tick_t time;
102 float position;
104 bool recording;
106 float signal_quality;
107 float signal_strength;
108 float cache;
110 struct input_stats_t stats;
112 vlc_tick_t audio_delay;
113 vlc_tick_t subtitle_delay;
115 struct
117 vlc_tick_t audio_time;
118 vlc_tick_t subtitle_time;
119 } subsync;
121 vlc_player_program_vector program_vector;
122 vlc_player_track_vector video_track_vector;
123 vlc_player_track_vector audio_track_vector;
124 vlc_player_track_vector spu_track_vector;
125 struct vlc_player_track *teletext_menu;
127 struct vlc_player_title_list *titles;
129 size_t title_selected;
130 size_t chapter_selected;
132 struct vlc_list node;
134 bool teletext_enabled;
135 bool teletext_transparent;
136 unsigned teletext_page;
138 struct
140 vlc_tick_t time;
141 float pos;
142 bool set;
143 } abloop_state[2];
146 struct vlc_player_t
148 struct vlc_common_members obj;
149 vlc_mutex_t lock;
150 vlc_mutex_t aout_listeners_lock;
151 vlc_mutex_t vout_listeners_lock;
152 vlc_cond_t start_delay_cond;
154 enum vlc_player_media_stopped_action media_stopped_action;
155 bool start_paused;
157 const struct vlc_player_media_provider *media_provider;
158 void *media_provider_data;
160 bool pause_on_cork;
161 bool corked;
163 struct vlc_list listeners;
164 struct vlc_list aout_listeners;
165 struct vlc_list vout_listeners;
167 input_resource_t *resource;
168 vlc_renderer_item_t *renderer;
170 input_item_t *media;
171 struct vlc_player_input *input;
173 bool releasing_media;
174 bool has_next_media;
175 input_item_t *next_media;
176 #if GAPLESS
177 struct vlc_player_input *next_input;
178 #endif
180 enum vlc_player_state global_state;
181 bool started;
183 unsigned error_count;
185 bool deleting;
186 struct
188 vlc_thread_t thread;
189 vlc_cond_t wait;
190 vlc_cond_t notify;
191 struct vlc_list inputs;
192 struct vlc_list stopping_inputs;
193 struct vlc_list joinable_inputs;
194 } destructor;
197 #define vlc_player_SendEvent(player, event, ...) do { \
198 vlc_player_listener_id *listener; \
199 vlc_list_foreach(listener, &player->listeners, node) \
201 if (listener->cbs->event) \
202 listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
204 } while(0)
206 #define vlc_player_aout_SendEvent(player, event, ...) do { \
207 vlc_mutex_lock(&player->aout_listeners_lock); \
208 vlc_player_aout_listener_id *listener; \
209 vlc_list_foreach(listener, &player->aout_listeners, node) \
211 if (listener->cbs->event) \
212 listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
214 vlc_mutex_unlock(&player->aout_listeners_lock); \
215 } while(0)
217 #define vlc_player_vout_SendEvent(player, event, ...) do { \
218 vlc_mutex_lock(&player->vout_listeners_lock); \
219 vlc_player_vout_listener_id *listener; \
220 vlc_list_foreach(listener, &player->vout_listeners, node) \
222 if (listener->cbs->event) \
223 listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
225 vlc_mutex_unlock(&player->vout_listeners_lock); \
226 } while(0)
228 #if GAPLESS
229 #define vlc_player_foreach_inputs(it) \
230 for (struct vlc_player_input *it = player->input; \
231 it != NULL; \
232 it = (it == player->input ? player->next_input : NULL))
233 #else
234 #define vlc_player_foreach_inputs(it) \
235 for (struct vlc_player_input *it = player->input; it != NULL; it = NULL)
236 #endif
238 static void
239 input_thread_Events(input_thread_t *, const struct vlc_input_event *, void *);
240 static void
241 vlc_player_input_HandleState(struct vlc_player_input *, enum vlc_player_state);
242 static int
243 vlc_player_VoutCallback(vlc_object_t *this, const char *var,
244 vlc_value_t oldval, vlc_value_t newval, void *data);
245 static int
246 vlc_player_VoutOSDCallback(vlc_object_t *this, const char *var,
247 vlc_value_t oldval, vlc_value_t newval, void *data);
249 void
250 vlc_player_assert_locked(vlc_player_t *player)
252 assert(player);
253 vlc_mutex_assert(&player->lock);
256 static inline struct vlc_player_input *
257 vlc_player_get_input_locked(vlc_player_t *player)
259 vlc_player_assert_locked(player);
260 return player->input;
263 static vout_thread_t **
264 vlc_player_vout_OSDHoldAll(vlc_player_t *player, size_t *count)
266 vout_thread_t **vouts = vlc_player_vout_HoldAll(player, count);
268 for (size_t i = 0; i < *count; ++i)
270 vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD);
271 vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER);
272 vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER);
274 return vouts;
277 static void
278 vlc_player_vout_OSDReleaseAll(vlc_player_t *player, vout_thread_t **vouts,
279 size_t count)
281 for (size_t i = 0; i < count; ++i)
282 vlc_object_release(vouts[i]);
283 free(vouts);
284 (void) player;
287 static inline void
288 vouts_osd_Message(vout_thread_t **vouts, size_t count, const char *fmt, ...)
290 va_list args;
291 va_start(args, fmt);
292 for (size_t i = 0; i < count; ++i)
293 vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, args);
294 va_end(args);
297 static inline void
298 vouts_osd_Icon(vout_thread_t **vouts, size_t count, short type)
300 for (size_t i = 0; i < count; ++i)
301 vout_OSDIcon(vouts[i], VOUT_SPU_CHANNEL_OSD, type);
304 static inline void
305 vouts_osd_Slider(vout_thread_t **vouts, size_t count, int position, short type)
307 int channel = type == OSD_HOR_SLIDER ?
308 VOUT_SPU_CHANNEL_OSD_HSLIDER : VOUT_SPU_CHANNEL_OSD_VSLIDER;
309 for (size_t i = 0; i < count; ++i)
310 vout_OSDSlider(vouts[i], channel, position, type);
313 void
314 vlc_player_vout_OSDMessage(vlc_player_t *player, const char *fmt, ...)
316 size_t count;
317 vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
319 va_list args;
320 va_start(args, fmt);
321 for (size_t i = 0; i < count; ++i)
322 vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, args);
323 va_end(args);
325 vlc_player_vout_OSDReleaseAll(player, vouts, count);
328 static void
329 vlc_player_vout_OSDIcon(vlc_player_t *player, short type)
331 size_t count;
332 vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
334 vouts_osd_Icon(vouts, count, type);
336 vlc_player_vout_OSDReleaseAll(player, vouts, count);
339 static char *
340 vlc_player_program_DupTitle(int id, const char *title)
342 char *dup;
343 if (title)
344 dup = strdup(title);
345 else if (asprintf(&dup, "%d", id) == -1)
346 dup = NULL;
347 return dup;
350 static struct vlc_player_program *
351 vlc_player_program_New(int id, const char *name)
353 struct vlc_player_program *prgm = malloc(sizeof(*prgm));
354 if (!prgm)
355 return NULL;
356 prgm->name = vlc_player_program_DupTitle(id, name);
357 if (!prgm->name)
359 free(prgm);
360 return NULL;
362 prgm->group_id = id;
363 prgm->selected = prgm->scrambled = false;
365 return prgm;
368 static int
369 vlc_player_program_Update(struct vlc_player_program *prgm, int id,
370 const char *name)
372 free((char *)prgm->name);
373 prgm->name = vlc_player_program_DupTitle(id, name);
374 return prgm->name != NULL ? VLC_SUCCESS : VLC_ENOMEM;
377 struct vlc_player_program *
378 vlc_player_program_Dup(const struct vlc_player_program *src)
380 struct vlc_player_program *dup =
381 vlc_player_program_New(src->group_id, src->name);
383 if (!dup)
384 return NULL;
385 dup->selected = src->selected;
386 dup->scrambled = src->scrambled;
387 return dup;
390 void
391 vlc_player_program_Delete(struct vlc_player_program *prgm)
393 free((char *)prgm->name);
394 free(prgm);
397 static struct vlc_player_program *
398 vlc_player_program_vector_FindById(vlc_player_program_vector *vec, int id,
399 size_t *idx)
401 for (size_t i = 0; i < vec->size; ++i)
403 struct vlc_player_program *prgm = vec->data[i];
404 if (prgm->group_id == id)
406 if (idx)
407 *idx = i;
408 return prgm;
411 return NULL;
414 static struct vlc_player_track *
415 vlc_player_track_New(vlc_es_id_t *id, const char *name, const es_format_t *fmt)
417 struct vlc_player_track *track = malloc(sizeof(*track));
418 if (!track)
419 return NULL;
420 track->name = strdup(name);
421 if (!track->name)
423 free(track);
424 return NULL;
427 int ret = es_format_Copy(&track->fmt, fmt);
428 if (ret != VLC_SUCCESS)
430 free((char *)track->name);
431 free(track);
432 return NULL;
434 track->es_id = vlc_es_id_Hold(id);
435 track->selected = false;
437 return track;
440 struct vlc_player_track *
441 vlc_player_track_Dup(const struct vlc_player_track *src)
443 struct vlc_player_track *dup =
444 vlc_player_track_New(src->es_id, src->name, &src->fmt);
446 if (!dup)
447 return NULL;
448 dup->selected = src->selected;
449 return dup;
452 void
453 vlc_player_track_Delete(struct vlc_player_track *track)
455 es_format_Clean(&track->fmt);
456 free((char *)track->name);
457 vlc_es_id_Release(track->es_id);
458 free(track);
461 static int
462 vlc_player_track_Update(struct vlc_player_track *track,
463 const char *name, const es_format_t *fmt)
465 if (strcmp(name, track->name) != 0)
467 char *dup = strdup(name);
468 if (!dup)
469 return VLC_ENOMEM;
470 free((char *)track->name);
471 track->name = dup;
474 es_format_t fmtdup;
475 int ret = es_format_Copy(&fmtdup, fmt);
476 if (ret != VLC_SUCCESS)
477 return ret;
479 es_format_Clean(&track->fmt);
480 track->fmt = fmtdup;
481 return VLC_SUCCESS;
484 struct vlc_player_title_list *
485 vlc_player_title_list_Hold(struct vlc_player_title_list *titles)
487 vlc_atomic_rc_inc(&titles->rc);
488 return titles;
491 void
492 vlc_player_title_list_Release(struct vlc_player_title_list *titles)
494 if (!vlc_atomic_rc_dec(&titles->rc))
495 return;
496 for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
498 struct vlc_player_title *title = &titles->array[title_idx];
499 free((char *)title->name);
500 for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
501 ++chapter_idx)
503 const struct vlc_player_chapter *chapter =
504 &title->chapters[chapter_idx];
505 free((char *)chapter->name);
507 free((void *)title->chapters);
509 free(titles);
512 static char *
513 input_title_GetName(const struct input_title_t *input_title, int idx,
514 int title_offset)
516 int ret;
517 char length_str[MSTRTIME_MAX_SIZE + sizeof(" []")];
519 if (input_title->i_length > 0)
521 strcpy(length_str, " [");
522 secstotimestr(&length_str[2], SEC_FROM_VLC_TICK(input_title->i_length));
523 strcat(length_str, "]");
525 else
526 length_str[0] = '\0';
528 char *dup;
529 if (input_title->psz_name && input_title->psz_name[0] != '\0')
530 ret = asprintf(&dup, "%s%s", input_title->psz_name, length_str);
531 else
532 ret = asprintf(&dup, _("Title %i%s"), idx + title_offset, length_str);
533 if (ret == -1)
534 return NULL;
535 return dup;
538 static char *
539 seekpoint_GetName(seekpoint_t *seekpoint, int idx, int chapter_offset)
541 if (seekpoint->psz_name && seekpoint->psz_name[0] != '\0' )
542 return strdup(seekpoint->psz_name);
544 char *dup;
545 int ret = asprintf(&dup, _("Chapter %i"), idx + chapter_offset);
546 if (ret == -1)
547 return NULL;
548 return dup;
551 static struct vlc_player_title_list *
552 vlc_player_title_list_Create(input_title_t *const *array, size_t count,
553 int title_offset, int chapter_offset)
555 if (count == 0)
556 return NULL;
558 /* Allocate the struct + the whole list */
559 size_t size;
560 if (mul_overflow(count, sizeof(struct vlc_player_title), &size))
561 return NULL;
562 if (add_overflow(size, sizeof(struct vlc_player_title_list), &size))
563 return NULL;
564 struct vlc_player_title_list *titles = malloc(size);
565 if (!titles)
566 return NULL;
568 vlc_atomic_rc_init(&titles->rc);
569 titles->count = count;
571 for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
573 const struct input_title_t *input_title = array[title_idx];
574 struct vlc_player_title *title = &titles->array[title_idx];
576 title->name = input_title_GetName(input_title, title_idx, title_offset);
577 title->length = input_title->i_length;
578 title->flags = input_title->i_flags;
579 const size_t seekpoint_count = input_title->i_seekpoint > 0 ?
580 input_title->i_seekpoint : 0;
581 title->chapter_count = seekpoint_count;
583 struct vlc_player_chapter *chapters = title->chapter_count == 0 ? NULL :
584 vlc_alloc(title->chapter_count, sizeof(*chapters));
586 if (chapters)
588 for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
589 ++chapter_idx)
591 struct vlc_player_chapter *chapter = &chapters[chapter_idx];
592 seekpoint_t *seekpoint = input_title->seekpoint[chapter_idx];
594 chapter->name = seekpoint_GetName(seekpoint, chapter_idx,
595 chapter_offset);
596 chapter->time = seekpoint->i_time_offset;
597 if (!chapter->name) /* Will trigger the error path */
598 title->chapter_count = chapter_idx;
601 else if (seekpoint_count > 0) /* Will trigger the error path */
602 title->chapter_count = 0;
604 title->chapters = chapters;
606 if (!title->name || seekpoint_count != title->chapter_count)
608 /* Release titles up to title_idx */
609 titles->count = title_idx;
610 vlc_player_title_list_Release(titles);
611 return NULL;
614 return titles;
617 const struct vlc_player_title *
618 vlc_player_title_list_GetAt(struct vlc_player_title_list *titles, size_t idx)
620 assert(idx < titles->count);
621 return &titles->array[idx];
624 size_t
625 vlc_player_title_list_GetCount(struct vlc_player_title_list *titles)
627 return titles->count;
630 static struct vlc_player_input *
631 vlc_player_input_New(vlc_player_t *player, input_item_t *item)
633 struct vlc_player_input *input = malloc(sizeof(*input));
634 if (!input)
635 return NULL;
637 input->player = player;
638 input->started = false;
640 input->state = VLC_PLAYER_STATE_STOPPED;
641 input->error = VLC_PLAYER_ERROR_NONE;
642 input->rate = 1.f;
643 input->capabilities = 0;
644 input->length = input->time = VLC_TICK_INVALID;
645 input->position = 0.f;
647 input->recording = false;
649 input->cache = 0.f;
650 input->signal_quality = input->signal_strength = -1.f;
652 memset(&input->stats, 0, sizeof(input->stats));
654 input->audio_delay = input->subtitle_delay = 0;
656 input->subsync.audio_time =
657 input->subsync.subtitle_time = VLC_TICK_INVALID;
659 vlc_vector_init(&input->program_vector);
660 vlc_vector_init(&input->video_track_vector);
661 vlc_vector_init(&input->audio_track_vector);
662 vlc_vector_init(&input->spu_track_vector);
663 input->teletext_menu = NULL;
665 input->titles = NULL;
666 input->title_selected = input->chapter_selected = 0;
668 input->teletext_enabled = input->teletext_transparent = false;
669 input->teletext_page = 0;
671 input->abloop_state[0].set = input->abloop_state[1].set = false;
673 input->thread = input_Create(player, input_thread_Events, input, item,
674 NULL, player->resource, player->renderer);
675 if (!input->thread)
677 free(input);
678 return NULL;
680 return input;
683 static void
684 vlc_player_input_Delete(struct vlc_player_input *input)
686 assert(input->titles == NULL);
687 assert(input->program_vector.size == 0);
688 assert(input->video_track_vector.size == 0);
689 assert(input->audio_track_vector.size == 0);
690 assert(input->spu_track_vector.size == 0);
691 assert(input->teletext_menu == NULL);
693 vlc_vector_destroy(&input->program_vector);
694 vlc_vector_destroy(&input->video_track_vector);
695 vlc_vector_destroy(&input->audio_track_vector);
696 vlc_vector_destroy(&input->spu_track_vector);
698 input_Close(input->thread);
699 free(input);
702 static int
703 vlc_player_input_Start(struct vlc_player_input *input)
705 int ret = input_Start(input->thread);
706 if (ret != VLC_SUCCESS)
707 return ret;
708 input->started = true;
709 return ret;
712 static void
713 vlc_player_PrepareNextMedia(vlc_player_t *player)
715 vlc_player_assert_locked(player);
717 if (!player->media_provider
718 || player->media_stopped_action != VLC_PLAYER_MEDIA_STOPPED_CONTINUE
719 || player->has_next_media)
720 return;
722 assert(player->next_media == NULL);
723 player->next_media =
724 player->media_provider->get_next(player, player->media_provider_data);
725 player->has_next_media = true;
728 static int
729 vlc_player_OpenNextMedia(vlc_player_t *player)
731 assert(player->input == NULL);
733 player->has_next_media = false;
735 int ret = VLC_SUCCESS;
736 if (player->releasing_media)
738 assert(player->media);
739 input_item_Release(player->media);
740 player->media = NULL;
741 player->releasing_media = false;
743 else
745 if (!player->next_media)
746 return VLC_EGENERIC;
748 if (player->media)
749 input_item_Release(player->media);
750 player->media = player->next_media;
751 player->next_media = NULL;
753 player->input = vlc_player_input_New(player, player->media);
754 if (!player->input)
756 input_item_Release(player->media);
757 player->media = NULL;
758 ret = VLC_ENOMEM;
761 vlc_player_SendEvent(player, on_current_media_changed, player->media);
762 return ret;
765 static void
766 vlc_player_CancelWaitError(vlc_player_t *player)
768 if (player->error_count != 0)
770 player->error_count = 0;
771 vlc_cond_signal(&player->start_delay_cond);
775 static bool
776 vlc_list_HasInput(struct vlc_list *list, struct vlc_player_input *input)
778 struct vlc_player_input *other_input;
779 vlc_list_foreach(other_input, list, node)
781 if (other_input == input)
782 return true;
784 return false;
787 static void
788 vlc_player_destructor_AddInput(vlc_player_t *player,
789 struct vlc_player_input *input)
791 if (input->started)
793 input->started = false;
794 /* Add this input to the stop list: it will be stopped by the
795 * destructor thread */
796 assert(!vlc_list_HasInput(&player->destructor.stopping_inputs, input));
797 assert(!vlc_list_HasInput(&player->destructor.joinable_inputs, input));
798 vlc_list_append(&input->node, &player->destructor.inputs);
800 else
802 /* Add this input to the joinable list: it will be deleted by the
803 * destructor thread */
804 assert(!vlc_list_HasInput(&player->destructor.inputs, input));
805 assert(!vlc_list_HasInput(&player->destructor.joinable_inputs, input));
806 vlc_list_append(&input->node, &player->destructor.joinable_inputs);
809 vlc_cond_signal(&input->player->destructor.wait);
812 static void
813 vlc_player_destructor_AddStoppingInput(vlc_player_t *player,
814 struct vlc_player_input *input)
816 /* Add this input to the stopping list */
817 if (vlc_list_HasInput(&player->destructor.inputs, input))
818 vlc_list_remove(&input->node);
819 if (!vlc_list_HasInput(&player->destructor.stopping_inputs, input))
821 vlc_list_append(&input->node, &player->destructor.stopping_inputs);
822 vlc_cond_signal(&input->player->destructor.wait);
826 static void
827 vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
828 struct vlc_player_input *input)
830 if (vlc_list_HasInput(&player->destructor.stopping_inputs, input))
831 vlc_list_remove(&input->node);
833 assert(!input->started);
834 vlc_player_destructor_AddInput(player, input);
837 static bool vlc_player_destructor_IsEmpty(vlc_player_t *player)
839 return vlc_list_is_empty(&player->destructor.inputs)
840 && vlc_list_is_empty(&player->destructor.stopping_inputs)
841 && vlc_list_is_empty(&player->destructor.joinable_inputs);
844 static void *
845 vlc_player_destructor_Thread(void *data)
847 vlc_player_t *player = data;
849 vlc_mutex_lock(&player->lock);
851 /* Terminate this thread when the player is deleting (vlc_player_Delete()
852 * was called) and when all input_thread_t all stopped and released. */
853 while (!player->deleting
854 || !vlc_player_destructor_IsEmpty(player))
856 /* Wait for an input to stop or close. No while loop here since we want
857 * to leave this code path when the player is deleting. */
858 if (vlc_list_is_empty(&player->destructor.inputs)
859 && vlc_list_is_empty(&player->destructor.joinable_inputs))
860 vlc_cond_wait(&player->destructor.wait, &player->lock);
862 struct vlc_player_input *input;
863 vlc_list_foreach(input, &player->destructor.inputs, node)
865 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING);
866 vlc_player_destructor_AddStoppingInput(player, input);
868 input_Stop(input->thread);
871 bool keep_sout = true;
872 const bool inputs_changed =
873 !vlc_list_is_empty(&player->destructor.joinable_inputs);
874 vlc_list_foreach(input, &player->destructor.joinable_inputs, node)
876 keep_sout = var_GetBool(input->thread, "sout-keep");
878 if (input->state == VLC_PLAYER_STATE_STOPPING)
879 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPED);
881 vlc_list_remove(&input->node);
882 vlc_player_input_Delete(input);
885 if (inputs_changed)
887 const bool started = player->started;
888 vlc_player_Unlock(player);
889 if (!started)
890 input_resource_TerminateVout(player->resource);
891 if (!keep_sout)
892 input_resource_TerminateSout(player->resource);
893 vlc_player_Lock(player);
896 vlc_mutex_unlock(&player->lock);
897 return NULL;
900 static bool
901 vlc_player_WaitRetryDelay(vlc_player_t *player)
903 if (player->error_count)
905 /* Delay the next opening in case of error to avoid busy loops */
906 vlc_tick_t delay = RETRY_TIMEOUT_BASE;
907 for (unsigned i = 1; i < player->error_count
908 && delay < RETRY_TIMEOUT_MAX; ++i)
909 delay *= 2; /* Wait 100, 200, 400, 800, 1600 and finally 3200ms */
910 delay += vlc_tick_now();
912 while (player->error_count > 0
913 && vlc_cond_timedwait(&player->start_delay_cond, &player->lock,
914 delay) == 0);
915 if (player->error_count == 0)
916 return false; /* canceled */
918 return true;
921 static void
922 vlc_player_input_HandleState(struct vlc_player_input *input,
923 enum vlc_player_state state)
925 vlc_player_t *player = input->player;
927 /* The STOPPING state can be set earlier by the player. In that case,
928 * ignore all future events except the STOPPED one */
929 if (input->state == VLC_PLAYER_STATE_STOPPING
930 && state != VLC_PLAYER_STATE_STOPPED)
931 return;
933 input->state = state;
935 /* Override the global state if the player is still playing and has a next
936 * media to play */
937 bool send_event = player->global_state != state;
938 switch (input->state)
940 case VLC_PLAYER_STATE_STOPPED:
941 assert(!input->started);
942 assert(input != player->input);
944 if (input->titles)
946 vlc_player_title_list_Release(input->titles);
947 input->titles = NULL;
948 vlc_player_SendEvent(player, on_titles_changed, NULL);
951 if (input->error != VLC_PLAYER_ERROR_NONE)
952 player->error_count++;
953 else
954 player->error_count = 0;
956 vlc_player_WaitRetryDelay(player);
958 if (!player->deleting)
959 vlc_player_OpenNextMedia(player);
960 if (!player->input)
961 player->started = false;
963 switch (player->media_stopped_action)
965 case VLC_PLAYER_MEDIA_STOPPED_EXIT:
966 if (player->input && player->started)
967 vlc_player_input_Start(player->input);
968 else
969 libvlc_Quit(player->obj.libvlc);
970 break;
971 case VLC_PLAYER_MEDIA_STOPPED_CONTINUE:
972 if (player->input && player->started)
973 vlc_player_input_Start(player->input);
974 break;
975 default:
976 break;
979 send_event = !player->started;
980 break;
981 case VLC_PLAYER_STATE_STOPPING:
982 input->started = false;
983 if (input == player->input)
984 player->input = NULL;
986 if (player->started)
988 vlc_player_PrepareNextMedia(player);
989 if (!player->next_media)
990 player->started = false;
992 send_event = !player->started;
993 break;
994 case VLC_PLAYER_STATE_STARTED:
995 case VLC_PLAYER_STATE_PLAYING:
996 if (player->started &&
997 player->global_state == VLC_PLAYER_STATE_PLAYING)
998 send_event = false;
999 break;
1001 case VLC_PLAYER_STATE_PAUSED:
1002 assert(player->started && input->started);
1003 break;
1004 default:
1005 vlc_assert_unreachable();
1008 if (send_event)
1010 player->global_state = input->state;
1011 vlc_player_SendEvent(player, on_state_changed, player->global_state);
1015 size_t
1016 vlc_player_GetProgramCount(vlc_player_t *player)
1018 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1020 return input ? input->program_vector.size : 0;
1023 const struct vlc_player_program *
1024 vlc_player_GetProgramAt(vlc_player_t *player, size_t index)
1026 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1028 if (!input)
1029 return NULL;
1031 assert(index < input->program_vector.size);
1032 return input->program_vector.data[index];
1035 const struct vlc_player_program *
1036 vlc_player_GetProgram(vlc_player_t *player, int id)
1038 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1040 if (!input)
1041 return NULL;
1043 struct vlc_player_program *prgm =
1044 vlc_player_program_vector_FindById(&input->program_vector, id, NULL);
1045 return prgm;
1048 static inline void
1049 vlc_player_vout_OSDProgram(vlc_player_t *player, const char *name)
1051 vlc_player_vout_OSDMessage(player, _("Program Service ID: %s"), name);
1054 void
1055 vlc_player_SelectProgram(vlc_player_t *player, int id)
1057 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1058 if (!input)
1059 return;
1061 const struct vlc_player_program *prgm =
1062 vlc_player_program_vector_FindById(&input->program_vector,
1063 id, NULL);
1064 if (prgm)
1066 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_PROGRAM,
1067 &(vlc_value_t) { .i_int = id });
1068 vlc_player_vout_OSDProgram(player, prgm->name);
1072 static void
1073 vlc_player_CycleProgram(vlc_player_t *player, bool next)
1075 size_t count = vlc_player_GetProgramCount(player);
1076 if (!count)
1077 return;
1078 size_t index = 0;
1079 bool selected = false;
1080 for (size_t i = 0; i < count; ++i)
1082 const struct vlc_player_program *prgm =
1083 vlc_player_GetProgramAt(player, i);
1084 if (prgm->selected)
1086 /* Only one program can be selected at a time */
1087 assert(!selected);
1088 index = i;
1089 selected = true;
1092 assert(selected);
1093 if (next && index + 1 == count) /* First program */
1094 index = 0;
1095 else if (!next && index == 0) /* Last program */
1096 index = count - 1;
1097 else /* Next or Previous program */
1098 index = index + (next ? 1 : -1);
1100 const struct vlc_player_program *prgm =
1101 vlc_player_GetProgramAt(player, index);
1102 assert(prgm);
1103 vlc_player_SelectProgram(player, prgm->group_id);
1106 void
1107 vlc_player_SelectNextProgram(vlc_player_t *player)
1109 vlc_player_CycleProgram(player, true);
1112 void
1113 vlc_player_SelectPrevProgram(vlc_player_t *player)
1115 vlc_player_CycleProgram(player, false);
1118 static void
1119 vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
1120 const struct vlc_input_event_program *ev)
1122 vlc_player_t *player = input->player;
1123 struct vlc_player_program *prgm;
1124 vlc_player_program_vector *vec = &input->program_vector;
1126 switch (ev->action)
1128 case VLC_INPUT_PROGRAM_ADDED:
1129 prgm = vlc_player_program_New(ev->id, ev->title);
1130 if (!prgm)
1131 break;
1133 if (!vlc_vector_push(vec, prgm))
1135 vlc_player_program_Delete(prgm);
1136 break;
1138 vlc_player_SendEvent(player, on_program_list_changed,
1139 VLC_PLAYER_LIST_ADDED, prgm);
1140 break;
1141 case VLC_INPUT_PROGRAM_DELETED:
1143 size_t idx;
1144 prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx);
1145 if (prgm)
1147 vlc_player_SendEvent(player, on_program_list_changed,
1148 VLC_PLAYER_LIST_REMOVED, prgm);
1149 vlc_vector_remove(vec, idx);
1150 vlc_player_program_Delete(prgm);
1152 break;
1154 case VLC_INPUT_PROGRAM_UPDATED:
1155 case VLC_INPUT_PROGRAM_SCRAMBLED:
1156 prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL);
1157 if (!prgm)
1158 break;
1159 if (ev->action == VLC_INPUT_PROGRAM_UPDATED)
1161 if (vlc_player_program_Update(prgm, ev->id, ev->title) != 0)
1162 break;
1164 else
1165 prgm->scrambled = ev->scrambled;
1166 vlc_player_SendEvent(player, on_program_list_changed,
1167 VLC_PLAYER_LIST_UPDATED, prgm);
1168 break;
1169 case VLC_INPUT_PROGRAM_SELECTED:
1171 int unselected_id = -1, selected_id = -1;
1172 vlc_vector_foreach(prgm, vec)
1174 if (prgm->group_id == ev->id)
1176 if (!prgm->selected)
1178 assert(selected_id == -1);
1179 prgm->selected = true;
1180 selected_id = prgm->group_id;
1183 else
1185 if (prgm->selected)
1187 assert(unselected_id == -1);
1188 prgm->selected = false;
1189 unselected_id = prgm->group_id;
1193 if (unselected_id != -1 || selected_id != -1)
1194 vlc_player_SendEvent(player, on_program_selection_changed,
1195 unselected_id, selected_id);
1196 break;
1198 default:
1199 vlc_assert_unreachable();
1203 static inline vlc_player_track_vector *
1204 vlc_player_input_GetTrackVector(struct vlc_player_input *input,
1205 enum es_format_category_e cat)
1207 switch (cat)
1209 case VIDEO_ES:
1210 return &input->video_track_vector;
1211 case AUDIO_ES:
1212 return &input->audio_track_vector;
1213 case SPU_ES:
1214 return &input->spu_track_vector;
1215 default:
1216 return NULL;
1220 static struct vlc_player_track *
1221 vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t *id,
1222 size_t *idx)
1224 for (size_t i = 0; i < vec->size; ++i)
1226 struct vlc_player_track *track = vec->data[i];
1227 if (track->es_id == id)
1229 if (idx)
1230 *idx = i;
1231 return track;
1234 return NULL;
1237 size_t
1238 vlc_player_GetTrackCount(vlc_player_t *player, enum es_format_category_e cat)
1240 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1242 if (!input)
1243 return 0;
1244 vlc_player_track_vector *vec = vlc_player_input_GetTrackVector(input, cat);
1245 if (!vec)
1246 return 0;
1247 return vec->size;
1250 const struct vlc_player_track *
1251 vlc_player_GetTrackAt(vlc_player_t *player, enum es_format_category_e cat,
1252 size_t index)
1254 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1256 if (!input)
1257 return NULL;
1258 vlc_player_track_vector *vec = vlc_player_input_GetTrackVector(input, cat);
1259 if (!vec)
1260 return NULL;
1261 assert(index < vec->size);
1262 return vec->data[index];
1265 const struct vlc_player_track *
1266 vlc_player_GetTrack(vlc_player_t *player, vlc_es_id_t *id)
1268 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1270 if (!input)
1271 return NULL;
1272 vlc_player_track_vector *vec =
1273 vlc_player_input_GetTrackVector(input, vlc_es_id_GetCat(id));
1274 if (!vec)
1275 return NULL;
1276 return vlc_player_track_vector_FindById(vec, id, NULL);
1279 static inline const char *
1280 es_format_category_to_string(enum es_format_category_e cat)
1282 switch (cat)
1284 case VIDEO_ES: return "Video";
1285 case AUDIO_ES: return "Audio";
1286 case SPU_ES: return "Subtitle";
1287 default: return NULL;
1291 static void
1292 vlc_player_vout_OSDTrack(vlc_player_t *player, vlc_es_id_t *id, bool select)
1294 enum es_format_category_e cat = vlc_es_id_GetCat(id);
1295 const struct vlc_player_track *track = vlc_player_GetTrack(player, id);
1296 if (!track && select)
1297 return;
1299 const char *cat_name = es_format_category_to_string(cat);
1300 assert(cat_name);
1301 const char *track_name = select ? track->name : _("N/A");
1302 vlc_player_vout_OSDMessage(player, _("%s track: %s"), cat_name, track_name);
1305 void
1306 vlc_player_SelectTrack(vlc_player_t *player, vlc_es_id_t *id)
1308 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1309 if (!input)
1310 return;
1312 input_ControlPushEsHelper(input->thread, INPUT_CONTROL_SET_ES, id);
1313 vlc_player_vout_OSDTrack(player, id, true);
1316 static void
1317 vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat,
1318 bool next)
1320 size_t count = vlc_player_GetTrackCount(player, cat);
1321 if (!count)
1322 return;
1324 size_t index;
1325 bool selected = false;
1326 for (size_t i = 0; i < count; ++i)
1328 const struct vlc_player_track *track =
1329 vlc_player_GetTrackAt(player, cat, i);
1330 assert(track);
1331 if (track->selected)
1333 if (selected)
1335 /* Can't cycle through tracks if there are more than one
1336 * selected */
1337 return;
1339 index = i;
1340 selected = true;
1344 if (!selected)
1346 /* No track selected: select the first or the last track */
1347 index = next ? 0 : count - 1;
1348 selected = true;
1350 else
1352 /* Unselect if we reach the end of the cycle */
1353 if ((next && index + 1 == count) || (!next && index == 0))
1354 selected = false;
1355 else /* Switch to the next or previous track */
1356 index = index + (next ? 1 : -1);
1359 const struct vlc_player_track *track =
1360 vlc_player_GetTrackAt(player, cat, index);
1361 if (selected)
1362 vlc_player_SelectTrack(player, track->es_id);
1363 else
1364 vlc_player_UnselectTrack(player, track->es_id);
1367 void
1368 vlc_player_SelectNextTrack(vlc_player_t *player,
1369 enum es_format_category_e cat)
1371 vlc_player_CycleTrack(player, cat, true);
1374 void
1375 vlc_player_SelectPrevTrack(vlc_player_t *player,
1376 enum es_format_category_e cat)
1378 vlc_player_CycleTrack(player, cat, false);
1381 void
1382 vlc_player_UnselectTrack(vlc_player_t *player, vlc_es_id_t *id)
1384 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1385 if (!input)
1386 return;
1388 input_ControlPushEsHelper(input->thread, INPUT_CONTROL_UNSET_ES, id);
1389 vlc_player_vout_OSDTrack(player, id, false);
1392 void
1393 vlc_player_RestartTrack(vlc_player_t *player, vlc_es_id_t *id)
1395 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1397 if (input)
1398 input_ControlPushEsHelper(input->thread, INPUT_CONTROL_RESTART_ES, id);
1401 void
1402 vlc_player_SelectDefaultTrack(vlc_player_t *player,
1403 enum es_format_category_e cat, const char *lang)
1405 vlc_player_assert_locked(player);
1406 /* TODO */ (void) cat; (void) lang;
1409 static void
1410 vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input,
1411 const struct vlc_input_event_es *ev)
1413 vlc_player_t *player = input->player;
1414 switch (ev->action)
1416 case VLC_INPUT_ES_ADDED:
1417 if (input->teletext_menu)
1419 msg_Warn(player, "Can't handle more than one teletext menu "
1420 "track. Using the last one.");
1421 vlc_player_track_Delete(input->teletext_menu);
1423 input->teletext_menu = vlc_player_track_New(ev->id, ev->title,
1424 ev->fmt);
1425 if (!input->teletext_menu)
1426 return;
1428 vlc_player_SendEvent(player, on_teletext_menu_changed, true);
1429 break;
1430 case VLC_INPUT_ES_DELETED:
1432 if (input->teletext_menu && input->teletext_menu->es_id == ev->id)
1434 assert(!input->teletext_enabled);
1436 vlc_player_track_Delete(input->teletext_menu);
1437 input->teletext_menu = NULL;
1438 vlc_player_SendEvent(player, on_teletext_menu_changed, false);
1440 break;
1442 case VLC_INPUT_ES_UPDATED:
1443 break;
1444 case VLC_INPUT_ES_SELECTED:
1445 case VLC_INPUT_ES_UNSELECTED:
1446 if (input->teletext_menu->es_id == ev->id)
1448 input->teletext_enabled = ev->action == VLC_INPUT_ES_SELECTED;
1449 vlc_player_SendEvent(player, on_teletext_enabled_changed,
1450 input->teletext_enabled);
1452 break;
1453 default:
1454 vlc_assert_unreachable();
1458 void
1459 vlc_player_SetTeletextEnabled(vlc_player_t *player, bool enabled)
1461 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1462 if (!input || !input->teletext_menu)
1463 return;
1464 if (enabled)
1465 vlc_player_SelectTrack(player, input->teletext_menu->es_id);
1466 else
1467 vlc_player_UnselectTrack(player, input->teletext_menu->es_id);
1470 void
1471 vlc_player_SelectTeletextPage(vlc_player_t *player, unsigned page)
1473 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1474 if (!input || !input->teletext_menu)
1475 return;
1477 input_ControlPush(input->thread, INPUT_CONTROL_SET_VBI_PAGE,
1478 &(input_control_param_t) {
1479 .vbi_page.id = input->teletext_menu->es_id,
1480 .vbi_page.page = page,
1484 void
1485 vlc_player_SetTeletextTransparency(vlc_player_t *player, bool enabled)
1487 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1488 if (!input || !input->teletext_menu)
1489 return;
1491 input_ControlPush(input->thread, INPUT_CONTROL_SET_VBI_TRANSPARENCY,
1492 &(input_control_param_t) {
1493 .vbi_transparency.id = input->teletext_menu->es_id,
1494 .vbi_transparency.enabled = enabled,
1498 bool
1499 vlc_player_HasTeletextMenu(vlc_player_t *player)
1501 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1502 return input && input->teletext_menu;
1505 bool
1506 vlc_player_IsTeletextEnabled(vlc_player_t *player)
1508 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1509 if (input && input->teletext_enabled)
1511 assert(input->teletext_menu);
1512 return true;
1514 return false;
1517 unsigned
1518 vlc_player_GetTeletextPage(vlc_player_t *player)
1520 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1521 return vlc_player_IsTeletextEnabled(player) ? input->teletext_page : 0;
1524 bool
1525 vlc_player_IsTeletextTransparent(vlc_player_t *player)
1527 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1528 return vlc_player_IsTeletextEnabled(player) && input->teletext_transparent;
1531 static void
1532 vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
1533 const struct vlc_input_event_es *ev)
1535 assert(ev->id && ev->title && ev->fmt);
1537 if (ev->fmt->i_cat == SPU_ES && ev->fmt->i_codec == VLC_CODEC_TELETEXT
1538 && (ev->fmt->subs.teletext.i_magazine == 1
1539 || ev->fmt->subs.teletext.i_magazine == -1))
1541 vlc_player_input_HandleTeletextMenu(input, ev);
1542 return;
1545 vlc_player_track_vector *vec =
1546 vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
1547 if (!vec)
1548 return; /* UNKNOWN_ES or DATA_ES not handled */
1550 vlc_player_t *player = input->player;
1551 struct vlc_player_track *track;
1552 switch (ev->action)
1554 case VLC_INPUT_ES_ADDED:
1555 track = vlc_player_track_New(ev->id, ev->title, ev->fmt);
1556 if (!track)
1557 break;
1559 if (!vlc_vector_push(vec, track))
1561 vlc_player_track_Delete(track);
1562 break;
1564 vlc_player_SendEvent(player, on_track_list_changed,
1565 VLC_PLAYER_LIST_ADDED, track);
1566 break;
1567 case VLC_INPUT_ES_DELETED:
1569 size_t idx;
1570 track = vlc_player_track_vector_FindById(vec, ev->id, &idx);
1571 if (track)
1573 vlc_player_SendEvent(player, on_track_list_changed,
1574 VLC_PLAYER_LIST_REMOVED, track);
1575 vlc_vector_remove(vec, idx);
1576 vlc_player_track_Delete(track);
1578 break;
1580 case VLC_INPUT_ES_UPDATED:
1581 track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
1582 if (!track)
1583 break;
1584 if (vlc_player_track_Update(track, ev->title, ev->fmt) != 0)
1585 break;
1586 vlc_player_SendEvent(player, on_track_list_changed,
1587 VLC_PLAYER_LIST_UPDATED, track);
1588 break;
1589 case VLC_INPUT_ES_SELECTED:
1590 track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
1591 if (track)
1593 track->selected = true;
1594 vlc_player_SendEvent(player, on_track_selection_changed,
1595 NULL, track->es_id);
1597 break;
1598 case VLC_INPUT_ES_UNSELECTED:
1599 track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
1600 if (track)
1602 track->selected = false;
1603 vlc_player_SendEvent(player, on_track_selection_changed,
1604 track->es_id, NULL);
1606 break;
1607 default:
1608 vlc_assert_unreachable();
1612 static void
1613 vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
1614 const struct vlc_input_event_title *ev)
1616 vlc_player_t *player = input->player;
1617 switch (ev->action)
1619 case VLC_INPUT_TITLE_NEW_LIST:
1621 input_thread_private_t *input_th = input_priv(input->thread);
1622 const int title_offset = input_th->i_title_offset;
1623 const int chapter_offset = input_th->i_seekpoint_offset;
1625 if (input->titles)
1626 vlc_player_title_list_Release(input->titles);
1627 input->title_selected = input->chapter_selected = 0;
1628 input->titles =
1629 vlc_player_title_list_Create(ev->list.array, ev->list.count,
1630 title_offset, chapter_offset);
1631 vlc_player_SendEvent(player, on_titles_changed, input->titles);
1632 if (input->titles)
1633 vlc_player_SendEvent(player, on_title_selection_changed,
1634 &input->titles->array[0], 0);
1635 break;
1637 case VLC_INPUT_TITLE_SELECTED:
1638 if (!input->titles)
1639 return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
1640 assert(ev->selected_idx < input->titles->count);
1641 input->title_selected = ev->selected_idx;
1642 vlc_player_SendEvent(player, on_title_selection_changed,
1643 &input->titles->array[input->title_selected],
1644 input->title_selected);
1645 break;
1646 default:
1647 vlc_assert_unreachable();
1651 static void
1652 vlc_player_input_HandleChapterEvent(struct vlc_player_input *input,
1653 const struct vlc_input_event_chapter *ev)
1655 vlc_player_t *player = input->player;
1656 if (!input->titles || ev->title < 0 || ev->seekpoint < 0)
1657 return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
1659 assert((size_t)ev->title < input->titles->count);
1660 const struct vlc_player_title *title = &input->titles->array[ev->title];
1661 if (!title->chapter_count)
1662 return;
1664 assert(ev->seekpoint < (int)title->chapter_count);
1665 input->title_selected = ev->title;
1666 input->chapter_selected = ev->seekpoint;
1668 const struct vlc_player_chapter *chapter = &title->chapters[ev->seekpoint];
1669 vlc_player_SendEvent(player, on_chapter_selection_changed, title, ev->title,
1670 chapter, ev->seekpoint);
1673 struct vlc_player_title_list *
1674 vlc_player_GetTitleList(vlc_player_t *player)
1676 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1677 return input ? input->titles : NULL;
1680 ssize_t
1681 vlc_player_GetSelectedTitleIdx(vlc_player_t *player)
1683 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1685 if (!input)
1686 return -1;
1687 return input->title_selected;
1690 static ssize_t
1691 vlc_player_GetTitleIdx(vlc_player_t *player,
1692 const struct vlc_player_title *title)
1694 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1695 if (input && input->titles)
1696 for (size_t i = 0; i < input->titles->count; ++i)
1697 if (&input->titles->array[i] == title)
1698 return i;
1699 return -1;
1702 void
1703 vlc_player_SelectTitleIdx(vlc_player_t *player, size_t index)
1705 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1706 if (input)
1707 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_TITLE,
1708 &(vlc_value_t){ .i_int = index });
1711 void
1712 vlc_player_SelectTitle(vlc_player_t *player,
1713 const struct vlc_player_title *title)
1715 ssize_t idx = vlc_player_GetTitleIdx(player, title);
1716 if (idx != -1)
1717 vlc_player_SelectTitleIdx(player, idx);
1720 void
1721 vlc_player_SelectChapter(vlc_player_t *player,
1722 const struct vlc_player_title *title,
1723 size_t chapter_idx)
1725 ssize_t idx = vlc_player_GetTitleIdx(player, title);
1726 if (idx != -1 && idx == vlc_player_GetSelectedTitleIdx(player))
1727 vlc_player_SelectChapterIdx(player, chapter_idx);
1730 void
1731 vlc_player_SelectNextTitle(vlc_player_t *player)
1733 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1734 if (!input)
1735 return;
1736 input_ControlPush(input->thread, INPUT_CONTROL_SET_TITLE_NEXT, NULL);
1737 vlc_player_vout_OSDMessage(player, _("Next title"));
1740 void
1741 vlc_player_SelectPrevTitle(vlc_player_t *player)
1743 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1744 if (!input)
1745 return;
1746 input_ControlPush(input->thread, INPUT_CONTROL_SET_TITLE_PREV, NULL);
1747 vlc_player_vout_OSDMessage(player, _("Previous title"));
1750 ssize_t
1751 vlc_player_GetSelectedChapterIdx(vlc_player_t *player)
1753 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1755 if (!input)
1756 return -1;
1757 return input->chapter_selected;
1760 void
1761 vlc_player_SelectChapterIdx(vlc_player_t *player, size_t index)
1763 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1764 if (!input)
1765 return;
1766 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_SEEKPOINT,
1767 &(vlc_value_t){ .i_int = index });
1768 vlc_player_vout_OSDMessage(player, _("Chapter %ld"), index);
1771 void
1772 vlc_player_SelectNextChapter(vlc_player_t *player)
1774 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1775 if (!input)
1776 return;
1777 input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_NEXT, NULL);
1778 vlc_player_vout_OSDMessage(player, _("Next chapter"));
1781 void
1782 vlc_player_SelectPrevChapter(vlc_player_t *player)
1784 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1785 if (!input)
1786 return;
1787 input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_PREV, NULL);
1788 vlc_player_vout_OSDMessage(player, _("Previous chapter"));
1791 static void
1792 vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
1793 const struct vlc_input_event_vout *ev)
1795 assert(ev->vout);
1797 static const char osd_vars[][sizeof("deinterlace-mode")] = {
1798 "aspect-ratio", "autoscale", "crop", "crop-bottom",
1799 "crop-top", "crop-left", "crop-right", "deinterlace",
1800 "deinterlace-mode", "sub-margin", "zoom"
1803 vlc_player_t *player = input->player;
1804 switch (ev->action)
1806 case VLC_INPUT_EVENT_VOUT_ADDED:
1807 vlc_player_SendEvent(player, on_vout_list_changed,
1808 VLC_PLAYER_LIST_ADDED, ev->vout);
1810 /* Register vout callbacks after the vout list event */
1811 var_AddCallback(ev->vout, "fullscreen",
1812 vlc_player_VoutCallback, player);
1813 var_AddCallback(ev->vout, "video-wallpaper",
1814 vlc_player_VoutCallback, player);
1815 for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i)
1816 var_AddCallback(ev->vout, osd_vars[i],
1817 vlc_player_VoutOSDCallback, player);
1818 break;
1819 case VLC_INPUT_EVENT_VOUT_DELETED:
1820 /* Un-register vout callbacks before the vout list event */
1821 var_DelCallback(ev->vout, "fullscreen",
1822 vlc_player_VoutCallback, player);
1823 var_DelCallback(ev->vout, "video-wallpaper",
1824 vlc_player_VoutCallback, player);
1825 for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i)
1826 var_DelCallback(ev->vout, osd_vars[i],
1827 vlc_player_VoutOSDCallback, player);
1829 vlc_player_SendEvent(player, on_vout_list_changed,
1830 VLC_PLAYER_LIST_REMOVED, ev->vout);
1831 break;
1832 default:
1833 vlc_assert_unreachable();
1837 static void
1838 vlc_player_input_HandleStateEvent(struct vlc_player_input *input,
1839 input_state_e state)
1841 switch (state)
1843 case OPENING_S:
1844 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED);
1845 break;
1846 case PLAYING_S:
1847 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING);
1848 break;
1849 case PAUSE_S:
1850 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED);
1851 break;
1852 case END_S:
1853 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING);
1854 vlc_player_destructor_AddStoppingInput(input->player, input);
1855 break;
1856 case ERROR_S:
1857 /* Don't send errors if the input is stopped by the user */
1858 if (input->started)
1860 /* Contrary to the input_thead_t, an error is not a state */
1861 input->error = VLC_PLAYER_ERROR_GENERIC;
1862 vlc_player_SendEvent(input->player, on_error_changed, input->error);
1864 break;
1865 default:
1866 vlc_assert_unreachable();
1870 static void
1871 vlc_player_HandleAtoBLoop(vlc_player_t *player)
1873 struct vlc_player_input *input = vlc_player_get_input_locked(player);
1874 assert(input);
1875 assert(input->abloop_state[0].set && input->abloop_state[1].set);
1877 if (input->time != VLC_TICK_INVALID
1878 && input->abloop_state[0].time != VLC_TICK_INVALID
1879 && input->abloop_state[1].time != VLC_TICK_INVALID)
1881 if (input->time >= input->abloop_state[1].time)
1882 vlc_player_SetTime(player, input->abloop_state[0].time);
1884 else if (input->position >= input->abloop_state[1].pos)
1885 vlc_player_SetPosition(player, input->abloop_state[0].pos);
1888 static void
1889 input_thread_Events(input_thread_t *input_thread,
1890 const struct vlc_input_event *event, void *user_data)
1892 struct vlc_player_input *input = user_data;
1893 vlc_player_t *player = input->player;
1895 assert(input_thread == input->thread);
1897 vlc_mutex_lock(&player->lock);
1899 switch (event->type)
1901 case INPUT_EVENT_STATE:
1902 vlc_player_input_HandleStateEvent(input, event->state);
1903 break;
1904 case INPUT_EVENT_RATE:
1905 input->rate = event->rate;
1906 vlc_player_SendEvent(player, on_rate_changed, input->rate);
1907 break;
1908 case INPUT_EVENT_CAPABILITIES:
1909 input->capabilities = event->capabilities;
1910 vlc_player_SendEvent(player, on_capabilities_changed,
1911 input->capabilities);
1912 break;
1913 case INPUT_EVENT_POSITION:
1914 #if GAPLESS
1915 /* XXX case INPUT_EVENT_EOF: */
1916 if (player->next_input == NULL)
1917 break;
1918 vlc_tick_t length = input->length;
1919 vlc_tick_t time = event->position.ms;
1920 if (length > 0 && time > 0
1921 && length - time <= AOUT_MAX_PREPARE_TIME)
1922 vlc_player_OpenNextMedia(player);
1923 #endif
1924 if (input->time != event->position.ms ||
1925 input->position != event->position.percentage)
1927 input->time = event->position.ms;
1928 input->position = event->position.percentage;
1929 vlc_player_SendEvent(player, on_position_changed,
1930 input->time,
1931 input->position);
1933 if (input->abloop_state[0].set && input->abloop_state[1].set
1934 && input == player->input)
1935 vlc_player_HandleAtoBLoop(player);
1937 break;
1938 case INPUT_EVENT_LENGTH:
1939 if (input->length != event->length)
1941 input->length = event->length;
1942 vlc_player_SendEvent(player, on_length_changed, input->length);
1944 break;
1945 case INPUT_EVENT_PROGRAM:
1946 vlc_player_input_HandleProgramEvent(input, &event->program);
1947 break;
1948 case INPUT_EVENT_ES:
1949 vlc_player_input_HandleEsEvent(input, &event->es);
1950 break;
1951 case INPUT_EVENT_TITLE:
1952 vlc_player_input_HandleTitleEvent(input, &event->title);
1953 break;
1954 case INPUT_EVENT_CHAPTER:
1955 vlc_player_input_HandleChapterEvent(input, &event->chapter);
1956 break;
1957 case INPUT_EVENT_RECORD:
1958 input->recording = event->record;
1959 vlc_player_SendEvent(player, on_recording_changed, input->recording);
1960 break;
1961 case INPUT_EVENT_STATISTICS:
1962 input->stats = *event->stats;
1963 vlc_player_SendEvent(player, on_statistics_changed, &input->stats);
1964 break;
1965 case INPUT_EVENT_SIGNAL:
1966 input->signal_quality = event->signal.quality;
1967 input->signal_strength = event->signal.strength;
1968 vlc_player_SendEvent(player, on_signal_changed,
1969 input->signal_quality, input->signal_strength);
1970 break;
1971 case INPUT_EVENT_AUDIO_DELAY:
1972 input->audio_delay = event->audio_delay;
1973 vlc_player_SendEvent(player, on_audio_delay_changed,
1974 input->audio_delay);
1975 break;
1976 case INPUT_EVENT_SUBTITLE_DELAY:
1977 input->subtitle_delay = event->subtitle_delay;
1978 vlc_player_SendEvent(player, on_subtitle_delay_changed,
1979 input->subtitle_delay);
1980 break;
1981 case INPUT_EVENT_CACHE:
1982 input->cache = event->cache;
1983 vlc_player_SendEvent(player, on_buffering_changed, event->cache);
1984 break;
1985 case INPUT_EVENT_VOUT:
1986 vlc_player_input_HandleVoutEvent(input, &event->vout);
1987 break;
1988 case INPUT_EVENT_ITEM_META:
1989 vlc_player_SendEvent(player, on_media_meta_changed,
1990 input_GetItem(input->thread));
1991 break;
1992 case INPUT_EVENT_ITEM_EPG:
1993 vlc_player_SendEvent(player, on_media_epg_changed,
1994 input_GetItem(input->thread));
1995 break;
1996 case INPUT_EVENT_SUBITEMS:
1997 vlc_player_SendEvent(player, on_media_subitems_changed,
1998 input_GetItem(input->thread), event->subitems);
1999 break;
2000 case INPUT_EVENT_DEAD:
2001 if (input->started) /* Can happen with early input_thread fails */
2002 vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING);
2003 vlc_player_destructor_AddJoinableInput(player, input);
2004 break;
2005 case INPUT_EVENT_VBI_PAGE:
2006 input->teletext_page = event->vbi_page < 999 ? event->vbi_page : 100;
2007 vlc_player_SendEvent(player, on_teletext_page_changed,
2008 input->teletext_page);
2009 break;
2010 case INPUT_EVENT_VBI_TRANSPARENCY:
2011 input->teletext_transparent = event->vbi_transparent;
2012 vlc_player_SendEvent(player, on_teletext_transparency_changed,
2013 input->teletext_transparent);
2014 break;
2015 default:
2016 break;
2019 vlc_mutex_unlock(&player->lock);
2022 void
2023 vlc_player_Lock(vlc_player_t *player)
2025 vlc_mutex_lock(&player->lock);
2028 void
2029 vlc_player_Unlock(vlc_player_t *player)
2031 vlc_mutex_unlock(&player->lock);
2034 void
2035 vlc_player_CondWait(vlc_player_t *player, vlc_cond_t *cond)
2037 vlc_player_assert_locked(player);
2038 vlc_cond_wait(cond, &player->lock);
2041 vlc_player_listener_id *
2042 vlc_player_AddListener(vlc_player_t *player,
2043 const struct vlc_player_cbs *cbs, void *cbs_data)
2045 assert(cbs);
2046 vlc_player_assert_locked(player);
2048 vlc_player_listener_id *listener = malloc(sizeof(*listener));
2049 if (!listener)
2050 return NULL;
2052 listener->cbs = cbs;
2053 listener->cbs_data = cbs_data;
2055 vlc_list_append(&listener->node, &player->listeners);
2057 return listener;
2060 void
2061 vlc_player_RemoveListener(vlc_player_t *player,
2062 vlc_player_listener_id *id)
2064 assert(id);
2065 vlc_player_assert_locked(player);
2067 vlc_list_remove(&id->node);
2068 free(id);
2072 vlc_player_SetCurrentMedia(vlc_player_t *player, input_item_t *media)
2074 vlc_player_assert_locked(player);
2076 vlc_player_CancelWaitError(player);
2078 vlc_player_InvalidateNextMedia(player);
2080 if (media)
2082 /* Switch to this new media when the current input is stopped */
2083 player->next_media = input_item_Hold(media);
2084 player->releasing_media = false;
2085 player->has_next_media = true;
2087 else if (player->media)
2089 /* The current media will be set to NULL once the current input is
2090 * stopped */
2091 player->releasing_media = true;
2092 player->has_next_media = false;
2094 else
2095 return VLC_SUCCESS;
2097 if (player->input)
2099 vlc_player_destructor_AddInput(player, player->input);
2100 player->input = NULL;
2103 assert(media == player->next_media);
2104 if (!vlc_player_destructor_IsEmpty(player))
2106 /* This media will be opened when the input is finally stopped */
2107 return VLC_SUCCESS;
2110 /* We can switch to the next media directly */
2111 return vlc_player_OpenNextMedia(player);
2114 input_item_t *
2115 vlc_player_GetCurrentMedia(vlc_player_t *player)
2117 vlc_player_assert_locked(player);
2119 return player->media;
2123 vlc_player_AddAssociatedMedia(vlc_player_t *player,
2124 enum es_format_category_e cat, const char *uri,
2125 bool select, bool notify, bool check_ext)
2127 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2129 if (!input)
2130 return VLC_EGENERIC;
2132 enum slave_type type;
2133 switch (cat)
2135 case AUDIO_ES:
2136 type = SLAVE_TYPE_AUDIO;
2137 break;
2138 case SPU_ES:
2139 type = SLAVE_TYPE_SPU;
2140 break;
2141 default:
2142 return VLC_EGENERIC;
2144 return input_AddSlave(input->thread, type, uri, select, notify, check_ext);
2147 void
2148 vlc_player_SetAssociatedSubsFPS(vlc_player_t *player, float fps)
2150 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2152 var_SetFloat(player, "sub-fps", fps);
2153 if (input)
2154 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_SUBS_FPS,
2155 &(vlc_value_t) { .f_float = fps });
2156 vlc_player_SendEvent(player, on_associated_subs_fps_changed, fps);
2159 float
2160 vlc_player_GetAssociatedSubsFPS(vlc_player_t *player)
2162 vlc_player_assert_locked(player);
2163 return var_GetFloat(player, "sub-fps");
2166 void
2167 vlc_player_InvalidateNextMedia(vlc_player_t *player)
2169 vlc_player_assert_locked(player);
2170 if (player->next_media)
2172 input_item_Release(player->next_media);
2173 player->next_media = NULL;
2175 player->has_next_media = false;
2177 #if GAPLESS
2178 if (player->next_input)
2180 /* Cause the get_next callback to be called when this input is
2181 * dead */
2182 vlc_player_destructor_AddInput(player, player->next_input);
2183 player->next_input = NULL;
2185 #endif
2189 vlc_player_Start(vlc_player_t *player)
2191 vlc_player_assert_locked(player);
2193 vlc_player_CancelWaitError(player);
2195 if (player->started)
2196 return VLC_SUCCESS;
2198 if (!vlc_player_destructor_IsEmpty(player))
2200 if (player->next_media)
2202 player->started = true;
2203 return VLC_SUCCESS;
2205 else
2206 return VLC_EGENERIC;
2209 if (!player->media)
2210 return VLC_EGENERIC;
2212 if (!player->input)
2214 /* Possible if the player was stopped by the user */
2215 player->input = vlc_player_input_New(player, player->media);
2217 if (!player->input)
2218 return VLC_ENOMEM;
2220 assert(!player->input->started);
2222 if (player->start_paused)
2224 var_Create(player->input->thread, "start-paused", VLC_VAR_BOOL);
2225 var_SetBool(player->input->thread, "start-paused", true);
2228 int ret = vlc_player_input_Start(player->input);
2229 if (ret == VLC_SUCCESS)
2230 player->started = true;
2232 vlc_player_vout_OSDIcon(player, OSD_PLAY_ICON);
2233 return ret;
2236 void
2237 vlc_player_Stop(vlc_player_t *player)
2239 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2241 vlc_player_CancelWaitError(player);
2243 vlc_player_InvalidateNextMedia(player);
2245 if (!input || !player->started)
2246 return;
2247 player->started = false;
2249 vlc_player_destructor_AddInput(player, input);
2250 player->input = NULL;
2252 #if GAPLESS
2253 if (player->next_input)
2255 vlc_player_destructor_AddInput(player, next_input);
2256 player->next_input = NULL;
2258 #endif
2261 void
2262 vlc_player_SetMediaStoppedAction(vlc_player_t *player,
2263 enum vlc_player_media_stopped_action action)
2265 vlc_player_assert_locked(player);
2266 player->media_stopped_action = action;
2267 var_SetBool(player, "play-and-pause",
2268 action == VLC_PLAYER_MEDIA_STOPPED_PAUSE);
2269 vlc_player_SendEvent(player, on_media_stopped_action_changed, action);
2272 void
2273 vlc_player_SetStartPaused(vlc_player_t *player, bool start_paused)
2275 vlc_player_assert_locked(player);
2276 player->start_paused = start_paused;
2279 static void
2280 vlc_player_SetPause(vlc_player_t *player, bool pause)
2282 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2284 if (!input || !input->started)
2285 return;
2287 vlc_value_t val = { .i_int = pause ? PAUSE_S : PLAYING_S };
2288 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_STATE, &val);
2290 vlc_player_vout_OSDIcon(player, pause ? OSD_PAUSE_ICON : OSD_PLAY_ICON);
2293 void
2294 vlc_player_Pause(vlc_player_t *player)
2296 vlc_player_SetPause(player, true);
2299 void
2300 vlc_player_Resume(vlc_player_t *player)
2302 vlc_player_SetPause(player, false);
2305 void
2306 vlc_player_NextVideoFrame(vlc_player_t *player)
2308 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2309 if (!input)
2310 return;
2311 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_FRAME_NEXT, NULL);
2312 vlc_player_vout_OSDMessage(player, _("Next frame"));
2315 enum vlc_player_state
2316 vlc_player_GetState(vlc_player_t *player)
2318 vlc_player_assert_locked(player);
2319 return player->global_state;
2322 enum vlc_player_error
2323 vlc_player_GetError(vlc_player_t *player)
2325 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2326 return input ? input->error : VLC_PLAYER_ERROR_NONE;
2330 vlc_player_GetCapabilities(vlc_player_t *player)
2332 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2333 return input ? input->capabilities : 0;
2336 float
2337 vlc_player_GetRate(vlc_player_t *player)
2339 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2340 if (input)
2341 return input->rate;
2342 else
2343 return var_GetFloat(player, "rate");
2346 void
2347 vlc_player_ChangeRate(vlc_player_t *player, float rate)
2349 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2351 if (rate == 0.0)
2352 return;
2354 /* Save rate accross inputs */
2355 var_SetFloat(player, "rate", rate);
2357 if (input)
2359 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_RATE,
2360 &(vlc_value_t) { .f_float = rate });
2362 else
2363 vlc_player_SendEvent(player, on_rate_changed, rate);
2365 vlc_player_vout_OSDMessage(player, ("Speed: %.2fx"), rate);
2368 static void
2369 vlc_player_ChangeRateOffset(vlc_player_t *player, bool increment)
2371 static const float rates[] = {
2372 1.0/64, 1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0/3, 1.0/2, 2.0/3,
2373 1.0/1,
2374 3.0/2, 2.0/1, 3.0/1, 4.0/1, 8.0/1, 16.0/1, 32.0/1, 64.0/1,
2376 float rate = vlc_player_GetRate(player) * (increment ? 1.1f : 0.9f);
2378 /* find closest rate (if any) in the desired direction */
2379 for (size_t i = 0; i < ARRAY_SIZE(rates); ++i)
2381 if ((increment && rates[i] > rate) ||
2382 (!increment && rates[i] >= rate && i))
2384 rate = increment ? rates[i] : rates[i-1];
2385 break;
2389 vlc_player_ChangeRate(player, rate);
2392 void
2393 vlc_player_IncrementRate(vlc_player_t *player)
2395 vlc_player_ChangeRateOffset(player, true);
2398 void
2399 vlc_player_DecrementRate(vlc_player_t *player)
2401 vlc_player_ChangeRateOffset(player, false);
2404 vlc_tick_t
2405 vlc_player_GetLength(vlc_player_t *player)
2407 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2408 return input ? input->length : VLC_TICK_INVALID;
2411 vlc_tick_t
2412 vlc_player_GetTime(vlc_player_t *player)
2414 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2416 if (!input || input->time == VLC_TICK_INVALID)
2417 return VLC_TICK_INVALID;
2419 return input->time;
2422 float
2423 vlc_player_GetPosition(vlc_player_t *player)
2425 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2427 return input ? input->position : -1.f;
2430 static inline void
2431 vlc_player_assert_seek_params(enum vlc_player_seek_speed speed,
2432 enum vlc_player_whence whence)
2434 assert(speed == VLC_PLAYER_SEEK_PRECISE
2435 || speed == VLC_PLAYER_SEEK_FAST);
2436 assert(whence == VLC_PLAYER_WHENCE_ABSOLUTE
2437 || whence == VLC_PLAYER_WHENCE_RELATIVE);
2438 (void) speed; (void) whence;
2441 static inline void
2442 vlc_player_vout_OSDPosition(vlc_player_t *player,
2443 struct vlc_player_input *input, vlc_tick_t time,
2444 float position, enum vlc_player_whence whence)
2446 if (input->length != VLC_TICK_INVALID)
2448 if (time == VLC_TICK_INVALID)
2449 time = position * input->length;
2450 else
2451 position = time / (float) input->length;
2454 size_t count;
2455 vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
2457 if (time != VLC_TICK_INVALID)
2459 if (whence == VLC_PLAYER_WHENCE_RELATIVE)
2461 time += input->time; /* XXX: TOCTOU */
2462 if (time < 0)
2463 time = 0;
2466 char time_text[MSTRTIME_MAX_SIZE];
2467 secstotimestr(time_text, SEC_FROM_VLC_TICK(time));
2468 if (input->length != VLC_TICK_INVALID)
2470 char len_text[MSTRTIME_MAX_SIZE];
2471 secstotimestr(len_text, SEC_FROM_VLC_TICK(input->length));
2472 vouts_osd_Message(vouts, count, "%s / %s", time_text, len_text);
2474 else
2475 vouts_osd_Message(vouts, count, "%s", time_text);
2478 if (vlc_player_vout_IsFullscreen(player))
2480 if (whence == VLC_PLAYER_WHENCE_RELATIVE)
2482 position += input->position; /* XXX: TOCTOU */
2483 if (position < 0.f)
2484 position = 0.f;
2486 vouts_osd_Slider(vouts, count, position * 100, OSD_HOR_SLIDER);
2488 vlc_player_vout_OSDReleaseAll(player, vouts, count);
2491 void
2492 vlc_player_DisplayPosition(vlc_player_t *player)
2494 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2495 if (!input)
2496 return;
2497 vlc_player_vout_OSDPosition(player, input, input->time, input->position,
2498 VLC_PLAYER_WHENCE_ABSOLUTE);
2501 void
2502 vlc_player_SeekByPos(vlc_player_t *player, float position,
2503 enum vlc_player_seek_speed speed,
2504 enum vlc_player_whence whence)
2506 vlc_player_assert_seek_params(speed, whence);
2508 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2509 if (!input)
2510 return;
2512 const int type =
2513 whence == VLC_PLAYER_WHENCE_ABSOLUTE ? INPUT_CONTROL_SET_POSITION
2514 : INPUT_CONTROL_JUMP_POSITION;
2515 input_ControlPush(input->thread, type,
2516 &(input_control_param_t) {
2517 .pos.f_val = position,
2518 .pos.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
2521 vlc_player_vout_OSDPosition(player, input, VLC_TICK_INVALID, position,
2522 whence);
2525 void
2526 vlc_player_SeekByTime(vlc_player_t *player, vlc_tick_t time,
2527 enum vlc_player_seek_speed speed,
2528 enum vlc_player_whence whence)
2530 vlc_player_assert_seek_params(speed, whence);
2532 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2533 if (!input)
2534 return;
2536 const int type =
2537 whence == VLC_PLAYER_WHENCE_ABSOLUTE ? INPUT_CONTROL_SET_TIME
2538 : INPUT_CONTROL_JUMP_TIME;
2539 input_ControlPush(input->thread, type,
2540 &(input_control_param_t) {
2541 .time.i_val = time,
2542 .time.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
2545 vlc_player_vout_OSDPosition(player, input, time, -1, whence);
2548 void
2549 vlc_player_SetRenderer(vlc_player_t *player, vlc_renderer_item_t *renderer)
2551 vlc_player_assert_locked(player);
2553 if (player->renderer)
2554 vlc_renderer_item_release(player->renderer);
2555 player->renderer = renderer ? vlc_renderer_item_hold(renderer) : NULL;
2557 vlc_player_foreach_inputs(input)
2559 vlc_value_t val = {
2560 .p_address = renderer ? vlc_renderer_item_hold(renderer) : NULL
2562 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_RENDERER,
2563 &val);
2565 vlc_player_SendEvent(player, on_renderer_changed, player->renderer);
2568 vlc_renderer_item_t *
2569 vlc_player_GetRenderer(vlc_player_t *player)
2571 vlc_player_assert_locked(player);
2572 return player->renderer;
2576 vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop abloop)
2578 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2580 if (!input || !vlc_player_CanSeek(player))
2581 return VLC_EGENERIC;
2583 vlc_tick_t time = vlc_player_GetTime(player);
2584 float pos = vlc_player_GetPosition(player);
2585 int ret = VLC_SUCCESS;
2586 switch (abloop)
2588 case VLC_PLAYER_ABLOOP_A:
2589 if (input->abloop_state[1].set)
2590 return VLC_EGENERIC;
2591 input->abloop_state[0].time = time;
2592 input->abloop_state[0].pos = pos;
2593 input->abloop_state[0].set = true;
2594 break;
2595 case VLC_PLAYER_ABLOOP_B:
2596 if (!input->abloop_state[0].set)
2597 return VLC_EGENERIC;
2598 input->abloop_state[1].time = time;
2599 input->abloop_state[1].pos = pos;
2600 input->abloop_state[1].set = true;
2601 if (input->abloop_state[0].time != VLC_TICK_INVALID
2602 && time != VLC_TICK_INVALID)
2604 if (time > input->abloop_state[0].time)
2606 vlc_player_SetTime(player, input->abloop_state[0].time);
2607 break;
2610 else if (pos > input->abloop_state[0].pos)
2612 vlc_player_SetPosition(player, input->abloop_state[0].pos);
2613 break;
2616 /* Error: A time is superior to B time. */
2617 abloop = VLC_PLAYER_ABLOOP_NONE;
2618 ret = VLC_EGENERIC;
2619 /* fall-through */
2620 case VLC_PLAYER_ABLOOP_NONE:
2621 input->abloop_state[0].set = input->abloop_state[1].set = false;
2622 time = VLC_TICK_INVALID;
2623 pos = 0.f;
2624 break;
2625 default:
2626 vlc_assert_unreachable();
2628 vlc_player_SendEvent(player, on_atobloop_changed, abloop, time, pos);
2629 return ret;
2632 enum vlc_player_abloop
2633 vlc_player_GetAtoBLoop(vlc_player_t *player, vlc_tick_t *a_time, float *a_pos,
2634 vlc_tick_t *b_time, float *b_pos)
2636 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2638 if (!input || !vlc_player_CanSeek(player) || !input->abloop_state[0].set)
2639 return VLC_PLAYER_ABLOOP_NONE;
2641 if (a_time)
2642 *a_time = input->abloop_state[0].time;
2643 if (a_pos)
2644 *a_pos = input->abloop_state[0].pos;
2645 if (!input->abloop_state[1].set)
2646 return VLC_PLAYER_ABLOOP_A;
2648 if (b_time)
2649 *b_time = input->abloop_state[1].time;
2650 if (b_pos)
2651 *b_pos = input->abloop_state[1].pos;
2652 return VLC_PLAYER_ABLOOP_B;
2655 void
2656 vlc_player_Navigate(vlc_player_t *player, enum vlc_player_nav nav)
2658 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2660 if (!input)
2661 return;
2663 enum input_control_e control;
2664 switch (nav)
2666 case VLC_PLAYER_NAV_ACTIVATE:
2667 control = INPUT_CONTROL_NAV_ACTIVATE;
2668 break;
2669 case VLC_PLAYER_NAV_UP:
2670 control = INPUT_CONTROL_NAV_UP;
2671 break;
2672 case VLC_PLAYER_NAV_DOWN:
2673 control = INPUT_CONTROL_NAV_DOWN;
2674 break;
2675 case VLC_PLAYER_NAV_LEFT:
2676 control = INPUT_CONTROL_NAV_LEFT;
2677 break;
2678 case VLC_PLAYER_NAV_RIGHT:
2679 control = INPUT_CONTROL_NAV_RIGHT;
2680 break;
2681 case VLC_PLAYER_NAV_POPUP:
2682 control = INPUT_CONTROL_NAV_POPUP;
2683 break;
2684 case VLC_PLAYER_NAV_MENU:
2685 control = INPUT_CONTROL_NAV_MENU;
2686 break;
2687 default:
2688 vlc_assert_unreachable();
2690 input_ControlPushHelper(input->thread, control, NULL);
2693 void
2694 vlc_player_UpdateViewpoint(vlc_player_t *player,
2695 const vlc_viewpoint_t *viewpoint,
2696 enum vlc_player_whence whence)
2698 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2699 if (input)
2700 input_UpdateViewpoint(input->thread, viewpoint,
2701 whence == VLC_PLAYER_WHENCE_ABSOLUTE);
2704 bool
2705 vlc_player_IsRecording(vlc_player_t *player)
2707 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2709 return input ? input->recording : false;
2712 void
2713 vlc_player_SetRecordingEnabled(vlc_player_t *player, bool enable)
2715 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2716 if (!input)
2717 return;
2718 input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_RECORD_STATE,
2719 &(vlc_value_t) { .b_bool = enable });
2721 vlc_player_vout_OSDMessage(player, enable ?
2722 _("Recording") : _("Recording done"));
2725 void
2726 vlc_player_SetAudioDelay(vlc_player_t *player, vlc_tick_t delay,
2727 enum vlc_player_whence whence)
2729 bool absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE;
2730 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2731 if (!input)
2732 return;
2734 input_ControlPush(input->thread, INPUT_CONTROL_SET_AUDIO_DELAY,
2735 &(input_control_param_t) {
2736 .delay = {
2737 .b_absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE,
2738 .i_val = delay,
2742 if (!absolute)
2743 delay += input->audio_delay;
2744 vlc_player_vout_OSDMessage(player, _("Audio delay: %i ms"),
2745 (int)MS_FROM_VLC_TICK(delay));
2748 vlc_tick_t
2749 vlc_player_GetAudioDelay(vlc_player_t *player)
2751 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2752 return input ? input->audio_delay : 0;
2755 static void
2756 vlc_player_SetSubtitleDelayInternal(vlc_player_t *player, vlc_tick_t delay,
2757 enum vlc_player_whence whence)
2759 bool absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE;
2760 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2761 if (!input)
2762 return;
2764 input_ControlPush(input->thread, INPUT_CONTROL_SET_SPU_DELAY,
2765 &(input_control_param_t) {
2766 .delay = {
2767 .b_absolute = absolute,
2768 .i_val = delay,
2773 void
2774 vlc_player_SetSubtitleDelay(vlc_player_t *player, vlc_tick_t delay,
2775 enum vlc_player_whence whence)
2777 vlc_player_SetSubtitleDelayInternal(player, delay, whence);
2778 vlc_player_vout_OSDMessage(player, _("Subtitle delay: %s%i ms"),
2779 whence == VLC_PLAYER_WHENCE_ABSOLUTE ? "" : "+",
2780 (int)MS_FROM_VLC_TICK(delay));
2783 static struct {
2784 const char var[sizeof("video")];
2785 const char sout_var[sizeof("sout-video")];
2786 } cat2vars[] = {
2787 [VIDEO_ES] = { "video", "sout-video" },
2788 [AUDIO_ES] = { "audio", "sout-audio" },
2789 [SPU_ES] = { "spu", "sout-spu" },
2792 void
2793 vlc_player_SetTrackCategoryEnabled(vlc_player_t *player,
2794 enum es_format_category_e cat, bool enabled)
2796 assert(cat >= UNKNOWN_ES && cat <= DATA_ES);
2797 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2799 var_SetBool(player, cat2vars[cat].var, enabled);
2800 var_SetBool(player, cat2vars[cat].sout_var, enabled);
2802 if (input)
2804 var_SetBool(input->thread, cat2vars[cat].var, enabled);
2805 var_SetBool(input->thread, cat2vars[cat].sout_var, enabled);
2807 if (!enabled)
2808 vlc_player_UnselectTrackCategory(player, cat);
2812 bool
2813 vlc_player_IsTrackCategoryEnabled(vlc_player_t *player,
2814 enum es_format_category_e cat)
2816 assert(cat >= UNKNOWN_ES && cat <= DATA_ES);
2817 return var_GetBool(player, cat2vars[cat].var);
2820 void
2821 vlc_player_SetSubtitleTextScale(vlc_player_t *player, unsigned scale)
2823 assert(scale >= 10 && scale <= 500);
2824 var_SetInteger(player, "sub-text-scale", scale);
2827 unsigned
2828 vlc_player_GetSubtitleTextScale(vlc_player_t *player)
2830 return var_GetInteger(player, "sub-text-scale");
2833 static void
2834 vlc_player_SubtitleSyncMarkAudio(vlc_player_t *player)
2836 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2837 if (!input)
2838 return;
2839 input->subsync.audio_time = vlc_tick_now();
2840 vlc_player_vout_OSDMessage(player, _("Sub sync: bookmarked audio time"));
2843 static void
2844 vlc_player_SubtitleSyncMarkSubtitle(vlc_player_t *player)
2846 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2847 if (!input)
2848 return;
2849 input->subsync.subtitle_time = vlc_tick_now();
2850 vlc_player_vout_OSDMessage(player, _("Sub sync: bookmarked subtitle time"));
2853 static void
2854 vlc_player_SubtitleSyncApply(vlc_player_t *player)
2856 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2857 if (!input)
2858 return;
2859 if (input->subsync.audio_time == VLC_TICK_INVALID ||
2860 input->subsync.subtitle_time == VLC_TICK_INVALID)
2862 vlc_player_vout_OSDMessage(player, _("Sub sync: set bookmarks first!"));
2863 return;
2865 vlc_tick_t delay =
2866 input->subsync.audio_time - input->subsync.subtitle_time;
2867 input->subsync.audio_time = VLC_TICK_INVALID;
2868 input->subsync.subtitle_time = VLC_TICK_INVALID;
2869 vlc_player_SetSubtitleDelayInternal(player, delay,
2870 VLC_PLAYER_WHENCE_RELATIVE);
2872 long long delay_ms = MS_FROM_VLC_TICK(delay);
2873 long long totdelay_ms = MS_FROM_VLC_TICK(input->subtitle_delay + delay);
2874 vlc_player_vout_OSDMessage(player, _("Sub sync: corrected %"PRId64
2875 " ms (total delay = %"PRId64" ms)"),
2876 delay_ms, totdelay_ms);
2879 static void
2880 vlc_player_SubtitleSyncReset(vlc_player_t *player)
2882 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2883 if (!input)
2884 return;
2885 vlc_player_SetSubtitleDelayInternal(player, 0, VLC_PLAYER_WHENCE_ABSOLUTE);
2886 input->subsync.audio_time = VLC_TICK_INVALID;
2887 input->subsync.subtitle_time = VLC_TICK_INVALID;
2888 vlc_player_vout_OSDMessage(player, _("Sub sync: delay reset"));
2891 void
2892 vlc_player_SetSubtitleSync(vlc_player_t *player,
2893 enum vlc_player_subtitle_sync sync)
2895 switch (sync)
2897 case VLC_PLAYER_SUBTITLE_SYNC_RESET:
2898 vlc_player_SubtitleSyncReset(player);
2899 break;
2900 case VLC_PLAYER_SUBTITLE_SYNC_MARK_AUDIO:
2901 vlc_player_SubtitleSyncMarkAudio(player);
2902 break;
2903 case VLC_PLAYER_SUBTITLE_SYNC_MARK_SUBTITLE:
2904 vlc_player_SubtitleSyncMarkSubtitle(player);
2905 break;
2906 case VLC_PLAYER_SUBTITLE_SYNC_APPLY:
2907 vlc_player_SubtitleSyncApply(player);
2908 break;
2909 default:
2910 vlc_assert_unreachable();
2914 vlc_tick_t
2915 vlc_player_GetSubtitleDelay(vlc_player_t *player)
2917 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2918 return input ? input->subtitle_delay : 0;
2922 vlc_player_GetSignal(vlc_player_t *player, float *quality, float *strength)
2924 assert(quality && strength);
2925 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2927 if (input && input->signal_quality >= 0 && input->signal_strength >= 0)
2929 *quality = input->signal_quality;
2930 *strength = input->signal_strength;
2931 return VLC_SUCCESS;
2933 return VLC_EGENERIC;
2936 const struct input_stats_t *
2937 vlc_player_GetStatistics(vlc_player_t *player)
2939 struct vlc_player_input *input = vlc_player_get_input_locked(player);
2941 return input ? &input->stats : NULL;
2944 void
2945 vlc_player_SetPauseOnCork(vlc_player_t *player, bool enabled)
2947 vlc_player_assert_locked(player);
2948 player->pause_on_cork = enabled;
2951 static int
2952 vlc_player_CorkCallback(vlc_object_t *this, const char *var,
2953 vlc_value_t oldval, vlc_value_t newval, void *data)
2955 vlc_player_t *player = data;
2957 if (oldval.i_int == newval.i_int )
2958 return VLC_SUCCESS;
2960 vlc_player_Lock(player);
2962 if (player->pause_on_cork)
2964 if (newval.i_int)
2966 player->corked = player->global_state == VLC_PLAYER_STATE_PLAYING
2967 || player->global_state == VLC_PLAYER_STATE_STARTED;
2968 if (player->corked)
2969 vlc_player_Pause(player);
2971 else
2973 if (player->corked)
2975 vlc_player_Resume(player);
2976 player->corked = false;
2980 else
2981 vlc_player_SendEvent(player, on_cork_changed, newval.i_int);
2983 vlc_player_Unlock(player);
2985 return VLC_SUCCESS;
2986 (void) this; (void) var;
2989 audio_output_t *
2990 vlc_player_aout_Hold(vlc_player_t *player)
2992 return input_resource_HoldAout(player->resource);
2995 vlc_player_aout_listener_id *
2996 vlc_player_aout_AddListener(vlc_player_t *player,
2997 const struct vlc_player_aout_cbs *cbs,
2998 void *cbs_data)
3000 assert(cbs);
3002 vlc_player_aout_listener_id *listener = malloc(sizeof(*listener));
3003 if (!listener)
3004 return NULL;
3006 listener->cbs = cbs;
3007 listener->cbs_data = cbs_data;
3009 vlc_mutex_lock(&player->aout_listeners_lock);
3010 vlc_list_append(&listener->node, &player->aout_listeners);
3011 vlc_mutex_unlock(&player->aout_listeners_lock);
3013 return listener;
3016 void
3017 vlc_player_aout_RemoveListener(vlc_player_t *player,
3018 vlc_player_aout_listener_id *id)
3020 assert(id);
3022 vlc_mutex_lock(&player->aout_listeners_lock);
3023 vlc_list_remove(&id->node);
3024 vlc_mutex_unlock(&player->aout_listeners_lock);
3025 free(id);
3028 static void
3029 vlc_player_vout_OSDVolume(vlc_player_t *player, bool mute_action)
3031 size_t count;
3032 vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
3034 bool mute = vlc_player_aout_IsMuted(player);
3035 int volume = lroundf(vlc_player_aout_GetVolume(player) * 100.f);
3036 if (mute_action && mute)
3037 vouts_osd_Icon(vouts, count, OSD_MUTE_ICON);
3038 else
3040 if (vlc_player_vout_IsFullscreen(player))
3041 vouts_osd_Slider(vouts, count, volume, OSD_VERT_SLIDER);
3042 vouts_osd_Message(vouts, count, _("Volume: %ld%%"), volume);
3045 vlc_player_vout_OSDReleaseAll(player, vouts, count);
3048 static int
3049 vlc_player_AoutCallback(vlc_object_t *this, const char *var,
3050 vlc_value_t oldval, vlc_value_t newval, void *data)
3052 vlc_player_t *player = data;
3054 if (strcmp(var, "volume") == 0)
3056 if (oldval.f_float != newval.f_float)
3058 vlc_player_aout_SendEvent(player, on_volume_changed, newval.f_float);
3059 vlc_player_vout_OSDVolume(player, false);
3062 else if (strcmp(var, "mute") == 0)
3064 if (oldval.b_bool != newval.b_bool)
3066 vlc_player_aout_SendEvent(player, on_mute_changed, newval.b_bool);
3067 vlc_player_vout_OSDVolume(player, true);
3070 else
3071 vlc_assert_unreachable();
3073 return VLC_SUCCESS;
3074 (void) this;
3077 float
3078 vlc_player_aout_GetVolume(vlc_player_t *player)
3080 audio_output_t *aout = vlc_player_aout_Hold(player);
3081 if (!aout)
3082 return -1.f;
3083 float vol = aout_VolumeGet(aout);
3084 vlc_object_release(aout);
3086 return vol;
3090 vlc_player_aout_SetVolume(vlc_player_t *player, float volume)
3092 audio_output_t *aout = vlc_player_aout_Hold(player);
3093 if (!aout)
3094 return -1;
3095 int ret = aout_VolumeSet(aout, volume);
3096 vlc_object_release(aout);
3098 return ret;
3102 vlc_player_aout_IncrementVolume(vlc_player_t *player, int steps, float *result)
3104 audio_output_t *aout = vlc_player_aout_Hold(player);
3105 if (!aout)
3106 return -1;
3107 int ret = aout_VolumeUpdate(aout, steps, result);
3108 vlc_object_release(aout);
3110 return ret;
3114 vlc_player_aout_IsMuted(vlc_player_t *player)
3116 audio_output_t *aout = vlc_player_aout_Hold(player);
3117 if (!aout)
3118 return -1;
3119 int ret = aout_MuteGet(aout);
3120 vlc_object_release(aout);
3122 return ret;
3126 vlc_player_aout_Mute(vlc_player_t *player, bool mute)
3128 audio_output_t *aout = vlc_player_aout_Hold(player);
3129 if (!aout)
3130 return -1;
3131 int ret = aout_MuteSet (aout, mute);
3132 vlc_object_release(aout);
3134 return ret;
3139 vlc_player_aout_EnableFilter(vlc_player_t *player, const char *name, bool add)
3141 audio_output_t *aout = vlc_player_aout_Hold(player);
3142 if (!aout)
3143 return -1;
3144 aout_EnableFilter(aout, name, add);
3145 vlc_object_release(aout);
3147 return 0;
3150 vout_thread_t *
3151 vlc_player_vout_Hold(vlc_player_t *player)
3153 return input_resource_HoldVout(player->resource);
3156 vout_thread_t **
3157 vlc_player_vout_HoldAll(vlc_player_t *player, size_t *count)
3159 vout_thread_t **vouts;
3160 input_resource_HoldVouts(player->resource, &vouts, count);
3161 return vouts;
3164 vlc_player_vout_listener_id *
3165 vlc_player_vout_AddListener(vlc_player_t *player,
3166 const struct vlc_player_vout_cbs *cbs,
3167 void *cbs_data)
3169 assert(cbs);
3171 vlc_player_vout_listener_id *listener = malloc(sizeof(*listener));
3172 if (!listener)
3173 return NULL;
3175 listener->cbs = cbs;
3176 listener->cbs_data = cbs_data;
3178 vlc_mutex_lock(&player->vout_listeners_lock);
3179 vlc_list_append(&listener->node, &player->vout_listeners);
3180 vlc_mutex_unlock(&player->vout_listeners_lock);
3182 return listener;
3185 void
3186 vlc_player_vout_RemoveListener(vlc_player_t *player,
3187 vlc_player_vout_listener_id *id)
3189 assert(id);
3191 vlc_mutex_lock(&player->vout_listeners_lock);
3192 vlc_list_remove(&id->node);
3193 vlc_mutex_unlock(&player->vout_listeners_lock);
3194 free(id);
3197 bool
3198 vlc_player_vout_IsFullscreen(vlc_player_t *player)
3200 return var_GetBool(player, "fullscreen");
3203 static int
3204 vlc_player_VoutCallback(vlc_object_t *this, const char *var,
3205 vlc_value_t oldval, vlc_value_t newval, void *data)
3207 vlc_player_t *player = data;
3209 if (strcmp(var, "fullscreen") == 0)
3211 if (oldval.b_bool != newval.b_bool )
3212 vlc_player_vout_SendEvent(player, on_fullscreen_changed,
3213 (vout_thread_t *)this, newval.b_bool);
3215 else if (strcmp(var, "video-wallpaper") == 0)
3217 if (oldval.b_bool != newval.b_bool )
3218 vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed,
3219 (vout_thread_t *)this, newval.b_bool);
3221 else
3222 vlc_assert_unreachable();
3224 return VLC_SUCCESS;
3227 static bool
3228 vout_osd_PrintVariableText(vout_thread_t *vout, const char *varname, int vartype,
3229 vlc_value_t varval, const char *osdfmt)
3231 bool found = false;
3232 bool isvarstring = vartype == VLC_VAR_STRING;
3233 size_t num_choices;
3234 vlc_value_t *choices;
3235 char **choices_text;
3236 var_Change(vout, varname, VLC_VAR_GETCHOICES,
3237 &num_choices, &choices, &choices_text);
3238 for (size_t i = 0; i < num_choices; ++i)
3240 if (!found)
3241 if ((isvarstring &&
3242 strcmp(choices[i].psz_string, varval.psz_string) == 0) ||
3243 (!isvarstring && choices[i].f_float == varval.f_float))
3245 vouts_osd_Message(&vout, 1, osdfmt, choices_text[i]);
3246 found = true;
3248 if (isvarstring)
3249 free(choices[i].psz_string);
3250 free(choices_text[i]);
3252 free(choices);
3253 free(choices_text);
3254 return found;
3257 static int
3258 vlc_player_VoutOSDCallback(vlc_object_t *this, const char *var,
3259 vlc_value_t oldval, vlc_value_t newval, void *data)
3261 VLC_UNUSED(oldval);
3263 vout_thread_t *vout = (vout_thread_t *)this;
3265 if (strcmp(var, "aspect-ratio") == 0)
3266 vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING,
3267 newval, _("Aspect ratio: %s"));
3269 else if (strcmp(var, "autoscale") == 0)
3270 vouts_osd_Message(&vout, 1, newval.b_bool ?
3271 _("Scaled to screen") : _("Original size"));
3273 else if (strcmp(var, "crop") == 0)
3274 vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING, newval,
3275 _("Crop: %s"));
3277 else if (strcmp(var, "crop-bottom") == 0)
3278 vouts_osd_Message(&vout, 1, _("Bottom crop: %d px"), newval.i_int);
3280 else if (strcmp(var, "crop-top") == 0)
3281 vouts_osd_Message(&vout, 1, _("Top crop: %d px"), newval.i_int);
3283 else if (strcmp(var, "crop-left") == 0)
3284 vouts_osd_Message(&vout, 1, _("Left crop: %d px"), newval.i_int);
3286 else if (strcmp(var, "crop-right") == 0)
3287 vouts_osd_Message(&vout, 1, _("Right crop: %d px"), newval.i_int);
3289 else if (strcmp(var, "deinterlace") == 0 ||
3290 strcmp(var, "deinterlace-mode") == 0)
3292 bool varmode = strcmp(var, "deinterlace-mode") == 0;
3293 int on = !varmode ?
3294 newval.i_int : var_GetInteger(vout, "deinterlace");
3295 char *mode = varmode ?
3296 newval.psz_string : var_GetString(vout, "deinterlace-mode");
3297 vouts_osd_Message(&vout, 1, _("Deinterlace %s (%s)"),
3298 on == 1 ? _("On") : _("Off"), mode);
3299 free(mode);
3302 else if (strcmp(var, "sub-margin") == 0)
3303 vouts_osd_Message(&vout, 1, _("Subtitle position %d px"), newval.i_int);
3305 else if (strcmp(var, "sub-text-scale") == 0)
3306 vouts_osd_Message(&vout, 1, _("Subtitle text scale %d%%"), newval.i_int);
3308 else if (strcmp(var, "zoom") == 0)
3310 if (newval.f_float == 1.f)
3311 vouts_osd_Message(&vout, 1, _("Zooming reset"));
3312 else
3314 bool found = vout_osd_PrintVariableText(vout, var, VLC_VAR_FLOAT,
3315 newval, _("Zoom mode: %s"));
3316 if (!found)
3317 vouts_osd_Message(&vout, 1, _("Zoom: x%f"), newval.f_float);
3321 (void) data;
3322 return VLC_SUCCESS;
3325 static void
3326 vlc_player_vout_SetVar(vlc_player_t *player, const char *name, int type,
3327 vlc_value_t val)
3329 var_SetChecked(player, name, type, val);
3331 size_t count;
3332 vout_thread_t **vouts = vlc_player_vout_HoldAll(player, &count);
3333 for (size_t i = 0; i < count; i++)
3335 var_SetChecked(vouts[i], name, type, val);
3336 vlc_object_release(vouts[i]);
3338 free(vouts);
3342 static void
3343 vlc_player_vout_TriggerOption(vlc_player_t *player, const char *option)
3345 size_t count;
3346 vout_thread_t **vouts = vlc_player_vout_HoldAll(player, &count);
3347 for (size_t i = 0; i < count; ++i)
3349 var_TriggerCallback(vouts[i], option);
3350 vlc_object_release(vouts[i]);
3352 free(vouts);
3355 void
3356 vlc_player_vout_SetFullscreen(vlc_player_t *player, bool enabled)
3358 vlc_player_vout_SetVar(player, "fullscreen", VLC_VAR_BOOL,
3359 (vlc_value_t) { .b_bool = enabled });
3360 vlc_player_vout_SendEvent(player, on_fullscreen_changed, NULL, enabled);
3363 bool
3364 vlc_player_vout_IsWallpaperModeEnabled(vlc_player_t *player)
3366 return var_GetBool(player, "video-wallpaper");
3369 void
3370 vlc_player_vout_SetWallpaperModeEnabled(vlc_player_t *player, bool enabled)
3372 vlc_player_vout_SetVar(player, "video-wallpaper", VLC_VAR_BOOL,
3373 (vlc_value_t) { .b_bool = enabled });
3374 vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed, NULL, enabled);
3377 static const char *
3378 vlc_vout_filter_type_to_varname(enum vlc_vout_filter_type type)
3380 switch (type)
3382 case VLC_VOUT_FILTER_VIDEO_SPLITTER:
3383 return "video-splitter";
3384 case VLC_VOUT_FILTER_VIDEO_FILTER:
3385 return "video-filter";
3386 case VLC_VOUT_FILTER_SUB_SOURCE:
3387 return "sub-source";
3388 case VLC_VOUT_FILTER_SUB_FILTER:
3389 return "sub-filter";
3390 default:
3391 vlc_assert_unreachable();
3395 void
3396 vlc_player_vout_SetFilter(vlc_player_t *player, enum vlc_vout_filter_type type,
3397 const char *value)
3399 const char *varname = vlc_vout_filter_type_to_varname(type);
3400 vlc_player_vout_SetVar(player, varname, VLC_VAR_STRING,
3401 (vlc_value_t) { .psz_string = (char *) value });
3404 char *
3405 vlc_player_vout_GetFilter(vlc_player_t *player, enum vlc_vout_filter_type type)
3407 const char *varname = vlc_vout_filter_type_to_varname(type);
3408 return var_GetString(player, varname);
3411 void
3412 vlc_player_vout_Snapshot(vlc_player_t *player)
3414 vlc_player_vout_TriggerOption(player, "video-snapshot");
3417 static void
3418 vlc_player_InitLocks(vlc_player_t *player)
3420 vlc_mutex_init(&player->lock);
3421 vlc_mutex_init(&player->vout_listeners_lock);
3422 vlc_mutex_init(&player->aout_listeners_lock);
3423 vlc_cond_init(&player->start_delay_cond);
3424 vlc_cond_init(&player->destructor.wait);
3427 static void
3428 vlc_player_DestroyLocks(vlc_player_t *player)
3430 vlc_mutex_destroy(&player->lock);
3431 vlc_mutex_destroy(&player->vout_listeners_lock);
3432 vlc_mutex_destroy(&player->aout_listeners_lock);
3433 vlc_cond_destroy(&player->start_delay_cond);
3434 vlc_cond_destroy(&player->destructor.wait);
3437 void
3438 vlc_player_Delete(vlc_player_t *player)
3440 vlc_mutex_lock(&player->lock);
3442 if (player->input)
3443 vlc_player_destructor_AddInput(player, player->input);
3444 #if GAPLESS
3445 if (player->next_input)
3446 vlc_player_destructor_AddInput(player, player->next_inpu);
3447 #endif
3449 player->deleting = true;
3450 vlc_cond_signal(&player->destructor.wait);
3452 assert(vlc_list_is_empty(&player->listeners));
3454 vlc_mutex_unlock(&player->lock);
3456 vlc_join(player->destructor.thread, NULL);
3458 if (player->media)
3459 input_item_Release(player->media);
3460 if (player->next_media)
3461 input_item_Release(player->next_media);
3463 vlc_player_DestroyLocks(player);
3465 audio_output_t *aout = vlc_player_aout_Hold(player);
3466 if (aout)
3468 var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
3469 var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
3470 var_DelCallback(player, "corks", vlc_player_CorkCallback, NULL);
3471 vlc_object_release(aout);
3473 input_resource_Release(player->resource);
3474 if (player->renderer)
3475 vlc_renderer_item_release(player->renderer);
3477 vlc_object_release(player);
3480 vlc_player_t *
3481 vlc_player_New(vlc_object_t *parent,
3482 const struct vlc_player_media_provider *media_provider,
3483 void *media_provider_data)
3485 audio_output_t *aout = NULL;
3486 vlc_player_t *player = vlc_custom_create(parent, sizeof(*player), "player");
3487 if (!player)
3488 return NULL;
3490 assert(!media_provider || media_provider->get_next);
3492 vlc_list_init(&player->listeners);
3493 vlc_list_init(&player->vout_listeners);
3494 vlc_list_init(&player->aout_listeners);
3495 vlc_list_init(&player->destructor.inputs);
3496 vlc_list_init(&player->destructor.stopping_inputs);
3497 vlc_list_init(&player->destructor.joinable_inputs);
3498 player->media_stopped_action = VLC_PLAYER_MEDIA_STOPPED_CONTINUE;
3499 player->start_paused = false;
3500 player->pause_on_cork = false;
3501 player->corked = false;
3502 player->renderer = NULL;
3503 player->media_provider = media_provider;
3504 player->media_provider_data = media_provider_data;
3505 player->media = NULL;
3506 player->input = NULL;
3507 player->global_state = VLC_PLAYER_STATE_STOPPED;
3508 player->started = false;
3510 player->error_count = 0;
3512 player->releasing_media = false;
3513 player->has_next_media = false;
3514 player->next_media = NULL;
3515 #if GAPLESS
3516 player->next_input = NULL;
3517 #endif
3519 #define VAR_CREATE(var, flag) do { \
3520 if (var_Create(player, var, flag) != VLC_SUCCESS) \
3521 goto error; \
3522 } while(0)
3524 /* player variables */
3525 VAR_CREATE("rate", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
3526 VAR_CREATE("sub-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
3527 VAR_CREATE("sub-text-scale", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
3528 VAR_CREATE("demux-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3530 /* vout variables */
3531 VAR_CREATE("video-splitter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3532 VAR_CREATE("video-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3533 VAR_CREATE("sub-source", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3534 VAR_CREATE("sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3535 VAR_CREATE("fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3536 VAR_CREATE("video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3537 VAR_CREATE("video-wallpaper", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3539 /* aout variables */
3540 VAR_CREATE("audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3541 VAR_CREATE("mute", VLC_VAR_BOOL);
3542 VAR_CREATE("corks", VLC_VAR_INTEGER);
3544 /* es_out variables */
3545 VAR_CREATE("sout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
3546 VAR_CREATE("video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3547 VAR_CREATE("sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3548 VAR_CREATE("audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3549 VAR_CREATE("sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3550 VAR_CREATE("spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3551 VAR_CREATE("sout-spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
3553 /* TODO: Override these variables since the player handle media ended
3554 * action itself. */
3555 VAR_CREATE("start-paused", VLC_VAR_BOOL);
3556 VAR_CREATE("play-and-pause", VLC_VAR_BOOL);
3558 #undef VAR_CREATE
3560 player->resource = input_resource_New(VLC_OBJECT(player));
3562 if (!player->resource)
3563 goto error;
3566 aout = input_resource_GetAout(player->resource);
3567 if (aout != NULL)
3569 var_AddCallback(aout, "volume", vlc_player_AoutCallback, player);
3570 var_AddCallback(aout, "mute", vlc_player_AoutCallback, player);
3571 var_AddCallback(player, "corks", vlc_player_CorkCallback, NULL);
3572 input_resource_PutAout(player->resource, aout);
3575 player->deleting = false;
3576 vlc_player_InitLocks(player);
3578 if (vlc_clone(&player->destructor.thread, vlc_player_destructor_Thread,
3579 player, VLC_THREAD_PRIORITY_LOW) != 0)
3581 vlc_player_DestroyLocks(player);
3582 goto error;
3585 return player;
3587 error:
3588 if (aout)
3590 var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
3591 var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
3592 var_DelCallback(player, "corks", vlc_player_AoutCallback, NULL);
3594 if (player->resource)
3595 input_resource_Release(player->resource);
3597 vlc_object_release(player);
3598 return NULL;