Coverage Report

Created: 2026-05-21 08:06

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_SignalAtoBLoop(player);
300
0
            vlc_player_UpdateTimerEvent(player, NULL,
301
0
                                        VLC_PLAYER_TIMER_EVENT_PLAYING,
302
0
                                        input->pause_date);
303
            /* fall through */
304
0
        case VLC_PLAYER_STATE_STARTED:
305
0
            if (player->started &&
306
0
                player->global_state == VLC_PLAYER_STATE_PLAYING)
307
0
                send_event = false;
308
0
            break;
309
310
0
        case VLC_PLAYER_STATE_PAUSED:
311
0
            assert(player->global_state == VLC_PLAYER_STATE_PLAYING);
312
0
            assert(state_date != VLC_TICK_INVALID);
313
0
            input->pause_date = state_date;
314
315
0
            vlc_player_SignalAtoBLoop(player);
316
0
            vlc_player_UpdateTimerEvent(player, NULL,
317
0
                                        VLC_PLAYER_TIMER_EVENT_PAUSED,
318
0
                                        input->pause_date);
319
0
            break;
320
0
        default:
321
0
            vlc_assert_unreachable();
322
0
    }
323
324
0
    if (send_event)
325
0
    {
326
0
        assert(player->global_state != input->state);
327
0
        player->global_state = input->state;
328
0
        vlc_player_SendEvent(player, on_state_changed, player->global_state);
329
0
    }
330
0
}
331
332
static void
333
vlc_player_input_HandleStateEvent(struct vlc_player_input *input,
334
                                  input_state_e state, vlc_tick_t state_date)
335
0
{
336
0
    switch (state)
337
0
    {
338
0
        case OPENING_S:
339
0
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED,
340
0
                                         VLC_TICK_INVALID);
341
0
            break;
342
0
        case PLAYING_S:
343
0
            input->playing = true;
344
0
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING,
345
0
                                         state_date);
346
0
            break;
347
0
        case PAUSE_S:
348
0
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED,
349
0
                                         state_date);
350
0
            break;
351
0
        case END_S:
352
0
            input->playing = false;
353
0
            input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_EOS;
354
0
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
355
0
                                         VLC_TICK_INVALID);
356
0
            vlc_player_destructor_AddStoppingInput(input->player, input);
357
0
            break;
358
0
        case ERROR_S:
359
            /* Don't send errors if the input is stopped by the user */
360
0
            if (input->started)
361
0
            {
362
                /* Contrary to the input_thead_t, an error is not a state */
363
0
                input->error = VLC_PLAYER_ERROR_GENERIC;
364
0
                vlc_player_SendEvent(input->player, on_error_changed, input->error);
365
0
            }
366
            /* input->playing monitor the OPENING_S -> PLAYING_S transition.
367
             * If input->playing is false, we have an error at the opening of
368
             * the input thread and we won't reach END_S. */
369
0
            if (!input->playing)
370
0
            {
371
0
                input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_ERROR;
372
0
                vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
373
0
                                             VLC_TICK_INVALID);
374
0
                vlc_player_destructor_AddStoppingInput(input->player, input);
375
0
            }
376
0
            break;
377
0
        default:
378
0
            vlc_assert_unreachable();
379
0
    }
380
0
}
381
382
static void
383
vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
384
                                    const struct vlc_input_event_program *ev)
385
0
{
386
0
    vlc_player_t *player = input->player;
387
0
    struct vlc_player_program *prgm;
388
0
    vlc_player_program_vector *vec = &input->program_vector;
389
390
0
    switch (ev->action)
391
0
    {
392
0
        case VLC_INPUT_PROGRAM_ADDED:
393
0
            prgm = vlc_player_program_New(ev->id, ev->title);
394
0
            if (!prgm)
395
0
                break;
396
397
0
            if (!vlc_vector_push(vec, prgm))
398
0
            {
399
0
                vlc_player_program_Delete(prgm);
400
0
                break;
401
0
            }
402
0
            vlc_player_SendEvent(player, on_program_list_changed,
403
0
                                 VLC_PLAYER_LIST_ADDED, prgm);
404
0
            break;
405
0
        case VLC_INPUT_PROGRAM_DELETED:
406
0
        {
407
0
            size_t idx;
408
0
            prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx);
409
0
            if (prgm)
410
0
            {
411
0
                vlc_player_SendEvent(player, on_program_list_changed,
412
0
                                     VLC_PLAYER_LIST_REMOVED, prgm);
413
0
                vlc_vector_remove(vec, idx);
414
0
                vlc_player_program_Delete(prgm);
415
0
            }
416
0
            break;
417
0
        }
418
0
        case VLC_INPUT_PROGRAM_UPDATED:
419
0
        case VLC_INPUT_PROGRAM_SCRAMBLED:
420
0
            prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL);
421
0
            if (!prgm)
422
0
                break;
423
0
            if (ev->action == VLC_INPUT_PROGRAM_UPDATED)
424
0
            {
425
0
                if (vlc_player_program_Update(prgm, ev->id, ev->title) != 0)
426
0
                    break;
427
0
            }
428
0
            else
429
0
                prgm->scrambled = ev->scrambled;
430
0
            vlc_player_SendEvent(player, on_program_list_changed,
431
0
                                 VLC_PLAYER_LIST_UPDATED, prgm);
432
0
            break;
433
0
        case VLC_INPUT_PROGRAM_SELECTED:
434
0
        {
435
0
            int unselected_id = -1, selected_id = -1;
436
0
            vlc_vector_foreach(prgm, vec)
437
0
            {
438
0
                if (prgm->group_id == ev->id)
439
0
                {
440
0
                    if (!prgm->selected)
441
0
                    {
442
0
                        assert(selected_id == -1);
443
0
                        prgm->selected = true;
444
0
                        selected_id = prgm->group_id;
445
0
                    }
446
0
                }
447
0
                else
448
0
                {
449
0
                    if (prgm->selected)
450
0
                    {
451
0
                        assert(unselected_id == -1);
452
0
                        prgm->selected = false;
453
0
                        unselected_id = prgm->group_id;
454
0
                    }
455
0
                }
456
0
            }
457
0
            if (unselected_id != -1 || selected_id != -1)
458
0
                vlc_player_SendEvent(player, on_program_selection_changed,
459
0
                                     unselected_id, selected_id);
460
0
            break;
461
0
        }
462
0
        default:
463
0
            vlc_assert_unreachable();
464
0
    }
465
0
}
466
467
static const struct vlc_player_track_priv *
468
vlc_player_FindTeletextSource(const struct vlc_player_input *input,
469
                              const struct vlc_player_track_priv *exclude,
470
                              bool selected)
471
0
{
472
0
    const struct vlc_player_track_priv *t;
473
0
    vlc_vector_foreach(t, &input->spu_track_vector)
474
0
    {
475
0
        if (t->t.fmt.i_codec == VLC_CODEC_TELETEXT &&
476
0
           t != exclude &&
477
0
           t->t.selected == selected)
478
0
            return t;
479
0
    }
480
0
    return NULL;
481
0
}
482
483
static unsigned
484
vlc_player_input_TeletextUserPage(const struct vlc_player_track_priv *t)
485
0
{
486
0
    const uint8_t mag = t->t.fmt.subs.teletext.i_magazine;
487
0
    const uint8_t page = t->t.fmt.subs.teletext.i_page;
488
0
    return (mag % 10) * 100 +
489
0
           (page & 0x0F) + ((page >> 4) & 0x0F) * 10;
490
0
}
491
492
static void
493
vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input,
494
                                    const struct vlc_input_event_es *ev,
495
                                    const struct vlc_player_track_priv *trackpriv)
496
0
{
497
0
    vlc_player_t *player = input->player;
498
0
    if (ev->fmt->i_cat != SPU_ES ||
499
0
        ev->fmt->i_codec != VLC_CODEC_TELETEXT)
500
0
        return;
501
0
    switch (ev->action)
502
0
    {
503
0
        case VLC_INPUT_ES_ADDED:
504
0
        {
505
0
            if (!input->teletext_source)
506
0
            {
507
0
                input->teletext_source = trackpriv;
508
0
                vlc_player_SendEvent(player, on_teletext_menu_changed, true);
509
0
            }
510
0
            break;
511
0
        }
512
0
        case VLC_INPUT_ES_DELETED:
513
0
        {
514
0
            if (input->teletext_source == trackpriv)
515
0
            {
516
0
                input->teletext_source =
517
0
                        vlc_player_FindTeletextSource(input, trackpriv, true);
518
0
                if (!input->teletext_source)
519
0
                    input->teletext_source =
520
0
                            vlc_player_FindTeletextSource(input, trackpriv, false);
521
0
                if (!input->teletext_source) /* no more teletext ES */
522
0
                {
523
0
                    if (input->teletext_enabled)
524
0
                    {
525
0
                        input->teletext_enabled = false;
526
0
                        vlc_player_SendEvent(player, on_teletext_enabled_changed, false);
527
0
                    }
528
0
                    vlc_player_SendEvent(player, on_teletext_menu_changed, false);
529
0
                }
530
0
                else /* another teletext ES was reselected */
531
0
                {
532
0
                    if (input->teletext_source->t.selected != input->teletext_enabled)
533
0
                    {
534
0
                        input->teletext_enabled = input->teletext_source->t.selected;
535
0
                        vlc_player_SendEvent(player, on_teletext_enabled_changed,
536
0
                                             input->teletext_source->t.selected);
537
0
                    }
538
0
                    input->teletext_page =
539
0
                            vlc_player_input_TeletextUserPage(input->teletext_source);
540
0
                    vlc_player_SendEvent(player, on_teletext_page_changed,
541
0
                                         input->teletext_page);
542
0
                }
543
0
            }
544
0
            break;
545
0
        }
546
0
        case VLC_INPUT_ES_UPDATED:
547
0
            break;
548
0
        case VLC_INPUT_ES_SELECTED:
549
0
        {
550
0
            if (!input->teletext_enabled) /* we stick with the first selected */
551
0
            {
552
0
                input->teletext_source = trackpriv;
553
0
                input->teletext_enabled = true;
554
0
                input->teletext_page = vlc_player_input_TeletextUserPage(trackpriv);
555
0
                vlc_player_SendEvent(player, on_teletext_enabled_changed, true);
556
0
                vlc_player_SendEvent(player, on_teletext_page_changed,
557
0
                                     input->teletext_page);
558
0
            }
559
0
            break;
560
0
        }
561
0
        case VLC_INPUT_ES_UNSELECTED:
562
0
            if (input->teletext_source == trackpriv)
563
0
            {
564
                /* If there's another selected teletext, it needs to become source */
565
0
                const struct vlc_player_track_priv *other =
566
0
                        vlc_player_FindTeletextSource(input, trackpriv, true);
567
0
                if (other)
568
0
                {
569
0
                    input->teletext_source = other;
570
0
                    if (!input->teletext_enabled)
571
0
                    {
572
0
                        input->teletext_enabled = true;
573
0
                        vlc_player_SendEvent(player, on_teletext_enabled_changed, true);
574
0
                    }
575
0
                    input->teletext_page = vlc_player_input_TeletextUserPage(other);
576
0
                    vlc_player_SendEvent(player, on_teletext_page_changed,
577
0
                                         input->teletext_page);
578
0
                }
579
0
                else
580
0
                {
581
0
                    input->teletext_enabled = false;
582
0
                    vlc_player_SendEvent(player, on_teletext_enabled_changed, false);
583
0
                }
584
0
            }
585
0
            break;
586
0
        default:
587
0
            vlc_assert_unreachable();
588
0
    }
589
0
}
590
591
static void
592
vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
593
                               const struct vlc_input_event_es *ev)
594
0
{
595
0
    assert(ev->id && ev->title && ev->fmt);
596
597
0
    vlc_player_track_vector *vec =
598
0
        vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
599
0
    if (!vec)
600
0
        return; /* UNKNOWN_ES or DATA_ES not handled */
601
602
0
    vlc_player_t *player = input->player;
603
0
    struct vlc_player_track_priv *trackpriv;
604
0
    switch (ev->action)
605
0
    {
606
0
        case VLC_INPUT_ES_ADDED:
607
0
            trackpriv = vlc_player_track_priv_New(ev->id, ev->title, ev->fmt);
608
0
            if (!trackpriv)
609
0
                break;
610
611
0
            if (ev->fmt->i_cat == VIDEO_ES)
612
0
                input->ml.has_video_tracks = true;
613
0
            else if (ev->fmt->i_cat == AUDIO_ES)
614
0
                input->ml.has_audio_tracks = true;
615
0
            if (!vlc_vector_push(vec, trackpriv))
616
0
            {
617
0
                vlc_player_track_priv_Delete(trackpriv);
618
0
                break;
619
0
            }
620
0
            vlc_player_SendEvent(player, on_track_list_changed,
621
0
                                 VLC_PLAYER_LIST_ADDED, &trackpriv->t);
622
0
            vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
623
0
            break;
624
0
        case VLC_INPUT_ES_DELETED:
625
0
        {
626
0
            size_t idx;
627
0
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, &idx);
628
0
            if (trackpriv)
629
0
            {
630
0
                vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
631
0
                vlc_player_SendEvent(player, on_track_list_changed,
632
0
                                     VLC_PLAYER_LIST_REMOVED, &trackpriv->t);
633
0
                vlc_vector_remove(vec, idx);
634
0
                vlc_player_track_priv_Delete(trackpriv);
635
0
            }
636
0
            break;
637
0
        }
638
0
        case VLC_INPUT_ES_UPDATED:
639
0
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
640
0
            if (!trackpriv)
641
0
                break;
642
0
            if (vlc_player_track_priv_Update(trackpriv, ev->title, ev->fmt) != 0)
643
0
                break;
644
0
            vlc_player_SendEvent(player, on_track_list_changed,
645
0
                                 VLC_PLAYER_LIST_UPDATED, &trackpriv->t);
646
0
            vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
647
0
            break;
648
0
        case VLC_INPUT_ES_SELECTED:
649
0
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
650
0
            if (trackpriv)
651
0
            {
652
0
                trackpriv->t.selected = true;
653
0
                trackpriv->selected_by_user = ev->forced;
654
0
                trackpriv->vout_order = ev->vout_order;
655
0
                vlc_player_SendEvent(player, on_track_selection_changed,
656
0
                                     NULL, trackpriv->t.es_id);
657
0
                vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
658
0
            }
659
0
            break;
660
0
        case VLC_INPUT_ES_UNSELECTED:
661
0
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
662
0
            if (trackpriv)
663
0
            {
664
0
                vlc_player_RemoveTimerSource(player, ev->id);
665
0
                trackpriv->t.selected = false;
666
0
                trackpriv->selected_by_user = false;
667
0
                vlc_player_SendEvent(player, on_track_selection_changed,
668
0
                                     trackpriv->t.es_id, NULL);
669
0
                vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
670
0
            }
671
0
            break;
672
0
        default:
673
0
            vlc_assert_unreachable();
674
0
    }
675
0
}
676
677
static void
678
vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
679
                                  const struct vlc_input_event_title *ev)
680
0
{
681
0
    vlc_player_t *player = input->player;
682
0
    switch (ev->action)
683
0
    {
684
0
        case VLC_INPUT_TITLE_NEW_LIST:
685
0
        {
686
0
            input_thread_private_t *input_th = input_priv(input->thread);
687
0
            const int title_offset = input_th->i_title_offset;
688
0
            const int chapter_offset = input_th->i_seekpoint_offset;
689
690
0
            if (input->titles)
691
0
                vlc_player_title_list_Release(input->titles);
692
0
            input->title_selected = input->chapter_selected = 0;
693
0
            input->titles =
694
0
                vlc_player_title_list_Create(ev->list.array, ev->list.count,
695
0
                                             title_offset, chapter_offset);
696
0
            vlc_player_SendEvent(player, on_titles_changed, input->titles);
697
0
            if (input->titles)
698
0
            {
699
0
                vlc_player_SendEvent(player, on_title_selection_changed,
700
0
                                     &input->titles->array[0], 0);
701
0
                if (input->ml.restore == VLC_RESTOREPOINT_TITLE &&
702
0
                    (size_t)input->ml.states.current_title < ev->list.count)
703
0
                {
704
0
                    vlc_player_SelectTitleIdx(player, input->ml.states.current_title);
705
0
                }
706
0
                input->ml.restore = VLC_RESTOREPOINT_POSITION;
707
0
            }
708
0
            else input->ml.restore = VLC_RESTOREPOINT_NONE;
709
0
            break;
710
0
        }
711
0
        case VLC_INPUT_TITLE_SELECTED:
712
0
            if (!input->titles)
713
0
                return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
714
0
            assert(ev->selected_idx < input->titles->count);
715
0
            input->title_selected = ev->selected_idx;
716
0
            vlc_player_SendEvent(player, on_title_selection_changed,
717
0
                                 &input->titles->array[input->title_selected],
718
0
                                 input->title_selected);
719
0
            if (input->ml.restore == VLC_RESTOREPOINT_POSITION &&
720
0
                input->ml.states.current_title >= 0 &&
721
0
                (size_t)input->ml.states.current_title == ev->selected_idx &&
722
0
                input->ml.pos > .0f)
723
0
            {
724
0
                input_SetPosition(input->thread, input->ml.pos, false);
725
0
            }
726
            /* Reset the wanted title to avoid forcing it or the position
727
             * again during the next title change
728
             */
729
0
            input->ml.restore = VLC_RESTOREPOINT_NONE;
730
0
            break;
731
0
        default:
732
0
            vlc_assert_unreachable();
733
0
    }
734
0
}
735
736
static void
737
vlc_player_input_HandleChapterEvent(struct vlc_player_input *input,
738
                                    const struct vlc_input_event_chapter *ev)
739
0
{
740
0
    vlc_player_t *player = input->player;
741
0
    if (!input->titles || ev->title < 0 || ev->seekpoint < 0)
742
0
        return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
743
744
0
    assert((size_t)ev->title < input->titles->count);
745
0
    const struct vlc_player_title *title = &input->titles->array[ev->title];
746
0
    if (!title->chapter_count)
747
0
        return;
748
749
0
    assert(ev->seekpoint < (int)title->chapter_count);
750
0
    input->title_selected = ev->title;
751
0
    input->chapter_selected = ev->seekpoint;
752
753
0
    const struct vlc_player_chapter *chapter = &title->chapters[ev->seekpoint];
754
0
    vlc_player_SendEvent(player, on_chapter_selection_changed, title, ev->title,
755
0
                         chapter, ev->seekpoint);
756
0
}
757
758
static void
759
vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
760
                                 const struct vlc_input_event_vout *ev)
761
0
{
762
0
    assert(ev->vout);
763
0
    assert(ev->id);
764
765
0
    vlc_player_t *player = input->player;
766
767
0
    struct vlc_player_track_priv *trackpriv =
768
0
        vlc_player_input_FindTrackById(input, ev->id, NULL);
769
0
    if (!trackpriv)
770
0
        return;
771
772
0
    const bool is_video_es = trackpriv->t.fmt.i_cat == VIDEO_ES;
773
774
0
    switch (ev->action)
775
0
    {
776
0
        case VLC_INPUT_EVENT_VOUT_STARTED:
777
0
            trackpriv->vout = ev->vout;
778
0
            vlc_player_SendEvent(player, on_vout_changed,
779
0
                                 VLC_PLAYER_VOUT_STARTED, ev->vout,
780
0
                                 ev->order, ev->id);
781
782
0
            if (is_video_es)
783
0
            {
784
                /* Register vout callbacks after the vout list event */
785
0
                vlc_player_vout_AddCallbacks(player, ev->vout);
786
0
            }
787
0
            break;
788
0
        case VLC_INPUT_EVENT_VOUT_STOPPED:
789
0
            if (is_video_es)
790
0
            {
791
                /* Un-register vout callbacks before the vout list event */
792
0
                vlc_player_vout_DelCallbacks(player, ev->vout);
793
0
            }
794
795
0
            trackpriv->vout = NULL;
796
0
            vlc_player_SendEvent(player, on_vout_changed,
797
0
                                 VLC_PLAYER_VOUT_STOPPED, ev->vout,
798
0
                                 VLC_VOUT_ORDER_NONE, ev->id);
799
0
            break;
800
0
        default:
801
0
            vlc_assert_unreachable();
802
0
    }
803
0
}
804
805
static void
806
vlc_player_input_NavigationFallback(struct vlc_player_input *input, int nav_type)
807
0
{
808
0
    vlc_player_t *player = input->player;
809
810
    /* Handle Up/Down/Left/Right if the demux can't navigate */
811
0
    vlc_viewpoint_t vp = { 0 };
812
0
    bool viewpoint_updated = true;
813
0
    float yaw = 0.f, pitch = 0.f, roll = 0.f;
814
815
0
    int vol_direction = 0;
816
0
    int seek_direction = 0;
817
0
    switch (nav_type)
818
0
    {
819
0
        case INPUT_CONTROL_NAV_UP:
820
0
            vol_direction = 1;
821
0
            pitch = -1.f;
822
0
            break;
823
0
        case INPUT_CONTROL_NAV_DOWN:
824
0
            vol_direction = -1;
825
0
            pitch = 1.f;
826
0
            break;
827
0
        case INPUT_CONTROL_NAV_LEFT:
828
0
            seek_direction = -1;
829
0
            yaw = -1.f;
830
0
            break;
831
0
        case INPUT_CONTROL_NAV_RIGHT:
832
0
            seek_direction = 1;
833
0
            yaw = 1.f;
834
0
            break;
835
0
        case INPUT_CONTROL_NAV_ACTIVATE:
836
0
        case INPUT_CONTROL_NAV_POPUP:
837
0
        case INPUT_CONTROL_NAV_MENU:
838
0
            viewpoint_updated = false;
839
0
            return;
840
0
        default:
841
0
            vlc_assert_unreachable();
842
0
    }
843
844
    /* Try to change the viewpoint if possible */
845
0
    bool viewpoint_ch = false;
846
0
    size_t vout_count;
847
0
    vout_thread_t **vouts = vlc_player_vout_HoldAll(input->player, &vout_count);
848
0
    for (size_t i = 0; i < vout_count; ++i)
849
0
    {
850
0
        if (!viewpoint_ch && var_GetBool(vouts[i], "viewpoint-changeable"))
851
0
            viewpoint_ch = true;
852
0
        vout_Release(vouts[i]);
853
0
    }
854
0
    free(vouts);
855
856
0
    if (viewpoint_ch && viewpoint_updated)
857
0
    {
858
0
        vlc_viewpoint_from_euler(&vp, yaw, pitch, roll);
859
0
        vlc_player_input_UpdateViewpoint(input, &vp, VLC_PLAYER_WHENCE_RELATIVE);
860
0
    }
861
0
    else if (seek_direction != 0)
862
0
    {
863
        /* Seek or change volume if the input doesn't have navigation or viewpoint */
864
0
        vlc_tick_t it = vlc_tick_from_sec(seek_direction
865
0
                      * var_InheritInteger(player, "short-jump-size"));
866
0
        vlc_player_input_SeekByTime(input, it, VLC_PLAYER_SEEK_PRECISE,
867
0
                                    VLC_PLAYER_WHENCE_RELATIVE);
868
0
    }
869
0
    else
870
0
    {
871
0
        assert(vol_direction != 0);
872
0
        if (input == player->input)
873
0
            vlc_player_aout_IncrementVolume(player, vol_direction, NULL);
874
0
    }
875
0
}
876
877
static void
878
vlc_player_input_MouseFallback(struct vlc_player_input *input)
879
0
{
880
0
    vlc_player_t *player = input->player;
881
0
    vlc_player_TogglePause(player);
882
0
}
883
884
static void
885
vlc_player_DisplayFrameError(vlc_player_t *player,
886
                             const char *title, const char *title_error,
887
                             int status)
888
0
{
889
0
    static const char erange[] = {N_("can't seek back")};
890
0
    static const char ebusy[] = {N_("no video found")};
891
0
    static const char enotusp[] = {N_("can't pause/seek/pace")};
892
0
    static const char einval[] = {N_("invalid state")};
893
894
0
    switch (status)
895
0
    {
896
0
        case 0:
897
0
            vlc_player_osd_Message(player, title);
898
0
            break;
899
0
        case -EAGAIN:
900
0
            break;
901
0
        case -ERANGE:
902
0
            vlc_dialog_display_error(player, title_error, erange);
903
0
            break;
904
0
        case -EBUSY:
905
0
            vlc_dialog_display_error(player, title_error, ebusy);
906
0
            break;
907
0
        case -ENOTSUP:
908
0
            vlc_dialog_display_error(player, title_error, enotusp);
909
0
            break;
910
0
        default:
911
0
        case -EINVAL:
912
0
            vlc_dialog_display_error(player, title_error, einval);
913
0
            break;
914
0
    }
915
0
}
916
917
static void
918
vlc_player_input_FrameNextStatus(struct vlc_player_input *input, int status)
919
0
{
920
0
    vlc_player_t *player = input->player;
921
922
0
    unsigned count;
923
0
    vlc_player_SendEventCount(player, on_next_frame_status, count, status);
924
925
    /* Don't display errors if status is handled by the player user */
926
0
    if (count != 0)
927
0
        return;
928
929
0
    vlc_player_DisplayFrameError(player, _("Next frame"),
930
0
                                 _("Next frame error"), status);
931
0
}
932
933
static void
934
vlc_player_input_FramePreviousStatus(struct vlc_player_input *input, int status)
935
0
{
936
0
    vlc_player_t *player = input->player;
937
938
0
    unsigned count;
939
0
    vlc_player_SendEventCount(player, on_prev_frame_status, count, status);
940
941
    /* Don't display errors if status is handled by the player user */
942
0
    if (count != 0)
943
0
        return;
944
945
0
    vlc_player_DisplayFrameError(player, _("Previous frame"),
946
0
                                 _("Previous frame error"), status);
947
0
}
948
949
static bool
950
input_thread_Events(input_thread_t *input_thread,
951
                    const struct vlc_input_event *event, void *user_data)
952
0
{
953
0
    struct vlc_player_input *input = user_data;
954
0
    vlc_player_t *player = input->player;
955
0
    input_thread_private_t *priv = input_priv(input_thread);
956
957
0
    assert(input_thread == input->thread);
958
959
    /* No player lock for this event */
960
0
    if (event->type == INPUT_EVENT_OUTPUT_CLOCK)
961
0
    {
962
0
        if (event->output_clock.system_ts != VLC_TICK_INVALID)
963
0
        {
964
0
            const struct vlc_player_timer_point point = {
965
0
                .position = 0,
966
0
                .rate = event->output_clock.rate,
967
0
                .ts = event->output_clock.ts,
968
0
                .length = VLC_TICK_INVALID,
969
0
                .system_date = event->output_clock.system_ts,
970
0
            };
971
0
            vlc_player_UpdateTimer(player, event->output_clock.id,
972
0
                                   event->output_clock.master, &point,
973
0
                                   VLC_TICK_INVALID,
974
0
                                   event->output_clock.frame_rate,
975
0
                                   event->output_clock.frame_rate_base, 0);
976
0
        }
977
0
        else
978
0
        {
979
0
            vlc_player_UpdateTimerEvent(player, event->output_clock.id,
980
0
                                        VLC_PLAYER_TIMER_EVENT_DISCONTINUITY,
981
0
                                        VLC_TICK_INVALID);
982
0
        }
983
0
        return true;
984
0
    }
985
986
0
    bool handled = true;
987
988
0
    vlc_mutex_lock(&player->lock);
989
990
0
    switch (event->type)
991
0
    {
992
0
        case INPUT_EVENT_STATE:
993
0
            vlc_player_input_HandleStateEvent(input, event->state.value,
994
0
                                              event->state.date);
995
0
            break;
996
0
        case INPUT_EVENT_EOF:
997
0
            handled = vlc_player_input_HandleAtoBLoop(input, true);
998
0
            if (handled)
999
0
                break;
1000
0
            if (player->play_and_pause)
1001
0
            {
1002
0
                vlc_player_Pause(player);
1003
0
                handled = true;
1004
0
            }
1005
0
            else if (input->repeat > 0)
1006
0
            {
1007
0
                input->repeat--;
1008
0
                handled =
1009
0
                    input_ControlPush(input->thread,
1010
0
                                      INPUT_CONTROL_RESET_POSITION, NULL) == 0;
1011
0
            }
1012
0
            else
1013
0
                handled = false;
1014
0
            break;
1015
0
        case INPUT_EVENT_RATE:
1016
0
            input->rate = event->rate;
1017
0
            vlc_player_SignalAtoBLoop(player);
1018
0
            vlc_player_SendEvent(player, on_rate_changed, input->rate);
1019
0
            break;
1020
0
        case INPUT_EVENT_CAPABILITIES:
1021
0
        {
1022
0
            int old_caps = input->capabilities;
1023
0
            input->capabilities = event->capabilities;
1024
0
            vlc_player_SendEvent(player, on_capabilities_changed,
1025
0
                                 old_caps, input->capabilities);
1026
0
            break;
1027
0
        }
1028
0
        case INPUT_EVENT_TIMES:
1029
0
        {
1030
0
            bool changed = false;
1031
0
            vlc_tick_t system_date = VLC_TICK_INVALID;
1032
0
            vlc_tick_t duration = input_GetItemDuration(input->thread, event->times.length);
1033
1034
0
            if (event->times.time != VLC_TICK_INVALID
1035
0
             && (input->time != event->times.time
1036
0
              || input->position != event->times.position))
1037
0
            {
1038
0
                input->time = event->times.time;
1039
0
                input->position = event->times.position;
1040
0
                system_date = vlc_tick_now();
1041
0
                changed = true;
1042
0
                vlc_player_SendEvent(player, on_position_changed,
1043
0
                                     input->time, input->position);
1044
1045
0
                vlc_player_input_HandleAtoBLoop(input, false);
1046
0
            }
1047
0
            if (input->length != duration)
1048
0
            {
1049
0
                input->length = duration;
1050
0
                input_item_SetDuration(input_GetItem(input->thread), duration);
1051
0
                vlc_player_SendEvent(player, on_length_changed, input->length);
1052
0
                changed = true;
1053
0
            }
1054
1055
0
            if (input->live != event->times.live)
1056
0
            {
1057
0
                input->live = event->times.live;
1058
0
                changed = true;
1059
0
            }
1060
1061
0
            if (input->normal_time != event->times.normal_time)
1062
0
            {
1063
0
                input->normal_time = event->times.normal_time;
1064
0
                changed = true;
1065
0
            }
1066
1067
0
            if (changed)
1068
0
            {
1069
0
                const struct vlc_player_timer_point point = {
1070
0
                    .position = input->position,
1071
0
                    .rate = input->rate,
1072
0
                    .ts = input->time,
1073
0
                    .length = input->length,
1074
0
                    .live = input->live,
1075
0
                    .system_date = system_date,
1076
0
                };
1077
0
                vlc_player_UpdateTimer(player, NULL, false, &point,
1078
0
                                       input->normal_time, 0, 0, priv->i_start);
1079
0
                vlc_player_SignalAtoBLoop(player);
1080
0
            }
1081
0
            break;
1082
0
        }
1083
0
        case INPUT_EVENT_PROGRAM:
1084
0
            vlc_player_input_HandleProgramEvent(input, &event->program);
1085
0
            break;
1086
0
        case INPUT_EVENT_ES:
1087
0
            vlc_player_input_HandleEsEvent(input, &event->es);
1088
0
            break;
1089
0
        case INPUT_EVENT_TITLE:
1090
0
            vlc_player_input_HandleTitleEvent(input, &event->title);
1091
0
            break;
1092
0
        case INPUT_EVENT_CHAPTER:
1093
0
            vlc_player_input_HandleChapterEvent(input, &event->chapter);
1094
0
            break;
1095
0
        case INPUT_EVENT_RECORD:
1096
0
            input->recording = event->record;
1097
0
            vlc_player_SendEvent(player, on_recording_changed, input->recording);
1098
0
            break;
1099
0
        case INPUT_EVENT_STATISTICS:
1100
0
            input->stats = *event->stats;
1101
0
            vlc_player_SendEvent(player, on_statistics_changed, &input->stats);
1102
0
            break;
1103
0
        case INPUT_EVENT_SIGNAL:
1104
0
            input->signal_quality = event->signal.quality;
1105
0
            input->signal_strength = event->signal.strength;
1106
0
            vlc_player_SendEvent(player, on_signal_changed,
1107
0
                                 input->signal_quality, input->signal_strength);
1108
0
            break;
1109
0
        case INPUT_EVENT_CACHE:
1110
0
            if (event->cache == 0.0f)
1111
0
                vlc_player_UpdateTimerEvent(player, NULL,
1112
0
                                            VLC_PLAYER_TIMER_EVENT_DISCONTINUITY,
1113
0
                                            VLC_TICK_INVALID);
1114
0
            input->cache = event->cache;
1115
0
            vlc_player_SendEvent(player, on_buffering_changed, event->cache);
1116
0
            break;
1117
0
        case INPUT_EVENT_VOUT:
1118
0
            vlc_player_input_HandleVoutEvent(input, &event->vout);
1119
0
            break;
1120
0
        case INPUT_EVENT_OUTPUT_STATE:
1121
0
            if (event->output_state.action == VLC_INPUT_EVENT_OUTPUT_STATE_PAUSED)
1122
0
                vlc_player_UpdateTimerEvent(player, event->output_state.id,
1123
0
                                            VLC_PLAYER_TIMER_EVENT_PAUSED,
1124
0
                                            event->output_state.paused_date);
1125
0
            break;
1126
0
        case INPUT_EVENT_ITEM_META:
1127
0
        case INPUT_EVENT_ITEM_INFO:
1128
0
            vlc_player_SendEvent(player, on_media_meta_changed,
1129
0
                                 input_GetItem(input->thread));
1130
0
            break;
1131
0
        case INPUT_EVENT_ITEM_EPG:
1132
0
            vlc_player_SendEvent(player, on_media_epg_changed,
1133
0
                                 input_GetItem(input->thread));
1134
0
            break;
1135
0
        case INPUT_EVENT_SUBITEMS:
1136
0
            vlc_player_SendEvent(player, on_media_subitems_changed,
1137
0
                                 input_GetItem(input->thread), event->subitems);
1138
0
            input_item_node_Delete(event->subitems);
1139
0
            break;
1140
0
        case INPUT_EVENT_DEAD:
1141
0
            if (input->started) /* Can happen with early input_thread fails */
1142
0
            {
1143
0
                input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_ERROR;
1144
0
                vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
1145
0
                                             VLC_TICK_INVALID);
1146
0
            }
1147
0
            vlc_player_destructor_AddJoinableInput(player, input);
1148
0
            break;
1149
0
        case INPUT_EVENT_VBI_PAGE:
1150
0
            input->teletext_page = event->vbi_page < 999 ? event->vbi_page : 100;
1151
0
            vlc_player_SendEvent(player, on_teletext_page_changed,
1152
0
                                 input->teletext_page);
1153
0
            break;
1154
0
        case INPUT_EVENT_VBI_TRANSPARENCY:
1155
0
            input->teletext_transparent = event->vbi_transparent;
1156
0
            vlc_player_SendEvent(player, on_teletext_transparency_changed,
1157
0
                                 input->teletext_transparent);
1158
0
            break;
1159
0
        case INPUT_EVENT_ATTACHMENTS:
1160
0
            vlc_player_SendEvent(player, on_media_attachments_added,
1161
0
                                 input_GetItem(input->thread),
1162
0
                                 event->attachments.array,
1163
0
                                 event->attachments.count);
1164
0
            break;
1165
0
        case INPUT_EVENT_NAV_FAILED:
1166
0
            vlc_player_input_NavigationFallback(input, event->nav_type);
1167
0
            break;
1168
0
        case INPUT_EVENT_MOUSE_LEFT:
1169
0
            vlc_player_input_MouseFallback(input);
1170
0
            break;
1171
0
        case INPUT_EVENT_FRAME_NEXT_STATUS:
1172
0
            vlc_player_input_FrameNextStatus(input,
1173
0
                                             event->frame_next_status);
1174
0
            break;
1175
0
        case INPUT_EVENT_FRAME_PREVIOUS_STATUS:
1176
0
            vlc_player_input_FramePreviousStatus(input,
1177
0
                                                 event->frame_previous_status);
1178
0
            break;
1179
0
        default:
1180
0
            handled = false;
1181
0
            break;
1182
0
    }
1183
1184
0
    vlc_mutex_unlock(&player->lock);
1185
0
    return handled;
1186
0
}
1187
1188
void
1189
vlc_player_input_SelectTracksByStringIds(struct vlc_player_input *input,
1190
                                         enum es_format_category_e cat,
1191
                                         const char *str_ids)
1192
0
{
1193
0
    input_SetEsCatIds(input->thread, cat, str_ids);
1194
0
}
1195
1196
char *
1197
vlc_player_input_GetSelectedTrackStringIds(struct vlc_player_input *input,
1198
                                           enum es_format_category_e cat)
1199
0
{
1200
0
    vlc_player_track_vector *vec = vlc_player_input_GetTrackVector(input, cat);
1201
0
    assert(vec);
1202
0
    bool first_track = true;
1203
0
    struct vlc_memstream ms;
1204
1205
0
    struct vlc_player_track_priv* t;
1206
0
    vlc_vector_foreach(t, vec)
1207
0
    {
1208
0
        if (t->selected_by_user && vlc_es_id_IsStrIdStable(t->t.es_id))
1209
0
        {
1210
0
            if (first_track)
1211
0
            {
1212
0
                int ret = vlc_memstream_open(&ms);
1213
0
                if (ret != 0)
1214
0
                    return NULL;
1215
0
            }
1216
0
            const char *str_id = vlc_es_id_GetStrId(t->t.es_id);
1217
0
            assert(str_id);
1218
1219
0
            if (!first_track)
1220
0
                vlc_memstream_putc(&ms, ',');
1221
0
            vlc_memstream_puts(&ms, str_id);
1222
0
            first_track = false;
1223
0
        }
1224
0
    }
1225
0
    return !first_track && vlc_memstream_close(&ms) == 0 ? ms.ptr : NULL;
1226
0
}
1227
1228
struct vlc_player_input *
1229
vlc_player_input_New(vlc_player_t *player, input_item_t *item)
1230
0
{
1231
0
    struct vlc_player_input *input = malloc(sizeof(*input));
1232
0
    if (!input)
1233
0
        return NULL;
1234
1235
0
    input->player = player;
1236
0
    input->started = false;
1237
0
    input->playing = false;
1238
1239
0
    input->state = VLC_PLAYER_STATE_STOPPED;
1240
0
    input->error = VLC_PLAYER_ERROR_NONE;
1241
0
    input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_ERROR;
1242
0
    input->rate = 1.f;
1243
0
    input->capabilities = 0;
1244
0
    input->length = input->time = VLC_TICK_INVALID;
1245
0
    input->live = false;
1246
0
    input->normal_time = VLC_TICK_0;
1247
0
    input->pause_date = VLC_TICK_INVALID;
1248
0
    input->position = 0.f;
1249
1250
0
    input->recording = false;
1251
1252
0
    input->cache = 0.f;
1253
0
    input->signal_quality = input->signal_strength = -1.f;
1254
1255
0
    memset(&input->stats, 0, sizeof(input->stats));
1256
1257
0
    vlc_vector_init(&input->program_vector);
1258
0
    vlc_vector_init(&input->video_track_vector);
1259
0
    vlc_vector_init(&input->audio_track_vector);
1260
0
    vlc_vector_init(&input->spu_track_vector);
1261
0
    input->teletext_source = NULL;
1262
1263
0
    input->titles = NULL;
1264
0
    input->title_selected = input->chapter_selected = 0;
1265
1266
0
    input->teletext_enabled = input->teletext_transparent = false;
1267
0
    input->teletext_page = 0;
1268
1269
0
    input->abloop_state[0].set = input->abloop_state[1].set = false;
1270
1271
0
    memset(&input->ml.states, 0, sizeof(input->ml.states));
1272
0
    input->ml.states.aspect_ratio = input->ml.states.crop =
1273
0
        input->ml.states.deinterlace = input->ml.states.video_filter = NULL;
1274
0
    input->ml.states.current_title = -1;
1275
0
    input->ml.states.current_video_track =
1276
0
        input->ml.states.current_audio_track =
1277
0
        input->ml.states.current_subtitle_track = NULL;
1278
0
    input->ml.restore = VLC_RESTOREPOINT_NONE;
1279
0
    input->ml.restore_states = false;
1280
0
    input->ml.delay_restore = false;
1281
0
    input->ml.pos = -1.f;
1282
0
    input->ml.has_audio_tracks = input->ml.has_video_tracks = false;
1283
1284
0
    static const struct vlc_input_thread_callbacks cbs = {
1285
0
        .on_event = input_thread_Events,
1286
0
    };
1287
1288
0
    const struct vlc_input_thread_cfg cfg = {
1289
0
        .type = INPUT_TYPE_PLAYBACK,
1290
0
        .hw_dec = INPUT_CFG_HW_DEC_DEFAULT,
1291
0
        .resource = player->resource,
1292
0
        .renderer = player->renderer,
1293
0
        .cbs = &cbs,
1294
0
        .cbs_data = input,
1295
0
    };
1296
1297
0
    input->repeat = player->repeat;
1298
1299
0
    input->thread = input_Create(player, item, &cfg);
1300
0
    if (!input->thread)
1301
0
    {
1302
0
        free(input);
1303
0
        return NULL;
1304
0
    }
1305
0
    vlc_player_input_RestoreMlStates(input, false);
1306
1307
0
    if (player->video_string_ids)
1308
0
        vlc_player_input_SelectTracksByStringIds(input, VIDEO_ES,
1309
0
                                                 player->video_string_ids);
1310
1311
0
    if (player->audio_string_ids)
1312
0
        vlc_player_input_SelectTracksByStringIds(input, AUDIO_ES,
1313
0
                                                 player->audio_string_ids);
1314
1315
0
    if (player->sub_string_ids)
1316
0
        vlc_player_input_SelectTracksByStringIds(input, SPU_ES,
1317
0
                                                 player->sub_string_ids);
1318
1319
    /* Initial sub/audio delay */
1320
0
    const vlc_tick_t cat_delays[DATA_ES] = {
1321
0
        [AUDIO_ES] =
1322
0
            VLC_TICK_FROM_MS(var_InheritInteger(player, "audio-desync")),
1323
0
        [SPU_ES] =
1324
0
            vlc_tick_from_samples(var_InheritInteger(player, "sub-delay"), 10),
1325
0
    };
1326
1327
0
    for (enum es_format_category_e i = UNKNOWN_ES; i < DATA_ES; ++i)
1328
0
    {
1329
0
        input->cat_delays[i] = cat_delays[i];
1330
0
        if (cat_delays[i] != 0)
1331
0
        {
1332
0
            int ret = input_SetEsCatDelay(input->thread, i, cat_delays[i]);
1333
0
            if (ret == VLC_SUCCESS)
1334
0
                vlc_player_SendEvent(player, on_category_delay_changed, i,
1335
0
                                     cat_delays[i]);
1336
0
        }
1337
0
    }
1338
0
    return input;
1339
0
}
1340
1341
void
1342
vlc_player_input_Delete(struct vlc_player_input *input)
1343
0
{
1344
0
    assert(input->titles == NULL);
1345
0
    assert(input->program_vector.size == 0);
1346
0
    assert(input->video_track_vector.size == 0);
1347
0
    assert(input->audio_track_vector.size == 0);
1348
0
    assert(input->spu_track_vector.size == 0);
1349
0
    assert(input->teletext_source == NULL);
1350
1351
0
    vlc_vector_destroy(&input->program_vector);
1352
0
    vlc_vector_destroy(&input->video_track_vector);
1353
0
    vlc_vector_destroy(&input->audio_track_vector);
1354
0
    vlc_vector_destroy(&input->spu_track_vector);
1355
1356
0
    input_Close(input->thread);
1357
0
    free(input);
1358
0
}