Coverage Report

Created: 2026-01-17 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
                          &param);
182
0
    else
183
0
        input_ControlPush(input->thread, INPUT_CONTROL_UPDATE_VIEWPOINT,
184
0
                          &param);
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
}