Video de reproducción automática en UITableViewCell hipo
He leído la mayoría de las preguntas enStackOverflow
para reproducir videos automáticamente y puedo reproducirlos automáticamente enUITableView
, pero tengo algunos problemas como los mencionados a continuación
Lo que quiero es una experiencia fluida para la reproducción automática de videos como Facebook sin usar ninguna biblioteca de terceros comoASYNCDisplayKit
. Todos los videosurls
son deAWSS3
URLS en el frente de la nube. También he subido un video del problema en caso de que alguien quiera echar un vistazo.
Aquí está mi código completo
- (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];
}
}
// Lista de videos Clase de celda
#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
}
¿Podría alguien ayudarme en esto?