gpt4 book ai didi

ios - UITableViewCell 打嗝中的自动播放视频

转载 作者:塔克拉玛干 更新时间:2023-11-02 22:02:00 25 4
gpt4 key购买 nike

我已经阅读了 StackOverflow 上有关自动播放视频的大部分问题,并且我能够在 UITableView 中自动播放它们,但我遇到的问题很少,如下所述

  1. 视频开始时滚动会暂停一秒钟
  2. 播放前视频闪烁
  3. 如果我向上滚动,视频不会自动播放

我想要的是在不使用任何第三方库(如 ASYNCDisplayKit)的情况下像 Facebook 一样自动播放视频的流畅体验。所有视频 url 均来自 AWSS3 云端 URLS。我还上传了该问题的视频,以防有人想看。

Video Autoplay Hiccups

这是我的完整代码

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)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)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)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)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)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 action:@selector(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 action:@selector(tapOnCounterView)];
countGesture.numberOfTapsRequired = 1;
self.counterView.userInteractionEnabled = YES;
[self.counterView addGestureRecognizer:countGesture];

[self.btnFullScreen addTarget:self action:@selector(btnFSTapped:) forControlEvents:UIControlEventTouchUpInside];

self.btnFullScreen.hidden = NO;
UIImage* fullScreenImage = [[UIImage imageNamed:@"fullScreenIcon"] imageTintedWithColor:kSliderDarkYellowColor];
[self.btnFullScreen setImage:fullScreenImage forState:UIControlStateNormal];

}

- (void)showThumbnail:(BOOL)yesOrNo {
self.videoThumbnail.hidden = !yesOrNo;
self.viewForVideo.hidden = yesOrNo;
}

- (void)hideVideoAndShowThumbnail {
[self stopVideo];
[self showThumbnail:YES];
self.btnPlay.selected = NO;
self.isPlaying = NO;
}

- (void)btnFSTapped:(UIButton*)sender {
if (self.delegate && [self.delegate respondsToSelector:@selector(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

selector:@selector(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
}

有人可以帮我解决这个问题吗?

最佳答案

我有一些建议给你:

  1. 不要尝试在滚动期间调用播放/暂停。滚动停止后立即执行此操作:https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

  2. 您可以尝试使用这个肮脏的技巧:https://gist.github.com/k06a/66f7815b0325f239411e26f498c75755要对 Apple Review Team 隐藏它,只需使用 UAObfuscateString 库混淆键路径 @"_player.stateDispatchQueue"

  3. 我听说无需肮脏的黑客攻击即可获得平稳的投资返回。我会请我的一个 friend 来回答你的问题。

关于ios - UITableViewCell 打嗝中的自动播放视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51787983/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com