AVPlayerItem initial timedMetadata not being observed (KVO)

Tengo una class que está manejando un AVPlayer (y AVPlayerItem) que reporta el estado, la hora y el calendar de Metadata a un delegado.

Funciona bien, excepto que aproximadamente el 70-80% del time, el Mometado inicial no es "valor key observado". Sin embargo, después de que se haya pasado por alto la primera instancia de timedMetadata, todos los demás timedMetadata parecen observarse sin problema.

Como solución temporal, he comenzado a incrustar tags dummy timedMetadata al principio de los videos que no hacen más que "patear los neumáticos" por así decirlo y todo funciona bien después de eso. Sin embargo, esto parece bastante complicado. Sospecho que estoy configurando AVPlayerItem y KVO de una manera subóptima O simplemente hay un error aquí.

¡Cualquier idea sobre por qué esto podría estar sucediendo es muy apreciada! Código a continuación …

// 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:&error]; if (status == AVKeyValueStatusLoaded) { AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset]; [item addObserver:_self forKeyPath:@"status" options:0 context:&ItemStatusContext]; [item addObserver:_self forKeyPath:@"timedMetadata" options:0 context:&ItemMetadataContext]; [item addObserver:_self forKeyPath:@"playbackLikelyToKeepUp" options:0 context:&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 == &ItemStatusContext) { dispatch_async(dispatch_get_main_queue(), ^{ if (((AVPlayerItem *)object).status == AVPlayerItemStatusReadyToPlay) { [_self.delegate videoIsLoadedInPlayer:_self]; } }); return; } else if (context == &ItemMetadataContext) { dispatch_async(dispatch_get_main_queue(), ^{ [_self checkMetaDataForPlayerItem: (AVPlayerItem *)object]; }); return; } else if (context == &ItemPlaybackForcastContext) { dispatch_async(dispatch_get_main_queue(), ^{ AVPlayerItem *playerItem = object; if (CMTimeGetSeconds([playerItem currentTime]) <= 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 && [item.timedMetadata count] > 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]]; } } 

Ahhh, KVO Probablemente una de las peores decisiones de layout de Apple.

Supongo que ya no es relevante, pero conjetura que el problema que tienes es que a veces el valor que estás tratando de observar ya se ha asignado a la key cuando te acercas a sumrte a ti mismo como observador, por lo que tu selector de observadores no se llama

Para evitar esto, puede agregar NSKeyValueObservingOptionInitial a las options al llamar a addObserver:forKeyPath:options:context: y su método observador se invocará inmediatamente con el valor actual.