Note that there are some explanatory texts on larger screens.

plurals
  1. POAVPlayerItem initial timedMetadata not being observed (KVO)
    text
    copied!<p>I have a class that is handling an AVPlayer (and AVPlayerItem) that reports back state, time, and timedMetadata to a delegate.</p> <p>Works well except that about 70-80% of the time, the initial timedMetadata is not "key value observed". However after the first instance of timedMetadata being missed, all other timedMetadata seems to be observed without issue.</p> <p>As a temporary fix, I've started to embed dummy timedMetadata tags in the beginning of videos that do nothing but "kick the tires" so to speak and everything works fine after that. Yet this seems pretty kludgy. I suspect that either I'm setting up the AVPlayerItem and KVO in a sub-optimal manner OR there's just a bug here.</p> <p>Any ideas on why this might be happening are greatly appreciated! Code below....</p> <pre><code>// CL: Define constants for the key-value observation contexts. static const NSString *ItemStatusContext; static const NSString *ItemMetadataContext; static const NSString *ItemPlaybackForcastContext; - (id)initWithURL:(NSURL *)url { if (self = [super init]) { __weak TFPAVController *_self = self; AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; NSString *tracksKey = @"tracks"; [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:tracksKey] completionHandler: ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSError *error = nil; AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&amp;error]; if (status == AVKeyValueStatusLoaded) { AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset]; [item addObserver:_self forKeyPath:@"status" options:0 context:&amp;ItemStatusContext]; [item addObserver:_self forKeyPath:@"timedMetadata" options:0 context:&amp;ItemMetadataContext]; [item addObserver:_self forKeyPath:@"playbackLikelyToKeepUp" options:0 context:&amp;ItemPlaybackForcastContext]; [[NSNotificationCenter defaultCenter] addObserver:_self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:item]; AVPlayer *player = [AVPlayer playerWithPlayerItem:item]; _self.totalRunTime = CMTimeGetSeconds(item.duration); [_self.delegate avPlayerNeedsView:player]; _self.playerItem = item; _self.player = player; } else { NSLog(@"The asset's tracks were not loaded: %@ // [%@ %@]", error.localizedDescription, NSStringFromClass([self class]), NSStringFromSelector(_cmd)); } _self.playerObserver = [_self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, _FrameRate_) queue:NULL usingBlock: ^(CMTime time) { _self.currentVideoTime = CMTimeGetSeconds([_self.playerItem currentTime]); }]; }); }]; } return self; } #pragma mark - KVO Response Methods - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { __weak TFPAVController *_self = self; if (context == &amp;ItemStatusContext) { dispatch_async(dispatch_get_main_queue(), ^{ if (((AVPlayerItem *)object).status == AVPlayerItemStatusReadyToPlay) { [_self.delegate videoIsLoadedInPlayer:_self]; } }); return; } else if (context == &amp;ItemMetadataContext) { dispatch_async(dispatch_get_main_queue(), ^{ [_self checkMetaDataForPlayerItem: (AVPlayerItem *)object]; }); return; } else if (context == &amp;ItemPlaybackForcastContext) { dispatch_async(dispatch_get_main_queue(), ^{ AVPlayerItem *playerItem = object; if (CMTimeGetSeconds([playerItem currentTime]) &lt;= 0) return; NSDictionary *notificationDictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:playerItem.playbackLikelyToKeepUp] forKey:kAVPlayerStateKey]; [[NSNotificationCenter defaultCenter] postNotificationName:kAVPlayerNotification object:self userInfo:notificationDictionary]; }); return; } [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } - (void)checkMetaDataForPlayerItem:(AVPlayerItem *)item { NSMutableDictionary *metaDict = [NSMutableDictionary dictionary]; // CL: make sure there's stuff there if (item.timedMetadata != nil &amp;&amp; [item.timedMetadata count] &gt; 0) { // CL: if there is, cycle through the items and create a Dictionary for (AVMetadataItem *metadata in item.timedMetadata) { [metaDict setObject:[metadata valueForKey:@"value"] forKey:[metadata valueForKey:@"key"]]; } // CL: pass it to the delegate [self.delegate parseNewMetaData:[NSDictionary dictionaryWithDictionary:metaDict]]; } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload