2012-08-22 69 views
4

我有一个类正在处理一个AVPlayer(和AVPlayerItem),它将状态,时间和timedMetadata报告给一个委托。AVPlayerItem初始timedMetadata不被观察(KVO)

除了大约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]]; 
    } 
} 
+0

是否具有相同格式的所有文件? – oltman

+0

总之:是的。详细地说:initWithURL的URL指向一个.m3u8文件(我使用的是HTTP Live Streaming),该文件随后指向.ts段文件流。 – GnarlyDog

回答

1

Ahhh,KVO。可能是苹果公司历史上最糟糕的设计决策之一。

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

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

+0

不是这个问题,因为所有这些都是在我称之为播放视频之前设置的。此外,第一次定时元数据通常是在视频中大约5分钟。但不错的建议! – GnarlyDog