Merge pull request #599 from Rafostar/framestepping

clapper: Implement frame advance function
This commit is contained in:
Rafał Dzięgiel
2025-12-10 19:07:30 +01:00
committed by GitHub
7 changed files with 95 additions and 0 deletions

View File

@@ -883,6 +883,18 @@ _handle_speed_key_press (ClapperAppWindow *self, gboolean forward)
(forward) ? "av.speed-up" : "av.speed-down", NULL);
}
static void
_handle_advance_frame_key_press (ClapperAppWindow *self)
{
ClapperPlayer *player = clapper_gtk_av_get_player (
CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayerState state = clapper_player_get_state (player);
/* Do not try to advance frame when stopped or buffering */
if (state >= CLAPPER_PLAYER_STATE_PAUSED)
clapper_player_advance_frame (player);
}
static inline void
_handle_progression_key_press (ClapperAppWindow *self)
{
@@ -939,6 +951,10 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval,
if ((state & GDK_MODIFIER_MASK) == 0)
_handle_seek_key_press (self, TRUE);
break;
case GDK_KEY_e:
if ((state & GDK_MODIFIER_MASK) == 0)
_handle_advance_frame_key_press (self);
break;
case GDK_KEY_space:
case GDK_KEY_k:
if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0)

View File

@@ -190,6 +190,16 @@
<property name="accelerator">less</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Advance frame</property>
<property name="accelerator">e</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkShortcutsGroup">
<child>
<object class="GtkShortcutsShortcut">
<property name="direction">ltr</property>

View File

@@ -43,6 +43,8 @@ void clapper_playbin_bus_post_seek (GstBus *bus, gdouble position, ClapperPlayer
void clapper_playbin_bus_post_rate_change (GstBus *bus, gdouble rate);
void clapper_playbin_bus_post_advance_frame (GstBus *bus);
void clapper_playbin_bus_post_stream_change (GstBus *bus);
void clapper_playbin_bus_post_current_item_change (GstBus *bus, ClapperMediaItem *current_item, ClapperQueueItemChangeMode mode);

View File

@@ -41,6 +41,7 @@ enum
CLAPPER_PLAYBIN_BUS_STRUCTURE_SET_PLAY_FLAG,
CLAPPER_PLAYBIN_BUS_STRUCTURE_SEEK,
CLAPPER_PLAYBIN_BUS_STRUCTURE_RATE_CHANGE,
CLAPPER_PLAYBIN_BUS_STRUCTURE_ADVANCE_FRAME,
CLAPPER_PLAYBIN_BUS_STRUCTURE_STREAM_CHANGE,
CLAPPER_PLAYBIN_BUS_STRUCTURE_CURRENT_ITEM_CHANGE,
CLAPPER_PLAYBIN_BUS_STRUCTURE_ITEM_SUBURI_CHANGE,
@@ -53,6 +54,7 @@ static ClapperBusQuark _structure_quarks[] = {
{"set-play-flag", 0},
{"seek", 0},
{"rate-change", 0},
{"advance-frame", 0},
{"stream-change", 0},
{"current-item-change", 0},
{"item-suburi-change", 0},
@@ -568,6 +570,42 @@ _handle_rate_change_msg (GstMessage *msg, const GstStructure *structure, Clapper
}
}
void
clapper_playbin_bus_post_advance_frame (GstBus *bus)
{
GstStructure *structure = gst_structure_new_id_empty (_STRUCTURE_QUARK (ADVANCE_FRAME));
gst_bus_post (bus, gst_message_new_application (NULL, structure));
}
static inline void
_handle_advance_frame_msg (GstMessage *msg, const GstStructure *structure, ClapperPlayer *player)
{
GstEvent *step_event;
/* If we are starting playback or pipeline is going
* to be stopped, ignore advance frame operation */
if (player->current_state < GST_STATE_PAUSED
|| player->target_state < GST_STATE_PAUSED)
return;
/* Pause playback for frame stepping */
if (player->target_state != GST_STATE_PAUSED) {
player->target_state = GST_STATE_PAUSED;
gst_element_set_state (player->playbin, player->target_state);
}
step_event = gst_event_new_step (GST_FORMAT_BUFFERS, 1, 1.0, TRUE, FALSE);
GST_DEBUG_OBJECT (player, "Advancing frame");
clapper_player_remove_tick_source (player);
if (!(player->stepping = gst_element_send_event (player->playbin, step_event))) {
/* FIXME: Should we maybe call _handle_error_msg with
* some error here? Or will playbin post such message for us? */
GST_ERROR_OBJECT (player, "Could not advance frame");
}
}
static inline void
_handle_state_changed_msg (GstMessage *msg, ClapperPlayer *player)
{
@@ -904,6 +942,8 @@ _handle_app_msg (GstMessage *msg, ClapperPlayer *player)
_handle_seek_msg (msg, structure, player);
else if (quark == _STRUCTURE_QUARK (RATE_CHANGE))
_handle_rate_change_msg (msg, structure, player);
else if (quark == _STRUCTURE_QUARK (ADVANCE_FRAME))
_handle_advance_frame_msg (msg, structure, player);
else if (quark == _STRUCTURE_QUARK (STREAM_CHANGE))
_handle_stream_change_msg (msg, structure, player);
else if (quark == _STRUCTURE_QUARK (CURRENT_ITEM_CHANGE))
@@ -1216,6 +1256,11 @@ _handle_async_done_msg (GstMessage *msg G_GNUC_UNUSED, ClapperPlayer *player)
clapper_app_bus_post_simple_signal (player->app_bus,
GST_OBJECT_CAST (player), signal_id);
}
if (player->stepping) {
player->stepping = FALSE;
GST_DEBUG_OBJECT (player, "Frame advanced");
clapper_player_refresh_position (player);
}
if (player->speed_changing) {
if (player->pending_speed != 0) {
GST_DEBUG_OBJECT (player, "Changing rate to pending value: %.2lf -> %.2lf",

View File

@@ -87,6 +87,7 @@ struct _ClapperPlayer
gboolean use_playbin3; // when using playbin3
gboolean had_error; // so we do not do stuff after error
gboolean seeking; // during seek operation
gboolean stepping; // during frame step operation
gboolean speed_changing; // during rate change operation
gboolean pending_eos; // when pausing due to EOS
gboolean pending_flush; // after another stream selection

View File

@@ -2195,6 +2195,24 @@ clapper_player_seek_custom (ClapperPlayer *self, gdouble position, ClapperPlayer
clapper_playbin_bus_post_seek (self->bus, position, method);
}
/**
* clapper_player_advance_frame:
* @player: a #ClapperPlayer
*
* Request the player to perform a frame step operation.
*
* Note that this will pause playback automatically.
*
* Since: 0.10
*/
void
clapper_player_advance_frame (ClapperPlayer *self)
{
g_return_if_fail (CLAPPER_IS_PLAYER (self));
clapper_playbin_bus_post_advance_frame (self->bus);
}
/**
* clapper_player_add_feature:
* @player: a #ClapperPlayer

View File

@@ -204,6 +204,9 @@ void clapper_player_seek (ClapperPlayer *player, gdouble position);
CLAPPER_API
void clapper_player_seek_custom (ClapperPlayer *player, gdouble position, ClapperPlayerSeekMethod method);
CLAPPER_API
void clapper_player_advance_frame (ClapperPlayer *player);
CLAPPER_API
void clapper_player_add_feature (ClapperPlayer *player, ClapperFeature *feature);