我已经阅读了关于 StackOverflow 自动播放视频的大部分问题,并且我能够在 UITableView 中自动播放它们,但我遇到的问题如下所述
- 视频开始时滚动暂停一秒钟
- 播放前视频闪烁
- 如果我向上滚动,视频不会自动播放
我想要的是在不使用任何第三方库(如 ASYNCDisplayKit )的情况下自动播放视频(如 Facebook)的流畅体验。
所有视频 urls 均来自 AWSS3 云端 URL。
我还上传了这个问题的视频,以防有人想看。
Video Autoplay Hiccups
这是我的完整代码
- (UITableViewCell*)tableViewUITableView *)tableView cellForRowAtIndexPathNSIndexPath *)indexPath {
PostViewModel* model = self.posts[indexPath.section];
Post* post = model.post;
PostItems* item = model.items[indexPath.row];
if(item.itemType == nameAndPicture) {
//Removed code as it's not related to question
}
else if(item.itemType == textContent){
//Removed code as it's not related to question
}
else if(item.itemType == images){
//Removed code as it's not related to question
}
else if(item.itemType == videos){
VideoListCell *cell = nil;
cell = (VideoListCell*)[tableView dequeueReusableCellWithIdentifier:kFeedVideoListCellIdentifier forIndexPath:indexPath];
cell.delegate = self;
cell.indexPath = indexPath;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [UIColor clearColor];
cell.videoThumbnail.image = nil;
[cell setCounter:post.medias.count];
if (post.medias.count > 0) {
MediaItem* item = post.medias[0];
if ([item getMediaType] == VIDEO) {
NSString* thumbnailURL = item.thumbnailUrl;
[cell.videoThumbnail downloadImageWithURL:thumbnailURL andPlaceholderImage:self.timelinePlaceholder indicatorStyle:UIActivityIndicatorViewStyleWhiteLarge cachePolicy:NSURLRequestReturnCacheDataElseLoad andTimeOut:120];
[cell hideVideoAndShowThumbnail];
dispatch_async(dispatch_get_main_queue(), ^{
[cell setMediaItem:item withUserID:post.userId];
});
}
}
cell.clipsToBounds = YES;
return cell;
}
}
- (void)tableViewUITableView *)tableView willDisplayCellUITableViewCell *)cell forRowAtIndexPathNSIndexPath *)indexPath
{
//Check if the cell displayed is video cell then try to autoplay the video
if([cell isKindOfClass:[VideoListCell class]]){
VideoListCell* videoCell = (VideoListCell*)cell;
dispatch_async(dispatch_get_main_queue(), ^{
[videoCell hideVideoAndShowThumbnail];
});
PostViewModel* model = self.posts[indexPath.section];
Post* post = model.post;
PostItems* item = model.items[indexPath.row];
if(item.itemType == videos){
videoCell.videoThumbnail.image = nil;
[videoCell setCounter:post.medias.count];
if (post.medias.count > 0) {
MediaItem* item = post.medias[0];
if ([item getMediaType] == VIDEO) {
//dispatch_async(dispatch_get_main_queue(), ^{
NSString* profilePic = item.thumbnailUrl;
[videoCell.videoThumbnail downloadImageWithURL:profilePic andPlaceholderImage:self.timelinePlaceholder indicatorStyle:UIActivityIndicatorViewStyleWhiteLarge cachePolicy:NSURLRequestReturnCacheDataElseLoad andTimeOut:120];
[videoCell setMediaItem:item withUserID:post.userId];
[videoCell playVideo];
}
}
}
}
}
- (void)tableViewUITableView *)tableView didEndDisplayingCellUITableViewCell *)cell forRowAtIndexPathNSIndexPath*)indexPath {
if([cell isKindOfClass:[VideoListCell class]]){
VideoListCell* videoCell = (VideoListCell*)cell;
[videoCell stopVideo];
videoCell.avLayer = nil;
videoCell.videoPlayer = nil;
[videoCell hideVideoAndShowThumbnail];
}
}
//视频列表单元类
#define kHeight 200
@implementation VideoListCell
- (void)awakeFromNib {
[super awakeFromNib];
UIImage* icon = [[UIImage imageNamed"play-icon"] imageTintedWithColor:kSliderDarkYellowColor];
[self.btnPlay setImage:icon forState:UIControlStateNormal];
UIImage* pauseIcon = [[UIImage imageNamed"pause-icon"] imageTintedWithColor:kSliderDarkYellowColor];
[self.btnPlay setImage:icon forState:UIControlStateNormal];
[self.btnPlay setImage:pauseIcon forState:UIControlStateSelected];
UITapGestureRecognizer *viewTap = [[UITapGestureRecognizer alloc]initWithTarget:self actionselector(tapOnView)];
viewTap.numberOfTapsRequired = 1;
self.viewPlayer.userInteractionEnabled = YES;
[self.viewPlayer addGestureRecognizer:viewTap];
self.counterView.hidden = YES;
self.counterView.layer.cornerRadius = 12.0f;
self.counterView.layer.masksToBounds = YES;
//Add Gesture to label
UITapGestureRecognizer *countGesture = [[UITapGestureRecognizer alloc]initWithTarget:self actionselector(tapOnCounterView)];
countGesture.numberOfTapsRequired = 1;
self.counterView.userInteractionEnabled = YES;
[self.counterView addGestureRecognizer:countGesture];
[self.btnFullScreen addTarget:self actionselector(btnFSTapped forControlEvents:UIControlEventTouchUpInside];
self.btnFullScreen.hidden = NO;
UIImage* fullScreenImage = [[UIImage imageNamed"fullScreenIcon"] imageTintedWithColor:kSliderDarkYellowColor];
[self.btnFullScreen setImage:fullScreenImage forState:UIControlStateNormal];
}
- (void)showThumbnailBOOL)yesOrNo {
self.videoThumbnail.hidden = !yesOrNo;
self.viewForVideo.hidden = yesOrNo;
}
- (void)hideVideoAndShowThumbnail {
[self stopVideo];
[self showThumbnail:YES];
self.btnPlay.selected = NO;
self.isPlaying = NO;
}
- (void)btnFSTappedUIButton*)sender {
if (self.delegate && [self.delegate respondsToSelectorselector(fullScreenButtonTapped:andURL:andPlayer]) {
[self.delegate fullScreenButtonTapped:self.indexPath andURL:self.videoURL andPlayer:self.player.player];
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
// if (self.avLayer) {
// [self.avLayer setFrame:CGRectMake(self.viewForVideo.frame.origin.x, self.viewForVideo.frame.origin.y, self.viewForVideo.frame.size.width, self.viewForVideo.frame.size.height)];
// }
}
- (void)initNewPlayerItem {
// Pause the existing video (if there is one)
//[self stopVideo];
if(self.asset){
[self.asset cancelLoading];
}
// First we need to make sure we have a valid URL
if (!self.videoURL) {
return;
}
// Create a new AVAsset from the URL
self.asset = [AVAsset assetWithURL:self.videoURL];
// Now we need an AVPlayerItem to pass to the AVPlayer
AVPlayerItem* item = [[AVPlayerItem alloc] initWithAsset:self.asset];
if(item){
[[NSNotificationCenter defaultCenter] addObserver:self
selectorselector(playerItemDidReachEnd
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
}
//[self.player.player replaceCurrentItemWithPlayerItem:item];
// Finally, we set this as the current AVPlayer item
[self.asset loadValuesAsynchronouslyForKeys[@"duration"] completionHandler:^{
NSError* error = nil;
AVKeyValueStatus status = [self.asset statusOfValueForKey"duration" error:&error];
if (status == AVKeyValueStatusFailed) {
[self.playerSetupLoading stopAnimating];
self.btnPlay.hidden = NO;
self.btnPlay.selected = NO;
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.playerSetupLoading stopAnimating];
[self.player.player replaceCurrentItemWithPlayerItem:item];
self.btnPlay.selected = YES;
self.btnPlay.hidden = YES;
[self showThumbnail:NO];
[self.player.player play];
self.isPlaying = YES;
});
}];
}
- (void)playerItemDidReachEnd:(NSNotification*)notif {
id object = [notif object];
if (object && [object isKindOfClass:[AVPlayerItem class]]) {
AVPlayerItem* item = (AVPlayerItem*)[notif object];
[item seekToTime:kCMTimeZero];
}
//[self stopVideo];
[self showThumbnail:YES];
self.btnPlay.selected = NO;
self.btnPlay.hidden = NO;
}
-(void)prepareForReuse {
// self.videoURL = nil;
// self.videoThumbnail.image = nil;
//[self.player pauseContent];
self.videoThumbnail.image = nil;
if (self.avLayer.superlayer) {
[self.avLayer removeFromSuperlayer];
}
if (self.viewForVideo.subviews.count > 0) {
for (UIView* v in self.viewForVideo.subviews) {
[v removeFromSuperview];
}
}
self.videoURL = nil;
self.player = nil;
self.userID = nil;
self.videoItem = nil;
self.videoPlayer = nil;
self.btnPlay.selected = NO;
[super prepareForReuse];
}
- (void)tapOnView {
//if(self.counterView.hidden){
if (self.delegate && [self.delegate respondsToSelector:@selector(fullScreenButtonTapped:andURL:andPlayer]) {
[self.delegate fullScreenButtonTapped:self.indexPath andURL:self.videoURL andPlayer:self.player.player];
}
//}
// else
// {
// if (self.delegate && [self.delegate respondsToSelector:@selector(playVideo:withURL]) {
// [self.delegate playVideo:self.indexPath withURL:nil];
// }
// }
}
-(void)tapOnCounterView {
if (self.delegate && [self.delegate respondsToSelector:@selector(playVideo:withURL]) {
[self.delegate playVideo:self.indexPath withURL:nil];
}
}
- (void)setCounter:(NSUInteger)count {
if (count > 1) {
self.counterView.hidden = NO;
self.lblCounter.text = [NSString stringWithFormat:@"+%lu more",(unsigned long)count-1];
}
else{
self.counterView.hidden = YES;
}
}
- (IBAction)btnPlayTapped:(id)sender {
//[self playVideo];
//if(self.counterView.hidden){
if(self.btnPlay.selected){
[self stopVideo];
self.btnPlay.selected = NO;
}else{
[self playVideo];
self.btnPlay.selected = YES;
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if(self.player.player.timeControlStatus == AVPlayerTimeControlStatusPlaying){
if(self.btnPlay.hidden){
self.btnPlay.hidden = NO;
}
}
}
- (void)playVideo {
//if (!self.player) {
if ([self.videoObject doesPreSignedURLExpired]) {
//Call API here and update media item object URL
dispatch_async(dispatch_get_main_queue(), ^{
//Call API here
//URL is expired then give a call to our server to generate a new URL
[self generateNewPreSignedURL];
});
}
else{
if (!self.videoURL) {
dispatch_async(dispatch_get_main_queue(), ^{
[self generatePreSignedURLWithVideoThumbnail];
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
[self setupPlayer];
});
}
}
}
- (void)stopVideo {
if (self.player) {
self.isPlaying = NO;
self.btnPlay.hidden = NO;
[self.player.player pause];
}
}
- (void)setMediaItem:(MediaItem*)item withUserID:(NSNumber*)userId {
self.videoObject = item;
self.userID = userId;
[self showThumbnail:YES];
}
- (void)generatePreSignedURLWithVideoThumbnail {
if (self.videoObject.mediaUrl && [self.videoObject hasPreSignedURL]) {
//Already have pre signed url check if URL is expired
//If URL expired then call our own server to generate a new presigned URL
dispatch_async(dispatch_get_main_queue(), ^{
self.videoURL = [NSURL URLWithString:self.videoObject.mediaUrl];
[self setupPlayer];
});
}
else if(self.videoObject.mediaUrl && [self.videoObject hasPlayListURL]){
AppDelegate* delegate = [AppDelegate applicationDelegate];
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
getPreSignedURLRequest.bucket = S3BucketName;
getPreSignedURLRequest.key = kS3OutputVideoFileInternalPath(delegate.loggedInUser.userId,[self.videoObject getVideoFolderName],self.videoObject.mediaUrl);
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodGET;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:Hour*24*5];
[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(@"Error: %@",task.error);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
self.videoURL = task.result;
[self setupPlayer];
});
}
return nil;
}];
}
else{
//Generate Pre signed URL
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
getPreSignedURLRequest.bucket = S3BucketName;
getPreSignedURLRequest.key = [kS3InputVideoFilePath(self.userID) stringByAppendingString:self.videoObject.mediaUrl];
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodGET;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:Hour*24*5];
[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(@"Error: %@",task.error);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
self.videoURL = task.result;
[self setupPlayer];
});
}
return nil;
}];
}
}
- (void)setupPlayer {
self.btnPlay.hidden = YES;
self.videoItem = nil;
self.videoPlayer = nil;
self.videoItem = [[AVPlayerItem alloc] initWithURL:self.videoURL];
if (self.avLayer.superlayer) {
[self.avLayer removeFromSuperlayer];
}
if (self.viewForVideo.subviews.count > 0) {
for (UIView* v in self.viewForVideo.subviews) {
[v removeFromSuperview];
}
}
self.videoPlayer = [[AVPlayer alloc] initWithPlayerItem:self.videoItem];
self.avLayer = [AVPlayerLayer playerLayerWithPlayer:self.videoPlayer];
self.avLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.player = [[AVPlayerViewController alloc] init];
self.player.player = self.videoPlayer;
self.player.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.player.showsPlaybackControls = NO;
// Insert the player into the cell view hierarchy and setup autolayout
self.player.view.translatesAutoresizingMaskIntoConstraints = false;
[self.viewForVideo insertSubview:self.player.view atIndex:0];
//Trailing
NSLayoutConstraint *trailing =[NSLayoutConstraint
constraintWithItem:self.player.view
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.viewForVideo
attribute:NSLayoutAttributeTrailing
multiplier:1.0f
constant:0.f];
//Leading
NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:self.player.view
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.viewForVideo
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.f];
//Bottom
NSLayoutConstraint *bottom =[NSLayoutConstraint
constraintWithItem:self.player.view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.viewForVideo
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.f];
//Height to be fixed for SubView same as AdHeight
NSLayoutConstraint *height = [NSLayoutConstraint
constraintWithItem:self.player.view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0
constant:kHeight];
//Add constraints to the Parent
[self.viewForVideo addConstraint:trailing];
[self.viewForVideo addConstraint:bottom];
[self.viewForVideo addConstraint:leading];
//Add height constraint to the subview, as subview owns it.
[self.player.view addConstraint:height];
[self initNewPlayerItem];
}
- (void)generateNewPreSignedURL {
if (self.videoObject) {
NSDictionary* postParams = @{kMediaId:self.videoObject.mediaId};
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
TBWebAPIConsumer *web = [TBWebAPIConsumer sharedWebAPIManager];
[web generatePreSignedURL:postParams andCompletionBlock:^(NSError *error, id serverResponse) {
// Do something...
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil){
//Parse user data here
NSDictionary* data = (NSDictionary*)serverResponse;
if (![data valueForKeyIsNull:@"mediaUrl"]) {
self.videoObject.mediaUrl = [data valueForKey:@"mediaUrl"];
}
if (![data valueForKeyIsNull:@"videoSignedUrlExpiry"]) {
self.videoObject.videoSignedUrlExpiry = [data valueForKey:@"videoSignedUrlExpiry"];
}
[self generatePreSignedURLWithVideoThumbnail];
}
});
}];
});
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
有人可以帮我解决这个问题吗?
Best Answer-推荐答案 strong>
我有一些建议给你:
在滚动过程中不要尝试调用播放/暂停。只需在滚动停止后立即执行此操作:https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e
你可以尝试使用这个肮脏的技巧:https://gist.github.com/k06a/66f7815b0325f239411e26f498c75755要对 Apple Review Team 隐藏它,只需使用 UAObfuscateString 库混淆 keypath @"_player.stateDispatchQueue" 。
我听说没有肮脏的黑客可以实现平稳的返回。我会请我的一个 friend 来回答你的问题。
关于ios - UITableViewCell 中的自动播放视频,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/51787983/
|