/src/vlc/src/player/input.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * player_input.c: Player input implementation |
3 | | ***************************************************************************** |
4 | | * Copyright © 2018-2019 VLC authors and VideoLAN |
5 | | * |
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. |
10 | | * |
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. |
15 | | * |
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 | | *****************************************************************************/ |
20 | | |
21 | | #ifdef HAVE_CONFIG_H |
22 | | # include "config.h" |
23 | | #endif |
24 | | |
25 | | #include <vlc_common.h> |
26 | | #include <vlc_memstream.h> |
27 | | #include <vlc_dialog.h> |
28 | | #include "player.h" |
29 | | |
30 | | struct vlc_player_track_priv * |
31 | | vlc_player_input_FindTrackById(struct vlc_player_input *input, vlc_es_id_t *id, |
32 | | size_t *idx) |
33 | 0 | { |
34 | 0 | vlc_player_track_vector *vec = |
35 | 0 | vlc_player_input_GetTrackVector(input, vlc_es_id_GetCat(id)); |
36 | 0 | return vec ? vlc_player_track_vector_FindById(vec, id, idx) : NULL; |
37 | 0 | } |
38 | | |
39 | | vlc_tick_t |
40 | | vlc_player_input_GetTime(struct vlc_player_input *input, bool seeking, |
41 | | vlc_tick_t system_now) |
42 | 0 | { |
43 | 0 | vlc_player_t *player = input->player; |
44 | 0 | vlc_tick_t ts; |
45 | |
|
46 | 0 | if (input == player->input |
47 | 0 | && vlc_player_GetTimerPoint(player, &seeking, system_now, &ts, NULL) == 0) |
48 | 0 | return ts; |
49 | 0 | return input->time; |
50 | 0 | } |
51 | | |
52 | | double |
53 | | vlc_player_input_GetPos(struct vlc_player_input *input, bool seeking, |
54 | | vlc_tick_t system_now) |
55 | 0 | { |
56 | 0 | vlc_player_t *player = input->player; |
57 | 0 | double pos; |
58 | |
|
59 | 0 | if (input == player->input |
60 | 0 | && vlc_player_GetTimerPoint(player, &seeking, system_now, NULL, &pos) == 0) |
61 | 0 | return pos; |
62 | 0 | return input->position; |
63 | 0 | } |
64 | | |
65 | | bool |
66 | | vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, bool forced) |
67 | 0 | { |
68 | 0 | if (!input->abloop_state[0].set || !input->abloop_state[1].set |
69 | 0 | || (input->capabilities & VLC_PLAYER_CAP_SEEK) == 0) |
70 | 0 | return false; |
71 | | |
72 | 0 | vlc_player_t *player = input->player; |
73 | |
|
74 | 0 | if (player->input != input) |
75 | 0 | return false; |
76 | | |
77 | 0 | vlc_tick_t now = vlc_tick_now(); |
78 | 0 | if (input->abloop_state[0].time != VLC_TICK_INVALID |
79 | 0 | && input->abloop_state[1].time != VLC_TICK_INVALID) |
80 | 0 | { |
81 | 0 | vlc_tick_t time = vlc_player_input_GetTime(input, false, now); |
82 | 0 | if (forced |
83 | 0 | || (time != VLC_TICK_INVALID && time >= input->abloop_state[1].time)) |
84 | 0 | vlc_player_SetTime(player, input->abloop_state[0].time); |
85 | 0 | return true; |
86 | 0 | } |
87 | | |
88 | 0 | double pos = vlc_player_input_GetPos(input, false, now); |
89 | 0 | if (forced || pos >= input->abloop_state[1].pos) |
90 | 0 | { |
91 | 0 | vlc_player_SetPosition(player, input->abloop_state[0].pos); |
92 | 0 | return true; |
93 | 0 | } |
94 | | |
95 | 0 | return false; |
96 | 0 | } |
97 | | |
98 | | int |
99 | | vlc_player_input_Start(struct vlc_player_input *input) |
100 | 0 | { |
101 | 0 | int ret = input_Start(input->thread); |
102 | 0 | if (ret != VLC_SUCCESS) |
103 | 0 | return ret; |
104 | 0 | input->started = true; |
105 | 0 | return ret; |
106 | 0 | } |
107 | | |
108 | | static inline void |
109 | | vlc_player_assert_seek_params(enum vlc_player_seek_speed speed, |
110 | | enum vlc_player_whence whence) |
111 | 0 | { |
112 | 0 | assert(speed == VLC_PLAYER_SEEK_PRECISE |
113 | 0 | || speed == VLC_PLAYER_SEEK_FAST); |
114 | 0 | assert(whence == VLC_PLAYER_WHENCE_ABSOLUTE |
115 | 0 | || whence == VLC_PLAYER_WHENCE_RELATIVE); |
116 | 0 | (void) speed; (void) whence; |
117 | 0 | } |
118 | | |
119 | | void |
120 | | vlc_player_input_SeekByPos(struct vlc_player_input *input, double position, |
121 | | enum vlc_player_seek_speed speed, |
122 | | enum vlc_player_whence whence) |
123 | 0 | { |
124 | 0 | vlc_player_t *player = input->player; |
125 | 0 | vlc_player_assert_seek_params(speed, whence); |
126 | |
|
127 | 0 | if (whence != VLC_PLAYER_WHENCE_ABSOLUTE) |
128 | 0 | position += vlc_player_input_GetPos(input, true, vlc_tick_now()); |
129 | |
|
130 | 0 | if (position < 0) |
131 | 0 | position = 0; |
132 | 0 | else if (position > 1) |
133 | 0 | position = 1; |
134 | |
|
135 | 0 | vlc_player_UpdateTimerSeekState(player, VLC_TICK_INVALID, position); |
136 | |
|
137 | 0 | int ret = input_ControlPush(input->thread, INPUT_CONTROL_SET_POSITION, |
138 | 0 | &(input_control_param_t) { |
139 | 0 | .pos.f_val = position, |
140 | 0 | .pos.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST, |
141 | 0 | }); |
142 | |
|
143 | 0 | if (ret == VLC_SUCCESS) |
144 | 0 | vlc_player_osd_Position(player, input, VLC_TICK_INVALID, position); |
145 | 0 | } |
146 | | |
147 | | void |
148 | | vlc_player_input_SeekByTime(struct vlc_player_input *input, vlc_tick_t time, |
149 | | enum vlc_player_seek_speed speed, |
150 | | enum vlc_player_whence whence) |
151 | 0 | { |
152 | 0 | vlc_player_t *player = input->player; |
153 | 0 | vlc_player_assert_seek_params(speed, whence); |
154 | |
|
155 | 0 | if (whence != VLC_PLAYER_WHENCE_ABSOLUTE) |
156 | 0 | time += vlc_player_input_GetTime(input, true, vlc_tick_now()); |
157 | |
|
158 | 0 | if (time < VLC_TICK_0) |
159 | 0 | time = VLC_TICK_0; |
160 | |
|
161 | 0 | vlc_player_UpdateTimerSeekState(player, time, -1); |
162 | |
|
163 | 0 | int ret = input_ControlPush(input->thread, INPUT_CONTROL_SET_TIME, |
164 | 0 | &(input_control_param_t) { |
165 | 0 | .time.i_val = time, |
166 | 0 | .time.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST, |
167 | 0 | }); |
168 | |
|
169 | 0 | if (ret == VLC_SUCCESS) |
170 | 0 | vlc_player_osd_Position(player, input, time, -1); |
171 | 0 | } |
172 | | |
173 | | void |
174 | | vlc_player_input_UpdateViewpoint(struct vlc_player_input *input, |
175 | | const vlc_viewpoint_t *viewpoint, |
176 | | enum vlc_player_whence whence) |
177 | 0 | { |
178 | 0 | input_control_param_t param = { .viewpoint = *viewpoint }; |
179 | 0 | if (whence == VLC_PLAYER_WHENCE_ABSOLUTE) |
180 | 0 | input_ControlPush(input->thread, INPUT_CONTROL_SET_VIEWPOINT, |
181 | 0 | ¶m); |
182 | 0 | else |
183 | 0 | input_ControlPush(input->thread, INPUT_CONTROL_UPDATE_VIEWPOINT, |
184 | 0 | ¶m); |
185 | 0 | } |
186 | | |
187 | | static bool |
188 | | vlc_player_WaitRetryDelay(vlc_player_t *player) |
189 | 0 | { |
190 | 0 | #define RETRY_TIMEOUT_BASE VLC_TICK_FROM_MS(100) |
191 | 0 | #define RETRY_TIMEOUT_MAX VLC_TICK_FROM_MS(3200) |
192 | 0 | #define MAX_EOS_BURST 4 |
193 | | /* Temporize only after a few successive EOS */ |
194 | 0 | if (player->eos_burst_count > MAX_EOS_BURST) |
195 | 0 | { |
196 | 0 | unsigned pow = player->eos_burst_count - MAX_EOS_BURST; |
197 | | /* Delay the next opening to avoid busy loops */ |
198 | 0 | vlc_tick_t delay = RETRY_TIMEOUT_BASE; |
199 | 0 | for (unsigned i = 1; i < pow && delay < RETRY_TIMEOUT_MAX; ++i) |
200 | 0 | delay *= 2; /* Wait 100, 200, 400, 800, 1600 and finally 3200ms */ |
201 | 0 | delay += vlc_tick_now(); |
202 | |
|
203 | 0 | while (player->eos_burst_count != 0 |
204 | 0 | && vlc_cond_timedwait(&player->start_delay_cond, &player->lock, |
205 | 0 | delay) == 0); |
206 | 0 | if (player->eos_burst_count == 0) |
207 | 0 | return false; /* canceled */ |
208 | 0 | } |
209 | 0 | return true; |
210 | 0 | } |
211 | | |
212 | | void |
213 | | vlc_player_input_HandleState(struct vlc_player_input *input, |
214 | | enum vlc_player_state state, vlc_tick_t state_date) |
215 | 0 | { |
216 | 0 | vlc_player_t *player = input->player; |
217 | | |
218 | | /* The STOPPING state can be set earlier by the player. In that case, |
219 | | * ignore all future events except the STOPPED one */ |
220 | 0 | if (input->state == VLC_PLAYER_STATE_STOPPING |
221 | 0 | && state != VLC_PLAYER_STATE_STOPPED) |
222 | 0 | return; |
223 | | |
224 | 0 | enum vlc_player_state last_state = input->state; |
225 | 0 | input->state = state; |
226 | | |
227 | | /* Override the global state if the player is still playing and has a next |
228 | | * media to play */ |
229 | 0 | bool send_event = player->global_state != state; |
230 | 0 | switch (input->state) |
231 | 0 | { |
232 | 0 | case VLC_PLAYER_STATE_STOPPED: |
233 | 0 | assert(!input->started); |
234 | 0 | assert(input != player->input); |
235 | |
|
236 | 0 | if (input->titles) |
237 | 0 | { |
238 | 0 | vlc_player_title_list_Release(input->titles); |
239 | 0 | input->titles = NULL; |
240 | 0 | vlc_player_SendEvent(player, on_titles_changed, NULL); |
241 | 0 | } |
242 | |
|
243 | 0 | vlc_player_ResetTimer(player); |
244 | |
|
245 | 0 | if (player->last_eos != VLC_TICK_INVALID) |
246 | 0 | { |
247 | 0 | vlc_tick_t diff = vlc_tick_now() - player->last_eos; |
248 | 0 | if (diff < VLC_PLAYER_EOS_BURST_THRESHOLD) |
249 | 0 | ++player->eos_burst_count; |
250 | 0 | else |
251 | 0 | player->eos_burst_count = 0; |
252 | 0 | } |
253 | |
|
254 | 0 | if (player->started) |
255 | 0 | vlc_player_WaitRetryDelay(player); |
256 | | |
257 | | /* Assign the current date after the wait */ |
258 | 0 | player->last_eos = vlc_tick_now(); |
259 | |
|
260 | 0 | if (!player->deleting) |
261 | 0 | { |
262 | 0 | vlc_player_OpenNextMedia(player); |
263 | | /* It is possible to open several medias in a stopped state */ |
264 | 0 | if (player->input && player->started) |
265 | 0 | vlc_player_input_Start(player->input); |
266 | 0 | } |
267 | 0 | if (!player->input) |
268 | 0 | player->started = false; |
269 | |
|
270 | 0 | send_event = !player->started && last_state != VLC_PLAYER_STATE_STOPPED; |
271 | 0 | break; |
272 | 0 | case VLC_PLAYER_STATE_STOPPING: |
273 | 0 | input->started = false; |
274 | | |
275 | | /* Note: no need to hold the media here, as it is already protected |
276 | | by the player lock. User can hold the input_item, if they want |
277 | | to use it beyond the callback scope. */ |
278 | 0 | input_item_t *media = input_GetItem(input->thread); |
279 | 0 | vlc_player_SendEvent(player, on_stopping_current_media, |
280 | 0 | media, input->stopping_reason); |
281 | |
|
282 | 0 | vlc_player_UpdateTimerEvent(player, NULL, |
283 | 0 | VLC_PLAYER_TIMER_EVENT_DISCONTINUITY, |
284 | 0 | VLC_TICK_INVALID); |
285 | |
|
286 | 0 | vlc_player_UpdateTimerEvent(player, NULL, |
287 | 0 | VLC_PLAYER_TIMER_EVENT_STOPPING, |
288 | 0 | VLC_TICK_INVALID); |
289 | |
|
290 | 0 | if (input == player->input) |
291 | 0 | player->input = NULL; |
292 | |
|
293 | 0 | if (player->started && !player->next_media) |
294 | 0 | player->started = false; |
295 | 0 | send_event = !player->started; |
296 | 0 | break; |
297 | 0 | case VLC_PLAYER_STATE_PLAYING: |
298 | 0 | input->pause_date = VLC_TICK_INVALID; |
299 | 0 | vlc_player_UpdateTimerEvent(player, NULL, |
300 | 0 | VLC_PLAYER_TIMER_EVENT_PLAYING, |
301 | 0 | input->pause_date); |
302 | | /* fall through */ |
303 | 0 | case VLC_PLAYER_STATE_STARTED: |
304 | 0 | if (player->started && |
305 | 0 | player->global_state == VLC_PLAYER_STATE_PLAYING) |
306 | 0 | send_event = false; |
307 | 0 | break; |
308 | | |
309 | 0 | case VLC_PLAYER_STATE_PAUSED: |
310 | 0 | assert(player->global_state == VLC_PLAYER_STATE_PLAYING); |
311 | 0 | assert(state_date != VLC_TICK_INVALID); |
312 | 0 | input->pause_date = state_date; |
313 | |
|
314 | 0 | vlc_player_UpdateTimerEvent(player, NULL, |
315 | 0 | VLC_PLAYER_TIMER_EVENT_PAUSED, |
316 | 0 | input->pause_date); |
317 | 0 | break; |
318 | 0 | default: |
319 | 0 | vlc_assert_unreachable(); |
320 | 0 | } |
321 | | |
322 | 0 | if (send_event) |
323 | 0 | { |
324 | 0 | assert(player->global_state != input->state); |
325 | 0 | player->global_state = input->state; |
326 | 0 | vlc_player_SendEvent(player, on_state_changed, player->global_state); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | static void |
331 | | vlc_player_input_HandleStateEvent(struct vlc_player_input *input, |
332 | | input_state_e state, vlc_tick_t state_date) |
333 | 0 | { |
334 | 0 | switch (state) |
335 | 0 | { |
336 | 0 | case OPENING_S: |
337 | 0 | vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED, |
338 | 0 | VLC_TICK_INVALID); |
339 | 0 | break; |
340 | 0 | case PLAYING_S: |
341 | 0 | input->playing = true; |
342 | 0 | vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING, |
343 | 0 | state_date); |
344 | 0 | break; |
345 | 0 | case PAUSE_S: |
346 | 0 | vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED, |
347 | 0 | state_date); |
348 | 0 | break; |
349 | 0 | case END_S: |
350 | 0 | input->playing = false; |
351 | 0 | input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_EOS; |
352 | 0 | vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING, |
353 | 0 | VLC_TICK_INVALID); |
354 | 0 | vlc_player_destructor_AddStoppingInput(input->player, input); |
355 | 0 | break; |
356 | 0 | case ERROR_S: |
357 | | /* Don't send errors if the input is stopped by the user */ |
358 | 0 | if (input->started) |
359 | 0 | { |
360 | | /* Contrary to the input_thead_t, an error is not a state */ |
361 | 0 | input->error = VLC_PLAYER_ERROR_GENERIC; |
362 | 0 | vlc_player_SendEvent(input->player, on_error_changed, input->error); |
363 | 0 | } |
364 | | /* input->playing monitor the OPENING_S -> PLAYING_S transition. |
365 | | * If input->playing is false, we have an error at the opening of |
366 | | * the input thread and we won't reach END_S. */ |
367 | 0 | if (!input->playing) |
368 | 0 | { |
369 | 0 | input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_ERROR; |
370 | 0 | vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING, |
371 | 0 | VLC_TICK_INVALID); |
372 | 0 | vlc_player_destructor_AddStoppingInput(input->player, input); |
373 | 0 | } |
374 | 0 | break; |
375 | 0 | default: |
376 | 0 | vlc_assert_unreachable(); |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | | static void |
381 | | vlc_player_input_HandleProgramEvent(struct vlc_player_input *input, |
382 | | const struct vlc_input_event_program *ev) |
383 | 0 | { |
384 | 0 | vlc_player_t *player = input->player; |
385 | 0 | struct vlc_player_program *prgm; |
386 | 0 | vlc_player_program_vector *vec = &input->program_vector; |
387 | |
|
388 | 0 | switch (ev->action) |
389 | 0 | { |
390 | 0 | case VLC_INPUT_PROGRAM_ADDED: |
391 | 0 | prgm = vlc_player_program_New(ev->id, ev->title); |
392 | 0 | if (!prgm) |
393 | 0 | break; |
394 | | |
395 | 0 | if (!vlc_vector_push(vec, prgm)) |
396 | 0 | { |
397 | 0 | vlc_player_program_Delete(prgm); |
398 | 0 | break; |
399 | 0 | } |
400 | 0 | vlc_player_SendEvent(player, on_program_list_changed, |
401 | 0 | VLC_PLAYER_LIST_ADDED, prgm); |
402 | 0 | break; |
403 | 0 | case VLC_INPUT_PROGRAM_DELETED: |
404 | 0 | { |
405 | 0 | size_t idx; |
406 | 0 | prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx); |
407 | 0 | if (prgm) |
408 | 0 | { |
409 | 0 | vlc_player_SendEvent(player, on_program_list_changed, |
410 | 0 | VLC_PLAYER_LIST_REMOVED, prgm); |
411 | 0 | vlc_vector_remove(vec, idx); |
412 | 0 | vlc_player_program_Delete(prgm); |
413 | 0 | } |
414 | 0 | break; |
415 | 0 | } |
416 | 0 | case VLC_INPUT_PROGRAM_UPDATED: |
417 | 0 | case VLC_INPUT_PROGRAM_SCRAMBLED: |
418 | 0 | prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL); |
419 | 0 | if (!prgm) |
420 | 0 | break; |
421 | 0 | if (ev->action == VLC_INPUT_PROGRAM_UPDATED) |
422 | 0 | { |
423 | 0 | if (vlc_player_program_Update(prgm, ev->id, ev->title) != 0) |
424 | 0 | break; |
425 | 0 | } |
426 | 0 | else |
427 | 0 | prgm->scrambled = ev->scrambled; |
428 | 0 | vlc_player_SendEvent(player, on_program_list_changed, |
429 | 0 | VLC_PLAYER_LIST_UPDATED, prgm); |
430 | 0 | break; |
431 | 0 | case VLC_INPUT_PROGRAM_SELECTED: |
432 | 0 | { |
433 | 0 | int unselected_id = -1, selected_id = -1; |
434 | 0 | vlc_vector_foreach(prgm, vec) |
435 | 0 | { |
436 | 0 | if (prgm->group_id == ev->id) |
437 | 0 | { |
438 | 0 | if (!prgm->selected) |
439 | 0 | { |
440 | 0 | assert(selected_id == -1); |
441 | 0 | prgm->selected = true; |
442 | 0 | selected_id = prgm->group_id; |
443 | 0 | } |
444 | 0 | } |
445 | 0 | else |
446 | 0 | { |
447 | 0 | if (prgm->selected) |
448 | 0 | { |
449 | 0 | assert(unselected_id == -1); |
450 | 0 | prgm->selected = false; |
451 | 0 | unselected_id = prgm->group_id; |
452 | 0 | } |
453 | 0 | } |
454 | 0 | } |
455 | 0 | if (unselected_id != -1 || selected_id != -1) |
456 | 0 | vlc_player_SendEvent(player, on_program_selection_changed, |
457 | 0 | unselected_id, selected_id); |
458 | 0 | break; |
459 | 0 | } |
460 | 0 | default: |
461 | 0 | vlc_assert_unreachable(); |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | static const struct vlc_player_track_priv * |
466 | | vlc_player_FindTeletextSource(const struct vlc_player_input *input, |
467 | | const struct vlc_player_track_priv *exclude, |
468 | | bool selected) |
469 | 0 | { |
470 | 0 | const struct vlc_player_track_priv *t; |
471 | 0 | vlc_vector_foreach(t, &input->spu_track_vector) |
472 | 0 | { |
473 | 0 | if (t->t.fmt.i_codec == VLC_CODEC_TELETEXT && |
474 | 0 | t != exclude && |
475 | 0 | t->t.selected == selected) |
476 | 0 | return t; |
477 | 0 | } |
478 | 0 | return NULL; |
479 | 0 | } |
480 | | |
481 | | static unsigned |
482 | | vlc_player_input_TeletextUserPage(const struct vlc_player_track_priv *t) |
483 | 0 | { |
484 | 0 | const uint8_t mag = t->t.fmt.subs.teletext.i_magazine; |
485 | 0 | const uint8_t page = t->t.fmt.subs.teletext.i_page; |
486 | 0 | return (mag % 10) * 100 + |
487 | 0 | (page & 0x0F) + ((page >> 4) & 0x0F) * 10; |
488 | 0 | } |
489 | | |
490 | | static void |
491 | | vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input, |
492 | | const struct vlc_input_event_es *ev, |
493 | | const struct vlc_player_track_priv *trackpriv) |
494 | 0 | { |
495 | 0 | vlc_player_t *player = input->player; |
496 | 0 | if (ev->fmt->i_cat != SPU_ES || |
497 | 0 | ev->fmt->i_codec != VLC_CODEC_TELETEXT) |
498 | 0 | return; |
499 | 0 | switch (ev->action) |
500 | 0 | { |
501 | 0 | case VLC_INPUT_ES_ADDED: |
502 | 0 | { |
503 | 0 | if (!input->teletext_source) |
504 | 0 | { |
505 | 0 | input->teletext_source = trackpriv; |
506 | 0 | vlc_player_SendEvent(player, on_teletext_menu_changed, true); |
507 | 0 | } |
508 | 0 | break; |
509 | 0 | } |
510 | 0 | case VLC_INPUT_ES_DELETED: |
511 | 0 | { |
512 | 0 | if (input->teletext_source == trackpriv) |
513 | 0 | { |
514 | 0 | input->teletext_source = |
515 | 0 | vlc_player_FindTeletextSource(input, trackpriv, true); |
516 | 0 | if (!input->teletext_source) |
517 | 0 | input->teletext_source = |
518 | 0 | vlc_player_FindTeletextSource(input, trackpriv, false); |
519 | 0 | if (!input->teletext_source) /* no more teletext ES */ |
520 | 0 | { |
521 | 0 | if (input->teletext_enabled) |
522 | 0 | { |
523 | 0 | input->teletext_enabled = false; |
524 | 0 | vlc_player_SendEvent(player, on_teletext_enabled_changed, false); |
525 | 0 | } |
526 | 0 | vlc_player_SendEvent(player, on_teletext_menu_changed, false); |
527 | 0 | } |
528 | 0 | else /* another teletext ES was reselected */ |
529 | 0 | { |
530 | 0 | if (input->teletext_source->t.selected != input->teletext_enabled) |
531 | 0 | { |
532 | 0 | input->teletext_enabled = input->teletext_source->t.selected; |
533 | 0 | vlc_player_SendEvent(player, on_teletext_enabled_changed, |
534 | 0 | input->teletext_source->t.selected); |
535 | 0 | } |
536 | 0 | input->teletext_page = |
537 | 0 | vlc_player_input_TeletextUserPage(input->teletext_source); |
538 | 0 | vlc_player_SendEvent(player, on_teletext_page_changed, |
539 | 0 | input->teletext_page); |
540 | 0 | } |
541 | 0 | } |
542 | 0 | break; |
543 | 0 | } |
544 | 0 | case VLC_INPUT_ES_UPDATED: |
545 | 0 | break; |
546 | 0 | case VLC_INPUT_ES_SELECTED: |
547 | 0 | { |
548 | 0 | if (!input->teletext_enabled) /* we stick with the first selected */ |
549 | 0 | { |
550 | 0 | input->teletext_source = trackpriv; |
551 | 0 | input->teletext_enabled = true; |
552 | 0 | input->teletext_page = vlc_player_input_TeletextUserPage(trackpriv); |
553 | 0 | vlc_player_SendEvent(player, on_teletext_enabled_changed, true); |
554 | 0 | vlc_player_SendEvent(player, on_teletext_page_changed, |
555 | 0 | input->teletext_page); |
556 | 0 | } |
557 | 0 | break; |
558 | 0 | } |
559 | 0 | case VLC_INPUT_ES_UNSELECTED: |
560 | 0 | if (input->teletext_source == trackpriv) |
561 | 0 | { |
562 | | /* If there's another selected teletext, it needs to become source */ |
563 | 0 | const struct vlc_player_track_priv *other = |
564 | 0 | vlc_player_FindTeletextSource(input, trackpriv, true); |
565 | 0 | if (other) |
566 | 0 | { |
567 | 0 | input->teletext_source = other; |
568 | 0 | if (!input->teletext_enabled) |
569 | 0 | { |
570 | 0 | input->teletext_enabled = true; |
571 | 0 | vlc_player_SendEvent(player, on_teletext_enabled_changed, true); |
572 | 0 | } |
573 | 0 | input->teletext_page = vlc_player_input_TeletextUserPage(other); |
574 | 0 | vlc_player_SendEvent(player, on_teletext_page_changed, |
575 | 0 | input->teletext_page); |
576 | 0 | } |
577 | 0 | else |
578 | 0 | { |
579 | 0 | input->teletext_enabled = false; |
580 | 0 | vlc_player_SendEvent(player, on_teletext_enabled_changed, false); |
581 | 0 | } |
582 | 0 | } |
583 | 0 | break; |
584 | 0 | default: |
585 | 0 | vlc_assert_unreachable(); |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | static void |
590 | | vlc_player_input_HandleEsEvent(struct vlc_player_input *input, |
591 | | const struct vlc_input_event_es *ev) |
592 | 0 | { |
593 | 0 | assert(ev->id && ev->title && ev->fmt); |
594 | |
|
595 | 0 | vlc_player_track_vector *vec = |
596 | 0 | vlc_player_input_GetTrackVector(input, ev->fmt->i_cat); |
597 | 0 | if (!vec) |
598 | 0 | return; /* UNKNOWN_ES or DATA_ES not handled */ |
599 | | |
600 | 0 | vlc_player_t *player = input->player; |
601 | 0 | struct vlc_player_track_priv *trackpriv; |
602 | 0 | switch (ev->action) |
603 | 0 | { |
604 | 0 | case VLC_INPUT_ES_ADDED: |
605 | 0 | trackpriv = vlc_player_track_priv_New(ev->id, ev->title, ev->fmt); |
606 | 0 | if (!trackpriv) |
607 | 0 | break; |
608 | | |
609 | 0 | if (ev->fmt->i_cat == VIDEO_ES) |
610 | 0 | input->ml.has_video_tracks = true; |
611 | 0 | else if (ev->fmt->i_cat == AUDIO_ES) |
612 | 0 | input->ml.has_audio_tracks = true; |
613 | 0 | if (!vlc_vector_push(vec, trackpriv)) |
614 | 0 | { |
615 | 0 | vlc_player_track_priv_Delete(trackpriv); |
616 | 0 | break; |
617 | 0 | } |
618 | 0 | vlc_player_SendEvent(player, on_track_list_changed, |
619 | 0 | VLC_PLAYER_LIST_ADDED, &trackpriv->t); |
620 | 0 | vlc_player_input_HandleTeletextMenu(input, ev, trackpriv); |
621 | 0 | break; |
622 | 0 | case VLC_INPUT_ES_DELETED: |
623 | 0 | { |
624 | 0 | size_t idx; |
625 | 0 | trackpriv = vlc_player_track_vector_FindById(vec, ev->id, &idx); |
626 | 0 | if (trackpriv) |
627 | 0 | { |
628 | 0 | vlc_player_input_HandleTeletextMenu(input, ev, trackpriv); |
629 | 0 | vlc_player_SendEvent(player, on_track_list_changed, |
630 | 0 | VLC_PLAYER_LIST_REMOVED, &trackpriv->t); |
631 | 0 | vlc_vector_remove(vec, idx); |
632 | 0 | vlc_player_track_priv_Delete(trackpriv); |
633 | 0 | } |
634 | 0 | break; |
635 | 0 | } |
636 | 0 | case VLC_INPUT_ES_UPDATED: |
637 | 0 | trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL); |
638 | 0 | if (!trackpriv) |
639 | 0 | break; |
640 | 0 | if (vlc_player_track_priv_Update(trackpriv, ev->title, ev->fmt) != 0) |
641 | 0 | break; |
642 | 0 | vlc_player_SendEvent(player, on_track_list_changed, |
643 | 0 | VLC_PLAYER_LIST_UPDATED, &trackpriv->t); |
644 | 0 | vlc_player_input_HandleTeletextMenu(input, ev, trackpriv); |
645 | 0 | break; |
646 | 0 | case VLC_INPUT_ES_SELECTED: |
647 | 0 | trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL); |
648 | 0 | if (trackpriv) |
649 | 0 | { |
650 | 0 | trackpriv->t.selected = true; |
651 | 0 | trackpriv->selected_by_user = ev->forced; |
652 | 0 | trackpriv->vout_order = ev->vout_order; |
653 | 0 | vlc_player_SendEvent(player, on_track_selection_changed, |
654 | 0 | NULL, trackpriv->t.es_id); |
655 | 0 | vlc_player_input_HandleTeletextMenu(input, ev, trackpriv); |
656 | 0 | } |
657 | 0 | break; |
658 | 0 | case VLC_INPUT_ES_UNSELECTED: |
659 | 0 | trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL); |
660 | 0 | if (trackpriv) |
661 | 0 | { |
662 | 0 | vlc_player_RemoveTimerSource(player, ev->id); |
663 | 0 | trackpriv->t.selected = false; |
664 | 0 | trackpriv->selected_by_user = false; |
665 | 0 | vlc_player_SendEvent(player, on_track_selection_changed, |
666 | 0 | trackpriv->t.es_id, NULL); |
667 | 0 | vlc_player_input_HandleTeletextMenu(input, ev, trackpriv); |
668 | 0 | } |
669 | 0 | break; |
670 | 0 | default: |
671 | 0 | vlc_assert_unreachable(); |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | | static void |
676 | | vlc_player_input_HandleTitleEvent(struct vlc_player_input *input, |
677 | | const struct vlc_input_event_title *ev) |
678 | 0 | { |
679 | 0 | vlc_player_t *player = input->player; |
680 | 0 | switch (ev->action) |
681 | 0 | { |
682 | 0 | case VLC_INPUT_TITLE_NEW_LIST: |
683 | 0 | { |
684 | 0 | input_thread_private_t *input_th = input_priv(input->thread); |
685 | 0 | const int title_offset = input_th->i_title_offset; |
686 | 0 | const int chapter_offset = input_th->i_seekpoint_offset; |
687 | |
|
688 | 0 | if (input->titles) |
689 | 0 | vlc_player_title_list_Release(input->titles); |
690 | 0 | input->title_selected = input->chapter_selected = 0; |
691 | 0 | input->titles = |
692 | 0 | vlc_player_title_list_Create(ev->list.array, ev->list.count, |
693 | 0 | title_offset, chapter_offset); |
694 | 0 | vlc_player_SendEvent(player, on_titles_changed, input->titles); |
695 | 0 | if (input->titles) |
696 | 0 | { |
697 | 0 | vlc_player_SendEvent(player, on_title_selection_changed, |
698 | 0 | &input->titles->array[0], 0); |
699 | 0 | if (input->ml.restore == VLC_RESTOREPOINT_TITLE && |
700 | 0 | (size_t)input->ml.states.current_title < ev->list.count) |
701 | 0 | { |
702 | 0 | vlc_player_SelectTitleIdx(player, input->ml.states.current_title); |
703 | 0 | } |
704 | 0 | input->ml.restore = VLC_RESTOREPOINT_POSITION; |
705 | 0 | } |
706 | 0 | else input->ml.restore = VLC_RESTOREPOINT_NONE; |
707 | 0 | break; |
708 | 0 | } |
709 | 0 | case VLC_INPUT_TITLE_SELECTED: |
710 | 0 | if (!input->titles) |
711 | 0 | return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */ |
712 | 0 | assert(ev->selected_idx < input->titles->count); |
713 | 0 | input->title_selected = ev->selected_idx; |
714 | 0 | vlc_player_SendEvent(player, on_title_selection_changed, |
715 | 0 | &input->titles->array[input->title_selected], |
716 | 0 | input->title_selected); |
717 | 0 | if (input->ml.restore == VLC_RESTOREPOINT_POSITION && |
718 | 0 | input->ml.states.current_title >= 0 && |
719 | 0 | (size_t)input->ml.states.current_title == ev->selected_idx && |
720 | 0 | input->ml.pos > .0f) |
721 | 0 | { |
722 | 0 | input_SetPosition(input->thread, input->ml.pos, false); |
723 | 0 | } |
724 | | /* Reset the wanted title to avoid forcing it or the position |
725 | | * again during the next title change |
726 | | */ |
727 | 0 | input->ml.restore = VLC_RESTOREPOINT_NONE; |
728 | 0 | break; |
729 | 0 | default: |
730 | 0 | vlc_assert_unreachable(); |
731 | 0 | } |
732 | 0 | } |
733 | | |
734 | | static void |
735 | | vlc_player_input_HandleChapterEvent(struct vlc_player_input *input, |
736 | | const struct vlc_input_event_chapter *ev) |
737 | 0 | { |
738 | 0 | vlc_player_t *player = input->player; |
739 | 0 | if (!input->titles || ev->title < 0 || ev->seekpoint < 0) |
740 | 0 | return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */ |
741 | | |
742 | 0 | assert((size_t)ev->title < input->titles->count); |
743 | 0 | const struct vlc_player_title *title = &input->titles->array[ev->title]; |
744 | 0 | if (!title->chapter_count) |
745 | 0 | return; |
746 | | |
747 | 0 | assert(ev->seekpoint < (int)title->chapter_count); |
748 | 0 | input->title_selected = ev->title; |
749 | 0 | input->chapter_selected = ev->seekpoint; |
750 | |
|
751 | 0 | const struct vlc_player_chapter *chapter = &title->chapters[ev->seekpoint]; |
752 | 0 | vlc_player_SendEvent(player, on_chapter_selection_changed, title, ev->title, |
753 | 0 | chapter, ev->seekpoint); |
754 | 0 | } |
755 | | |
756 | | static void |
757 | | vlc_player_input_HandleVoutEvent(struct vlc_player_input *input, |
758 | | const struct vlc_input_event_vout *ev) |
759 | 0 | { |
760 | 0 | assert(ev->vout); |
761 | 0 | assert(ev->id); |
762 | |
|
763 | 0 | vlc_player_t *player = input->player; |
764 | |
|
765 | 0 | struct vlc_player_track_priv *trackpriv = |
766 | 0 | vlc_player_input_FindTrackById(input, ev->id, NULL); |
767 | 0 | if (!trackpriv) |
768 | 0 | return; |
769 | | |
770 | 0 | const bool is_video_es = trackpriv->t.fmt.i_cat == VIDEO_ES; |
771 | |
|
772 | 0 | switch (ev->action) |
773 | 0 | { |
774 | 0 | case VLC_INPUT_EVENT_VOUT_STARTED: |
775 | 0 | trackpriv->vout = ev->vout; |
776 | 0 | vlc_player_SendEvent(player, on_vout_changed, |
777 | 0 | VLC_PLAYER_VOUT_STARTED, ev->vout, |
778 | 0 | ev->order, ev->id); |
779 | |
|
780 | 0 | if (is_video_es) |
781 | 0 | { |
782 | | /* Register vout callbacks after the vout list event */ |
783 | 0 | vlc_player_vout_AddCallbacks(player, ev->vout); |
784 | 0 | } |
785 | 0 | break; |
786 | 0 | case VLC_INPUT_EVENT_VOUT_STOPPED: |
787 | 0 | if (is_video_es) |
788 | 0 | { |
789 | | /* Un-register vout callbacks before the vout list event */ |
790 | 0 | vlc_player_vout_DelCallbacks(player, ev->vout); |
791 | 0 | } |
792 | |
|
793 | 0 | trackpriv->vout = NULL; |
794 | 0 | vlc_player_SendEvent(player, on_vout_changed, |
795 | 0 | VLC_PLAYER_VOUT_STOPPED, ev->vout, |
796 | 0 | VLC_VOUT_ORDER_NONE, ev->id); |
797 | 0 | break; |
798 | 0 | default: |
799 | 0 | vlc_assert_unreachable(); |
800 | 0 | } |
801 | 0 | } |
802 | | |
803 | | static void |
804 | | vlc_player_input_NavigationFallback(struct vlc_player_input *input, int nav_type) |
805 | 0 | { |
806 | 0 | vlc_player_t *player = input->player; |
807 | | |
808 | | /* Handle Up/Down/Left/Right if the demux can't navigate */ |
809 | 0 | vlc_viewpoint_t vp = { 0 }; |
810 | 0 | bool viewpoint_updated = true; |
811 | 0 | float yaw = 0.f, pitch = 0.f, roll = 0.f; |
812 | |
|
813 | 0 | int vol_direction = 0; |
814 | 0 | int seek_direction = 0; |
815 | 0 | switch (nav_type) |
816 | 0 | { |
817 | 0 | case INPUT_CONTROL_NAV_UP: |
818 | 0 | vol_direction = 1; |
819 | 0 | pitch = -1.f; |
820 | 0 | break; |
821 | 0 | case INPUT_CONTROL_NAV_DOWN: |
822 | 0 | vol_direction = -1; |
823 | 0 | pitch = 1.f; |
824 | 0 | break; |
825 | 0 | case INPUT_CONTROL_NAV_LEFT: |
826 | 0 | seek_direction = -1; |
827 | 0 | yaw = -1.f; |
828 | 0 | break; |
829 | 0 | case INPUT_CONTROL_NAV_RIGHT: |
830 | 0 | seek_direction = 1; |
831 | 0 | yaw = 1.f; |
832 | 0 | break; |
833 | 0 | case INPUT_CONTROL_NAV_ACTIVATE: |
834 | 0 | case INPUT_CONTROL_NAV_POPUP: |
835 | 0 | case INPUT_CONTROL_NAV_MENU: |
836 | 0 | viewpoint_updated = false; |
837 | 0 | return; |
838 | 0 | default: |
839 | 0 | vlc_assert_unreachable(); |
840 | 0 | } |
841 | | |
842 | | /* Try to change the viewpoint if possible */ |
843 | 0 | bool viewpoint_ch = false; |
844 | 0 | size_t vout_count; |
845 | 0 | vout_thread_t **vouts = vlc_player_vout_HoldAll(input->player, &vout_count); |
846 | 0 | for (size_t i = 0; i < vout_count; ++i) |
847 | 0 | { |
848 | 0 | if (!viewpoint_ch && var_GetBool(vouts[i], "viewpoint-changeable")) |
849 | 0 | viewpoint_ch = true; |
850 | 0 | vout_Release(vouts[i]); |
851 | 0 | } |
852 | 0 | free(vouts); |
853 | |
|
854 | 0 | if (viewpoint_ch && viewpoint_updated) |
855 | 0 | { |
856 | 0 | vlc_viewpoint_from_euler(&vp, yaw, pitch, roll); |
857 | 0 | vlc_player_input_UpdateViewpoint(input, &vp, VLC_PLAYER_WHENCE_RELATIVE); |
858 | 0 | } |
859 | 0 | else if (seek_direction != 0) |
860 | 0 | { |
861 | | /* Seek or change volume if the input doesn't have navigation or viewpoint */ |
862 | 0 | vlc_tick_t it = vlc_tick_from_sec(seek_direction |
863 | 0 | * var_InheritInteger(player, "short-jump-size")); |
864 | 0 | vlc_player_input_SeekByTime(input, it, VLC_PLAYER_SEEK_PRECISE, |
865 | 0 | VLC_PLAYER_WHENCE_RELATIVE); |
866 | 0 | } |
867 | 0 | else |
868 | 0 | { |
869 | 0 | assert(vol_direction != 0); |
870 | 0 | if (input == player->input) |
871 | 0 | vlc_player_aout_IncrementVolume(player, vol_direction, NULL); |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | static void |
876 | | vlc_player_input_MouseFallback(struct vlc_player_input *input) |
877 | 0 | { |
878 | 0 | vlc_player_t *player = input->player; |
879 | 0 | vlc_player_TogglePause(player); |
880 | 0 | } |
881 | | |
882 | | static void |
883 | | vlc_player_DisplayFrameError(vlc_player_t *player, |
884 | | const char *title, const char *title_error, |
885 | | int status) |
886 | 0 | { |
887 | 0 | static const char erange[] = {N_("can't seek back")}; |
888 | 0 | static const char ebusy[] = {N_("no video found")}; |
889 | 0 | static const char enotusp[] = {N_("can't pause/seek/pace")}; |
890 | 0 | static const char einval[] = {N_("invalid state")}; |
891 | |
|
892 | 0 | switch (status) |
893 | 0 | { |
894 | 0 | case 0: |
895 | 0 | vlc_player_osd_Message(player, title); |
896 | 0 | break; |
897 | 0 | case -EAGAIN: |
898 | 0 | break; |
899 | 0 | case -ERANGE: |
900 | 0 | vlc_dialog_display_error(player, title_error, erange); |
901 | 0 | break; |
902 | 0 | case -EBUSY: |
903 | 0 | vlc_dialog_display_error(player, title_error, ebusy); |
904 | 0 | break; |
905 | 0 | case -ENOTSUP: |
906 | 0 | vlc_dialog_display_error(player, title_error, enotusp); |
907 | 0 | break; |
908 | 0 | default: |
909 | 0 | case -EINVAL: |
910 | 0 | vlc_dialog_display_error(player, title_error, einval); |
911 | 0 | break; |
912 | 0 | } |
913 | 0 | } |
914 | | |
915 | | static void |
916 | | vlc_player_input_FrameNextStatus(struct vlc_player_input *input, int status) |
917 | 0 | { |
918 | 0 | vlc_player_t *player = input->player; |
919 | |
|
920 | 0 | unsigned count; |
921 | 0 | vlc_player_SendEventCount(player, on_next_frame_status, count, status); |
922 | | |
923 | | /* Don't display errors if status is handled by the player user */ |
924 | 0 | if (count != 0) |
925 | 0 | return; |
926 | | |
927 | 0 | vlc_player_DisplayFrameError(player, _("Next frame"), |
928 | 0 | _("Next frame error"), status); |
929 | 0 | } |
930 | | |
931 | | static void |
932 | | vlc_player_input_FramePreviousStatus(struct vlc_player_input *input, int status) |
933 | 0 | { |
934 | 0 | vlc_player_t *player = input->player; |
935 | |
|
936 | 0 | unsigned count; |
937 | 0 | vlc_player_SendEventCount(player, on_prev_frame_status, count, status); |
938 | | |
939 | | /* Don't display errors if status is handled by the player user */ |
940 | 0 | if (count != 0) |
941 | 0 | return; |
942 | | |
943 | 0 | vlc_player_DisplayFrameError(player, _("Previous frame"), |
944 | 0 | _("Previous frame error"), status); |
945 | 0 | } |
946 | | |
947 | | static bool |
948 | | input_thread_Events(input_thread_t *input_thread, |
949 | | const struct vlc_input_event *event, void *user_data) |
950 | 0 | { |
951 | 0 | struct vlc_player_input *input = user_data; |
952 | 0 | vlc_player_t *player = input->player; |
953 | 0 | input_thread_private_t *priv = input_priv(input_thread); |
954 | |
|
955 | 0 | assert(input_thread == input->thread); |
956 | | |
957 | | /* No player lock for this event */ |
958 | 0 | if (event->type == INPUT_EVENT_OUTPUT_CLOCK) |
959 | 0 | { |
960 | 0 | if (event->output_clock.system_ts != VLC_TICK_INVALID) |
961 | 0 | { |
962 | 0 | const struct vlc_player_timer_point point = { |
963 | 0 | .position = 0, |
964 | 0 | .rate = event->output_clock.rate, |
965 | 0 | .ts = event->output_clock.ts, |
966 | 0 | .length = VLC_TICK_INVALID, |
967 | 0 | .system_date = event->output_clock.system_ts, |
968 | 0 | }; |
969 | 0 | vlc_player_UpdateTimer(player, event->output_clock.id, |
970 | 0 | event->output_clock.master, &point, |
971 | 0 | VLC_TICK_INVALID, |
972 | 0 | event->output_clock.frame_rate, |
973 | 0 | event->output_clock.frame_rate_base, 0); |
974 | 0 | } |
975 | 0 | else |
976 | 0 | { |
977 | 0 | vlc_player_UpdateTimerEvent(player, event->output_clock.id, |
978 | 0 | VLC_PLAYER_TIMER_EVENT_DISCONTINUITY, |
979 | 0 | VLC_TICK_INVALID); |
980 | 0 | } |
981 | 0 | return true; |
982 | 0 | } |
983 | | |
984 | 0 | bool handled = true; |
985 | |
|
986 | 0 | vlc_mutex_lock(&player->lock); |
987 | |
|
988 | 0 | switch (event->type) |
989 | 0 | { |
990 | 0 | case INPUT_EVENT_STATE: |
991 | 0 | vlc_player_input_HandleStateEvent(input, event->state.value, |
992 | 0 | event->state.date); |
993 | 0 | break; |
994 | 0 | case INPUT_EVENT_EOF: |
995 | 0 | handled = vlc_player_input_HandleAtoBLoop(input, true); |
996 | 0 | if (handled) |
997 | 0 | break; |
998 | 0 | if (player->play_and_pause) |
999 | 0 | { |
1000 | 0 | vlc_player_Pause(player); |
1001 | 0 | handled = true; |
1002 | 0 | } |
1003 | 0 | else if (input->repeat > 0) |
1004 | 0 | { |
1005 | 0 | input->repeat--; |
1006 | 0 | handled = |
1007 | 0 | input_ControlPush(input->thread, |
1008 | 0 | INPUT_CONTROL_RESET_POSITION, NULL) == 0; |
1009 | 0 | } |
1010 | 0 | else |
1011 | 0 | handled = false; |
1012 | 0 | break; |
1013 | 0 | case INPUT_EVENT_RATE: |
1014 | 0 | input->rate = event->rate; |
1015 | 0 | vlc_player_SignalAtoBLoop(player); |
1016 | 0 | vlc_player_SendEvent(player, on_rate_changed, input->rate); |
1017 | 0 | break; |
1018 | 0 | case INPUT_EVENT_CAPABILITIES: |
1019 | 0 | { |
1020 | 0 | int old_caps = input->capabilities; |
1021 | 0 | input->capabilities = event->capabilities; |
1022 | 0 | vlc_player_SendEvent(player, on_capabilities_changed, |
1023 | 0 | old_caps, input->capabilities); |
1024 | 0 | break; |
1025 | 0 | } |
1026 | 0 | case INPUT_EVENT_TIMES: |
1027 | 0 | { |
1028 | 0 | bool changed = false; |
1029 | 0 | vlc_tick_t system_date = VLC_TICK_INVALID; |
1030 | 0 | vlc_tick_t duration = input_GetItemDuration(input->thread, event->times.length); |
1031 | |
|
1032 | 0 | if (event->times.time != VLC_TICK_INVALID |
1033 | 0 | && (input->time != event->times.time |
1034 | 0 | || input->position != event->times.position)) |
1035 | 0 | { |
1036 | 0 | input->time = event->times.time; |
1037 | 0 | input->position = event->times.position; |
1038 | 0 | system_date = vlc_tick_now(); |
1039 | 0 | changed = true; |
1040 | 0 | vlc_player_SendEvent(player, on_position_changed, |
1041 | 0 | input->time, input->position); |
1042 | |
|
1043 | 0 | vlc_player_input_HandleAtoBLoop(input, false); |
1044 | 0 | } |
1045 | 0 | if (input->length != duration) |
1046 | 0 | { |
1047 | 0 | input->length = duration; |
1048 | 0 | input_item_SetDuration(input_GetItem(input->thread), duration); |
1049 | 0 | vlc_player_SendEvent(player, on_length_changed, input->length); |
1050 | 0 | changed = true; |
1051 | 0 | } |
1052 | |
|
1053 | 0 | if (input->live != event->times.live) |
1054 | 0 | { |
1055 | 0 | input->live = event->times.live; |
1056 | 0 | changed = true; |
1057 | 0 | } |
1058 | |
|
1059 | 0 | if (input->normal_time != event->times.normal_time) |
1060 | 0 | { |
1061 | 0 | input->normal_time = event->times.normal_time; |
1062 | 0 | changed = true; |
1063 | 0 | } |
1064 | |
|
1065 | 0 | if (changed) |
1066 | 0 | { |
1067 | 0 | const struct vlc_player_timer_point point = { |
1068 | 0 | .position = input->position, |
1069 | 0 | .rate = input->rate, |
1070 | 0 | .ts = input->time, |
1071 | 0 | .length = input->length, |
1072 | 0 | .live = input->live, |
1073 | 0 | .system_date = system_date, |
1074 | 0 | }; |
1075 | 0 | vlc_player_UpdateTimer(player, NULL, false, &point, |
1076 | 0 | input->normal_time, 0, 0, priv->i_start); |
1077 | 0 | vlc_player_SignalAtoBLoop(player); |
1078 | 0 | } |
1079 | 0 | break; |
1080 | 0 | } |
1081 | 0 | case INPUT_EVENT_PROGRAM: |
1082 | 0 | vlc_player_input_HandleProgramEvent(input, &event->program); |
1083 | 0 | break; |
1084 | 0 | case INPUT_EVENT_ES: |
1085 | 0 | vlc_player_input_HandleEsEvent(input, &event->es); |
1086 | 0 | break; |
1087 | 0 | case INPUT_EVENT_TITLE: |
1088 | 0 | vlc_player_input_HandleTitleEvent(input, &event->title); |
1089 | 0 | break; |
1090 | 0 | case INPUT_EVENT_CHAPTER: |
1091 | 0 | vlc_player_input_HandleChapterEvent(input, &event->chapter); |
1092 | 0 | break; |
1093 | 0 | case INPUT_EVENT_RECORD: |
1094 | 0 | input->recording = event->record; |
1095 | 0 | vlc_player_SendEvent(player, on_recording_changed, input->recording); |
1096 | 0 | break; |
1097 | 0 | case INPUT_EVENT_STATISTICS: |
1098 | 0 | input->stats = *event->stats; |
1099 | 0 | vlc_player_SendEvent(player, on_statistics_changed, &input->stats); |
1100 | 0 | break; |
1101 | 0 | case INPUT_EVENT_SIGNAL: |
1102 | 0 | input->signal_quality = event->signal.quality; |
1103 | 0 | input->signal_strength = event->signal.strength; |
1104 | 0 | vlc_player_SendEvent(player, on_signal_changed, |
1105 | 0 | input->signal_quality, input->signal_strength); |
1106 | 0 | break; |
1107 | 0 | case INPUT_EVENT_CACHE: |
1108 | 0 | if (event->cache == 0.0f) |
1109 | 0 | vlc_player_UpdateTimerEvent(player, NULL, |
1110 | 0 | VLC_PLAYER_TIMER_EVENT_DISCONTINUITY, |
1111 | 0 | VLC_TICK_INVALID); |
1112 | 0 | input->cache = event->cache; |
1113 | 0 | vlc_player_SendEvent(player, on_buffering_changed, event->cache); |
1114 | 0 | break; |
1115 | 0 | case INPUT_EVENT_VOUT: |
1116 | 0 | vlc_player_input_HandleVoutEvent(input, &event->vout); |
1117 | 0 | break; |
1118 | 0 | case INPUT_EVENT_OUTPUT_STATE: |
1119 | 0 | if (event->output_state.action == VLC_INPUT_EVENT_OUTPUT_STATE_PAUSED) |
1120 | 0 | vlc_player_UpdateTimerEvent(player, event->output_state.id, |
1121 | 0 | VLC_PLAYER_TIMER_EVENT_PAUSED, |
1122 | 0 | event->output_state.paused_date); |
1123 | 0 | break; |
1124 | 0 | case INPUT_EVENT_ITEM_META: |
1125 | 0 | case INPUT_EVENT_ITEM_INFO: |
1126 | 0 | vlc_player_SendEvent(player, on_media_meta_changed, |
1127 | 0 | input_GetItem(input->thread)); |
1128 | 0 | break; |
1129 | 0 | case INPUT_EVENT_ITEM_EPG: |
1130 | 0 | vlc_player_SendEvent(player, on_media_epg_changed, |
1131 | 0 | input_GetItem(input->thread)); |
1132 | 0 | break; |
1133 | 0 | case INPUT_EVENT_SUBITEMS: |
1134 | 0 | vlc_player_SendEvent(player, on_media_subitems_changed, |
1135 | 0 | input_GetItem(input->thread), event->subitems); |
1136 | 0 | input_item_node_Delete(event->subitems); |
1137 | 0 | break; |
1138 | 0 | case INPUT_EVENT_DEAD: |
1139 | 0 | if (input->started) /* Can happen with early input_thread fails */ |
1140 | 0 | { |
1141 | 0 | input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_ERROR; |
1142 | 0 | vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING, |
1143 | 0 | VLC_TICK_INVALID); |
1144 | 0 | } |
1145 | 0 | vlc_player_destructor_AddJoinableInput(player, input); |
1146 | 0 | break; |
1147 | 0 | case INPUT_EVENT_VBI_PAGE: |
1148 | 0 | input->teletext_page = event->vbi_page < 999 ? event->vbi_page : 100; |
1149 | 0 | vlc_player_SendEvent(player, on_teletext_page_changed, |
1150 | 0 | input->teletext_page); |
1151 | 0 | break; |
1152 | 0 | case INPUT_EVENT_VBI_TRANSPARENCY: |
1153 | 0 | input->teletext_transparent = event->vbi_transparent; |
1154 | 0 | vlc_player_SendEvent(player, on_teletext_transparency_changed, |
1155 | 0 | input->teletext_transparent); |
1156 | 0 | break; |
1157 | 0 | case INPUT_EVENT_ATTACHMENTS: |
1158 | 0 | vlc_player_SendEvent(player, on_media_attachments_added, |
1159 | 0 | input_GetItem(input->thread), |
1160 | 0 | event->attachments.array, |
1161 | 0 | event->attachments.count); |
1162 | 0 | break; |
1163 | 0 | case INPUT_EVENT_NAV_FAILED: |
1164 | 0 | vlc_player_input_NavigationFallback(input, event->nav_type); |
1165 | 0 | break; |
1166 | 0 | case INPUT_EVENT_MOUSE_LEFT: |
1167 | 0 | vlc_player_input_MouseFallback(input); |
1168 | 0 | break; |
1169 | 0 | case INPUT_EVENT_FRAME_NEXT_STATUS: |
1170 | 0 | vlc_player_input_FrameNextStatus(input, |
1171 | 0 | event->frame_next_status); |
1172 | 0 | break; |
1173 | 0 | case INPUT_EVENT_FRAME_PREVIOUS_STATUS: |
1174 | 0 | vlc_player_input_FramePreviousStatus(input, |
1175 | 0 | event->frame_previous_status); |
1176 | 0 | break; |
1177 | 0 | default: |
1178 | 0 | handled = false; |
1179 | 0 | break; |
1180 | 0 | } |
1181 | | |
1182 | 0 | vlc_mutex_unlock(&player->lock); |
1183 | 0 | return handled; |
1184 | 0 | } |
1185 | | |
1186 | | void |
1187 | | vlc_player_input_SelectTracksByStringIds(struct vlc_player_input *input, |
1188 | | enum es_format_category_e cat, |
1189 | | const char *str_ids) |
1190 | 0 | { |
1191 | 0 | input_SetEsCatIds(input->thread, cat, str_ids); |
1192 | 0 | } |
1193 | | |
1194 | | char * |
1195 | | vlc_player_input_GetSelectedTrackStringIds(struct vlc_player_input *input, |
1196 | | enum es_format_category_e cat) |
1197 | 0 | { |
1198 | 0 | vlc_player_track_vector *vec = vlc_player_input_GetTrackVector(input, cat); |
1199 | 0 | assert(vec); |
1200 | 0 | bool first_track = true; |
1201 | 0 | struct vlc_memstream ms; |
1202 | |
|
1203 | 0 | struct vlc_player_track_priv* t; |
1204 | 0 | vlc_vector_foreach(t, vec) |
1205 | 0 | { |
1206 | 0 | if (t->selected_by_user && vlc_es_id_IsStrIdStable(t->t.es_id)) |
1207 | 0 | { |
1208 | 0 | if (first_track) |
1209 | 0 | { |
1210 | 0 | int ret = vlc_memstream_open(&ms); |
1211 | 0 | if (ret != 0) |
1212 | 0 | return NULL; |
1213 | 0 | } |
1214 | 0 | const char *str_id = vlc_es_id_GetStrId(t->t.es_id); |
1215 | 0 | assert(str_id); |
1216 | |
|
1217 | 0 | if (!first_track) |
1218 | 0 | vlc_memstream_putc(&ms, ','); |
1219 | 0 | vlc_memstream_puts(&ms, str_id); |
1220 | 0 | first_track = false; |
1221 | 0 | } |
1222 | 0 | } |
1223 | 0 | return !first_track && vlc_memstream_close(&ms) == 0 ? ms.ptr : NULL; |
1224 | 0 | } |
1225 | | |
1226 | | struct vlc_player_input * |
1227 | | vlc_player_input_New(vlc_player_t *player, input_item_t *item) |
1228 | 0 | { |
1229 | 0 | struct vlc_player_input *input = malloc(sizeof(*input)); |
1230 | 0 | if (!input) |
1231 | 0 | return NULL; |
1232 | | |
1233 | 0 | input->player = player; |
1234 | 0 | input->started = false; |
1235 | 0 | input->playing = false; |
1236 | |
|
1237 | 0 | input->state = VLC_PLAYER_STATE_STOPPED; |
1238 | 0 | input->error = VLC_PLAYER_ERROR_NONE; |
1239 | 0 | input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_ERROR; |
1240 | 0 | input->rate = 1.f; |
1241 | 0 | input->capabilities = 0; |
1242 | 0 | input->length = input->time = VLC_TICK_INVALID; |
1243 | 0 | input->live = false; |
1244 | 0 | input->normal_time = VLC_TICK_0; |
1245 | 0 | input->pause_date = VLC_TICK_INVALID; |
1246 | 0 | input->position = 0.f; |
1247 | |
|
1248 | 0 | input->recording = false; |
1249 | |
|
1250 | 0 | input->cache = 0.f; |
1251 | 0 | input->signal_quality = input->signal_strength = -1.f; |
1252 | |
|
1253 | 0 | memset(&input->stats, 0, sizeof(input->stats)); |
1254 | |
|
1255 | 0 | vlc_vector_init(&input->program_vector); |
1256 | 0 | vlc_vector_init(&input->video_track_vector); |
1257 | 0 | vlc_vector_init(&input->audio_track_vector); |
1258 | 0 | vlc_vector_init(&input->spu_track_vector); |
1259 | 0 | input->teletext_source = NULL; |
1260 | |
|
1261 | 0 | input->titles = NULL; |
1262 | 0 | input->title_selected = input->chapter_selected = 0; |
1263 | |
|
1264 | 0 | input->teletext_enabled = input->teletext_transparent = false; |
1265 | 0 | input->teletext_page = 0; |
1266 | |
|
1267 | 0 | input->abloop_state[0].set = input->abloop_state[1].set = false; |
1268 | |
|
1269 | 0 | memset(&input->ml.states, 0, sizeof(input->ml.states)); |
1270 | 0 | input->ml.states.aspect_ratio = input->ml.states.crop = |
1271 | 0 | input->ml.states.deinterlace = input->ml.states.video_filter = NULL; |
1272 | 0 | input->ml.states.current_title = -1; |
1273 | 0 | input->ml.states.current_video_track = |
1274 | 0 | input->ml.states.current_audio_track = |
1275 | 0 | input->ml.states.current_subtitle_track = NULL; |
1276 | 0 | input->ml.restore = VLC_RESTOREPOINT_NONE; |
1277 | 0 | input->ml.restore_states = false; |
1278 | 0 | input->ml.delay_restore = false; |
1279 | 0 | input->ml.pos = -1.f; |
1280 | 0 | input->ml.has_audio_tracks = input->ml.has_video_tracks = false; |
1281 | |
|
1282 | 0 | static const struct vlc_input_thread_callbacks cbs = { |
1283 | 0 | .on_event = input_thread_Events, |
1284 | 0 | }; |
1285 | |
|
1286 | 0 | const struct vlc_input_thread_cfg cfg = { |
1287 | 0 | .type = INPUT_TYPE_PLAYBACK, |
1288 | 0 | .hw_dec = INPUT_CFG_HW_DEC_DEFAULT, |
1289 | 0 | .resource = player->resource, |
1290 | 0 | .renderer = player->renderer, |
1291 | 0 | .cbs = &cbs, |
1292 | 0 | .cbs_data = input, |
1293 | 0 | }; |
1294 | |
|
1295 | 0 | input->repeat = player->repeat; |
1296 | |
|
1297 | 0 | input->thread = input_Create(player, item, &cfg); |
1298 | 0 | if (!input->thread) |
1299 | 0 | { |
1300 | 0 | free(input); |
1301 | 0 | return NULL; |
1302 | 0 | } |
1303 | 0 | vlc_player_input_RestoreMlStates(input, false); |
1304 | |
|
1305 | 0 | if (player->video_string_ids) |
1306 | 0 | vlc_player_input_SelectTracksByStringIds(input, VIDEO_ES, |
1307 | 0 | player->video_string_ids); |
1308 | |
|
1309 | 0 | if (player->audio_string_ids) |
1310 | 0 | vlc_player_input_SelectTracksByStringIds(input, AUDIO_ES, |
1311 | 0 | player->audio_string_ids); |
1312 | |
|
1313 | 0 | if (player->sub_string_ids) |
1314 | 0 | vlc_player_input_SelectTracksByStringIds(input, SPU_ES, |
1315 | 0 | player->sub_string_ids); |
1316 | | |
1317 | | /* Initial sub/audio delay */ |
1318 | 0 | const vlc_tick_t cat_delays[DATA_ES] = { |
1319 | 0 | [AUDIO_ES] = |
1320 | 0 | VLC_TICK_FROM_MS(var_InheritInteger(player, "audio-desync")), |
1321 | 0 | [SPU_ES] = |
1322 | 0 | vlc_tick_from_samples(var_InheritInteger(player, "sub-delay"), 10), |
1323 | 0 | }; |
1324 | |
|
1325 | 0 | for (enum es_format_category_e i = UNKNOWN_ES; i < DATA_ES; ++i) |
1326 | 0 | { |
1327 | 0 | input->cat_delays[i] = cat_delays[i]; |
1328 | 0 | if (cat_delays[i] != 0) |
1329 | 0 | { |
1330 | 0 | int ret = input_SetEsCatDelay(input->thread, i, cat_delays[i]); |
1331 | 0 | if (ret == VLC_SUCCESS) |
1332 | 0 | vlc_player_SendEvent(player, on_category_delay_changed, i, |
1333 | 0 | cat_delays[i]); |
1334 | 0 | } |
1335 | 0 | } |
1336 | 0 | return input; |
1337 | 0 | } |
1338 | | |
1339 | | void |
1340 | | vlc_player_input_Delete(struct vlc_player_input *input) |
1341 | 0 | { |
1342 | 0 | assert(input->titles == NULL); |
1343 | 0 | assert(input->program_vector.size == 0); |
1344 | 0 | assert(input->video_track_vector.size == 0); |
1345 | 0 | assert(input->audio_track_vector.size == 0); |
1346 | 0 | assert(input->spu_track_vector.size == 0); |
1347 | 0 | assert(input->teletext_source == NULL); |
1348 | |
|
1349 | 0 | vlc_vector_destroy(&input->program_vector); |
1350 | 0 | vlc_vector_destroy(&input->video_track_vector); |
1351 | 0 | vlc_vector_destroy(&input->audio_track_vector); |
1352 | 0 | vlc_vector_destroy(&input->spu_track_vector); |
1353 | |
|
1354 | 0 | input_Close(input->thread); |
1355 | 0 | free(input); |
1356 | 0 | } |