loadValuesAsynchronouslyForKeys and multiple values loading
I would like to asynchronously load the duration, time (timestamp the video was created) and locale of an Asset.
All of the sample code shown by Apple for the usage of 'loadValuesAsynchronouslyForKeys:keys'
is always shows with only one key. ie:
NSURL *url = aUrl;
AVAsset asset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = [NSArray arrayWithObject:@"duration"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
NSError *error = nil;
AVKeyValueStatus durationStatus = [asset statusOfValueForKey:@"duration" error:&error];
switch (durationSatus) {
case AVKeyValueStatusLoaded:
// Read duration f开发者_运维技巧rom asset
CMTime assetDurationInCMTime = [asset duration];
break;
case AVKeyValueStatusFailed:
// Report error
break;
case AVKeyValueStatusCancelled:
// Do whatever is appropriate for cancelation
}
}];
Can I assume that if one item's status is 'AVKeyValueStatusLoaded', the other values can be read at the same time in the completion block? ie:
[asset tracks]
[asset commonMetadata];
[asset duration]
No, you can't assume that. One of my methods looks at two keys, playable and duration, and I have found that playable is often available while duration isn't yet. I therefor have moved the loadValuesAsynchronouslyForKeys: code into a separate method shouldSave:. The shouldSave: method I call from a timer in a method called saveWithDuration:. Once saveWithDuration: receives a non-zero duration, it goes ahead and saves stuff. To avoid waiting too long, I use an attempt counter for now -- in the future, I'll refine this (you'll notice that the error instance isn't really used at the moment)
- (void)shouldSave:(NSTimer*)theTimer {
NSString * const TBDuration = @"duration";
NSString * const TBPlayable = @"playable";
__block float theDuration = TBZeroDuration;
__block NSError *error = nil;
NSArray *assetKeys = [NSArray arrayWithObjects:TBDuration, TBPlayable, nil];
[_audioAsset loadValuesAsynchronouslyForKeys:assetKeys completionHandler:^() {
AVKeyValueStatus playableStatus = [_audioAsset statusOfValueForKey:TBPlayable error:&error];
switch (playableStatus) {
case AVKeyValueStatusLoaded:
//go on
break;
default:
return;
}
AVKeyValueStatus durationStatus = [_audioAsset statusOfValueForKey:TBDuration error:&error];
switch (durationStatus) {
case AVKeyValueStatusLoaded:
theDuration = CMTimeGetSeconds(_audioAsset.duration);
break;
default:
return;
}
}];
NSUInteger attempt = [[[theTimer userInfo] objectForKey:TBAttemptKey] integerValue];
attempt++;
[self saveWithDuration:theDuration attempt:attempt error:&error];
}
Technically you can't. The docs for loadValuesAsynchronouslyForKeys:completionHandler: says that
The completion states of the keys you specify in keys are not necessarily the same—some may be loaded, and others may have failed. You must check the status of each key individually.
In practice, I think this is often a safe assumption -- as you've noted, Apple's StitchedStreamPlayer sample project just looks at the status of the first key.
No you cannot assume so. I usually rely on @"duration"
key to create an AVPlayerItem
and start playback since loading of @"playable"
generally doesn't guarantee that the asset is ready yet. Then I spawn a timer to check periodically whether others keys such as @"tracks"
are loaded or not similar to what Elise van Looij has mentioned above.
Also, side note - do remember that the completionHandler
in loadValuesAsynchronouslyForKeys
is called on an arbitrary background thread. You will have to dispatch it to main thread if you are dealing with UI or AVPlayerLayer.
精彩评论