Skip to content

Instantly share code, notes, and snippets.

@k06a
Last active September 7, 2023 11:51
Show Gist options
  • Save k06a/66f7815b0325f239411e26f498c75755 to your computer and use it in GitHub Desktop.
Save k06a/66f7815b0325f239411e26f498c75755 to your computer and use it in GitHub Desktop.
Awesome optimized AVPlayer for smooth scrolling AVPlayerLayer inside UICollectionView/UITableView (tested on iOS10+)
#import <AVFoundation/AVFoundation.h>
@interface MLWAsyncAVPlayer : AVPlayer
@end
#import <JRSwizzle/JRSwizzle.h>
#import "MLWAsyncAVPlayer.h"
@implementation MLWAsyncAVPlayer
- (instancetype)init {
self = [super init];
if (self) {
[self setValue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) forKeyPath:@"_player.stateDispatchQueue"];
}
return self;
}
// Move completions from following methods to main thread
- (void)prerollAtRate:(float)rate completionHandler:(void (^)(BOOL))completionHandler {
[super prerollAtRate:rate completionHandler:^(BOOL finished) {
dispatch_sync(dispatch_get_main_queue(), ^{
completionHandler(finished);
});
}];
}
- (id)addBoundaryTimeObserverForTimes:(NSArray<NSValue *> *)times queue:(dispatch_queue_t)queue usingBlock:(void (^)(void))block {
return [super addBoundaryTimeObserverForTimes:times queue:queue usingBlock:^{
dispatch_sync(dispatch_get_main_queue(), ^{
block();
});
}];
}
- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime))block {
return [super addPeriodicTimeObserverForInterval:interval queue:queue usingBlock:^(CMTime time) {
dispatch_sync(dispatch_get_main_queue(), ^{
block(time);
});
}];
}
// Move KVO of player properties to main thread
- (void)willChangeValueForKey:(NSString *)key {
if ([NSThread isMainThread]) {
[super willChangeValueForKey:key];
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[super willChangeValueForKey:key];
});
}
- (void)didChangeValueForKey:(NSString *)key {
if ([NSThread isMainThread]) {
[super didChangeValueForKey:key];
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[super didChangeValueForKey:key];
});
}
@end
//
@implementation AVPlayerItem (MLWAsync)
+ (void)load {
[self jr_swizzleMethod:@selector(willChangeValueForKey:) withMethod:@selector(mlw_willChangeValueForKey:) error:nil];
[self jr_swizzleMethod:@selector(didChangeValueForKey:) withMethod:@selector(mlw_didChangeValueForKey:) error:nil];
}
- (void)mlw_willChangeValueForKey:(NSString *)key {
if ([NSThread isMainThread]) {
[self mlw_willChangeValueForKey:key];
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self mlw_willChangeValueForKey:key];
});
}
- (void)mlw_didChangeValueForKey:(NSString *)key {
if ([NSThread isMainThread]) {
[self mlw_didChangeValueForKey:key];
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self mlw_didChangeValueForKey:key];
});
}
@end
@Sneakr
Copy link

Sneakr commented Aug 11, 2017

This is accessing private APIS and would get the app banned from appstore. forKeyPath:@"_player.stateDispatchQueue"]; To bad :(

@k06a
Copy link
Author

k06a commented Aug 11, 2017

@Sneakr you can compose keyPath this way: [...[[@"_" stringByAppendingString:@"p"] stringByAppendingString:@"l"] …]

@Sneakr
Copy link

Sneakr commented Aug 13, 2017

@k06a Ahh, thanks man, I will see if I will, Im a bit scared of getting it removed nice tip tho!

@lowip
Copy link

lowip commented Mar 28, 2019

@k06a Should this be used with your other gist AVPlayerItem+MLWPerformance.m (https://gist.github.com/k06a/a279cdf0d479c4df2d05377ab3681788)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment