【问题标题】:Play video in Mac OS X 10.7在 Mac OS X 10.7 中播放视频
【发布时间】:2012-06-05 20:03:52
【问题描述】:

在 Mac OS X 10.7 (Lion) 中使用 Objective-C 以编程方式播放视频的最简单方法是什么?如果我也想支持 OS X 10.6 (Snow Leopard)?

我注意到 iOS AV Foundation 被引入 OS X 10.7。不幸的是,文档似乎是为 iOS 编写的,我发现它令人困惑。

【问题讨论】:

  • 当 Apple 提出建议时,他们几乎总是推荐最新和最闪亮的 API,尤其是当它可以轻松地在 OS X 和 iOS 之间移植代码时。这就是您所要求的“推荐”吗?或者您是在询问其他开发人员的个人经验?因为在那种情况下你会得到一个非常不同的答案(可能涉及 QTKit 和/或 CoreAnimation,但这完全取决于你想要做什么)。
  • 其他开发者的个人经历。我将改写为“最简单”。

标签: objective-c video osx-lion osx-snow-leopard avfoundation


【解决方案1】:

这是一个 NSView 子类,它使用 AV Foundation(因此仅适用于 Mac OS X 10.7 以上)播放给定 URL 的视频。基于AVSimplePlayer 示例代码。

标题:

@interface RMVideoView : NSView

@property (nonatomic, readonly, strong) AVPlayer* player;
@property (nonatomic, readonly, strong) AVPlayerLayer* playerLayer;
@property (nonatomic, retain) NSURL* videoURL;

- (void) play;

@end

实施:

static void *RMVideoViewPlayerLayerReadyForDisplay = &RMVideoViewPlayerLayerReadyForDisplay;
static void *RMVideoViewPlayerItemStatusContext = &RMVideoViewPlayerItemStatusContext;

@interface RMVideoView()

- (void)onError:(NSError*)error;
- (void)onReadyToPlay;
- (void)setUpPlaybackOfAsset:(AVAsset *)asset withKeys:(NSArray *)keys;

@end

@implementation RMVideoView

@synthesize player = _player;
@synthesize playerLayer = _playerLayer;
@synthesize videoURL = _videoURL;

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.wantsLayer = YES;
        _player = [[AVPlayer alloc] init];
        [self addObserver:self forKeyPath:@"player.currentItem.status" options:NSKeyValueObservingOptionNew context:RMVideoViewPlayerItemStatusContext];
    }

    return self;
}

- (void) dealloc {
    [self.player pause];
    [self removeObserver:self forKeyPath:@"player.currentItem.status"];
    [self removeObserver:self forKeyPath:@"playerLayer.readyForDisplay"];
    [_player release];
    [_playerLayer release];
    [_videoURL release];
    [super dealloc];
}

- (void) setVideoURL:(NSURL *)videoURL {
    _videoURL = videoURL;

    [self.player pause];
    [self.playerLayer removeFromSuperlayer];

    AVURLAsset *asset = [AVAsset assetWithURL:self.videoURL];
    NSArray *assetKeysToLoadAndTest = [NSArray arrayWithObjects:@"playable", @"hasProtectedContent", @"tracks", @"duration", nil];
    [asset loadValuesAsynchronouslyForKeys:assetKeysToLoadAndTest completionHandler:^(void) {
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            [self setUpPlaybackOfAsset:asset withKeys:assetKeysToLoadAndTest];
        });
    }];
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == RMVideoViewPlayerItemStatusContext) {
        AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
        switch (status) {
            case AVPlayerItemStatusUnknown:
                break;
            case AVPlayerItemStatusReadyToPlay:
                [self onReadyToPlay];
                break;
            case AVPlayerItemStatusFailed:
                [self onError:nil];
                break;
        }
    } else if (context == RMVideoViewPlayerLayerReadyForDisplay) {
        if ([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) {
            self.playerLayer.hidden = NO;
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}


#pragma mark - Private

- (void)onError:(NSError*)error {
    // Notify delegate 
}

- (void)onReadyToPlay {
    // Notify delegate
}

- (void)setUpPlaybackOfAsset:(AVAsset *)asset withKeys:(NSArray *)keys {
    for (NSString *key in keys) {
        NSError *error = nil;
        if ([asset statusOfValueForKey:key error:&error] == AVKeyValueStatusFailed) {
            [self onError:error];
            return;
        }
    }

    if (!asset.isPlayable || asset.hasProtectedContent) {
        [self onError:nil];
        return;
    }

    if ([[asset tracksWithMediaType:AVMediaTypeVideo] count] != 0) { // Asset has video tracks
        _playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        self.playerLayer.frame = self.layer.bounds;
        self.playerLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
        self.playerLayer.hidden = YES;
        [self.layer addSublayer:self.playerLayer];
        [self addObserver:self forKeyPath:@"playerLayer.readyForDisplay" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:RMVideoViewPlayerLayerReadyForDisplay];
    }

    // Create a new AVPlayerItem and make it our player's current item.
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
    [self.player replaceCurrentItemWithPlayerItem:playerItem];  
}

#pragma mark - Public

- (void) play {
    [self.player play];
}

@end

【讨论】:

  • 由于 OP 想要支持 10.6,这不是很有帮助。但他也确实要求不要对 AVFoundation 感到困惑。
  • @abarnert 我是 OP。我想用 AV Foundation 的方式来补充你的答案。
  • 啊,在那种情况下,你可能比我更清楚 OP 想要什么。 :)
【解决方案2】:

“最简单”取决于您想要做什么。如果您想要更多控制(例如,将影片渲染为 OpenGL 纹理)或更少(例如,您可以弹出并忽略的完全独立的窗口),可能会有不同的答案。

但是对于大多数用例,如果您想要 10.6+ 的支持,那么显示电影的最简单方法是 QTKit。请参阅 Xcode 文档中的文章“使用 QTKit 进行媒体播放”以获得良好的起点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多