mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +00:00
[ASVideoPlayerNode] fixes; Ability to add custom controls
This commit is contained in:
parent
03f193c58b
commit
4ab0f1ec60
@ -443,10 +443,6 @@ static NSString * const kStatus = @"status";
|
||||
_asset = asset;
|
||||
|
||||
[self setNeedsDataFetch];
|
||||
|
||||
if (_shouldAutoplay) {
|
||||
[self play];
|
||||
}
|
||||
}
|
||||
|
||||
- (AVAsset *)asset
|
||||
|
||||
@ -29,17 +29,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nullable, atomic, weak) id<ASVideoPlayerNodeDelegate> delegate;
|
||||
|
||||
@property (nonatomic,assign,readonly) CMTime duration;
|
||||
@property (nonatomic, assign, readonly) CMTime duration;
|
||||
|
||||
@property (nonatomic, assign) BOOL disableControls;
|
||||
@property (nonatomic, assign) BOOL controlsDisabled;
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL loadAssetWhenNodeBecomesVisible;
|
||||
|
||||
#pragma mark - ASVideoNode property proxy
|
||||
/**
|
||||
* When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState.
|
||||
* If it leaves the visible interfaceState it will pause but will resume once it has returned.
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAutoplay;
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAutorepeat;
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAutoPlay;
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAutoRepeat;
|
||||
@property (nonatomic, assign, readwrite) BOOL muted;
|
||||
@property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState;
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAggressivelyRecoverFromStall;
|
||||
@ -51,6 +53,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)initWithUrl:(NSURL*)url;
|
||||
- (instancetype)initWithAsset:(AVAsset*)asset;
|
||||
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
|
||||
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
|
||||
|
||||
#pragma mark - Public API
|
||||
- (void)seekToTime:(CGFloat)percentComplete;
|
||||
@ -67,7 +71,16 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @abstract Delegate method invoked before creating controlbar controls
|
||||
* @param videoPlayer
|
||||
*/
|
||||
- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode*)videoPlayer;
|
||||
- (NSArray *)videoPlayerNodeNeededDefaultControls:(ASVideoPlayerNode*)videoPlayer;
|
||||
|
||||
/**
|
||||
* @abstract Delegate method invoked before creating default controls, asks delegate for custom controls dictionary.
|
||||
* This dictionary must constain only ASDisplayNode subclass objects.
|
||||
* @param videoPlayer
|
||||
* @discussion - This method is invoked only when developer implements videoPlayerNodeLayoutSpec:forControls:forMaximumSize:
|
||||
* and gives ability to add custom constrols to ASVideoPlayerNode, for example mute button.
|
||||
*/
|
||||
- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode*)videoPlayer;
|
||||
|
||||
/**
|
||||
* @abstract Delegate method invoked in layoutSpecThatFits:
|
||||
@ -106,7 +119,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark ASVideoNodeDelegate proxy methods
|
||||
/**
|
||||
* @abstract Delegate method invoked when ASVideoPlayerNode playback time is taped.
|
||||
* @abstract Delegate method invoked when ASVideoPlayerNode is taped.
|
||||
* @param videoPlayerNode The ASVideoPlayerNode that was tapped.
|
||||
*/
|
||||
- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer;
|
||||
|
||||
@ -18,7 +18,8 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
__weak id<ASVideoPlayerNodeDelegate> _delegate;
|
||||
|
||||
struct {
|
||||
unsigned int delegateNeededControls:1;
|
||||
unsigned int delegateNeededDefaultControls:1;
|
||||
unsigned int delegateCustomControls:1;
|
||||
unsigned int delegateSpinnerTintColor:1;
|
||||
unsigned int delegatePlaybackButtonTint:1;
|
||||
unsigned int delegateScrubberMaximumTrackTintColor:1;
|
||||
@ -40,7 +41,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
ASVideoNode *_videoNode;
|
||||
|
||||
NSArray *_neededControls;
|
||||
NSArray *_neededDefaultControls;
|
||||
|
||||
NSMutableDictionary *_cachedControls;
|
||||
|
||||
@ -51,13 +52,14 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
ASStackLayoutSpec *_controlFlexGrowSpacerSpec;
|
||||
ASDisplayNode *_spinnerNode;
|
||||
|
||||
BOOL _loadAssetWhenNodeBecomesVisible;
|
||||
BOOL _isSeeking;
|
||||
CMTime _duration;
|
||||
|
||||
BOOL _disableControls;
|
||||
BOOL _controlsDisabled;
|
||||
|
||||
BOOL _shouldAutoplay;
|
||||
BOOL _shouldAutorepeat;
|
||||
BOOL _shouldAutoPlay;
|
||||
BOOL _shouldAutoRepeat;
|
||||
BOOL _muted;
|
||||
int32_t _periodicTimeObserverTimescale;
|
||||
NSString *_gravity;
|
||||
@ -89,6 +91,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
_url = url;
|
||||
_asset = [AVAsset assetWithURL:_url];
|
||||
_loadAssetWhenNodeBecomesVisible = YES;
|
||||
|
||||
[self _init];
|
||||
|
||||
@ -102,7 +105,36 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
}
|
||||
|
||||
_asset = asset;
|
||||
_disableControls = NO;
|
||||
_loadAssetWhenNodeBecomesVisible = YES;
|
||||
|
||||
[self _init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_url = url;
|
||||
_asset = [AVAsset assetWithURL:_url];
|
||||
_loadAssetWhenNodeBecomesVisible = loadAssetWhenNodeBecomesVisible;
|
||||
|
||||
[self _init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_asset = asset;
|
||||
_loadAssetWhenNodeBecomesVisible = loadAssetWhenNodeBecomesVisible;
|
||||
|
||||
[self _init];
|
||||
|
||||
@ -111,16 +143,15 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
|
||||
_defaultControlsColor = [UIColor whiteColor];
|
||||
_cachedControls = [[NSMutableDictionary alloc] init];
|
||||
|
||||
_videoNode = [[ASVideoNode alloc] init];
|
||||
_videoNode.asset = _asset;
|
||||
_videoNode.delegate = self;
|
||||
if (_loadAssetWhenNodeBecomesVisible == NO) {
|
||||
_videoNode.asset = _asset;
|
||||
}
|
||||
[self addSubnode:_videoNode];
|
||||
|
||||
[self addObservers];
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
@ -132,10 +163,21 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*)createControlElementArray
|
||||
- (void)visibilityDidChange:(BOOL)isVisible
|
||||
{
|
||||
if (_delegateFlags.delegateNeededControls) {
|
||||
return [_delegate videoPlayerNodeNeededControls:self];
|
||||
[super visibilityDidChange:isVisible];
|
||||
|
||||
ASDN::MutexLocker l(_videoPlayerLock);
|
||||
|
||||
if (isVisible && _loadAssetWhenNodeBecomesVisible && _asset != _videoNode.asset) {
|
||||
_videoNode.asset = _asset;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)createDefaultControlElementArray
|
||||
{
|
||||
if (_delegateFlags.delegateNeededDefaultControls) {
|
||||
return [_delegate videoPlayerNodeNeededDefaultControls:self];
|
||||
}
|
||||
|
||||
return @[ @(ASVideoPlayerNodeControlTypePlaybackButton),
|
||||
@ -144,35 +186,25 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
@(ASVideoPlayerNodeControlTypeDurationText) ];
|
||||
}
|
||||
|
||||
- (void)addObservers
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)removeObservers
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - UI
|
||||
- (void)createControls
|
||||
{
|
||||
ASDN::MutexLocker l(_videoPlayerLock);
|
||||
|
||||
if (_disableControls) {
|
||||
if (_controlsDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_neededControls == nil) {
|
||||
_neededControls = [self createControlElementArray];
|
||||
if (_neededDefaultControls == nil) {
|
||||
_neededDefaultControls = [self createDefaultControlElementArray];
|
||||
}
|
||||
|
||||
if (_cachedControls == nil) {
|
||||
_cachedControls = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
for (int i = 0; i < _neededControls.count; i++) {
|
||||
ASVideoPlayerNodeControlType type = (ASVideoPlayerNodeControlType)[[_neededControls objectAtIndex:i] integerValue];
|
||||
for (id object in _neededDefaultControls) {
|
||||
ASVideoPlayerNodeControlType type = (ASVideoPlayerNodeControlType)[object integerValue];
|
||||
switch (type) {
|
||||
case ASVideoPlayerNodeControlTypePlaybackButton:
|
||||
[self createPlaybackButton];
|
||||
@ -194,6 +226,19 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
}
|
||||
}
|
||||
|
||||
if (_delegateFlags.delegateCustomControls && _delegateFlags.delegateLayoutSpecForControls) {
|
||||
NSDictionary *customControls = [_delegate videoPlayerNodeCustomControls:self];
|
||||
for (id key in customControls) {
|
||||
id node = customControls[key];
|
||||
if (![node isKindOfClass:[ASDisplayNode class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[self addSubnode:node];
|
||||
[_cachedControls setObject:node forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
ASPerformBlockOnMainThread(^{
|
||||
ASDN::MutexLocker l(_videoPlayerLock);
|
||||
[self setNeedsLayout];
|
||||
@ -246,7 +291,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
{
|
||||
if (_elapsedTextNode == nil) {
|
||||
_elapsedTextNode = [[ASTextNode alloc] init];
|
||||
_elapsedTextNode.attributedString = [self timeLabelAttributedStringForString:@"00:00" forControlType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||
_elapsedTextNode.attributedString = [self timeLabelAttributedStringForString:@"00:00"
|
||||
forControlType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||
_elapsedTextNode.truncationMode = NSLineBreakByClipping;
|
||||
|
||||
[_cachedControls setObject:_elapsedTextNode forKey:@(ASVideoPlayerNodeControlTypeElapsedText)];
|
||||
}
|
||||
@ -257,7 +304,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
{
|
||||
if (_durationTextNode == nil) {
|
||||
_durationTextNode = [[ASTextNode alloc] init];
|
||||
_durationTextNode.attributedString = [self timeLabelAttributedStringForString:@"00:00" forControlType:ASVideoPlayerNodeControlTypeDurationText];
|
||||
_durationTextNode.attributedString = [self timeLabelAttributedStringForString:@"00:00"
|
||||
forControlType:ASVideoPlayerNodeControlTypeDurationText];
|
||||
_durationTextNode.truncationMode = NSLineBreakByClipping;
|
||||
|
||||
[_cachedControls setObject:_durationTextNode forKey:@(ASVideoPlayerNodeControlTypeDurationText)];
|
||||
}
|
||||
@ -317,14 +366,20 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)updateDurationTimeLabel
|
||||
{
|
||||
NSString *formatedDuration = [self timeStringForCMTime:_duration forTimeLabelType:ASVideoPlayerNodeControlTypeDurationText];
|
||||
_durationTextNode.attributedString = [self timeLabelAttributedStringForString:formatedDuration forControlType:ASVideoPlayerNodeControlTypeDurationText];
|
||||
if (!_durationTextNode) {
|
||||
return;
|
||||
}
|
||||
NSString *formattedDuration = [self timeStringForCMTime:_duration forTimeLabelType:ASVideoPlayerNodeControlTypeDurationText];
|
||||
_durationTextNode.attributedString = [self timeLabelAttributedStringForString:formattedDuration forControlType:ASVideoPlayerNodeControlTypeDurationText];
|
||||
}
|
||||
|
||||
- (void)updateElapsedTimeLabel:(NSTimeInterval)seconds
|
||||
{
|
||||
NSString *formatedDuration = [self timeStringForCMTime:CMTimeMakeWithSeconds( seconds, _videoNode.periodicTimeObserverTimescale ) forTimeLabelType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||
_elapsedTextNode.attributedString = [self timeLabelAttributedStringForString:formatedDuration forControlType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||
if (!_elapsedTextNode) {
|
||||
return;
|
||||
}
|
||||
NSString *formatteElapsed = [self timeStringForCMTime:CMTimeMakeWithSeconds( seconds, _videoNode.periodicTimeObserverTimescale ) forTimeLabelType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||
_elapsedTextNode.attributedString = [self timeLabelAttributedStringForString:formatteElapsed forControlType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||
}
|
||||
|
||||
- (NSAttributedString*)timeLabelAttributedStringForString:(NSString*)string forControlType:(ASVideoPlayerNodeControlType)controlType
|
||||
@ -352,7 +407,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
[_delegate videoPlayerNode:self willChangeVideoNodeState:state toVideoNodeState:toState];
|
||||
}
|
||||
|
||||
if (toState == ASVideoNodePlayerStateReadyToPlay && _durationTextNode) {
|
||||
if (toState == ASVideoNodePlayerStateReadyToPlay) {
|
||||
_duration = _videoNode.currentItem.duration;
|
||||
[self updateDurationTimeLabel];
|
||||
}
|
||||
@ -383,9 +438,6 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval
|
||||
{
|
||||
//TODO: ask Max about CMTime problem in ASVideoNode Header file
|
||||
//as we said yesterday, we must use CMTime in ASVideoNode instead of NSTimeInterval
|
||||
//when this will be done, must just proxy value to delegate
|
||||
if (_delegateFlags.delegateVideoNodeDidPlayToTime) {
|
||||
[_delegate videoPlayerNode:self didPlayToTime:_videoNode.player.currentTime];
|
||||
}
|
||||
@ -415,12 +467,12 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
if (_delegateFlags.delegateDidTapVideoPlayerNode) {
|
||||
[_delegate didTapVideoPlayerNode:self];
|
||||
} else {
|
||||
[self manageVideoNodePlayback];
|
||||
[self togglePlayPause];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
- (void)manageVideoNodePlayback
|
||||
- (void)togglePlayPause
|
||||
{
|
||||
if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) {
|
||||
[_videoNode pause];
|
||||
@ -463,7 +515,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)didTapPlaybackButton:(ASControlNode*)node
|
||||
{
|
||||
[self manageVideoNodePlayback];
|
||||
[self togglePlayPause];
|
||||
}
|
||||
|
||||
- (void)beginSeek
|
||||
@ -491,7 +543,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
[_videoNode.player seekToTime:CMTimeMakeWithSeconds(seconds, _videoNode.periodicTimeObserverTimescale)];
|
||||
|
||||
if (_videoNode.playerState != ASVideoNodePlayerStatePlaying) {
|
||||
[self manageVideoNodePlayback];
|
||||
[self togglePlayPause];
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,12 +661,17 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)setDelegate:(id<ASVideoPlayerNodeDelegate>)delegate
|
||||
{
|
||||
if (delegate == _delegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
_delegate = delegate;
|
||||
|
||||
if (_delegate == nil) {
|
||||
memset(&_delegateFlags, 0, sizeof(_delegateFlags));
|
||||
} else {
|
||||
_delegateFlags.delegateNeededControls = [_delegate respondsToSelector:@selector(videoPlayerNodeNeededControls:)];
|
||||
_delegateFlags.delegateNeededDefaultControls = [_delegate respondsToSelector:@selector(videoPlayerNodeNeededDefaultControls:)];
|
||||
_delegateFlags.delegateCustomControls = [_delegate respondsToSelector:@selector(videoPlayerNodeCustomControls:)];
|
||||
_delegateFlags.delegateSpinnerTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeSpinnerTint:)];
|
||||
_delegateFlags.delegateScrubberMaximumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMaximumTrackTint:)];
|
||||
_delegateFlags.delegateScrubberMinimumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMinimumTrackTint:)];
|
||||
@ -632,37 +689,53 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDisableControls:(BOOL)disableControls
|
||||
- (void)setControlsDisabled:(BOOL)controlsDisabled
|
||||
{
|
||||
_disableControls = disableControls;
|
||||
if (_controlsDisabled == controlsDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
_controlsDisabled = controlsDisabled;
|
||||
|
||||
if (_disableControls && _cachedControls.count > 0) {
|
||||
if (_controlsDisabled && _cachedControls.count > 0) {
|
||||
[self removeControls];
|
||||
} else if (!_disableControls) {
|
||||
} else if (!_controlsDisabled) {
|
||||
[self createControls];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setShouldAutoplay:(BOOL)shouldAutoplay
|
||||
- (void)setShouldAutoPlay:(BOOL)shouldAutoPlay
|
||||
{
|
||||
_shouldAutoplay = shouldAutoplay;
|
||||
_videoNode.shouldAutoplay = _shouldAutoplay;
|
||||
if (_shouldAutoPlay == shouldAutoPlay) {
|
||||
return;
|
||||
}
|
||||
_shouldAutoPlay = shouldAutoPlay;
|
||||
_videoNode.shouldAutoplay = _shouldAutoPlay;
|
||||
}
|
||||
|
||||
- (void)setShouldAutorepeat:(BOOL)shouldAutorepeat
|
||||
- (void)setShouldAutoRepeat:(BOOL)shouldAutoRepeat
|
||||
{
|
||||
_shouldAutorepeat = shouldAutorepeat;
|
||||
_videoNode.shouldAutorepeat = YES;
|
||||
if (_shouldAutoRepeat == shouldAutoRepeat) {
|
||||
return;
|
||||
}
|
||||
_shouldAutoRepeat = shouldAutoRepeat;
|
||||
_videoNode.shouldAutorepeat = _shouldAutoRepeat;
|
||||
}
|
||||
|
||||
- (void)setMuted:(BOOL)muted
|
||||
{
|
||||
if (_muted == muted) {
|
||||
return;
|
||||
}
|
||||
_muted = muted;
|
||||
_videoNode.muted = _muted;
|
||||
}
|
||||
|
||||
- (void)setPeriodicTimeObserverTimescale:(int32_t)periodicTimeObserverTimescale
|
||||
{
|
||||
if (_periodicTimeObserverTimescale == periodicTimeObserverTimescale) {
|
||||
return;
|
||||
}
|
||||
_periodicTimeObserverTimescale = periodicTimeObserverTimescale;
|
||||
_videoNode.periodicTimeObserverTimescale = _periodicTimeObserverTimescale;
|
||||
}
|
||||
@ -677,6 +750,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)setGravity:(NSString *)gravity
|
||||
{
|
||||
if (_gravity == gravity) {
|
||||
return;
|
||||
}
|
||||
_gravity = gravity;
|
||||
_videoNode.gravity = _gravity;
|
||||
}
|
||||
@ -693,6 +769,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
- (void)setShouldAggressivelyRecoverFromStall:(BOOL)shouldAggressivelyRecoverFromStall
|
||||
{
|
||||
if (_shouldAggressivelyRecoverFromStall == shouldAggressivelyRecoverFromStall) {
|
||||
return;
|
||||
}
|
||||
_shouldAggressivelyRecoverFromStall = shouldAggressivelyRecoverFromStall;
|
||||
_videoNode.shouldAggressivelyRecoverFromStall = _shouldAggressivelyRecoverFromStall;
|
||||
}
|
||||
@ -719,11 +798,4 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
return videoDurationText;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self removeObservers];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -19,6 +19,12 @@
|
||||
8B0768BF1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768BE1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m */; };
|
||||
8B0768C51CE7C707002E1453 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768C41CE7C707002E1453 /* Utilities.m */; };
|
||||
8B0768C91CE7C889002E1453 /* VideoFeedNodeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768C81CE7C889002E1453 /* VideoFeedNodeController.m */; };
|
||||
8B9075851CF386A400F924C1 /* ico-unmute.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B90757F1CF386A400F924C1 /* ico-unmute.png */; };
|
||||
8B9075861CF386A400F924C1 /* ico-unmute@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075801CF386A400F924C1 /* ico-unmute@2x.png */; };
|
||||
8B9075871CF386A400F924C1 /* ico-unmute@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075811CF386A400F924C1 /* ico-unmute@3x.png */; };
|
||||
8B9075881CF386A400F924C1 /* ico-mute.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075821CF386A400F924C1 /* ico-mute.png */; };
|
||||
8B9075891CF386A400F924C1 /* ico-mute@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075831CF386A400F924C1 /* ico-mute@2x.png */; };
|
||||
8B90758A1CF386A400F924C1 /* ico-mute@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075841CF386A400F924C1 /* ico-mute@3x.png */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -42,6 +48,12 @@
|
||||
8B0768C41CE7C707002E1453 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
|
||||
8B0768C71CE7C889002E1453 /* VideoFeedNodeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoFeedNodeController.h; sourceTree = "<group>"; };
|
||||
8B0768C81CE7C889002E1453 /* VideoFeedNodeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoFeedNodeController.m; sourceTree = "<group>"; };
|
||||
8B90757F1CF386A400F924C1 /* ico-unmute.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-unmute.png"; sourceTree = "<group>"; };
|
||||
8B9075801CF386A400F924C1 /* ico-unmute@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-unmute@2x.png"; sourceTree = "<group>"; };
|
||||
8B9075811CF386A400F924C1 /* ico-unmute@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-unmute@3x.png"; sourceTree = "<group>"; };
|
||||
8B9075821CF386A400F924C1 /* ico-mute.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-mute.png"; sourceTree = "<group>"; };
|
||||
8B9075831CF386A400F924C1 /* ico-mute@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-mute@2x.png"; sourceTree = "<group>"; };
|
||||
8B9075841CF386A400F924C1 /* ico-mute@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-mute@3x.png"; sourceTree = "<group>"; };
|
||||
A2092CAF5607B3863A3700A2 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CFD6AA1D30516C27DEE5602B /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E51646FF8D3676A1D826A5AE /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@ -83,6 +95,7 @@
|
||||
05E2128319D4DB510098F589 /* Sample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8B90757E1CF3869100F924C1 /* Icons */,
|
||||
8B0768C61CE7C85F002E1453 /* Controller */,
|
||||
8B0768B91CE7B07E002E1453 /* Nodes */,
|
||||
8B0768B51CE7ACE8002E1453 /* Models */,
|
||||
@ -155,6 +168,19 @@
|
||||
path = Controller;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B90757E1CF3869100F924C1 /* Icons */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8B90757F1CF386A400F924C1 /* ico-unmute.png */,
|
||||
8B9075801CF386A400F924C1 /* ico-unmute@2x.png */,
|
||||
8B9075811CF386A400F924C1 /* ico-unmute@3x.png */,
|
||||
8B9075821CF386A400F924C1 /* ico-mute.png */,
|
||||
8B9075831CF386A400F924C1 /* ico-mute@2x.png */,
|
||||
8B9075841CF386A400F924C1 /* ico-mute@3x.png */,
|
||||
);
|
||||
path = Icons;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -215,9 +241,15 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8B9075861CF386A400F924C1 /* ico-unmute@2x.png in Resources */,
|
||||
8B9075881CF386A400F924C1 /* ico-mute.png in Resources */,
|
||||
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */,
|
||||
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */,
|
||||
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */,
|
||||
8B90758A1CF386A400F924C1 /* ico-mute@3x.png in Resources */,
|
||||
8B9075851CF386A400F924C1 /* ico-unmute.png in Resources */,
|
||||
8B9075871CF386A400F924C1 /* ico-unmute@3x.png in Resources */,
|
||||
8B9075891CF386A400F924C1 /* ico-mute@2x.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
BIN
examples/ASDKTube/Sample/Icons/ico-mute.png
Normal file
BIN
examples/ASDKTube/Sample/Icons/ico-mute.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 483 B |
BIN
examples/ASDKTube/Sample/Icons/ico-mute@2x.png
Normal file
BIN
examples/ASDKTube/Sample/Icons/ico-mute@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 801 B |
BIN
examples/ASDKTube/Sample/Icons/ico-mute@3x.png
Normal file
BIN
examples/ASDKTube/Sample/Icons/ico-mute@3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/ASDKTube/Sample/Icons/ico-unmute.png
Normal file
BIN
examples/ASDKTube/Sample/Icons/ico-unmute.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 578 B |
BIN
examples/ASDKTube/Sample/Icons/ico-unmute@2x.png
Normal file
BIN
examples/ASDKTube/Sample/Icons/ico-unmute@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/ASDKTube/Sample/Icons/ico-unmute@3x.png
Normal file
BIN
examples/ASDKTube/Sample/Icons/ico-unmute@3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@ -25,6 +25,7 @@
|
||||
ASNetworkImageNode *_avatarNode;
|
||||
ASVideoPlayerNode *_videoPlayerNode;
|
||||
ASControlNode *_likeButtonNode;
|
||||
ASButtonNode *_muteButtonNode;
|
||||
}
|
||||
|
||||
- (instancetype)initWithVideoObject:(VideoModel *)video
|
||||
@ -52,10 +53,16 @@
|
||||
_likeButtonNode.backgroundColor = [UIColor redColor];
|
||||
[self addSubnode:_likeButtonNode];
|
||||
|
||||
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url];
|
||||
_muteButtonNode = [[ASButtonNode alloc] init];
|
||||
_muteButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
|
||||
[_muteButtonNode addTarget:self action:@selector(didTapMuteButton) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
|
||||
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url loadAssetWhenNodeBecomesVisible:YES];
|
||||
_videoPlayerNode.delegate = self;
|
||||
_videoPlayerNode.backgroundColor = [UIColor blackColor];
|
||||
[self addSubnode:_videoPlayerNode];
|
||||
|
||||
[self setMuteButtonIcon];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -98,55 +105,113 @@
|
||||
return verticalStack;
|
||||
}
|
||||
|
||||
- (void)setMuteButtonIcon
|
||||
{
|
||||
if (_videoPlayerNode.muted) {
|
||||
[_muteButtonNode setImage:[UIImage imageNamed:@"ico-mute"] forState:ASControlStateNormal];
|
||||
} else {
|
||||
[_muteButtonNode setImage:[UIImage imageNamed:@"ico-unmute"] forState:ASControlStateNormal];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didTapMuteButton
|
||||
{
|
||||
_videoPlayerNode.muted = !_videoPlayerNode.muted;
|
||||
[self setMuteButtonIcon];
|
||||
}
|
||||
|
||||
#pragma mark - ASVideoPlayerNodeDelegate
|
||||
- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer
|
||||
{
|
||||
if (_videoPlayerNode.isPlaying) {
|
||||
if (_videoPlayerNode.playerState == ASVideoNodePlayerStatePlaying) {
|
||||
NSLog(@"TRANSITION");
|
||||
_videoPlayerNode.controlsDisabled = !_videoPlayerNode.controlsDisabled;
|
||||
[_videoPlayerNode pause];
|
||||
} else {
|
||||
[_videoPlayerNode play];
|
||||
}
|
||||
}
|
||||
|
||||
/*- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer
|
||||
- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode *)videoPlayer
|
||||
{
|
||||
return @[ @(ASVideoPlayerNodeControlTypePlaybackButton) ];
|
||||
}*/
|
||||
return @{
|
||||
@"muteControl" : _muteButtonNode
|
||||
};
|
||||
}
|
||||
|
||||
/*- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize
|
||||
- (NSArray *)controlsForControlBar:(NSDictionary *)availableControls
|
||||
{
|
||||
NSMutableArray *bottomControls = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *controls = [[NSMutableArray alloc] init];
|
||||
|
||||
ASDisplayNode *playbackButtonNode = controls[@(ASVideoPlayerNodeControlTypePlaybackButton)];
|
||||
|
||||
if (playbackButtonNode) {
|
||||
[bottomControls addObject:playbackButtonNode];
|
||||
if (availableControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]) {
|
||||
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]];
|
||||
}
|
||||
|
||||
if (availableControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]) {
|
||||
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]];
|
||||
}
|
||||
|
||||
if (availableControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
|
||||
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]];
|
||||
}
|
||||
|
||||
if (availableControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]) {
|
||||
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]];
|
||||
}
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
- (ASLayoutSpec*)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize
|
||||
{
|
||||
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||
spacer.flexGrow = YES;
|
||||
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
|
||||
|
||||
if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
|
||||
ASDisplayNode *scrubber = controls[ @(ASVideoPlayerNodeControlTypeScrubber) ];
|
||||
scrubber.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
|
||||
}
|
||||
|
||||
NSArray *controlBarControls = [self controlsForControlBar:controls];
|
||||
NSMutableArray *topBarControls = [[NSMutableArray alloc] init];
|
||||
|
||||
//Our custom control
|
||||
if (controls[@"muteControl"]) {
|
||||
[topBarControls addObject:controls[@"muteControl"]];
|
||||
}
|
||||
|
||||
|
||||
ASStackLayoutSpec *topBarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:10.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:topBarControls];
|
||||
|
||||
ASInsetLayoutSpec *topBarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:topBarSpec];
|
||||
|
||||
ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:10.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:bottomControls];
|
||||
spacing:10.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children: controlBarControls ];
|
||||
controlbarSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
|
||||
|
||||
|
||||
ASInsetLayoutSpec *controlbarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:controlbarSpec];
|
||||
|
||||
controlbarInsetSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||
|
||||
ASStackLayoutSpec *mainVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:0.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStart
|
||||
children:@[ spacer, controlbarInsetSpec ]];
|
||||
|
||||
spacing:0.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStart
|
||||
children:@[topBarInsetSpec, spacer, controlbarInsetSpec]];
|
||||
|
||||
return mainVerticalStack;
|
||||
}*/
|
||||
|
||||
}
|
||||
@end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user