[vlc-commits] [Git][videolan/vlc][master] macosx: Port player time handling from Qt interface

Steve Lhomme (@robUx4) gitlab at videolan.org
Thu May 22 06:26:58 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
97c019ac by Claudio Cambra at 2025-05-22T06:07:39+00:00
macosx: Port player time handling from Qt interface

Based on the work in:
https://code.videolan.org/videolan/vlc/-/commit/6b638f31f82a0889950361d792ea1130109e4e00

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -


2 changed files:

- modules/gui/macosx/playqueue/VLCPlayerController.h
- modules/gui/macosx/playqueue/VLCPlayerController.m


Changes:

=====================================
modules/gui/macosx/playqueue/VLCPlayerController.h
=====================================
@@ -441,7 +441,7 @@ extern const CGFloat VLCVolumeDefault;
  * @note A started and playing media doesn't have necessarily a valid time.
  * @note listen to VLCPlayerTimeAndPositionChanged to be notified about changes to this property
  */
- at property (readonly) float position;
+ at property (readonly) double position;
 
 /**
  * set the playback position as a percentage (range 0.0 to 1.0) for the currently playing media


=====================================
modules/gui/macosx/playqueue/VLCPlayerController.m
=====================================
@@ -89,6 +89,7 @@ const CGFloat VLCVolumeDefault = 1.;
 {
     vlc_player_t *_p_player;
     vlc_player_listener_id *_playerListenerID;
+    vlc_player_timer_id *_playerTimerID;
     vlc_player_aout_listener_id *_playerAoutListenerID;
     vlc_player_vout_listener_id *_playerVoutListenerID;
     vlc_player_title_list *_currentTitleList;
@@ -104,19 +105,28 @@ const CGFloat VLCVolumeDefault = 1.;
 
     NSTimer *_playbackHasTruelyEndedTimer;
     VLCInputItem *_currentMedia;
+
+    struct vlc_player_timer_point _player_time;
 }
 
 @property (readwrite, atomic) iTunesApplication *appleMusicApp;
 @property (readwrite, atomic) iTunesApplication *iTunesApp;
 @property (readwrite, atomic) SpotifyApplication *spotifyApp;
-
+ at property (readwrite, atomic) struct vlc_player_timer_point playerTime;
+ at property (readwrite, atomic) NSTimer *positionTimer;
+ at property (readwrite, atomic) NSTimer *timeTimer;
+
+- (int)interpolateTime:(vlc_tick_t)system_now;
+- (void)updatePositionWithTimer:(NSTimer *)timer;
+- (void)updatePosition;
+- (void)updateTimeWithTimer:(NSTimer *)timer;
+- (void)updateTime:(vlc_tick_t)time forceUpdate:(BOOL)force;
 - (void)currentMediaItemChanged:(input_item_t *)newMediaItem;
 - (void)stateChanged:(enum vlc_player_state)state;
 - (void)errorChanged:(enum vlc_player_error)error;
 - (void)newBufferingValue:(float)bufferValue;
 - (void)newRateValue:(float)rateValue;
 - (void)capabilitiesChanged:(int)newCapabilities;
-- (void)position:(float)position andTimeChanged:(vlc_tick_t)time;
 - (void)lengthChanged:(vlc_tick_t)length;
 - (void)titleListChanged:(vlc_player_title_list *)p_titles;
 - (void)selectedTitleChanged:(size_t)selectedTitle;
@@ -187,15 +197,6 @@ static void cb_player_buffering(vlc_player_t *p_player, float newBufferValue, vo
     });
 }
 
-static void cb_player_rate_changed(vlc_player_t *p_player, float newRateValue, void *p_data)
-{
-    VLC_UNUSED(p_player);
-    dispatch_async(dispatch_get_main_queue(), ^{
-        VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
-        [playerController newRateValue:newRateValue];
-    });
-}
-
 static void cb_player_capabilities_changed(vlc_player_t *p_player, int oldCapabilities, int newCapabilities, void *p_data)
 {
     VLC_UNUSED(p_player); VLC_UNUSED(oldCapabilities);
@@ -205,24 +206,6 @@ static void cb_player_capabilities_changed(vlc_player_t *p_player, int oldCapabi
     });
 }
 
-static void cb_player_position_changed(vlc_player_t *p_player, vlc_tick_t time, double position, void *p_data)
-{
-    VLC_UNUSED(p_player);
-    dispatch_async(dispatch_get_main_queue(), ^{
-        VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
-        [playerController position:position andTimeChanged:time];
-    });
-}
-
-static void cb_player_length_changed(vlc_player_t *p_player, vlc_tick_t newLength, void *p_data)
-{
-    VLC_UNUSED(p_player);
-    dispatch_async(dispatch_get_main_queue(), ^{
-        VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
-        [playerController lengthChanged:newLength];
-    });
-}
-
 static void cb_player_titles_changed(vlc_player_t *p_player,
                                      vlc_player_title_list *p_titles,
                                      void *p_data)
@@ -469,15 +452,80 @@ static void cb_player_vout_changed(vlc_player_t *p_player,
     });
 }
 
+static void cb_player_timer_updated(const struct vlc_player_timer_point * const p_value, void * const p_data)
+{
+    VLCPlayerController * const playerController = (__bridge VLCPlayerController *)p_data;
+    const struct vlc_player_timer_point value_copy = *p_value;
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+        playerController.playerTime = value_copy;
+
+        BOOL lengthOrRateChanged = NO;
+        if (playerController.length != value_copy.length) {
+            [playerController lengthChanged:value_copy.length];
+            lengthOrRateChanged = YES;
+        }
+        if (playerController.playbackRate != value_copy.rate) {
+            [playerController newRateValue:value_copy.rate];
+            lengthOrRateChanged = YES;
+        }
+        const vlc_tick_t system_now = vlc_tick_now();
+        if ([playerController interpolateTime:system_now] == VLC_SUCCESS) {
+            if (lengthOrRateChanged || ![playerController.positionTimer isValid]) {
+                [playerController updatePosition];
+
+                if (value_copy.system_date != INT64_MAX) {
+                    static const vlc_tick_t POSITION_MIN_UPDATE_INTERVAL = VLC_TICK_FROM_MS(15);
+                    vlc_tick_t interval = MAX(value_copy.length / value_copy.rate / VLC_TICK_FROM_MS(1), POSITION_MIN_UPDATE_INTERVAL);
+                    if (interval < POSITION_MIN_UPDATE_INTERVAL)
+                        interval = POSITION_MIN_UPDATE_INTERVAL;
+
+                    playerController.positionTimer =
+                    [NSTimer scheduledTimerWithTimeInterval:SEC_FROM_VLC_TICK(interval)
+                                                     target:playerController
+                                                   selector:@selector(updatePositionWithTimer:)
+                                                   userInfo:nil
+                                                    repeats:YES];
+                }
+            }
+            [playerController updateTime:system_now forceUpdate:lengthOrRateChanged];
+        }
+    });
+}
+
+static void cb_player_timer_paused(const vlc_tick_t system_date, void * const p_data)
+{
+    VLCPlayerController * const playerController = (__bridge VLCPlayerController *)p_data;
+    dispatch_async(dispatch_get_main_queue(), ^{
+        if (system_date != VLC_TICK_INVALID && [playerController interpolateTime:system_date] == VLC_SUCCESS) {
+            // The discontinuity event got a valid system date, update the time properties.
+            [playerController updatePosition];
+            [playerController updateTime:system_date forceUpdate:NO];
+        }
+
+        // And stop the timers.
+        [playerController.positionTimer invalidate];
+        [playerController.timeTimer invalidate];
+    });
+}
+
+static void cb_player_timer_seeked(const struct vlc_player_timer_point * const p_value, void * const p_data)
+{
+    VLCPlayerController * const playerController = (__bridge VLCPlayerController *)p_data;
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [playerController updatePosition];
+    });
+}
+
 static const struct vlc_player_cbs player_callbacks = {
     .on_current_media_changed = cb_player_current_media_changed,
     .on_state_changed = cb_player_state_changed,
     .on_error_changed = cb_player_error_changed,
     .on_buffering_changed = cb_player_buffering,
-    .on_rate_changed = cb_player_rate_changed,
+    .on_rate_changed = NULL, // now handled by timer
     .on_capabilities_changed = cb_player_capabilities_changed,
-    .on_position_changed = cb_player_position_changed,
-    .on_length_changed = cb_player_length_changed,
+    .on_position_changed = NULL, // now handled by timer
+    .on_length_changed = NULL, // now handled by timer
     .on_track_list_changed = cb_player_track_list_changed,
     .on_track_selection_changed = cb_player_track_selection_changed,
     .on_track_delay_changed = cb_player_track_delay_changed,
@@ -504,6 +552,12 @@ static const struct vlc_player_cbs player_callbacks = {
     .on_cork_changed = NULL,
 };
 
+static const struct vlc_player_timer_cbs player_timer_callbacks = {
+    .on_update = cb_player_timer_updated,
+    .on_paused = cb_player_timer_paused,
+    .on_seek = cb_player_timer_seeked
+};
+
 #pragma mark - video specific callback implementations
 
 static void cb_player_vout_fullscreen_changed(vout_thread_t *p_vout, bool isFullscreen, void *p_data)
@@ -597,6 +651,10 @@ static int BossCallback(vlc_object_t *p_this,
         _playerListenerID = vlc_player_AddListener(_p_player,
                                &player_callbacks,
                                (__bridge void *)self);
+        _playerTimerID = vlc_player_AddTimer(_p_player,
+                                             VLC_TICK_FROM_MS(500),
+                                             &player_timer_callbacks,
+                                             (__bridge void *)self);
         vlc_player_Unlock(_p_player);
         _playerAoutListenerID = vlc_player_aout_AddListener(_p_player,
                                                             &player_aout_callbacks,
@@ -665,6 +723,7 @@ static int BossCallback(vlc_object_t *p_this,
         if (_playerListenerID) {
             vlc_player_Lock(_p_player);
             vlc_player_RemoveListener(_p_player, _playerListenerID);
+            vlc_player_RemoveTimer(_p_player, _playerTimerID);
             vlc_player_Unlock(_p_player);
         }
         if (_playerAoutListenerID) {
@@ -681,7 +740,8 @@ static int BossCallback(vlc_object_t *p_this,
     [_defaultNotificationCenter removeObserver:self];
 }
 
-- (VLCInputItem *)currentMedia {
+- (VLCInputItem *)currentMedia
+{
     if (_currentMedia)
         return _currentMedia;
     vlc_player_Lock(_p_player);
@@ -693,6 +753,16 @@ static int BossCallback(vlc_object_t *p_this,
     return _currentMedia;
 }
 
+- (int)interpolateTime:(vlc_tick_t)system_now
+{
+    vlc_tick_t new_time;
+    if (vlc_player_timer_point_Interpolate(&_playerTime, system_now, &new_time, &_position) == VLC_SUCCESS) {
+        _time = new_time != VLC_TICK_INVALID ? new_time - VLC_TICK_0 : 0;
+        return VLC_SUCCESS;
+    }
+    return VLC_EGENERIC;
+}
+
 #pragma mark - playback control methods
 
 - (int)start
@@ -1016,14 +1086,54 @@ static int BossCallback(vlc_object_t *p_this,
                                               object:self];
 }
 
-- (void)position:(float)position andTimeChanged:(vlc_tick_t)time
+- (void)updatePositionWithTimer:(NSTimer *)timer
+{
+    vlc_tick_t system_now = vlc_tick_now();
+    if ([self interpolateTime:system_now] == VLC_SUCCESS) {
+        [self updatePosition];
+    }
+}
+
+- (void)updatePosition
 {
-    _position = position;
-    _time = time;
     [_defaultNotificationCenter postNotificationName:VLCPlayerTimeAndPositionChanged
                                               object:self];
 }
 
+- (void)updateTimeWithTimer:(NSTimer *)timer
+{
+    const vlc_tick_t system_now = vlc_tick_now();
+    if ([self interpolateTime:system_now] == VLC_SUCCESS) {
+        [self updateTime:system_now forceUpdate:NO];
+    }
+}
+
+- (void)updateTime:(vlc_tick_t)systemNow forceUpdate:(BOOL)forceUpdate
+{
+    [_defaultNotificationCenter postNotificationName:VLCPlayerTimeAndPositionChanged
+                                              object:self];
+
+    if (self.playerTime.system_date != INT64_MAX && (forceUpdate || !self.timeTimer.isValid)) {
+        const vlc_tick_t nextUpdateDate =
+            vlc_player_timer_point_GetNextIntervalDate(&_playerTime, systemNow, _time, VLC_TICK_FROM_SEC(1));
+        const vlc_tick_t nextUpdateInterval = nextUpdateDate - systemNow;
+
+        if (nextUpdateInterval > 0) {
+            // The timer can be triggered a little before. In that case, it's
+            // likely that we didn't reach the next next second. It's better to
+            // add a very small delay in order to be triggered after the next
+            // seconds.
+            static const double imprecisionDelaySec = 0.03;
+
+            self.timeTimer = [NSTimer timerWithTimeInterval:SEC_FROM_VLC_TICK(nextUpdateInterval) + imprecisionDelaySec
+                                                     target:self
+                                                   selector:@selector(updateTimeWithTimer:)
+                                                   userInfo:nil
+                                                    repeats:NO];
+        }
+    }
+}
+
 - (void)setTimeFast:(vlc_tick_t)time
 {
     vlc_player_Lock(_p_player);



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/97c019ac1d22fc5a63c0974a7c32df45393cf74d

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/97c019ac1d22fc5a63c0974a7c32df45393cf74d
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list