diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj b/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj index dba7ebbbe18987cf774dbe78d4ee572267740361..c7e0477877e3d1064a6bee69d8afc0c5d4fa9c8e 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj +++ b/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ E62139BF180FAE5F00553533 /* IJKFFOptions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = E62139BC180FA89A00553533 /* IJKFFOptions.h */; }; E63FC27117F01143003551EB /* ijksdl_audio.c in Sources */ = {isa = PBXBuildFile; fileRef = E63FC27017F01143003551EB /* ijksdl_audio.c */; }; E63FC27617F013DE003551EB /* ijksdl_vout_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = E63FC27417F013DE003551EB /* ijksdl_vout_dummy.c */; }; + E65DC3B919D93D5F004F8A08 /* IJKKVOController.m in Sources */ = {isa = PBXBuildFile; fileRef = E65DC3B819D93D5F004F8A08 /* IJKKVOController.m */; }; E66F8DC117EEC65200354D80 /* IJKMPMoviePlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = E66F8DC017EEC65200354D80 /* IJKMPMoviePlayerController.m */; }; E66F8DE717EFD9C300354D80 /* IJKFFMoviePlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = E66F8DE617EFD9C300354D80 /* IJKFFMoviePlayerController.m */; }; E66F8DF117EFEA9400354D80 /* ijkplayer.c in Sources */ = {isa = PBXBuildFile; fileRef = E66F8DEF17EFEA9400354D80 /* ijkplayer.c */; }; @@ -83,6 +84,8 @@ E63FC27317F013DE003551EB /* ijksdl_dummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ijksdl_dummy.h; sourceTree = ""; }; E63FC27417F013DE003551EB /* ijksdl_vout_dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ijksdl_vout_dummy.c; sourceTree = ""; }; E63FC27517F013DE003551EB /* ijksdl_vout_dummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ijksdl_vout_dummy.h; sourceTree = ""; }; + E65DC3B719D93D5F004F8A08 /* IJKKVOController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJKKVOController.h; path = IJKMediaPlayer/IJKKVOController.h; sourceTree = ""; }; + E65DC3B819D93D5F004F8A08 /* IJKKVOController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJKKVOController.m; path = IJKMediaPlayer/IJKKVOController.m; sourceTree = ""; }; E66F8DBF17EEC65200354D80 /* IJKMPMoviePlayerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJKMPMoviePlayerController.h; path = IJKMediaPlayer/IJKMPMoviePlayerController.h; sourceTree = ""; }; E66F8DC017EEC65200354D80 /* IJKMPMoviePlayerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJKMPMoviePlayerController.m; path = IJKMediaPlayer/IJKMPMoviePlayerController.m; sourceTree = ""; }; E66F8DC217EECB1E00354D80 /* IJKMediaPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJKMediaPlayer.h; path = IJKMediaPlayer/IJKMediaPlayer.h; sourceTree = ""; }; @@ -652,6 +655,8 @@ E66F8DC217EECB1E00354D80 /* IJKMediaPlayer.h */, E6716E491807E5FC00B3FBC1 /* IJKMediaUtils.h */, E6716E4A1807E5FC00B3FBC1 /* IJKMediaUtils.m */, + E65DC3B719D93D5F004F8A08 /* IJKKVOController.h */, + E65DC3B819D93D5F004F8A08 /* IJKKVOController.m */, ); name = IJKMediaPlayer; sourceTree = ""; @@ -731,6 +736,7 @@ E66F8DF117EFEA9400354D80 /* ijkplayer.c in Sources */, E6EE92BB1878230C009EAB56 /* IJKSDLAudioUnitController.m in Sources */, E66F8E0317EFEEA400354D80 /* ijkplayer_ios.m in Sources */, + E65DC3B919D93D5F004F8A08 /* IJKKVOController.m in Sources */, E63FC27117F01143003551EB /* ijksdl_audio.c in Sources */, E6EE92BD1878230C009EAB56 /* IJKSDLGLRenderRV24.m in Sources */, E63FC27617F013DE003551EB /* ijksdl_vout_dummy.c in Sources */, diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKAVMoviePlayerController.m b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKAVMoviePlayerController.m index b5f0be15ef5618383f9d100e0223e9f59091d06d..7301ff0a5364b13c6871328a5f4e8ef7a8221209 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKAVMoviePlayerController.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKAVMoviePlayerController.m @@ -72,6 +72,7 @@ #import "IJKAudioKit.h" #import "IJKMediaModule.h" #import "IJKMediaUtils.h" +#import "IJKKVOController.h" #import static NSString *kErrorDomain = @"IJKAVMoviePlayer"; @@ -106,7 +107,10 @@ static void *KVO_AVPlayerItem_playbackBufferEmpty = &KVO_AVPlayerItem_play AVPlayerItem *_playerItem; AVPlayer *_player; IJKAVPlayerLayerView * _avView; - + + IJKKVOController *_playerKVO; + IJKKVOController *_playerItemKVO; + id _timeObserver; BOOL _isSeeking; @@ -224,42 +228,6 @@ static void *KVO_AVPlayerItem_playbackBufferEmpty = &KVO_AVPlayerItem_play return _player.rate >= 0.00001f; } -- (void)removeObserversFromPlayerItem:(AVPlayerItem*)playerItem -{ - if (playerItem == nil) - return; - - [IJKMediaUtils kvoQuietlyRemoveObserver:self - forKeyPath:@"status" - fromObject:playerItem]; - [IJKMediaUtils kvoQuietlyRemoveObserver:self - forKeyPath:@"loadedTimeRanges" - fromObject:playerItem]; - [IJKMediaUtils kvoQuietlyRemoveObserver:self - forKeyPath:@"playbackLikelyToKeepUp" - fromObject:playerItem]; - [IJKMediaUtils kvoQuietlyRemoveObserver:self - forKeyPath:@"playbackBufferFull" - fromObject:playerItem]; - [IJKMediaUtils kvoQuietlyRemoveObserver:self - forKeyPath:@"playbackBufferEmpty" - fromObject:playerItem]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:nil - object:playerItem]; -} - -- (void)removeObserversFromPlayer:(AVPlayer*)player -{ - if (player == nil) - return; - - [player removeObserver:self forKeyPath:@"currentItem"]; - [player removeObserver:self forKeyPath:@"rate"]; -} - - - (void)shutdown { [self stop]; @@ -268,7 +236,13 @@ static void *KVO_AVPlayerItem_playbackBufferEmpty = &KVO_AVPlayerItem_play [_playerItem cancelPendingSeeks]; } - [self removeObserversFromPlayerItem:_playerItem]; + [_playerItemKVO safelyRemoveAllObservers]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:nil + object:_playerItem]; + + [_playerKVO safelyRemoveAllObservers]; + [self unregisterApplicationObservers]; if (_avView != nil) { @@ -406,36 +380,40 @@ static void *KVO_AVPlayerItem_playbackBufferEmpty = &KVO_AVPlayerItem_play /* At this point we're ready to set up for playback of the asset. */ /* Stop observing our prior AVPlayerItem, if we have one. */ - [self removeObserversFromPlayerItem:_playerItem]; + [_playerItemKVO safelyRemoveAllObservers]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:nil + object:_playerItem]; /* Create a new instance of AVPlayerItem from the now successfully loaded AVAsset. */ _playerItem = [AVPlayerItem playerItemWithAsset:asset]; + _playerItemKVO = [[IJKKVOController alloc] initWithTarget:_playerItem]; /* Observe the player item "status" key to determine when it is ready to play. */ - [_playerItem addObserver:self - forKeyPath:@"status" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayerItem_state]; - - [_playerItem addObserver:self - forKeyPath:@"loadedTimeRanges" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayerItem_loadedTimeRanges]; - - [_playerItem addObserver:self - forKeyPath:@"playbackLikelyToKeepUp" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayerItem_playbackLikelyToKeepUp]; - - [_playerItem addObserver:self - forKeyPath:@"playbackBufferEmpty" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayerItem_playbackBufferEmpty]; - - [_playerItem addObserver:self - forKeyPath:@"playbackBufferFull" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayerItem_playbackBufferFull]; + [_playerItemKVO safelyAddObserver:self + forKeyPath:@"status" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayerItem_state]; + + [_playerItemKVO safelyAddObserver:self + forKeyPath:@"loadedTimeRanges" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayerItem_loadedTimeRanges]; + + [_playerItemKVO safelyAddObserver:self + forKeyPath:@"playbackLikelyToKeepUp" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayerItem_playbackLikelyToKeepUp]; + + [_playerItemKVO safelyAddObserver:self + forKeyPath:@"playbackBufferEmpty" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayerItem_playbackBufferEmpty]; + + [_playerItemKVO safelyAddObserver:self + forKeyPath:@"playbackBufferFull" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayerItem_playbackBufferFull]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) @@ -454,20 +432,21 @@ static void *KVO_AVPlayerItem_playbackBufferEmpty = &KVO_AVPlayerItem_play { /* Get a new AVPlayer initialized to play the specified player item. */ _player = [AVPlayer playerWithPlayerItem:_playerItem]; + _playerKVO = [[IJKKVOController alloc] initWithTarget:_player]; /* Observe the AVPlayer "currentItem" property to find out when any AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did occur.*/ - [_player addObserver:self - forKeyPath:@"currentItem" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayer_currentItem]; + [_playerKVO safelyAddObserver:self + forKeyPath:@"currentItem" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayer_currentItem]; /* Observe the AVPlayer "rate" property to update the scrubber control. */ - [_player addObserver:self - forKeyPath:@"rate" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:KVO_AVPlayer_rate]; + [_playerKVO safelyAddObserver:self + forKeyPath:@"rate" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:KVO_AVPlayer_rate]; } /* Make our new AVPlayerItem the AVPlayer's current item. */ diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKKVOController.h b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKKVOController.h new file mode 100644 index 0000000000000000000000000000000000000000..ddce21b5f7d2eacfb964c0d89c00b75005be05e1 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKKVOController.h @@ -0,0 +1,24 @@ +// +// IJKKVOController.h +// IJKMediaPlayer +// +// Created by Zhang Rui on 14-9-29. +// Copyright (c) 2014年 bilibili. All rights reserved. +// + +#import + +@interface IJKKVOController : NSObject + +- (id)initWithTarget:(NSObject *)target; + +- (void)safelyAddObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context; +- (void)safelyRemoveObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath; + +- (void)safelyRemoveAllObservers; + +@end diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKKVOController.m b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKKVOController.m new file mode 100644 index 0000000000000000000000000000000000000000..0cb9373b48f2aba85a9f3f25d372bf2a481aa0f9 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKKVOController.m @@ -0,0 +1,136 @@ +// +// IJKKVOController.m +// IJKMediaPlayer +// +// Created by Zhang Rui on 14-9-29. +// Copyright (c) 2014年 bilibili. All rights reserved. +// + +#import "IJKKVOController.h" + +@interface IJKKVOEntry : NSObject +@property(nonatomic, weak) NSObject *observer; +@property(nonatomic, strong) NSString *keyPath; +@end + +@implementation IJKKVOEntry +@synthesize observer; +@synthesize keyPath; +@end + +@implementation IJKKVOController { + __weak NSObject *_target; + NSMutableArray *_observerArray; +} + +- (id)initWithTarget:(NSObject *)target +{ + self = [super init]; + if (self) { + _target = target; + _observerArray = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)safelyAddObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context +{ + NSObject *target = _target; + if (target == nil) + return; + + BOOL removed = [self removeEntryOfObserver:observer forKeyPath:keyPath]; + if (removed) { + // duplicated register + NSLog(@"duplicated observer"); + } + + @try { + [target addObserver:observer + forKeyPath:keyPath + options:options + context:context]; + + IJKKVOEntry *entry = [[IJKKVOEntry alloc] init]; + entry.observer = observer; + entry.keyPath = keyPath; + [_observerArray addObject:entry]; + } @catch (NSException *e) { + NSLog(@"IJKKVO: failed to add observer for %@\n", keyPath); + } +} + +- (void)safelyRemoveObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath +{ + NSObject *target = _target; + if (target == nil) + return; + + BOOL removed = [self removeEntryOfObserver:observer forKeyPath:keyPath]; + if (removed) { + // duplicated register + NSLog(@"duplicated observer"); + } + + @try { + if (removed) { + [target removeObserver:observer + forKeyPath:keyPath]; + } + } @catch (NSException *e) { + NSLog(@"IJKKVO: failed to remove observer for %@\n", keyPath); + } +} + +- (void)safelyRemoveAllObservers +{ + __block NSObject *target = _target; + if (target == nil) + return; + + [_observerArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + IJKKVOEntry *entry = obj; + if (entry == nil) + return; + + NSObject *observer = entry.observer; + if (observer == nil) + return; + + @try { + [target removeObserver:observer + forKeyPath:entry.keyPath]; + } @catch (NSException *e) { + NSLog(@"IJKKVO: failed to remove observer for %@\n", entry.keyPath); + } + }]; + + [_observerArray removeAllObjects]; +} + +- (BOOL)removeEntryOfObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath +{ + __block NSInteger foundIndex = -1; + [_observerArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + IJKKVOEntry *entry = (IJKKVOEntry *)obj; + if (entry.observer == observer && + [entry.keyPath isEqualToString:keyPath]) { + foundIndex = idx; + *stop = YES; + } + }]; + + if (foundIndex >= 0) { + [_observerArray removeObjectAtIndex:foundIndex]; + return YES; + } + + return NO; +} + +@end diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.h b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.h index cd2ef1e0e9c972e34dfcb63c178a5fbefd0cbd66..3c01d5dfe989346c63d1a2f823e44cdf7709b25e 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.h +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.h @@ -36,8 +36,4 @@ description: (NSString*)description reason: (NSString*)reason; -+ (void)kvoQuietlyRemoveObserver:(NSObject *)anObserver - forKeyPath:(NSString *)keyPath - fromObject:(NSObject *)object; - @end diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.m b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.m index a928a08ee537420448f11a4644d16f3098d3ead6..74635fa45f7b94f9379fd212ae501fd98c810e4f 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKMediaUtils.m @@ -73,16 +73,4 @@ return error; } -+ (void)kvoQuietlyRemoveObserver:(NSObject *)anObserver - forKeyPath:(NSString *)keyPath - fromObject:(NSObject *)object -{ - @try { - [object removeObserver:anObserver forKeyPath:@"status"]; - } - @catch (NSException *exception) { - NSLog(@"dup remove observer\n"); - } -} - @end