未观察到AVPlayerItem初始timedMetadata(KVO)

时间:2012-08-22 03:01:09

标签: ios cocoa-touch avfoundation avplayer

我有一个处理AVPlayer(和AVPlayerItem)的类,它向州代表报告状态,时间和timedMetadata。

除了大约70-80%的时间之外,效果很好,初始timedMetadata不是“观察到的键值”。但是,在错过timedMetadata的第一个实例之后,似乎没有问题地观察到所有其他timedMetadata。

作为一个临时解决方案,我已经开始在视频的开头嵌入虚拟的timedMetadata标签,除了“踢出轮胎”之外什么都不做,可以说,之后一切正常。然而,这看起来非常糟糕。我怀疑要么是以次优的方式设置AVPlayerItem和KVO,要么就是这里的错误。

非常感谢任何关于为什么会发生这种情况的想法!代码如下....

// 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]];
    }
}

1 个答案:

答案 0 :(得分:1)

啊,KVO。可能是Apple历史上最糟糕的设计决策之一。

我想它已经不再相关了,但是猜测你遇到的问题是,当你开始将自己添加为观察者时,有时你试图观察到的值已被分配给键,所以你的观察者选择器没有被调用。

为避免这种情况,您可以在调用NSKeyValueObservingOptionInitial时向options添加addObserver:forKeyPath:options:context:,并使用当前值立即调用您的观察者方法。