AVPlayer+KSSeekSmoothly.m 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. //
  2. // AVPlayer+KSSeekSmoothly.m
  3. // KulexiuSchoolStudent
  4. //
  5. // Created by 王智 on 2023/11/24.
  6. //
  7. #import "AVPlayer+KSSeekSmoothly.h"
  8. #import <objc/runtime.h>
  9. @interface AVPlayerSeeker : NSObject
  10. {
  11. CMTime targetTime;
  12. BOOL isSeeking;
  13. }
  14. @property (weak, nonatomic) AVPlayer *player;
  15. @property (nonatomic, strong) dispatch_queue_t queue;
  16. @end
  17. @implementation AVPlayerSeeker
  18. - (instancetype)initWithPlayer:(AVPlayer *)player {
  19. self = [super init];
  20. if (self) {
  21. self.player = player;
  22. _queue = dispatch_queue_create("ks.seekTimeQueue", DISPATCH_QUEUE_SERIAL);
  23. }
  24. return self;
  25. }
  26. - (void)seekSmoothlyToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL))completionHandler {
  27. targetTime = time;
  28. if (!isSeeking) {
  29. [self trySeekToTargetTimeWithToleranceBefore:toleranceBefore toleranceAfter:toleranceAfter completionHandler:completionHandler];
  30. }
  31. }
  32. - (void)trySeekToTargetTimeWithToleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL))completionHandler {
  33. if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
  34. [self seekToTargetTimeToleranceBefore:toleranceBefore toleranceAfter:toleranceAfter completionHandler:completionHandler];
  35. }
  36. }
  37. - (void)seekToTargetTimeToleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL))completionHandler {
  38. isSeeking = YES;
  39. CMTime seekingTime = targetTime;
  40. dispatch_async(_queue, ^{
  41. [self.player seekToTime:seekingTime toleranceBefore:toleranceBefore
  42. toleranceAfter:toleranceAfter completionHandler:
  43. ^(BOOL isFinished) {
  44. if (CMTIME_COMPARE_INLINE(seekingTime, ==, self->targetTime)) {
  45. // seek completed
  46. self->isSeeking = NO;
  47. dispatch_main_async_safe(^{
  48. if (completionHandler) {
  49. completionHandler(isFinished);
  50. }
  51. });
  52. } else {
  53. // targetTime has changed, seek again
  54. [self trySeekToTargetTimeWithToleranceBefore:toleranceBefore toleranceAfter:toleranceAfter completionHandler:completionHandler];
  55. }
  56. }];
  57. });
  58. }
  59. @end
  60. static NSString *seekerKey = @"ss_seeker";
  61. @implementation AVPlayer (KSSeekSmoothly)
  62. - (void)ss_seekToTime:(CMTime)time {
  63. [self ss_seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:nil];
  64. }
  65. - (void)ss_seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL))completionHandler {
  66. AVPlayerSeeker *seeker = objc_getAssociatedObject(self, &seekerKey);
  67. if (!seeker) {
  68. seeker = [[AVPlayerSeeker alloc] initWithPlayer:self];
  69. objc_setAssociatedObject(self, &seekerKey, seeker, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  70. }
  71. // [self pause];
  72. [seeker seekSmoothlyToTime:time toleranceBefore:toleranceBefore toleranceAfter:toleranceAfter completionHandler:completionHandler];
  73. }
  74. @end