VLC ABLoop实现原理
设置ABLoop
vlc_player.h声明
VLC_API int
vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop abloop);
src/player/player.c实现
int
vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop abloop)
{
struct vlc_player_input *input = vlc_player_get_input_locked(player);
if (!input || !vlc_player_CanSeek(player))
return VLC_EGENERIC;
vlc_tick_t time = vlc_player_GetTime(player);
float pos = vlc_player_GetPosition(player);
int ret = VLC_SUCCESS;
switch (abloop)
{
case VLC_PLAYER_ABLOOP_A:
if (input->abloop_state[1].set)
return VLC_EGENERIC;
input->abloop_state[0].time = time;
input->abloop_state[0].pos = pos;
input->abloop_state[0].set = true;
break;
case VLC_PLAYER_ABLOOP_B:
if (!input->abloop_state[0].set)
return VLC_EGENERIC;
input->abloop_state[1].time = time;
input->abloop_state[1].pos = pos;
input->abloop_state[1].set = true;
if (input->abloop_state[0].time != VLC_TICK_INVALID
&& time != VLC_TICK_INVALID)
{
if (time > input->abloop_state[0].time)
{
vlc_player_SetTime(player, input->abloop_state[0].time);
break;
}
}
else if (pos > input->abloop_state[0].pos)
{
vlc_player_SetPosition(player, input->abloop_state[0].pos);
break;
}
/* Error: A time is superior to B time. */
abloop = VLC_PLAYER_ABLOOP_NONE;
ret = VLC_EGENERIC;
/* fall-through */
case VLC_PLAYER_ABLOOP_NONE:
input->abloop_state[0].set = input->abloop_state[1].set = false;
time = VLC_TICK_INVALID;
pos = 0.f;
break;
default:
vlc_assert_unreachable();
}
vlc_player_SendEvent(player, on_atobloop_changed, abloop, time, pos);
return ret;
}
业务调用过程:
//AtoBButton.qml
onClicked: Player.toggleABloopState()
==>
//player_controller.cpp
void PlayerController::toggleABloopState()
==>
void PlayerController::setABloopState(ABLoopState state)
==>
//vlc_player.h
vlc_player_SetAtoBLoop( d->m_player, static_cast<vlc_player_abloop>(state));
设置完后会有状态回调
//src/player/palyer.c
vlc_player_SendEvent(player, on_atobloop_changed, abloop, time, pos);
//src/player/palyer.h
struct vlc_list listeners;
#define vlc_player_SendEvent(player, event, ...) do { \
vlc_player_listener_id *listener; \
vlc_list_foreach(listener, &player->listeners, node) \
{ \
if (listener->cbs->event) \
listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
} \
} while(0)
<==
vlc_player_listener_id *
vlc_player_AddListener(vlc_player_t *player,
const struct vlc_player_cbs *cbs, void *cbs_data)
{
assert(cbs);
vlc_player_assert_locked(player);
vlc_player_listener_id *listener = malloc(sizeof(*listener));
if (!listener)
return NULL;
listener->cbs = cbs;
listener->cbs_data = cbs_data;
vlc_list_append(&listener->node, &player->listeners);
return listener;
}
<==
//modules/gui/qt/player_controller.cpp
PlayerControllerPrivate::PlayerControllerPrivate(PlayerController *playercontroller, qt_intf_t *p_intf)
{
{
vlc_player_locker locker{m_player};
m_player_listener = vlc_player_AddListener( m_player, &player_cbs, this );
m_player_aout_listener = vlc_player_aout_AddListener( m_player, &player_aout_cbs, this );
m_player_vout_listener = vlc_player_vout_AddListener( m_player, &player_vout_cbs, this );
m_player_timer = vlc_player_AddTimer( m_player, VLC_TICK_FROM_MS(500), &player_timer_cbs, this );
}
}
//modules/gui/qt/player_controller.cpp
static const struct vlc_player_cbs player_cbs = {
on_player_current_media_changed,
on_player_stats_changed,
on_player_atobloop_changed,
nullptr, // on_media_attachments_added: not used
on_player_vout_changed,
on_player_corks_changed,
};
//modules/gui/qt/player_controller.cpp 状态回调到了UI
static void on_player_atobloop_changed(vlc_player_t *, enum vlc_player_abloop state, vlc_tick_t time, double, void *data)
{
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
msg_Dbg( that->p_intf, "on_player_atobloop_changed");
that->callAsync([that,state,time] (){
PlayerController* q = that->q_func();
switch (state) {
case VLC_PLAYER_ABLOOP_NONE:
that->m_ABLoopA = VLC_TICK_INVALID;
that->m_ABLoopB = VLC_TICK_INVALID;
emit q->ABLoopAChanged(that->m_ABLoopA);
emit q->ABLoopBChanged(that->m_ABLoopB);
break;
case VLC_PLAYER_ABLOOP_A:
that->m_ABLoopA = time;
emit q->ABLoopAChanged(that->m_ABLoopA);
break;
case VLC_PLAYER_ABLOOP_B:
that->m_ABLoopB = time;
emit q->ABLoopBChanged(that->m_ABLoopB);
break;
}
that->m_ABLoopState = static_cast<PlayerController::ABLoopState>(state);
emit q->ABLoopStateChanged(that->m_ABLoopState);
});
}
player/innput.c 检查是否到了B点,到B点后重新从A点执行
//player/innput.c
static void
vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, vlc_tick_t time,
double pos)
{
vlc_player_t *player = input->player;
if (player->input != input)
return;
assert(input->abloop_state[0].set && input->abloop_state[1].set);
if (time != VLC_TICK_INVALID
&& input->abloop_state[0].time != VLC_TICK_INVALID
&& input->abloop_state[1].time != VLC_TICK_INVALID)
{ if (time >= input->abloop_state[1].time)
vlc_player_SetTime(player, input->abloop_state[0].time);
}
else if (pos >= input->abloop_state[1].pos)
vlc_player_SetPosition(player, input->abloop_state[0].pos);
}
<==
player/input.c
static void
vlc_player_input_UpdateTime(struct vlc_player_input *input)
{
if (input->abloop_state[0].set && input->abloop_state[1].set)
{
vlc_tick_t now = vlc_tick_now();
vlc_player_input_HandleAtoBLoop(input,
vlc_player_input_GetTime(input, false, now),
vlc_player_input_GetPos(input, false, now));
}
}
<==
//player/input.c
static void
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;
}
vlc_mutex_lock(&player->lock);
switch (event->type)
{
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_UpdateTime(input);
}
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->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,
.system_date = system_date,
};
vlc_player_UpdateTimer(player, NULL, false, &point,
input->normal_time, 0, 0, priv->i_start);
}
break;
}
case INPUT_EVENT_VOUT:
vlc_player_input_HandleVoutEvent(input, &event->vout);
break;
case INPUT_EVENT_SUBITEMS:
vlc_player_SendEvent(player, on_media_subitems_changed,
input_GetItem(input->thread), event->subitems);
break;
case INPUT_EVENT_DEAD:
if (input->started) /* Can happen with early input_thread fails */
vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
VLC_TICK_INVALID);
vlc_player_destructor_AddJoinableInput(player, input);
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;
}
vlc_mutex_unlock(&player->lock);
}
struct vlc_player_input *
vlc_player_input_New(vlc_player_t *player, input_item_t *item)
{
input->thread = input_Create(player, input_thread_Events, input, item,
INPUT_TYPE_NONE, player->resource,
player->renderer);
}
《==
static inline void input_SendEventTimes(input_thread_t *p_input,
double f_position, vlc_tick_t i_time,
vlc_tick_t i_normal_time,
vlc_tick_t i_length)
{
input_SendEvent(p_input, &(struct vlc_input_event) {
.type = INPUT_EVENT_TIMES,
.times = { f_position, i_time, i_normal_time, i_length }
});
}
static int EsOutVaPrivControlLocked(es_out_sys_t *p_sys, input_source_t *source,
int query, va_list args)
{
case ES_OUT_PRIV_SET_TIMES:
{
double f_position = va_arg( args, double );
vlc_tick_t i_time = va_arg( args, vlc_tick_t );
vlc_tick_t i_normal_time = va_arg( args, vlc_tick_t );
vlc_tick_t i_length = va_arg( args, vlc_tick_t );
if (i_normal_time != VLC_TICK_INVALID)
source->i_normal_time = i_normal_time;
if( source != p_sys->main_source )
return VLC_SUCCESS;
if (p_sys->b_buffering)
{
input_SendEventTimes(p_sys->p_input, 0.0, VLC_TICK_INVALID,
i_normal_time, i_length);
return VLC_SUCCESS;
}
vlc_tick_t i_delay;
/* Fix for buffering delay */
if (!input_priv(p_sys->p_input)->p_sout ||
!input_priv(p_sys->p_input)->b_out_pace_control)
i_delay = EsOutGetBuffering(p_sys);
else
i_delay = 0;
if (i_time != VLC_TICK_INVALID)
{
i_time -= i_delay;
if (i_time < VLC_TICK_0)
i_time = VLC_TICK_0;
}
if (i_length != 0)
f_position -= (double)i_delay / i_length;
if (f_position < 0)
f_position = 0;
input_SendEventTimes(p_sys->p_input, f_position, i_time,
i_normal_time, i_length);
return VLC_SUCCESS;
}
}
//src/input/es_out.c
static int EsOutPrivControl(struct vlc_input_es_out *out,
input_source_t *source,
int query,
va_list args)
{
es_out_sys_t *p_sys = PRIV(&out->out);
vlc_mutex_lock( &p_sys->lock );
int ret = EsOutVaPrivControlLocked(p_sys, source, query, args);
vlc_mutex_unlock( &p_sys->lock );
return ret;
}
//src/input/input.c
static void MainLoopStatistics( input_thread_t *p_input )
{
input_thread_private_t *priv = input_priv(p_input);
InputSourceStatistics( priv->master, priv->p_item, priv->p_es_out );
for (size_t i = 0; i < priv->i_slave; i++)
{
input_source_t *in = priv->slave[i];
InputSourceStatistics( in, NULL, in->p_slave_es_out );
}
if (priv->stats != NULL)
{
struct input_stats_t new_stats;
input_stats_Compute(priv->stats, &new_stats);
vlc_mutex_lock(&priv->p_item->lock);
*priv->p_item->p_stats = new_stats;
vlc_mutex_unlock(&priv->p_item->lock);
input_SendEventStatistics(p_input, &new_stats);
}
}
//src/input/input.c
static void MainLoop( input_thread_t *p_input, bool b_interactive )
{
/* Update interface and statistics */
vlc_tick_t now = vlc_tick_now();
if( now >= i_intf_update )
{
MainLoopStatistics( p_input );
i_intf_update = now + VLC_TICK_FROM_MS(250);
}
}
追踪到最后发现精度为250ms
原文地址:https://blog.csdn.net/gdliweibing/article/details/142698801
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!