Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playing stacked videos

I have multiple imageview subviews getting stacked based on my incoming data. Basically all these subviews are either set to an image or a video layer based on my incoming data. The problem i have is playing videos. i can play the first video in the stack but every video after that is just the sound of the first video. How can i play each accordingly?

the views are navigated through with a tap event like snapchat. see below:

@interface SceneImageViewController ()

@property (strong, nonatomic) NSURL *videoUrl;
@property (strong, nonatomic) AVPlayer *avPlayer;
@property (strong, nonatomic) AVPlayerLayer *avPlayerLayer;

@end

@implementation SceneImageViewController

- (void)viewDidLoad {

[super viewDidLoad];

self.mySubviews = [[NSMutableArray alloc] init];
self.videoCounterTags = [[NSMutableArray alloc] init];

int c = (int)[self.scenes count];
c--;
NSLog(@"int c = %d", c);
self.myCounter = [NSNumber numberWithInt:c];


for (int i=0; i<=c; i++) {

    //create imageView
    UIImageView *imageView =[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
    [imageView setUserInteractionEnabled:YES]; // <--- This is very important
    imageView.tag = i;                        // <--- Add tag to track this subview in the view stack
    [self.view addSubview:imageView];
    NSLog(@"added image view %d", i);


    //get scene object
    PFObject *sceneObject = self.scenes[i];


    //get the PFFile and filetype
    PFFile *file = [sceneObject objectForKey:@"file"];
    NSString *fileType = [sceneObject objectForKey:@"fileType"];



    //check the filetype
    if ([fileType  isEqual: @"image"])
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        //get image
        NSURL *imageFileUrl = [[NSURL alloc] initWithString:file.url];
        NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
            dispatch_async(dispatch_get_main_queue(), ^{
        imageView.image = [UIImage imageWithData:imageData];
            });
        });

    }

    //its a video
    else
    {
        // the video player
        NSURL *fileUrl = [NSURL URLWithString:file.url];

        self.avPlayer = [AVPlayer playerWithURL:fileUrl];
        self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;

        self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
        //self.avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(playerItemDidReachEnd:)
                                                     name:AVPlayerItemDidPlayToEndTimeNotification
                                                   object:[self.avPlayer currentItem]];

        CGRect screenRect = [[UIScreen mainScreen] bounds];

        self.avPlayerLayer.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
        [imageView.layer addSublayer:self.avPlayerLayer];

        NSNumber *tag = [NSNumber numberWithInt:i+1];

        NSLog(@"tag = %@", tag);

        [self.videoCounterTags addObject:tag];

        //[self.avPlayer play];
    }



}



UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTapped:)];

[self.view bringSubviewToFront:self.screen];

[self.screen addGestureRecognizer:tapGesture];


}


 - (void)viewTapped:(UIGestureRecognizer *)gesture{

NSLog(@"touch!");

[self.avPlayer pause];

int i = [self.myCounter intValue];
NSLog(@"counter = %d", i);



for(UIImageView *subview in [self.view subviews]) {

    if(subview.tag== i) {

        [subview removeFromSuperview];
    }
}

if ([self.videoCounterTags containsObject:self.myCounter]) {
    NSLog(@"play video!!!");
    [self.avPlayer play];
}

if (i == 0) {
    [self.avPlayer pause];
    [self.navigationController popViewControllerAnimated:NO];
}


i--;
self.myCounter = [NSNumber numberWithInt:i];


NSLog(@"counter after = %d", i);





}
like image 875
ian Avatar asked Nov 29 '15 01:11

ian


People also ask

What does stacked mean in gaming?

Stacked means having multiple good players on the team. Like for example if you hear someone saying a team is stacked in trials, all 3 probably have a high kd/elo rating etc.


2 Answers

What Brooks Hanes said is correct you keep overriding the avplayer. This is what i suggest for you to do:

  1. Add the tap gesture to the imageView instead of the screen (or for a cleaner approach use UIButton instead):

    UIImageView *imageView =[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
    [imageView setUserInteractionEnabled:YES]; // <--- This is very important
    imageView.tag = i;                        // <--- Add tag to track this subview in the view stack
    [self.view addSubview:imageView];
    NSLog(@"added image view %d", i);
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:imageView action:@selector(viewTapped:)];
    [imageView addGestureRecognizer:tapGesture];
    

This way in your viewTapped: method you could get the tag of the pressed image like so: gesture.view.tag instead of using the myCounter.

  1. To get the video working you could create a new AVPlayer for each video but that might turn quite expensive memory wise. A better approach will be to use AVPlayerItem and switch the AVPlayer's AVPlayerItem when changing the video.

So in the for loop do something like this where self.videoFiles is a NSMutableDictionary property:

           // the video player
            NSNumber *tag = [NSNumber numberWithInt:i+1];
            NSURL *fileUrl = [NSURL URLWithString:file.url];
          //save your video file url paired with the ImageView it belongs to.
           [self.videosFiles setObject:fileUrl forKey:tag];
// you only need to initialize the player once.
            if(self.avPlayer == nil){
                AVAsset *asset = [AVAsset assetWithURL:fileUrl];
                AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
                self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:item];
                self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
                [[NSNotificationCenter defaultCenter] addObserver:self
                    selector:@selector(playerItemDidReachEnd:)
                name:AVPlayerItemDidPlayToEndTimeNotification
                object:[self.avPlayer currentItem]];
            }
            // you don't need to keep the layer as a property 
            // (unless you need it for some reason 
            AVPlayerLayer* avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
            avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

            CGRect screenRect = [[UIScreen mainScreen] bounds];                
            avPlayerLayer.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
            [imageView.layer addSublayer:avPlayerLayer];
            NSLog(@"tag = %@", tag);                
            [self.videoCounterTags addObject:tag];

Now in your viewTapped:

 if ([self.videoCounterTags containsObject:gesture.view.tag]) { 

  NSLog(@"play video!!!");
    AVAsset *asset = [AVAsset assetWithURL:[self.videoFiles objectForKey:gesture.view.tag]];
    AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
    self.avPlayer replaceCurrentItemWithPlayerItem: item];
    [self.avLayer play];
}

Or use the self.videoFiles instead and then you don't need self.videoCounterTags at all:

 NSURL* fileURL = [self.videoFiles objectForKey:gesture.view.tag];
 if (fileURL!=nil) {    
     NSLog(@"play video!!!");
     AVAsset *asset = [AVAsset assetWithURL:fileURL];
     AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
     self.avPlayer replaceCurrentItemWithPlayerItem: item];
     [self.avLayer play];
 }

That's the gist of it.

like image 96
null Avatar answered Sep 30 '22 05:09

null


Take a look at the way you're setting up the myCounter variable. It is set one time and never changes until a view is tapped, and then it is set to the count of scenes, -1.

In addition, try looking at the way you're setting to the _avPlayer pointer var. It's always being set, over and over, and it seems that in a for loop you'd want to be storing references instead of simply updating the same pointer to the value latest in collection of scenes.

Also, from Apple's documentation:

You can create arbitrary numbers of player layers with the same AVPlayer object. Only the most recently created player layer will actually display the video content on-screen.

So, it's possible that since you're using the same AVPlayer object to create all these AVPlayer layers, that you're never going to see any more than one actual video layer work.

like image 24
Brooks Hanes Avatar answered Sep 30 '22 05:09

Brooks Hanes