Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVPlayer crashes after multiple playbacks-

I'm trying to create an app which plays videos from a file using AVFoundation. The videos are shown in a view accessed by tapping on a row in a parent tableview. The real app will have a video for each row, but at the moment I'm using just one for testing.

When run on the simulator the app is ok, but when run on the device (under ios 5.1) the video plays ok for about 5 times, then crashes unpredictably in a variety of ways. Most commonly, the video view loads but the video itself doesn't play, but sometimes I get an EXC_BAD_ACCESS on a coremedia.remote thread, complaining about objects being allocated with no autorelease pool in place. I've added an @autoreleasepool block wrapping the code launching the AVPlayer, but this doesn't seem to help.

I'm wondering whether what is happening is that GCD is creating multiple threads on the main queue to play the items, but they are not terminating.

So the key question is- how do I clear up the superfluous GCD threads the AVPlayer is running on if the user hits the back button in the video view As far as possible I've followed the example code provided in Apple's AVFoundation documentation here I've added some logging and (as mentioned above) an @autoreleasepool block inside one of the GCD blocks- other than that I haven't changed the code.

The viewDidLoad method is as follows:

-(void)viewDidLoad{   
[super viewDidLoad];

NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"TestLapCar2Vid" withExtension:@"m4v"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
NSString *tracksKey = @"tracks";

[asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:tracksKey] completionHandler:
 ^{
     dispatch_async(dispatch_get_main_queue(),
     ^{
         @autoreleasepool {
         NSError *error = nil;
             AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];

             if(status == AVKeyValueStatusLoaded){
                 avPlayerItem = [AVPlayerItem playerItemWithAsset:asset];
                 [avPlayerItem addObserver:self forKeyPath:@"status"
                                   options:0  context:&ItemStatusContext];

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

                 avPlayer = [AVPlayer playerWithPlayerItem:avPlayerItem];
                 [videoView setPlayer:avPlayer];
                 NSLog(@"Asset loaded");
                 [avPlayer play];
             }
             else{
                 NSLog(@"The asset's tracks were not loaded");
             }

         }
     });
 }];    

}

The viewWillDisappear method is:

-(void)viewWillDisappear:(BOOL)animated{
NSLog(@"view will disappear called");
[super viewWillDisappear:animated];
dispatch_async(dispatch_get_main_queue(), 
    ^{
        [avPlayer pause];
        [avPlayerItem removeObserver:self forKeyPath:@"status"];
        [[NSNotificationCenter defaultCenter]removeObserver:self];
        NSLog(@"Race timeline nav controller has %d sub controllers",self.navigationController.childViewControllers.count);
        avPlayerItem = nil;
        avPlayer = nil;
        videoView = nil;
        dataStore = nil;
        pkReader = nil;
        receivedData = nil;
        revDial = nil;
        speedDial = nil;
        mapView = nil;
        throttle = nil;
        NSLog(@"releasing stuff");
    });

}

I've been struggling with this for most of today- any help would be gratefully received

like image 437
Rich Tolley Avatar asked Apr 04 '12 17:04

Rich Tolley


2 Answers

You should remove from superview first as it will reduce the retain count by 1 and ARC will take care of the release for your code.

like this

[videoView removeFromSuperview];
[self setVideoView:nil];
like image 199
zuuk Avatar answered Oct 01 '22 19:10

zuuk


May be you leave your videoView retained in some place? Because if you do, your avPlayerItem and avPlayer stay alive and according to this topic you came up to iOS limitation for "render pipeline" with 4 videos staying in memory.

Remember that setting var to nil does not actually release underlying object. So your

videoView = nil;

can have zero effect.

like image 43
m8labs Avatar answered Oct 01 '22 18:10

m8labs