problems with getting video from camera roll as asset

Discussion in 'iOS Programming' started by lunadiviner, Jul 19, 2012.

  1. lunadiviner macrumors newbie

    Joined:
    Jul 19, 2012
    #1
    I'm trying to get the most recent video taken on an iOS device as AVAsset so that I can then proceed to do other things with it. I am using this code to try and do it:

    Code:
    NSMutableArray *assets = [[NSMutableArray alloc] init];
    
    library = [[ALAssetsLibrary alloc] init];
    
    // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
    
    [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
    
        [group setAssetsFilter:[ALAssetsFilter allVideos]];
    
        [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets]-1]
    
                                options:0
    
                             usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
    
    
                                 if (alAsset) {
    
                                     ALAssetRepresentation *representation = [alAsset defaultRepresentation];
    
                                     NSURL *url = [representation url];
    
                                     AVAsset *recentVideo = [AVURLAsset URLAssetWithURL:url options:nil];
    
                                     [assets addObject:recentVideo];
    
    
    
                                 }
    
    
                             }];
    
    }
    
                         failureBlock: ^(NSError *error) {
    
                             // better error handling needed
    
                             NSLog(@"error");
    
                         }];
    However, when running the app on an iPad, I get a SIGABRT error at this line in main:

    Code:
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    and the following error in the debugger:

    Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array' * First throw call stack: (0x37bc288f 0x32ef3259 0x37b0b9db 0x672af 0x309d4c8b 0x309df267 0x309df1d5 0x30a7e59b 0x30a7d367 0x30ad86a7 0x62351 0x36de860d 0x37b96a33 0x37b96699 0x37b9526f 0x37b184a5 0x37b1836d 0x35ffb439 0x309c9cd5 0x61f35 0x61ef4) terminate called throwing an exception

    It seems like it's just skipping over the part where it's supposed to get the asset and then when I try to use the asset later the array is empty so it errors, but I'm not entirely sure. The only information I've really been able to find is related to the lifetime of library objects only lasting as long as the library, but I release the library much later after I'm already done with it so I don't think that's the problem. Does anybody have any ideas? I'm at a complete loss here.
     
  2. xStep macrumors 68000

    Joined:
    Jan 28, 2003
    Location:
    Less lost in L.A.
    #2
    To find the line actually throwing the error you want to run the debugger with Exception Breakpoint set. I suspect you are accessing your assets array at an inappropriate time before the blocks have completed.

    I'm not sure this is helpful. I use it to get the thumbnail of the last video in the camera roll. There is a commented line that suggests you can get the url of that video. I guess I was playing and left that line in there for future review. It's been a while since I looked at this code so I'm not sure I can be more helpful.

    Notice how I'm using NSLog() to see what the code is doing. You might want to try that to gain information.


    Code:
    - (void) updateCameraRollPoster1
    {
        ALAssetsLibrary * library = [[ALAssetsLibrary alloc] init];
        
        [library enumerateGroupsWithTypes: ALAssetsGroupSavedPhotos // The Camera Roll
         usingBlock: ^(ALAssetsGroup *group, BOOL *stop) { 
    
             [group setAssetsFilter: [ALAssetsFilter allVideos]];
             NSLog(@"updateCameraRollPoster - group numberOfAssets2 : %d", group.numberOfAssets);
             
             if ([[group valueForProperty: ALAssetsGroupPropertyName] isEqual: @"Camera Roll"]) {
    
                 NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex: group.numberOfAssets - 1];
                 NSLog(@"updateCameraRollPoster - indexSet count: %u", [indexSet count]);
                 [group enumerateAssetsAtIndexes: indexSet options: NSEnumerationReverse usingBlock: ^(ALAsset *result, NSUInteger index, BOOL *stop) 
                  {
                      if (index == group.numberOfAssets - 1) {
                          NSLog(@"updateCameraRollPoster - asset: %@  index: %u", result, index);
    
                          photoRollPosterImage = [[UIImage alloc] initWithCGImage: result.thumbnail];
                          [cameraRollbutton setBackgroundImage: photoRollPosterImage forState: UIControlStateNormal];
    
    //                      NSURL * movieURL = [[result valueForProperty: ALAssetPropertyURLs] valueForKey: @"com.apple.quicktime-movie"];
                          *stop = YES;
                          return;
                      }
                  }];
                 
                 
                 *stop = YES;
                 return;
             }
         }
       failureBlock: ^(NSError *error){NSLog(@"updateCameraRollPoster - library failure: %@", error);}];
    }
    
     
  3. lunadiviner thread starter macrumors newbie

    Joined:
    Jul 19, 2012
    #3
    Thanks for the reply. I added a few NSLogs in the code I posted above, like this:

    Code:
    NSMutableArray *assets = [[NSMutableArray alloc] init];
        
        library = [[ALAssetsLibrary alloc] init];
        
        NSLog(@"library allocated");
        
        // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
        
        [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
            
            NSLog(@"Begin enmeration");
            
            [group setAssetsFilter:[ALAssetsFilter allVideos]];
            
            NSLog(@"Filter by videos");
            
            [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets]-1]
             
                                    options:0
             
                                 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
                                     
                                     NSLog(@"Asset retrieved");
                                     
                                     if (alAsset) {
                                         
                                         ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                                         
                                         NSURL *url = [representation url];
                                         
                                         AVAsset *recentVideo = [AVURLAsset URLAssetWithURL:url options:nil];
                                         
                                         [assets addObject:recentVideo];
                                         
                                         NSLog(@"Asset added to array");
                                         
                                         
                                         
                                     }
                                     
                                     
                                 }];
            
        }
    
    Then I added another log to the code that I try to run after the above code, that attempts to use the video that was (theoretically) just put into the array:

    Code:
    AVMutableComposition *composition = [[AVMutableComposition alloc] init];
        
        NSLog(@"creating source");
        AVURLAsset* sourceAsset = [assets objectAtIndex:0];
    
    When I run it I get the "library allocated" message, and then the "creating source" message, and then the program crashes and I receive the error. So it's skipping over the entire block where it's supposed to grab the error but I'm not sure why. I more or less took the code straight from an Apple documentation example, so I don't really know what could be wrong with it. For reference I stuck all of this under viewDidLoad, which is perhaps not the most elegant solution but I'm not sure that giving it its own function would really help in this case.

    Oh, and I have location data turned on for the app, so it definitely isn't that.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    Break it down.

    Make a simplified test case. Remove the inner enumeration and block. Make the enumerateGroupsWithTypes:usingBlock:failureBlock: use trivial blocks. Each block just NSLogs a unique message text. Run it. Does it produce the expected output?

    If it doesn't produce the expected output (including no output at all), then you need to solve that problem before proceeding. If you don't identify and solve the simplest version that has a problem, there's no way you can solve any version that's more complex.


    In the posted code, there are several NSLog outputs that should be produced, but which you haven't said are actually being produced. This suggests at least one of the following:
    1. It's not enumerating the way you expect.
    2. You're overlooking some detail.
    3. You're not telling us everything that happens.

    The simplest way to do #3 is to copy and paste the actual output.


    Also, instead of blindly assuming there's a result in the assets array, why don't you first NSLog the actual number of items in the array. Confirm your expected result first. If there's nothing there, then objectAtIndex: will inevitably throw an exception. The problem isn't the exception; the problem is there's nothing in the array. The exception is a symptom of the problem, it's not the real problem.
     
  5. lunadiviner thread starter macrumors newbie

    Joined:
    Jul 19, 2012
    #5
    This is all of the output that I'm getting when I run it:

    Code:
    2012-07-21 10:29:51.890 app[1212:707] library allocated
    2012-07-21 10:29:51.893 app[1212:707] assets array has 0 objects
    2012-07-21 10:29:51.898 app[1212:707] creating source
    2012-07-21 10:29:51.900 app[1212:707] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
    *** First throw call stack:
    (0x37bc288f 0x32ef3259 0x37b0b9db 0x941eb 0x309d4c8b 0x309df267 0x309df1d5 0x30a7e59b 0x30a7d367 0x30ad86a7 0x8f269 0x36de860d 0x37b96a33 0x37b96699 0x37b9526f 0x37b184a5 0x37b1836d 0x35ffb439 0x309c9cd5 0x8ee4d 0x8ee0c)
    terminate called throwing an exception(lldb)
    So the array has no objects in it which, combined with what I said above about how none of the NSLogs are printing that I put in the enumeration blocks, leads me to believe that the enumeration blocks aren't ever trying to run at all.
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    Well stated. You presented your evidence, you posted your code (earlier post), and you gave your conclusion. That's the correct way.

    Now try the simplified blocks, and do the same things: post the code (the simplified code), post the output, and describe the result. Also be sure to post the complete code that shows the method where your blocks are being called from.

    If it still fails, then try the example from Apple exactly as its given. Not "more or less", exactly. And be sure to post exactly where you got the example from (its URL).

    Finally, have you used the debugger to trace what happens? Do you know how to use the debugger?
     
  7. lunadiviner thread starter macrumors newbie

    Joined:
    Jul 19, 2012
    #7
    I kinda know how to use the debugger, I have a book that I've been using some to help me and it explains some aspects of it but it's also out of date, so Xcode has changed since then.

    I'll try what you said with the simplified blocks in a bit, but I just wanted to post the link where I got the Apple example from. It's here: http://developer.apple.com/library/...l/AVFoundationPG/Articles/01_UsingAssets.html and it's the first example under the "Accessing the User's Assets" heading about getting the first video in the saved photos album. The bit I changed was to get the last asset instead of the first one but I'll also try it exactly as it is there to see if that changes anything.
     
  8. lunadiviner thread starter macrumors newbie

    Joined:
    Jul 19, 2012
    #8
    I put the example code from Apple into a new app that doesn't do anything except for retrieve the asset and store it into an array, and my results when running it were interesting to say the least.

    Here's the code I have that retrieves the asset there:

    Code:
    library = [[ALAssetsLibrary alloc] init];
        assets = [[NSMutableArray alloc] initWithCapacity:2];
        
        NSLog(@"begin enumeration");
        
        [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
            
            NSLog(@"filter");
            
            // Within the group enumeration block, filter to enumerate just videos.
            
            [group setAssetsFilter:[ALAssetsFilter allVideos]];
            
            
            
            // For this example, we're only interested in the first item.
            
            [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0]
             
                                    options:0
             
                                 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
                                     
                                     NSLog(@"enumerate at indexes");
                                     
                                     
                                     
                                     // The end of the enumeration is signaled by asset == nil.
                                     
                                     if (alAsset) {
                                         
                                         ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                                         
                                         NSURL *url = [representation url];
                                         
                                         AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
                                         
                                         [assets addObject:avAsset];
                                         
                                         NSLog(@"object added");
                                         
                                     }
                                     
                                 }];
            
        }
         
                             failureBlock: ^(NSError *error) {
                                 
                                 // Typically you should handle an error more gracefully than this.
                                 
                                 NSLog(@"No groups");
                                 
                             }];
        
        arrayCount = [assets count];
        NSLog(@"assets in array: %i", arrayCount);
    and here's the output:

    Code:
    2012-07-22 11:55:31.234 cameraTest[1574:707] begin enumeration
    2012-07-22 11:55:31.238 cameraTest[1574:707] assets in array: 0
    2012-07-22 11:55:31.383 cameraTest[1574:707] filter
    2012-07-22 11:55:31.398 cameraTest[1574:707] enumerate at indexes
    2012-07-22 11:55:31.411 cameraTest[1574:707] object added
    2012-07-22 11:55:31.412 cameraTest[1574:707] enumerate at indexes
    2012-07-22 11:55:31.414 cameraTest[1574:707] filter
    So it looks like it's skipping over the blocks initially to the part where I count the array objects, then goes back and runs through the blocks to the end and then goes back through them. I'm guessing that it would do the same thing in the other app but because I have more code after that tries to use the asset it runs through that first and crashes, and never has a chance to go through the blocks for some reason. Maybe putting it in its own method is actually the key here. I'm just not sure why it would be doing this.
     

Share This Page