如何根据后端标识限制AVPlayer快进快退并获取视频完成时间
Hey there! Let's walk through how to implement both of your requirements for your AVPlayer-based app. I'll tie everything to your existing AVPlayerViewController setup so it fits right in.
First, you'll use the flag from your backend to toggle seek functionality. Here's a reliable approach that gives you full control:
Step 1: Track the Restriction Flag
Add properties to your view controller to track if seeking is allowed, plus another to keep the last valid playback time (to block unwanted seeks):
@property (nonatomic, assign) BOOL allowsSeek; @property (nonatomic, assign) CMTime lastValidPlaybackTime;
Step 2: Configure Controls Based on the Flag
When you receive video data from your backend, use the restriction flag to set up the player:
// Assume `isSeekRestricted` is the boolean flag from your backend response self.allowsSeek = !isSeekRestricted; if (!self.allowsSeek) { // Hide system controls to disable the default progress bar self.videoPlayerViewController.showsPlaybackControls = NO; // Add a custom play/pause button (adjust frame and styling as needed) UIButton *playPauseBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 60)]; playPauseBtn.center = self.videoPlayerViewController.view.center; playPauseBtn.tag = 100; // Use a tag to reference it later [playPauseBtn setImage:[UIImage systemImageNamed:@"play.fill"] forState:UIControlStateNormal]; [playPauseBtn setImage:[UIImage systemImageNamed:@"pause.fill"] forState:UIControlStateSelected]; [playPauseBtn addTarget:self action:@selector(togglePlayPause:) forControlEvents:UIControlEventTouchUpInside]; [self.videoPlayerViewController.view addSubview:playPauseBtn]; // Listen to playback status to update the button state [self.videoPlayerViewController.player addObserver:self forKeyPath:@"timeControlStatus" options:NSKeyValueObservingOptionNew context:nil]; // Track initial playback time to block unwanted seeks self.lastValidPlaybackTime = self.videoPlayerViewController.player.currentItem.currentTime; [self.videoPlayerViewController.player.currentItem addObserver:self forKeyPath:@"currentTime" options:NSKeyValueObservingOptionNew context:nil]; } else { // Re-enable system controls for unrestricted videos self.videoPlayerViewController.showsPlaybackControls = YES; // Clean up observers if they were added previously [self.videoPlayerViewController.player removeObserver:self forKeyPath:@"timeControlStatus"]; if (self.videoPlayerViewController.player.currentItem) { [self.videoPlayerViewController.player.currentItem removeObserver:self forKeyPath:@"currentTime"]; } }
Step 3: Handle KVO Observations and Play/Pause
Implement the observer method to enforce the seek restriction and update the play/pause button:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"timeControlStatus"]) { // Update play/pause button based on current playback state AVPlayerTimeControlStatus status = [change[NSKeyValueChangeNewKey] integerValue]; UIButton *playPauseBtn = [self.videoPlayerViewController.view viewWithTag:100]; playPauseBtn.selected = (status == AVPlayerTimeControlStatusPlaying); } else if ([keyPath isEqualToString:@"currentTime"]) { if (!self.allowsSeek) { CMTime newTime = [change[NSKeyValueChangeNewKey] CMTimeValue]; // If time changed unexpectedly (user tried to seek), reset to last valid time if (CMTimeCompare(newTime, self.lastValidPlaybackTime) != 0) { [self.videoPlayerViewController.player seekToTime:self.lastValidPlaybackTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; } else { // Update valid time as video plays normally self.lastValidPlaybackTime = newTime; } } } } - (void)togglePlayPause:(UIButton *)sender { sender.selected = !sender.selected; if (sender.selected) { [self.videoPlayerViewController.player play]; } else { [self.videoPlayerViewController.player pause]; } }
To get notified when a video completes, use AVPlayer's built-in notification system:
Step 1: Add a Notification Observer
When setting up your AVPlayerItem, register for the finish notification:
// Assume `videoURLString` is the URL from your backend NSURL *videoURL = [NSURL URLWithString:videoURLString]; AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:videoURL]; // Register for the finish notification [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem]; // Assign the item to your player self.videoPlayerViewController.player = [AVPlayer playerWithPlayerItem:playerItem];
Step 2: Handle the Finish Event
Implement the selector to capture the finish time and handle post-playback actions:
- (void)videoDidFinishPlaying:(NSNotification *)notification { AVPlayerItem *finishedItem = notification.object; // Get current system time as the finish time NSDate *finishTime = [NSDate date]; NSLog(@"Video completed at %@", finishTime); // Add your logic here: e.g., report to backend, play next video, etc. // Clean up observer to avoid memory leaks [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:finishedItem]; }
Step 3: Clean Up Observers
Don't forget to remove all observers in your view controller's dealloc method to prevent memory leaks:
- (void)dealloc { [self.videoPlayerViewController.player removeObserver:self forKeyPath:@"timeControlStatus"]; if (self.videoPlayerViewController.player.currentItem) { [self.videoPlayerViewController.player.currentItem removeObserver:self forKeyPath:@"currentTime"]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.videoPlayerViewController.player.currentItem]; } }
内容的提问来源于stack exchange,提问作者Keshav.K

