[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