Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  input.c   Sprache: C

 

/*****************************************************************************
 * player_input.c: Player input implementation
 *****************************************************************************
 * Copyright © 2018-2019 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_memstream.h>
#include <vlc_dialog.h>
#include "player.h"

struct vlc_player_track_priv *
vlc_player_input_FindTrackById(struct vlc_player_input *input, vlc_es_id_t *id,
                               size_t *idx)
{
    vlc_player_track_vector *vec =
        vlc_player_input_GetTrackVector(input, vlc_es_id_GetCat(id));
    return vec ? vlc_player_track_vector_FindById(vec, id, idx) : NULL;
}

vlc_tick_t
vlc_player_input_GetTime(struct vlc_player_input *input, bool seeking,
                         vlc_tick_t system_now)
{
    vlc_player_t *player = input->player;
    vlc_tick_t ts;

    if (input == player->input
     && vlc_player_GetTimerPoint(player, &seeking, system_now, &ts, NULL) == 0)
        return ts;
    return input->time;
}

double
vlc_player_input_GetPos(struct vlc_player_input *input, bool seeking,
                        vlc_tick_t system_now)
{
    vlc_player_t *player = input->player;
    double pos;

    if (input == player->input
     && vlc_player_GetTimerPoint(player, &seeking, system_now, NULL, &pos) == 0)
        return pos;
    return input->position;
}

bool
vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, bool forced)
{
    if (!input->abloop_state[0].set || !input->abloop_state[1].set
     || (input->capabilities & VLC_PLAYER_CAP_SEEK) == 0)
        return false;

    vlc_player_t *player = input->player;

    if (player->input != input)
        return false;

    vlc_tick_t now = vlc_tick_now();
    if (input->abloop_state[0].time != VLC_TICK_INVALID
     && input->abloop_state[1].time != VLC_TICK_INVALID)
    {
        vlc_tick_t time = vlc_player_input_GetTime(input, false, now);
        if (forced
         || (time != VLC_TICK_INVALID && time >= input->abloop_state[1].time))
            vlc_player_SetTime(player, input->abloop_state[0].time);
        return true;
    }

    double pos = vlc_player_input_GetPos(input, false, now);
    if (forced || pos >= input->abloop_state[1].pos)
    {
        vlc_player_SetPosition(player, input->abloop_state[0].pos);
        return true;
    }

    return false;
}

int
vlc_player_input_Start(struct vlc_player_input *input)
{
    int ret = input_Start(input->thread);
    if (ret != VLC_SUCCESS)
        return ret;
    input->started = true;
    return ret;
}

static inline void
vlc_player_assert_seek_params(enum vlc_player_seek_speed speed,
                              enum vlc_player_whence whence)
{
    assert(speed == VLC_PLAYER_SEEK_PRECISE
        || speed == VLC_PLAYER_SEEK_FAST);
    assert(whence == VLC_PLAYER_WHENCE_ABSOLUTE
        || whence == VLC_PLAYER_WHENCE_RELATIVE);
    (void) speed; (void) whence;
}

void
vlc_player_input_SeekByPos(struct vlc_player_input *input, double position,
                           enum vlc_player_seek_speed speed,
                           enum vlc_player_whence whence)
{
    vlc_player_t *player = input->player;
    vlc_player_assert_seek_params(speed, whence);

    /* Assert before we modify position internally, this only check that user
     * positions are correct. */

    if (whence != VLC_PLAYER_WHENCE_ABSOLUTE)
    {
        assert(position >= -1 && position <=1);
        position += vlc_player_input_GetPos(input, true, vlc_tick_now());
    }
    else
    {
        assert(position >= 0 && position <=1);
    }

    if (position < 0)
        position = 0;
    else if (position > 1)
        position = 1;

    vlc_player_UpdateTimerSeekState(player, VLC_TICK_INVALID, position);

    int ret = input_ControlPush(input->thread, INPUT_CONTROL_SET_POSITION,
        &(input_control_param_t) {
            .pos.f_val = position,
            .pos.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
    });

    if (ret == VLC_SUCCESS)
        vlc_player_osd_Position(player, input, VLC_TICK_INVALID, position);
}

void
vlc_player_input_SeekByTime(struct vlc_player_input *input, vlc_tick_t time,
                            enum vlc_player_seek_speed speed,
                            enum vlc_player_whence whence)
{
    vlc_player_t *player = input->player;
    vlc_player_assert_seek_params(speed, whence);

    if (whence != VLC_PLAYER_WHENCE_ABSOLUTE)
        time += vlc_player_input_GetTime(input, true, vlc_tick_now());

    if (time < VLC_TICK_0)
        time = VLC_TICK_0;

    vlc_player_UpdateTimerSeekState(player, time, -1);

    int ret = input_ControlPush(input->thread, INPUT_CONTROL_SET_TIME,
        &(input_control_param_t) {
            .time.i_val = time,
            .time.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
    });

    if (ret == VLC_SUCCESS)
        vlc_player_osd_Position(player, input, time, -1);
}

void
vlc_player_input_UpdateViewpoint(struct vlc_player_input *input,
                                 const vlc_viewpoint_t *viewpoint,
                                 enum vlc_player_whence whence)
{
    input_control_param_t param = { .viewpoint = *viewpoint };
    if (whence == VLC_PLAYER_WHENCE_ABSOLUTE)
        input_ControlPush(input->thread, INPUT_CONTROL_SET_VIEWPOINT,
                          ¶m);
    else
        input_ControlPush(input->thread, INPUT_CONTROL_UPDATE_VIEWPOINT,
                          ¶m);
}

static bool
vlc_player_WaitRetryDelay(vlc_player_t *player)
{
#define RETRY_TIMEOUT_BASE VLC_TICK_FROM_MS(100)
#define RETRY_TIMEOUT_MAX VLC_TICK_FROM_MS(3200)
#define MAX_EOS_BURST 4
    /* Temporize only after a few successive EOS */
    if (player->eos_burst_count > MAX_EOS_BURST)
    {
        unsigned pow = player->eos_burst_count - MAX_EOS_BURST;
        /* Delay the next opening to avoid busy loops */
        vlc_tick_t delay = RETRY_TIMEOUT_BASE;
        for (unsigned i = 1; i < pow && delay < RETRY_TIMEOUT_MAX; ++i)
            delay *= 2; /* Wait 100, 200, 400, 800, 1600 and finally 3200ms */
        delay += vlc_tick_now();

        while (player->eos_burst_count != 0
            && vlc_cond_timedwait(&player->start_delay_cond, &player->lock,
                                  delay) == 0);
        if (player->eos_burst_count == 0)
            return false; /* canceled */
    }
    return true;
}

void
vlc_player_input_HandleState(struct vlc_player_input *input,
                             enum vlc_player_state state, vlc_tick_t state_date)
{
    vlc_player_t *player = input->player;

    /* The STOPPING state can be set earlier by the player. In that case,
     * ignore all future events except the STOPPED one */

    if (input->state == VLC_PLAYER_STATE_STOPPING
     && state != VLC_PLAYER_STATE_STOPPED)
        return;

    enum vlc_player_state last_state = input->state;
    input->state = state;

    /* Override the global state if the player is still playing and has a next
     * media to play */

    bool send_event = player->global_state != state;
    switch (input->state)
    {
        case VLC_PLAYER_STATE_STOPPED:
            assert(!input->started);
            assert(input != player->input);

            if (input->titles)
            {
                vlc_player_title_list_Release(input->titles);
                input->titles = NULL;
                vlc_player_SendEvent(player, on_titles_changed, NULL);
            }

            vlc_player_ResetTimer(player);

            if (player->last_eos != VLC_TICK_INVALID)
            {
                vlc_tick_t diff = vlc_tick_now() - player->last_eos;
                if (diff < VLC_PLAYER_EOS_BURST_THRESHOLD)
                    ++player->eos_burst_count;
                else
                    player->eos_burst_count = 0;
            }

            if (player->started)
                vlc_player_WaitRetryDelay(player);

            /* Assign the current date after the wait */
            player->last_eos = vlc_tick_now();

            if (!player->deleting)
            {
                vlc_player_OpenNextMedia(player);
                /* It is possible to open several medias in a stopped state */
                if (player->input && player->started)
                    vlc_player_input_Start(player->input);
            }
            if (!player->input)
                player->started = false;

            send_event = !player->started && last_state != VLC_PLAYER_STATE_STOPPED;
            break;
        case VLC_PLAYER_STATE_STOPPING:
        {
            enum vlc_player_media_stopping_reason reason;
            /* The order matters. input->error is only set while the
               input->started flag is set, so an error or EOS that follows a
               user stop request is ignored and the stop is reported as USER,
               whereas an error that occurred before a user stop request takes
               precedence and is reported as ERROR. */

            if (input->error != VLC_PLAYER_ERROR_NONE)
                reason = VLC_PLAYER_MEDIA_STOPPING_ERROR;
            else if (!input->started)
                reason = VLC_PLAYER_MEDIA_STOPPING_USER;
            else
                reason = VLC_PLAYER_MEDIA_STOPPING_EOS;
            input->started = false;

            /* Note: no need to hold the media here, as it is already protected
               by the player lock. User can hold the input_item, if they want
               to use it beyond the callback scope. */

            input_item_t *media = input_GetItem(input->thread);
            vlc_player_SendEvent(player, on_stopping_current_media,
                                 media, reason);

            vlc_player_UpdateTimerEvent(player, NULL,
                                        VLC_PLAYER_TIMER_EVENT_DISCONTINUITY,
                                        VLC_TICK_INVALID);

            vlc_player_UpdateTimerEvent(player, NULL,
                                        VLC_PLAYER_TIMER_EVENT_STOPPING,
                                        VLC_TICK_INVALID);

            if (input == player->input)
                player->input = NULL;

            if (player->started && !player->next_media)
                player->started = false;
            send_event = !player->started;
            break;
        }
        case VLC_PLAYER_STATE_PLAYING:
            input->pause_date = VLC_TICK_INVALID;
            vlc_player_SignalAtoBLoop(player);
            vlc_player_UpdateTimerEvent(player, NULL,
                                        VLC_PLAYER_TIMER_EVENT_PLAYING,
                                        state_date);
            /* fall through */
        case VLC_PLAYER_STATE_STARTED:
            if (player->started &&
                player->global_state == VLC_PLAYER_STATE_PLAYING)
                send_event = false;
            break;

        case VLC_PLAYER_STATE_PAUSED:
            /* Can pause from PLAYING or STARTED (deferred pause during buffering) */
            assert(player->global_state == VLC_PLAYER_STATE_PLAYING
                   || player->global_state == VLC_PLAYER_STATE_STARTED);
            assert(state_date != VLC_TICK_INVALID);
            input->pause_date = state_date;

            vlc_player_SignalAtoBLoop(player);
            vlc_player_UpdateTimerEvent(player, NULL,
                                        VLC_PLAYER_TIMER_EVENT_PAUSED,
                                        input->pause_date);
            break;
        default:
            vlc_assert_unreachable();
    }

    if (send_event)
    {
        assert(player->global_state != input->state);
        player->global_state = input->state;
        vlc_player_SendEvent(player, on_state_changed, player->global_state);
    }
}

static void
vlc_player_input_HandleStateEvent(struct vlc_player_input *input,
                                  input_state_e state, vlc_tick_t state_date)
{
    switch (state)
    {
        case OPENING_S:
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED,
                                         VLC_TICK_INVALID);
            break;
        case PLAYING_S:
            input->playing = true;
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING,
                                         state_date);
            break;
        case PAUSE_S:
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED,
                                         state_date);
            break;
        case END_S:
            input->playing = false;
            vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
                                         VLC_TICK_INVALID);
            vlc_player_destructor_AddStoppingInput(input->player, input);
            break;
        case ERROR_S:
            /* Don't send errors if the input is stopped by the user */
            if (input->started)
            {
                /* Contrary to the input_thread_t, an error is not a state */
                input->error = VLC_PLAYER_ERROR_GENERIC;
                vlc_player_SendEvent(input->player, on_error_changed, input->error);
            }
            /* input->playing monitor the OPENING_S -> PLAYING_S transition.
             * If input->playing is false, we have an error at the opening of
             * the input thread and we won't reach END_S. */

            if (!input->playing)
            {
                vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
                                             VLC_TICK_INVALID);
                vlc_player_destructor_AddStoppingInput(input->player, input);
            }
            break;
        default:
            vlc_assert_unreachable();
    }
}

static void
vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
                                    const struct vlc_input_event_program *ev)
{
    vlc_player_t *player = input->player;
    struct vlc_player_program *prgm;
    vlc_player_program_vector *vec = &input->program_vector;

    switch (ev->action)
    {
        case VLC_INPUT_PROGRAM_ADDED:
            prgm = vlc_player_program_New(ev->id, ev->title);
            if (!prgm)
                break;

            if (!vlc_vector_push(vec, prgm))
            {
                vlc_player_program_Delete(prgm);
                break;
            }
            vlc_player_SendEvent(player, on_program_list_changed,
                                 VLC_PLAYER_LIST_ADDED, prgm);
            break;
        case VLC_INPUT_PROGRAM_DELETED:
        {
            size_t idx;
            prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx);
            if (prgm)
            {
                vlc_player_SendEvent(player, on_program_list_changed,
                                     VLC_PLAYER_LIST_REMOVED, prgm);
                vlc_vector_remove(vec, idx);
                vlc_player_program_Delete(prgm);
            }
            break;
        }
        case VLC_INPUT_PROGRAM_UPDATED:
        case VLC_INPUT_PROGRAM_SCRAMBLED:
            prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL);
            if (!prgm)
                break;
            if (ev->action == VLC_INPUT_PROGRAM_UPDATED)
            {
                if (vlc_player_program_Update(prgm, ev->id, ev->title) != 0)
                    break;
            }
            else
                prgm->scrambled = ev->scrambled;
            vlc_player_SendEvent(player, on_program_list_changed,
                                 VLC_PLAYER_LIST_UPDATED, prgm);
            break;
        case VLC_INPUT_PROGRAM_SELECTED:
        {
            int unselected_id = -1, selected_id = -1;
            vlc_vector_foreach(prgm, vec)
            {
                if (prgm->group_id == ev->id)
                {
                    if (!prgm->selected)
                    {
                        assert(selected_id == -1);
                        prgm->selected = true;
                        selected_id = prgm->group_id;
                    }
                }
                else
                {
                    if (prgm->selected)
                    {
                        assert(unselected_id == -1);
                        prgm->selected = false;
                        unselected_id = prgm->group_id;
                    }
                }
            }
            if (unselected_id != -1 || selected_id != -1)
                vlc_player_SendEvent(player, on_program_selection_changed,
                                     unselected_id, selected_id);
            break;
        }
        default:
            vlc_assert_unreachable();
    }
}

static const struct vlc_player_track_priv *
vlc_player_FindTeletextSource(const struct vlc_player_input *input,
                              const struct vlc_player_track_priv *exclude,
                              bool selected)
{
    const struct vlc_player_track_priv *t;
    vlc_vector_foreach(t, &input->spu_track_vector)
    {
        if (t->t.fmt.i_codec == VLC_CODEC_TELETEXT &&
           t != exclude &&
           t->t.selected == selected)
            return t;
    }
    return NULL;
}

static unsigned
vlc_player_input_TeletextUserPage(const struct vlc_player_track_priv *t)
{
    const uint8_t mag = t->t.fmt.subs.teletext.i_magazine;
    const uint8_t page = t->t.fmt.subs.teletext.i_page;
    return (mag % 10) * 100 +
           (page & 0x0F) + ((page >> 4) & 0x0F) * 10;
}

static void
vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input,
                                    const struct vlc_input_event_es *ev,
                                    const struct vlc_player_track_priv *trackpriv)
{
    vlc_player_t *player = input->player;
    if (ev->fmt->i_cat != SPU_ES ||
        ev->fmt->i_codec != VLC_CODEC_TELETEXT)
        return;
    switch (ev->action)
    {
        case VLC_INPUT_ES_ADDED:
        {
            if (!input->teletext_source)
            {
                input->teletext_source = trackpriv;
                vlc_player_SendEvent(player, on_teletext_menu_changed, true);
            }
            break;
        }
        case VLC_INPUT_ES_DELETED:
        {
            if (input->teletext_source == trackpriv)
            {
                input->teletext_source =
                        vlc_player_FindTeletextSource(input, trackpriv, true);
                if (!input->teletext_source)
                    input->teletext_source =
                            vlc_player_FindTeletextSource(input, trackpriv, false);
                if (!input->teletext_source) /* no more teletext ES */
                {
                    if (input->teletext_enabled)
                    {
                        input->teletext_enabled = false;
                        vlc_player_SendEvent(player, on_teletext_enabled_changed, false);
                    }
                    vlc_player_SendEvent(player, on_teletext_menu_changed, false);
                }
                else /* another teletext ES was reselected */
                {
                    if (input->teletext_source->t.selected != input->teletext_enabled)
                    {
                        input->teletext_enabled = input->teletext_source->t.selected;
                        vlc_player_SendEvent(player, on_teletext_enabled_changed,
                                             input->teletext_source->t.selected);
                    }
                    input->teletext_page =
                            vlc_player_input_TeletextUserPage(input->teletext_source);
                    vlc_player_SendEvent(player, on_teletext_page_changed,
                                         input->teletext_page);
                }
            }
            break;
        }
        case VLC_INPUT_ES_UPDATED:
            break;
        case VLC_INPUT_ES_SELECTED:
        {
            if (!input->teletext_enabled) /* we stick with the first selected */
            {
                input->teletext_source = trackpriv;
                input->teletext_enabled = true;
                input->teletext_page = vlc_player_input_TeletextUserPage(trackpriv);
                vlc_player_SendEvent(player, on_teletext_enabled_changed, true);
                vlc_player_SendEvent(player, on_teletext_page_changed,
                                     input->teletext_page);
            }
            break;
        }
        case VLC_INPUT_ES_UNSELECTED:
            if (input->teletext_source == trackpriv)
            {
                /* If there's another selected teletext, it needs to become source */
                const struct vlc_player_track_priv *other =
                        vlc_player_FindTeletextSource(input, trackpriv, true);
                if (other)
                {
                    input->teletext_source = other;
                    if (!input->teletext_enabled)
                    {
                        input->teletext_enabled = true;
                        vlc_player_SendEvent(player, on_teletext_enabled_changed, true);
                    }
                    input->teletext_page = vlc_player_input_TeletextUserPage(other);
                    vlc_player_SendEvent(player, on_teletext_page_changed,
                                         input->teletext_page);
                }
                else
                {
                    input->teletext_enabled = false;
                    vlc_player_SendEvent(player, on_teletext_enabled_changed, false);
                }
            }
            break;
        default:
            vlc_assert_unreachable();
    }
}

static void
vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
                               const struct vlc_input_event_es *ev)
{
    assert(ev->id && ev->title && ev->fmt);

    vlc_player_track_vector *vec =
        vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
    if (!vec)
        return; /* UNKNOWN_ES or DATA_ES not handled */

    vlc_player_t *player = input->player;
    struct vlc_player_track_priv *trackpriv;
    switch (ev->action)
    {
        case VLC_INPUT_ES_ADDED:
            trackpriv = vlc_player_track_priv_New(ev->id, ev->title, ev->fmt);
            if (!trackpriv)
                break;

            if (ev->fmt->i_cat == VIDEO_ES)
                input->ml.has_video_tracks = true;
            else if (ev->fmt->i_cat == AUDIO_ES)
                input->ml.has_audio_tracks = true;
            if (!vlc_vector_push(vec, trackpriv))
            {
                vlc_player_track_priv_Delete(trackpriv);
                break;
            }
            vlc_player_SendEvent(player, on_track_list_changed,
                                 VLC_PLAYER_LIST_ADDED, &trackpriv->t);
            vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
            break;
        case VLC_INPUT_ES_DELETED:
        {
            size_t idx;
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, &idx);
            if (trackpriv)
            {
                vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
                vlc_player_SendEvent(player, on_track_list_changed,
                                     VLC_PLAYER_LIST_REMOVED, &trackpriv->t);
                vlc_vector_remove(vec, idx);
                vlc_player_track_priv_Delete(trackpriv);
            }
            break;
        }
        case VLC_INPUT_ES_UPDATED:
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
            if (!trackpriv)
                break;
            if (vlc_player_track_priv_Update(trackpriv, ev->title, ev->fmt) != 0)
                break;
            vlc_player_SendEvent(player, on_track_list_changed,
                                 VLC_PLAYER_LIST_UPDATED, &trackpriv->t);
            vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
            break;
        case VLC_INPUT_ES_SELECTED:
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
            if (trackpriv)
            {
                trackpriv->t.selected = true;
                trackpriv->selected_by_user = ev->forced;
                trackpriv->vout_order = ev->vout_order;
                vlc_player_SendEvent(player, on_track_selection_changed,
                                     NULL, trackpriv->t.es_id);
                vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
            }
            break;
        case VLC_INPUT_ES_UNSELECTED:
            trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
            if (trackpriv)
            {
                vlc_player_RemoveTimerSource(player, ev->id);
                trackpriv->t.selected = false;
                trackpriv->selected_by_user = false;
                vlc_player_SendEvent(player, on_track_selection_changed,
                                     trackpriv->t.es_id, NULL);
                vlc_player_input_HandleTeletextMenu(input, ev, trackpriv);
            }
            break;
        default:
            vlc_assert_unreachable();
    }
}

static void
vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
                                  const struct vlc_input_event_title *ev)
{
    vlc_player_t *player = input->player;
    switch (ev->action)
    {
        case VLC_INPUT_TITLE_NEW_LIST:
        {
            input_thread_private_t *input_th = input_priv(input->thread);
            const int title_offset = input_th->i_title_offset;
            const int chapter_offset = input_th->i_seekpoint_offset;

            if (input->titles)
                vlc_player_title_list_Release(input->titles);
            input->title_selected = input->chapter_selected = 0;
            input->titles =
                vlc_player_title_list_Create(ev->list.array, ev->list.count,
                                             title_offset, chapter_offset);
            vlc_player_SendEvent(player, on_titles_changed, input->titles);
            if (input->titles)
            {
                vlc_player_SendEvent(player, on_title_selection_changed,
                                     &input->titles->array[0], 0);
                if (input->ml.restore == VLC_RESTOREPOINT_TITLE &&
                    (size_t)input->ml.states.current_title < ev->list.count)
                {
                    vlc_player_SelectTitleIdx(player, input->ml.states.current_title);
                }
                input->ml.restore = VLC_RESTOREPOINT_POSITION;
            }
            else input->ml.restore = VLC_RESTOREPOINT_NONE;
            break;
        }
        case VLC_INPUT_TITLE_SELECTED:
            if (!input->titles)
                return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
            assert(ev->selected_idx < input->titles->count);
            input->title_selected = ev->selected_idx;
            vlc_player_SendEvent(player, on_title_selection_changed,
                                 &input->titles->array[input->title_selected],
                                 input->title_selected);
            if (input->ml.restore == VLC_RESTOREPOINT_POSITION &&
                input->ml.states.current_title >= 0 &&
                (size_t)input->ml.states.current_title == ev->selected_idx &&
                input->ml.pos > .0f)
            {
                input_SetPosition(input->thread, input->ml.pos, false);
            }
            /* Reset the wanted title to avoid forcing it or the position
             * again during the next title change
             */

            input->ml.restore = VLC_RESTOREPOINT_NONE;
            break;
        default:
            vlc_assert_unreachable();
    }
}

static void
vlc_player_input_HandleChapterEvent(struct vlc_player_input *input,
                                    const struct vlc_input_event_chapter *ev)
{
    vlc_player_t *player = input->player;
    if (!input->titles || ev->title < 0 || ev->seekpoint < 0)
        return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */

    assert((size_t)ev->title < input->titles->count);
    const struct vlc_player_title *title = &input->titles->array[ev->title];
    if (!title->chapter_count)
        return;

    assert(ev->seekpoint < (int)title->chapter_count);
    input->title_selected = ev->title;
    input->chapter_selected = ev->seekpoint;

    const struct vlc_player_chapter *chapter = &title->chapters[ev->seekpoint];
    vlc_player_SendEvent(player, on_chapter_selection_changed, title, ev->title,
                         chapter, ev->seekpoint);
}

static void
vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
                                 const struct vlc_input_event_vout *ev)
{
    assert(ev->vout);
    assert(ev->id);

    vlc_player_t *player = input->player;

    struct vlc_player_track_priv *trackpriv =
        vlc_player_input_FindTrackById(input, ev->id, NULL);
    if (!trackpriv)
        return;

    const bool is_video_es = trackpriv->t.fmt.i_cat == VIDEO_ES;

    switch (ev->action)
    {
        case VLC_INPUT_EVENT_VOUT_STARTED:
            trackpriv->vout = ev->vout;
            vlc_player_SendEvent(player, on_vout_changed,
                                 VLC_PLAYER_VOUT_STARTED, ev->vout,
                                 ev->order, ev->id);

            if (is_video_es)
            {
                /* Register vout callbacks after the vout list event */
                vlc_player_vout_AddCallbacks(player, ev->vout);
            }
            break;
        case VLC_INPUT_EVENT_VOUT_STOPPED:
            if (is_video_es)
            {
                /* Un-register vout callbacks before the vout list event */
                vlc_player_vout_DelCallbacks(player, ev->vout);
            }

            trackpriv->vout = NULL;
            vlc_player_SendEvent(player, on_vout_changed,
                                 VLC_PLAYER_VOUT_STOPPED, ev->vout,
                                 VLC_VOUT_ORDER_NONE, ev->id);
            break;
        default:
            vlc_assert_unreachable();
    }
}

static void
vlc_player_input_NavigationFallback(struct vlc_player_input *input, int nav_type)
{
    vlc_player_t *player = input->player;

    /* Handle Up/Down/Left/Right if the demux can't navigate */
    vlc_viewpoint_t vp = { 0 };
    bool viewpoint_updated = true;
    float yaw = 0.f, pitch = 0.f, roll = 0.f;

    int vol_direction = 0;
    int seek_direction = 0;
    switch (nav_type)
    {
        case INPUT_CONTROL_NAV_UP:
            vol_direction = 1;
            pitch = -1.f;
            break;
        case INPUT_CONTROL_NAV_DOWN:
            vol_direction = -1;
            pitch = 1.f;
            break;
        case INPUT_CONTROL_NAV_LEFT:
            seek_direction = -1;
            yaw = -1.f;
            break;
        case INPUT_CONTROL_NAV_RIGHT:
            seek_direction = 1;
            yaw = 1.f;
            break;
        case INPUT_CONTROL_NAV_ACTIVATE:
        case INPUT_CONTROL_NAV_POPUP:
        case INPUT_CONTROL_NAV_MENU:
            viewpoint_updated = false;
            return;
        default:
            vlc_assert_unreachable();
    }

    /* Try to change the viewpoint if possible */
    bool viewpoint_ch = false;
    size_t vout_count;
    vout_thread_t **vouts = vlc_player_vout_HoldAll(input->player, &vout_count);
    for (size_t i = 0; i < vout_count; ++i)
    {
        if (!viewpoint_ch && var_GetBool(vouts[i], "viewpoint-changeable"))
            viewpoint_ch = true;
        vout_Release(vouts[i]);
    }
    free(vouts);

    if (viewpoint_ch && viewpoint_updated)
    {
        vlc_viewpoint_from_euler(&vp, yaw, pitch, roll);
        vlc_player_input_UpdateViewpoint(input, &vp, VLC_PLAYER_WHENCE_RELATIVE);
    }
    else if (seek_direction != 0)
    {
        /* Seek or change volume if the input doesn't have navigation or viewpoint */
        vlc_tick_t it = vlc_tick_from_sec(seek_direction
                      * var_InheritInteger(player, "short-jump-size"));
        vlc_player_input_SeekByTime(input, it, VLC_PLAYER_SEEK_PRECISE,
                                    VLC_PLAYER_WHENCE_RELATIVE);
    }
    else
    {
        assert(vol_direction != 0);
        if (input == player->input)
            vlc_player_aout_IncrementVolume(player, vol_direction, NULL);
    }
}

static void
vlc_player_input_MouseFallback(struct vlc_player_input *input)
{
    vlc_player_t *player = input->player;
    vlc_player_TogglePause(player);
}

static void
vlc_player_DisplayFrameError(vlc_player_t *player,
                             const char *title, const char *title_error,
                             int status)
{
    static const char erange[] = {N_("can't seek back")};
    static const char ebusy[] = {N_("no video found")};
    static const char enotusp[] = {N_("can't pause/seek/pace")};
    static const char einval[] = {N_("invalid state")};

    switch (status)
    {
        case 0:
            vlc_player_osd_Message(player, title);
            break;
        case -EAGAIN:
            break;
        case -ERANGE:
            vlc_dialog_display_error(player, title_error, erange);
            break;
        case -EBUSY:
            vlc_dialog_display_error(player, title_error, ebusy);
            break;
        case -ENOTSUP:
            vlc_dialog_display_error(player, title_error, enotusp);
            break;
        default:
        case -EINVAL:
            vlc_dialog_display_error(player, title_error, einval);
            break;
    }
}

static void
vlc_player_input_FrameNextStatus(struct vlc_player_input *input, int status)
{
    vlc_player_t *player = input->player;

    unsigned count;
    vlc_player_SendEventCount(player, on_next_frame_status, count, status);

    /* Don't display errors if status is handled by the player user */
    if (count != 0)
        return;

    vlc_player_DisplayFrameError(player, _("Next frame"),
                                 _("Next frame error"), status);
}

static void
vlc_player_input_FramePreviousStatus(struct vlc_player_input *input, int status)
{
    vlc_player_t *player = input->player;

    unsigned count;
    vlc_player_SendEventCount(player, on_prev_frame_status, count, status);

    /* Don't display errors if status is handled by the player user */
    if (count != 0)
        return;

    vlc_player_DisplayFrameError(player, _("Previous frame"),
                                 _("Previous frame error"), status);
}

static bool
input_thread_Events(input_thread_t *input_thread,
                    const struct vlc_input_event *event, void *user_data)
{
    struct vlc_player_input *input = user_data;
    vlc_player_t *player = input->player;
    input_thread_private_t *priv = input_priv(input_thread);

    assert(input_thread == input->thread);

    /* No player lock for this event */
    if (event->type == INPUT_EVENT_OUTPUT_CLOCK)
    {
        if (event->output_clock.system_ts != VLC_TICK_INVALID)
        {
            const struct vlc_player_timer_point point = {
                .position = 0,
                .rate = event->output_clock.rate,
                .ts = event->output_clock.ts,
                .length = VLC_TICK_INVALID,
                .system_date = event->output_clock.system_ts,
            };
            vlc_player_UpdateTimer(player, event->output_clock.id,
                                   event->output_clock.master, &point,
                                   VLC_TICK_INVALID,
                                   event->output_clock.frame_rate,
                                   event->output_clock.frame_rate_base, 0);
        }
        else
        {
            vlc_player_UpdateTimerEvent(player, event->output_clock.id,
                                        VLC_PLAYER_TIMER_EVENT_DISCONTINUITY,
                                        VLC_TICK_INVALID);
        }
        return true;
    }

    bool handled = true;

    vlc_mutex_lock(&player->lock);

    switch (event->type)
    {
        case INPUT_EVENT_STATE:
            vlc_player_input_HandleStateEvent(input, event->state.value,
                                              event->state.date);
            break;
        case INPUT_EVENT_EOF:
            handled = vlc_player_input_HandleAtoBLoop(input, true);
            if (handled)
                break;
            if (player->play_and_pause)
            {
                vlc_player_Pause(player);
                handled = true;
            }
            else if (input->repeat > 0)
            {
                input->repeat--;
                handled =
                    input_ControlPush(input->thread,
                                      INPUT_CONTROL_RESET_POSITION, NULL) == 0;
            }
            else
                handled = false;
            break;
        case INPUT_EVENT_RATE:
            input->rate = event->rate;
            vlc_player_SignalAtoBLoop(player);
            vlc_player_SendEvent(player, on_rate_changed, input->rate);
            break;
        case INPUT_EVENT_CAPABILITIES:
        {
            int old_caps = input->capabilities;
            input->capabilities = event->capabilities;
            vlc_player_SendEvent(player, on_capabilities_changed,
                                 old_caps, input->capabilities);
            break;
        }
        case INPUT_EVENT_TIMES:
        {
            bool changed = false;
            vlc_tick_t system_date = VLC_TICK_INVALID;
            vlc_tick_t duration = input_GetItemDuration(input->thread, event->times.length);

            if (event->times.time != VLC_TICK_INVALID
             && (input->time != event->times.time
              || input->position != event->times.position))
            {
                input->time = event->times.time;
                input->position = event->times.position;
                system_date = vlc_tick_now();
                changed = true;
                vlc_player_SendEvent(player, on_position_changed,
                                     input->time, input->position);

                vlc_player_input_HandleAtoBLoop(input, false);
            }
            if (input->length != duration)
            {
                input->length = duration;
                input_item_SetDuration(input_GetItem(input->thread), duration);
                vlc_player_SendEvent(player, on_length_changed, input->length);
                changed = true;
            }

            if (input->live != event->times.live)
            {
                input->live = event->times.live;
                changed = true;
            }

            if (input->normal_time != event->times.normal_time)
            {
                input->normal_time = event->times.normal_time;
                changed = true;
            }

            if (changed)
            {
                const struct vlc_player_timer_point point = {
                    .position = input->position,
                    .rate = input->rate,
                    .ts = input->time,
                    .length = input->length,
                    .live = input->live,
                    .system_date = system_date,
                };
                vlc_player_UpdateTimer(player, NULL, false, &point,
                                       input->normal_time, 0, 0, priv->i_start);
                vlc_player_SignalAtoBLoop(player);
            }
            break;
        }
        case INPUT_EVENT_PROGRAM:
            vlc_player_input_HandleProgramEvent(input, &event->program);
            break;
        case INPUT_EVENT_ES:
            vlc_player_input_HandleEsEvent(input, &event->es);
            break;
        case INPUT_EVENT_TITLE:
            vlc_player_input_HandleTitleEvent(input, &event->title);
            break;
        case INPUT_EVENT_CHAPTER:
            vlc_player_input_HandleChapterEvent(input, &event->chapter);
            break;
        case INPUT_EVENT_RECORD:
            input->recording = event->record;
            vlc_player_SendEvent(player, on_recording_changed, input->recording);
            break;
        case INPUT_EVENT_STATISTICS:
            input->stats = *event->stats;
            vlc_player_SendEvent(player, on_statistics_changed, &input->stats);
            break;
        case INPUT_EVENT_SIGNAL:
            input->signal_quality = event->signal.quality;
            input->signal_strength = event->signal.strength;
            vlc_player_SendEvent(player, on_signal_changed,
                                 input->signal_quality, input->signal_strength);
            break;
        case INPUT_EVENT_CACHE:
            if (event->cache == 0.0f)
                vlc_player_UpdateTimerEvent(player, NULL,
                                            VLC_PLAYER_TIMER_EVENT_DISCONTINUITY,
                                            VLC_TICK_INVALID);
            input->cache = event->cache;
            vlc_player_SendEvent(player, on_buffering_changed, event->cache);
            break;
        case INPUT_EVENT_VOUT:
            vlc_player_input_HandleVoutEvent(input, &event->vout);
            break;
        case INPUT_EVENT_OUTPUT_STATE:
            if (event->output_state.action == VLC_INPUT_EVENT_OUTPUT_STATE_PAUSED)
                vlc_player_UpdateTimerEvent(player, event->output_state.id,
                                            VLC_PLAYER_TIMER_EVENT_PAUSED,
                                            event->output_state.paused_date);
            break;
        case INPUT_EVENT_ITEM_META:
        case INPUT_EVENT_ITEM_INFO:
            vlc_player_SendEvent(player, on_media_meta_changed,
                                 input_GetItem(input->thread));
            break;
        case INPUT_EVENT_ITEM_EPG:
            vlc_player_SendEvent(player, on_media_epg_changed,
                                 input_GetItem(input->thread));
            break;
        case INPUT_EVENT_SUBITEMS:
            vlc_player_SendEvent(player, on_media_subitems_changed,
                                 input_GetItem(input->thread), event->subitems);
            input_item_node_Delete(event->subitems);
            break;
        case INPUT_EVENT_DEAD:
            vlc_player_destructor_AddJoinableInput(player, input);
            break;
        case INPUT_EVENT_VBI_PAGE:
            input->teletext_page = event->vbi_page < 999 ? event->vbi_page : 100;
            vlc_player_SendEvent(player, on_teletext_page_changed,
                                 input->teletext_page);
            break;
        case INPUT_EVENT_VBI_TRANSPARENCY:
            input->teletext_transparent = event->vbi_transparent;
            vlc_player_SendEvent(player, on_teletext_transparency_changed,
                                 input->teletext_transparent);
            break;
        case INPUT_EVENT_ATTACHMENTS:
            vlc_player_SendEvent(player, on_media_attachments_added,
                                 input_GetItem(input->thread),
                                 event->attachments.array,
                                 event->attachments.count);
            break;
        case INPUT_EVENT_NAV_FAILED:
            vlc_player_input_NavigationFallback(input, event->nav_type);
            break;
        case INPUT_EVENT_MOUSE_LEFT:
            vlc_player_input_MouseFallback(input);
            break;
        case INPUT_EVENT_FRAME_NEXT_STATUS:
            vlc_player_input_FrameNextStatus(input,
                                             event->frame_next_status);
            break;
        case INPUT_EVENT_FRAME_PREVIOUS_STATUS:
            vlc_player_input_FramePreviousStatus(input,
                                                 event->frame_previous_status);
            break;
        default:
            handled = false;
            break;
    }

    vlc_mutex_unlock(&player->lock);
    return handled;
}

void
vlc_player_input_SelectTracksByStringIds(struct vlc_player_input *input,
                                         enum es_format_category_e cat,
                                         const char *str_ids)
{
    input_SetEsCatIds(input->thread, cat, str_ids);
}

char *
vlc_player_input_GetSelectedTrackStringIds(struct vlc_player_input *input,
                                           enum es_format_category_e cat)
{
    vlc_player_track_vector *vec = vlc_player_input_GetTrackVector(input, cat);
    assert(vec);
    bool first_track = true;
    struct vlc_memstream ms;

    struct vlc_player_track_priv* t;
    vlc_vector_foreach(t, vec)
    {
        if (t->selected_by_user && vlc_es_id_IsStrIdStable(t->t.es_id))
        {
            if (first_track)
            {
                int ret = vlc_memstream_open(&ms);
                if (ret != 0)
                    return NULL;
            }
            const char *str_id = vlc_es_id_GetStrId(t->t.es_id);
            assert(str_id);

            if (!first_track)
                vlc_memstream_putc(&ms, ',');
            vlc_memstream_puts(&ms, str_id);
            first_track = false;
        }
    }
    return !first_track && vlc_memstream_close(&ms) == 0 ? ms.ptr : NULL;
}

struct vlc_player_input *
vlc_player_input_New(vlc_player_t *player, input_item_t *item)
{
    struct vlc_player_input *input = malloc(sizeof(*input));
    if (!input)
        return NULL;

    input->player = player;
    input->started = false;
    input->playing = false;

    input->state = VLC_PLAYER_STATE_STOPPED;
    input->error = VLC_PLAYER_ERROR_NONE;
    input->rate = 1.f;
    input->capabilities = 0;
    input->length = input->time = VLC_TICK_INVALID;
    input->live = false;
    input->normal_time = VLC_TICK_0;
    input->pause_date = VLC_TICK_INVALID;
    input->position = 0.f;

    input->recording = false;

    input->cache = 0.f;
    input->signal_quality = input->signal_strength = -1.f;

    memset(&input->stats, 0, sizeof(input->stats));

    vlc_vector_init(&input->program_vector);
    vlc_vector_init(&input->video_track_vector);
    vlc_vector_init(&input->audio_track_vector);
    vlc_vector_init(&input->spu_track_vector);
    input->teletext_source = NULL;

    input->titles = NULL;
    input->title_selected = input->chapter_selected = 0;

    input->teletext_enabled = input->teletext_transparent = false;
    input->teletext_page = 0;

    input->abloop_state[0].set = input->abloop_state[1].set = false;

    memset(&input->ml.states, 0, sizeof(input->ml.states));
    input->ml.states.aspect_ratio = input->ml.states.crop =
        input->ml.states.deinterlace = input->ml.states.video_filter = NULL;
    input->ml.states.current_title = -1;
    input->ml.states.current_video_track =
        input->ml.states.current_audio_track =
        input->ml.states.current_subtitle_track = NULL;
    input->ml.restore = VLC_RESTOREPOINT_NONE;
    input->ml.restore_states = false;
    input->ml.delay_restore = false;
    input->ml.pos = -1.f;
    input->ml.has_audio_tracks = input->ml.has_video_tracks = false;

    static const struct vlc_input_thread_callbacks cbs = {
        .on_event = input_thread_Events,
    };

    const struct vlc_input_thread_cfg cfg = {
        .type = INPUT_TYPE_PLAYBACK,
        .hw_dec = INPUT_CFG_HW_DEC_DEFAULT,
        .resource = player->resource,
        .renderer = player->renderer,
        .cbs = &cbs,
        .cbs_data = input,
    };

    input->repeat = player->repeat;

    input->thread = input_Create(player, item, &cfg);
    if (!input->thread)
    {
        free(input);
        return NULL;
    }
    vlc_player_input_RestoreMlStates(input, false);

    if (player->video_string_ids)
        vlc_player_input_SelectTracksByStringIds(input, VIDEO_ES,
                                                 player->video_string_ids);

    if (player->audio_string_ids)
        vlc_player_input_SelectTracksByStringIds(input, AUDIO_ES,
                                                 player->audio_string_ids);

    if (player->sub_string_ids)
        vlc_player_input_SelectTracksByStringIds(input, SPU_ES,
                                                 player->sub_string_ids);

    /* Initial sub/audio delay */
    const vlc_tick_t cat_delays[DATA_ES] = {
        [AUDIO_ES] =
            VLC_TICK_FROM_MS(var_InheritInteger(player, "audio-desync")),
        [SPU_ES] =
            vlc_tick_from_samples(var_InheritInteger(player, "sub-delay"), 10),
    };

    for (enum es_format_category_e i = UNKNOWN_ES; i < DATA_ES; ++i)
    {
        input->cat_delays[i] = cat_delays[i];
        if (cat_delays[i] != 0)
        {
            int ret = input_SetEsCatDelay(input->thread, i, cat_delays[i]);
            if (ret == VLC_SUCCESS)
                vlc_player_SendEvent(player, on_category_delay_changed, i,
                                     cat_delays[i]);
        }
    }
    return input;
}

void
vlc_player_input_Delete(struct vlc_player_input *input)
{
    assert(input->titles == NULL);
    assert(input->program_vector.size == 0);
    assert(input->video_track_vector.size == 0);
    assert(input->audio_track_vector.size == 0);
    assert(input->spu_track_vector.size == 0);
    assert(input->teletext_source == NULL);

    vlc_vector_destroy(&input->program_vector);
    vlc_vector_destroy(&input->video_track_vector);
    vlc_vector_destroy(&input->audio_track_vector);
    vlc_vector_destroy(&input->spu_track_vector);

    input_Close(input->thread);
    free(input);
}

Messung
CHZG
83989993

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104852
#Domains=417160