Playing many different videos on iphone using AVPlayer

12,247

Solution 1

I FINALLY SOLVED IT. The design I had was indeed very poor, It had a very bad memory management and so on. What I did was, instead of releasing everything I could, including views, layers, players, assets, etc, I just created a method to update the player's asset and item, recycling the player, views, layers, etc.

    [player pause];
    contentURL = [[NSURL alloc] initFileURLWithPath:newPath];
    AVAsset *newAsset = [AVURLAsset URLAssetWithURL:contentURL options:nil];
    AVPlayerItem *newPlayerItem = [AVPlayerItem playerItemWithAsset:newAsset];
    [player replaceCurrentItemWithPlayerItem:newPlayerItem];
    [contentURL release];

And now it's working wonders. Thank you all for your help.

Solution 2

There are 2 places in the code where you have retained an object, and I don't see the corresponding release anywhere in the code you have posted. I would start by making sure that the memory reference count increments and decrements are all appropriately balanced.

The first retain looks suspicious to me. Generally you should first autorelease the variable you are assigning to and then retain as follows:

[systemPath autorelease];
systemPath = [aContentURL retain]

In the above code is easy to see that the retain/release matching is maintained, and is far less error prone than other schemes. Note that if systemPath == aContentURL the object will not be released.

Share:
12,247
Samssonart
Author by

Samssonart

Professional geek

Updated on June 04, 2022

Comments

  • Samssonart
    Samssonart almost 2 years

    I'm working on a custom video player for iOS using AVFoundation. The idea is to be able to switch to a different video with a gesture (tap, swipe, whatever). Right now the player is working flawlessly on simulator, but when I test it on an actual device, the view goes blank after 3 or 4 swipes. I've even created playback controls for my player, when the view goes blank, these controls load correctly but do nothing. Any ideas guys? This is the initialization for the player

                - (id)initWithContentURL:(NSString *)aContentURL delegate:(id)aDelegate {
            self = [super initWithNibName:@"NoCashMoviePlayer" bundle:nil];
            if (self == nil)
                return nil;
            delegate = aDelegate;
            systemPath = [aContentURL retain];
            contentURL = [[NSURL alloc]initFileURLWithPath:systemPath];
            asset = [AVURLAsset URLAssetWithURL:contentURL options:nil];
            playerItem = [AVPlayerItem playerItemWithAsset:asset];
            isPaused = false;
            controlsHidden = false;
            self.player = [[AVPlayer playerWithPlayerItem:playerItem] retain];
             duration = self.player.currentItem.asset.duration;
    
            return self;
        }
    

    This is the code that plays the video:

        -(void)playMovie{
    
            UITapGestureRecognizer *tapRecon = [[UITapGestureRecognizer alloc]initWithTarget:self   action:@selector(toggleControls:)];
            [tapRecon setNumberOfTapsRequired:2]; 
            [self.movieContainer addGestureRecognizer:tapRecon];
            [tapRecon release];
    
            NSLog(@"Playing item: %@",contentURL);
            playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
            [movieContainer.layer addSublayer:playerLayer];
            playerLayer.frame = movieContainer.layer.bounds;
            playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
            self.seeker.alpha = 1.0;
            [self.view addSubview:movieContainer];
            [self.movieContainer addSubview:controls];
            [self setSlider];
            [player play];
            player.actionAtItemEnd = AVPlayerActionAtItemEndNone; 
    
            [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:[player currentItem]];
    
    
        }
    

    The code to select the clip to be played:

        -(void)viewSelect: (double) curTime{ 
            self.myView.backgroundColor = [UIColor blackColor];
            UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeFrom:)]; 
            swipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight; 
             [self.myView addGestureRecognizer:swipeRecognizer]; 
            [swipeRecognizer release];
    
            UISwipeGestureRecognizer *leftRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeFromLeft:)]; 
            swipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft; 
            [self.myView addGestureRecognizer:leftRecognizer]; 
            [leftRecognizer release];
    
    
            if(isMain){
                [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationTransitionFlipFromLeft animations:^{
                    self.myView.alpha = 1.0;
                    moviePlayer = [[NoCashMoviePlayer alloc]initWithContentURL:[self movieURL:vidIndex] delegate:self];
                    self.moviePlayer.view.frame = self.myView.bounds;
                    self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
                     [self.myView addSubview:moviePlayer.view];
    
                }completion:^(BOOL finished) {
                    [self.moviePlayer.player seekToTime:CMTimeMake(curTime, 1)];
                    [self.moviePlayer playMovie];
                }];
            }else{
    
                [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationTransitionFlipFromLeft animations:^{
                    self.otherView.alpha = 1.0;
                    moviePlayer = [[NoCashMoviePlayer alloc]initWithContentURL:[self movieURL:vidIndex] delegate:self];
                    self.moviePlayer.view.frame = self.otherView.bounds;
                    self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
                    [self.otherView addSubview:moviePlayer.view];
    
                }completion:^(BOOL finished) {
    
                   [self.moviePlayer.player seekToTime:CMTimeMake(curTime, 1)];
                    [self.moviePlayer playMovie];
                }];
            }
        }
    

    And last the gesture action:

        - (void)handleSwipeFromLeft:(UISwipeGestureRecognizer *)recognizer { 
    
            double elapsedTime = 0.0;
            if(vidIndex==0){
                vidIndex = 3;
            }else vidIndex = vidIndex --;
            elapsedTime = [self.moviePlayer currentTimeInSeconds];
            [self.moviePlayer stopMovie];
            isMain = !isMain;
            [self viewSelect: elapsedTime];
    
        }
    

    EDIT: Tried using different AVPlayerLayers for each video file, same situation, works in the simulator, not on the iPad.

    EDIT 2: I ran instruments to analyze core animation performance, and when the video is playing it's showing framerates of about 30 fps, when the player goes blank it drops all the way down to 1 or 2 fps. This may be ovbious, but still, if it helps give a little more light.....

    EDIT 3: Ok, I'm finally getting somewhere, I know what the problem is, I have a core animation memory leak, in the simulator it "works" because the computer has A LOT more memory than the iPad, but since the iPad has very limited memory it stops working very quickly. If anyone has any advice regarding Core Animation leaks, it will be very well received.

  • Samssonart
    Samssonart almost 13 years
    All the retained objects are released in the 'dealloc' method. Would that be enough? Actually I used to have the player without the 'retain' keyword, but It was worse, it eventually led to a crash, because the object was being released at some point during runtime.
  • Himadri Choudhury
    Himadri Choudhury almost 13 years
    Not sure without seeing the exact code. But the first retain doesn't seem to follow the standard pattern.
  • Samssonart
    Samssonart almost 13 years
    Thanks for the help Himadri, I've tried a few different memory/referece count solutions, but it's sill not working. I've tried using instruments and there were a few leaks, I took care of them, and still nothing. I'll keep trying.