Autoplay Video in UITableViewCell hiccupps

2019-02-15 16:28发布

问题:

I have read most of the questions on StackOverflow for autoplaying videos and I'm able to autoplay them in UITableView, but I'm having few issues like mentioned below

  1. scrolling hangs for a second when video starts
  2. Video flashes before playing
  3. Video does not auto play if I scroll Up

What I want is a smooth experience for autoplay of videos like Facebook without using any third party library like ASYNCDisplayKit. All videos urls are from AWSS3 cloud-front URLS. I have also uploaded video of the issue in case someone want to have a look.

Video Autoplay Hiccups

Here is my complete code

- (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];
    }
}

//Video List Cell Class

#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
}

Could someone help me out in this ?

回答1:

I have a few tips for you:

  1. Do not try to call play/pause during scrolling. Just perform this actions right after scrolling stops: https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

  2. You can try to use this dirty hacks: https://gist.github.com/k06a/66f7815b0325f239411e26f498c75755 To hide it from Apple Review Team just obfustace keypath @"_player.stateDispatchQueue" with UAObfuscateString library.

  3. I heard smooth payback can be achieved without dirty hacks. I'll ask a friend of mine to answer your question.



回答2:

So I achieved this smooth scrolling with Texturekit. In case anyone wants to achieve the smooth scrolling here is what I did.

I have created a new UItableViewCell -

#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/AsyncDisplayKit.h>

@interface AutoplayVideoTableViewCell : UITableViewCell {
    ASVideoPlayerNode *_videoPlayerNode;
    ASControlNode *_likeButtonNode;
    ASButtonNode *_muteButtonNode;
    UIImage* _fsIcon;
}
- (void)setVideoURL:(NSString*)aVideoURL;
@end


#import "AutoplayVideoTableViewCell.h"
#import "UIImage+Tint.h"

#define AVATAR_IMAGE_HEIGHT     30
#define HORIZONTAL_BUFFER       10
#define VERTICAL_BUFFER         5


@interface AutoplayVideoTableViewCell () <ASVideoPlayerNodeDelegate>
@end

@implementation AutoplayVideoTableViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code

    _muteButtonNode = [[ASButtonNode alloc] init];
    _muteButtonNode.style.width = ASDimensionMakeWithPoints(16.0);
    _muteButtonNode.style.height = ASDimensionMakeWithPoints(22.0);
    [_muteButtonNode addTarget:self action:@selector(didTapMuteButton) forControlEvents:ASControlNodeEventTouchUpInside];

    _videoPlayerNode = [[ASVideoPlayerNode alloc] init];
    _videoPlayerNode.delegate = self;
    _videoPlayerNode.backgroundColor = [UIColor blackColor];
    [self.contentView addSubnode:_videoPlayerNode];

    [self setMuteButtonIcon];

    _fsIcon = [[UIImage imageNamed:@"fullScreenIcon"] imageTintedWithColor:[UIColor whiteColor]];
}

- (void)setVideoURL:(NSString*)aVideoURL {

    //ASVideoNode *videoNode = [[ASVideoNode alloc] init];
    CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;

    _videoPlayerNode.view.frame = CGRectMake(0, 0, fullWidth, 200);

    [_videoPlayerNode setAssetURL:[NSURL URLWithString:aVideoURL]];
    [_videoPlayerNode setGravity:AVLayerVideoGravityResizeAspectFill];
    [_videoPlayerNode setShouldAutoPlay:YES];
    [_videoPlayerNode setShouldAutoRepeat:NO];
    [_videoPlayerNode play];
}

- (void)videoPlayerNodeDidPlayToEnd:(ASVideoPlayerNode *)videoPlayer {
    [videoPlayer seekToTime:0];
}

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
    CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;

    _videoPlayerNode.style.width = ASDimensionMakeWithPoints(fullWidth);
//    _videoPlayerNode.style.height = ASDimensionMakeWithPoints(200);


    ASStackLayoutSpec *bottomControlsStack  = [ASStackLayoutSpec horizontalStackLayoutSpec];
    bottomControlsStack.spacing = HORIZONTAL_BUFFER;
    bottomControlsStack.alignItems = ASStackLayoutAlignItemsCenter;
    bottomControlsStack.children = @[_likeButtonNode];

    UIEdgeInsets bottomControlsInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
    ASInsetLayoutSpec *bottomControlsInset  = [ASInsetLayoutSpec insetLayoutSpecWithInsets:bottomControlsInsets child:bottomControlsStack];


    ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
    verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
    verticalStack.children = @[_videoPlayerNode, bottomControlsInset];
    return verticalStack;
}

- (void)setMuteButtonIcon
{
    if (_videoPlayerNode.muted) {
        [_muteButtonNode setImage:[UIImage imageNamed:@"ico-mute1"] forState:UIControlStateNormal];
    } else {
        [_muteButtonNode setImage:[UIImage imageNamed:@"ico-unmute1"] forState:UIControlStateNormal];
    }
}

- (void)didTapMuteButton
{
    _videoPlayerNode.muted = !_videoPlayerNode.muted;
    [self setMuteButtonIcon];
}

#pragma mark - ASVideoPlayerNodeDelegate
- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer
{
    if (_videoPlayerNode.playerState == ASVideoNodePlayerStatePlaying) {
        _videoPlayerNode.controlsDisabled = !_videoPlayerNode.controlsDisabled;
        [_videoPlayerNode pause];
    } else {
        [_videoPlayerNode play];
    }
}

- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode *)videoPlayer
{
    return @{
             @"muteControl" : _muteButtonNode
             };
}

- (UIImage *)videoPlayerNodeFullScreenButtonImage:(ASVideoPlayerNode *)videoPlayer {
    return _fsIcon;
}

- (NSArray *)controlsForControlBar:(NSDictionary *)availableControls
{
    NSMutableArray *controls = [[NSMutableArray alloc] init];

    if (availableControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]) {
        [controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]];
    }

    if (availableControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]) {
        [controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]];
    }

    if (availableControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
        [controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]];
    }

    if (availableControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]) {
        [controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]];
    }
    if (availableControls[ @(ASVideoPlayerNodeControlTypeFullScreenButton) ]) {
        [controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeFullScreenButton) ]];
    }

    return controls;
}

#pragma mark - Layout
- (ASLayoutSpec*)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize
{
    ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
    spacer.style.flexGrow = 1.0;

    UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);

    if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
        ASDisplayNode *scrubber = controls[ @(ASVideoPlayerNodeControlTypeScrubber) ];
        scrubber.style.height = ASDimensionMakeWithPoints(44.0);
        scrubber.style.minWidth = ASDimensionMakeWithPoints(0.0);
        scrubber.style.maxWidth = ASDimensionMakeWithPoints(maxSize.width);
        scrubber.style.flexGrow = 1.0;
    }

    NSArray *controlBarControls = [self controlsForControlBar:controls];
    NSMutableArray *topBarControls = [[NSMutableArray alloc] init];

    //Our custom control
    if (controls[@"muteControl"]) {
        [topBarControls addObject:controls[@"muteControl"]];
    }


    ASStackLayoutSpec *topBarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
                                                                            spacing:10.0
                                                                     justifyContent:ASStackLayoutJustifyContentStart
                                                                         alignItems:ASStackLayoutAlignItemsCenter
                                                                           children:topBarControls];

    ASInsetLayoutSpec *topBarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:topBarSpec];

    ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
                                                                                spacing:10.0
                                                                         justifyContent:ASStackLayoutJustifyContentStart
                                                                             alignItems:ASStackLayoutAlignItemsCenter
                                                                               children: controlBarControls ];
    controlbarSpec.style.alignSelf = ASStackLayoutAlignSelfStretch;



    ASInsetLayoutSpec *controlbarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:controlbarSpec];

    controlbarInsetSpec.style.alignSelf = ASStackLayoutAlignSelfStretch;

    ASStackLayoutSpec *mainVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
                                                                                   spacing:0.0
                                                                            justifyContent:ASStackLayoutJustifyContentStart
                                                                                alignItems:ASStackLayoutAlignItemsStart
                                                                                  children:@[topBarInsetSpec, spacer, controlbarInsetSpec]];

    return mainVerticalStack;

}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end

And in the cellforRow method I changed the code

                    if (post.medias.count > 0) {
                        MediaItem* item = post.medias[0];
                        if ([item getMediaType] == VIDEO) {

                            [cell setVideoURL:item.mediaUrl];
                            // dispatch_async(dispatch_get_main_queue(), ^{

                            //cell.videoThumbnail.image = nil;

                            //});
                        }
                    }

And it worked like a charm. I'm still trying to figure other things but it will be a enough to start for anyone else. The Code I picked up is from the sample app named ASDKTube [ObjC] from the Texture samples.