mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
[ASVideoNode] Improve playerStatus behaviour and image generation (#2024)
* [ASVideoNode] Use the videoComposition when generating images. * [ASVideoNode] Improve playerState behaviour. * [ASVideoNode] Use KVO on _player.rate to determine the playerState.
This commit is contained in:
parent
3c0fea80ef
commit
39cb188b9e
@ -37,6 +37,7 @@ static void *ASVideoNodeContext = &ASVideoNodeContext;
|
|||||||
static NSString * const kPlaybackLikelyToKeepUpKey = @"playbackLikelyToKeepUp";
|
static NSString * const kPlaybackLikelyToKeepUpKey = @"playbackLikelyToKeepUp";
|
||||||
static NSString * const kplaybackBufferEmpty = @"playbackBufferEmpty";
|
static NSString * const kplaybackBufferEmpty = @"playbackBufferEmpty";
|
||||||
static NSString * const kStatus = @"status";
|
static NSString * const kStatus = @"status";
|
||||||
|
static NSString * const kRate = @"rate";
|
||||||
|
|
||||||
@interface ASVideoNode ()
|
@interface ASVideoNode ()
|
||||||
{
|
{
|
||||||
@ -208,6 +209,25 @@ static NSString * const kStatus = @"status";
|
|||||||
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
|
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)addPlayerObservers:(AVPlayer *)player
|
||||||
|
{
|
||||||
|
if (player == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[player addObserver:self forKeyPath:kRate options:NSKeyValueObservingOptionNew context:ASVideoNodeContext];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) removePlayerObservers:(AVPlayer *)player
|
||||||
|
{
|
||||||
|
@try {
|
||||||
|
[player removeObserver:self forKeyPath:kRate context:ASVideoNodeContext];
|
||||||
|
}
|
||||||
|
@catch (NSException * __unused exception) {
|
||||||
|
NSLog(@"Unnecessary KVO removal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)layout
|
- (void)layout
|
||||||
{
|
{
|
||||||
[super layout];
|
[super layout];
|
||||||
@ -267,6 +287,7 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
AVAssetImageGenerator *previewImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
|
AVAssetImageGenerator *previewImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
|
||||||
previewImageGenerator.appliesPreferredTrackTransform = YES;
|
previewImageGenerator.appliesPreferredTrackTransform = YES;
|
||||||
|
previewImageGenerator.videoComposition = _videoComposition;
|
||||||
|
|
||||||
[previewImageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:imageTime]]
|
[previewImageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:imageTime]]
|
||||||
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) {
|
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) {
|
||||||
@ -291,21 +312,28 @@ static NSString * const kStatus = @"status";
|
|||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (object != _currentPlayerItem) {
|
if (object == _currentPlayerItem) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([keyPath isEqualToString:kStatus]) {
|
if ([keyPath isEqualToString:kStatus]) {
|
||||||
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
||||||
|
if (self.playerState != ASVideoNodePlayerStatePlaying) {
|
||||||
self.playerState = ASVideoNodePlayerStateReadyToPlay;
|
self.playerState = ASVideoNodePlayerStateReadyToPlay;
|
||||||
|
}
|
||||||
// If we don't yet have a placeholder image update it now that we should have data available for it
|
// If we don't yet have a placeholder image update it now that we should have data available for it
|
||||||
if (self.image == nil && self.URL == nil) {
|
if (self.image == nil && self.URL == nil) {
|
||||||
[self generatePlaceholderImage];
|
[self generatePlaceholderImage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) {
|
} else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) {
|
||||||
|
BOOL likelyToKeepUp = [change[NSKeyValueChangeNewKey] boolValue];
|
||||||
|
if (likelyToKeepUp && self.playerState == ASVideoNodePlayerStatePlaying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!likelyToKeepUp) {
|
||||||
|
self.playerState = ASVideoNodePlayerStateLoading;
|
||||||
|
} else if (self.playerState != ASVideoNodePlayerStateFinished) {
|
||||||
self.playerState = ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying;
|
self.playerState = ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying;
|
||||||
if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || [change[NSKeyValueChangeNewKey] boolValue]) && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
}
|
||||||
|
if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || likelyToKeepUp) && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||||
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) {
|
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) {
|
||||||
[_delegate videoNodeDidRecoverFromStall:self];
|
[_delegate videoNodeDidRecoverFromStall:self];
|
||||||
}
|
}
|
||||||
@ -316,6 +344,17 @@ static NSString * const kStatus = @"status";
|
|||||||
self.playerState = ASVideoNodePlayerStateLoading;
|
self.playerState = ASVideoNodePlayerStateLoading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (object == _player) {
|
||||||
|
if ([keyPath isEqualToString:kRate]) {
|
||||||
|
if ([change[NSKeyValueChangeNewKey] floatValue] == 0.0) {
|
||||||
|
if (self.playerState == ASVideoNodePlayerStatePlaying) {
|
||||||
|
self.playerState = ASVideoNodePlayerStatePaused;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.playerState = ASVideoNodePlayerStatePlaying;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tapped
|
- (void)tapped
|
||||||
@ -345,14 +384,11 @@ static NSString * const kStatus = @"status";
|
|||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
AVAsset *asset = self.asset;
|
AVAsset *asset = self.asset;
|
||||||
// Return immediately if the asset is nil;
|
// Return immediately if the asset is nil;
|
||||||
if (asset == nil || self.playerState == ASVideoNodePlayerStateInitialLoading) {
|
if (asset == nil || self.playerState != ASVideoNodePlayerStateUnknown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Nothing appears to prevent this method from sending the delegate notification / calling load on the asset
|
self.playerState = ASVideoNodePlayerStateLoading;
|
||||||
// multiple times, even after the asset is fully loaded and ready to play. There should probably be a playerState
|
|
||||||
// for NotLoaded or such, besides Unknown, so this can be easily checked before proceeding.
|
|
||||||
self.playerState = ASVideoNodePlayerStateInitialLoading;
|
|
||||||
if (_delegateFlags.delegateVideoNodeDidStartInitialLoading) {
|
if (_delegateFlags.delegateVideoNodeDidStartInitialLoading) {
|
||||||
[_delegate videoNodeDidStartInitialLoading:self];
|
[_delegate videoNodeDidStartInitialLoading:self];
|
||||||
}
|
}
|
||||||
@ -396,6 +432,7 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
self.player = nil;
|
self.player = nil;
|
||||||
self.currentItem = nil;
|
self.currentItem = nil;
|
||||||
|
self.playerState = ASVideoNodePlayerStateUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,12 +640,6 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
[_player play];
|
[_player play];
|
||||||
_shouldBePlaying = YES;
|
_shouldBePlaying = YES;
|
||||||
|
|
||||||
if (![self ready]) {
|
|
||||||
self.playerState = ASVideoNodePlayerStateLoading;
|
|
||||||
} else {
|
|
||||||
self.playerState = ASVideoNodePlayerStatePlaying;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)ready
|
- (BOOL)ready
|
||||||
@ -622,7 +653,6 @@ static NSString * const kStatus = @"status";
|
|||||||
if (![self isStateChangeValid:ASVideoNodePlayerStatePaused]) {
|
if (![self isStateChangeValid:ASVideoNodePlayerStatePaused]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.playerState = ASVideoNodePlayerStatePaused;
|
|
||||||
[_player pause];
|
[_player pause];
|
||||||
_shouldBePlaying = NO;
|
_shouldBePlaying = NO;
|
||||||
}
|
}
|
||||||
@ -731,9 +761,16 @@ static NSString * const kStatus = @"status";
|
|||||||
- (void)setPlayer:(AVPlayer *)player
|
- (void)setPlayer:(AVPlayer *)player
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
|
[self removePlayerObservers:_player];
|
||||||
|
|
||||||
_player = player;
|
_player = player;
|
||||||
player.muted = _muted;
|
player.muted = _muted;
|
||||||
((AVPlayerLayer *)_playerNode.layer).player = player;
|
((AVPlayerLayer *)_playerNode.layer).player = player;
|
||||||
|
|
||||||
|
if (player != nil) {
|
||||||
|
[self addPlayerObservers:player];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldBePlaying
|
- (BOOL)shouldBePlaying
|
||||||
@ -755,6 +792,7 @@ static NSString * const kStatus = @"status";
|
|||||||
[_player removeTimeObserver:_timeObserver];
|
[_player removeTimeObserver:_timeObserver];
|
||||||
_timeObserver = nil;
|
_timeObserver = nil;
|
||||||
[self removePlayerItemObservers:_currentPlayerItem];
|
[self removePlayerItemObservers:_currentPlayerItem];
|
||||||
|
[self removePlayerObservers:_player];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user