AVPlayer无法渲染到其AVPlayerLayer

时间:2012-03-13 03:20:35

标签: macos avfoundation calayer avplayer

我有一个AVPlayerLayer(CALayer的子类),我需要进入一个可以传递给QCRenderer的图像类型(QCRenderer接受NSImages和CIImages。)我可以将CALayer转换为CGImageRef,并将其转换为NSImage,但内容始终清晰。

我把它缩小到以下两个原因之一:

  1. 我没有正确创建NSImage。
  2. AVPlayer未呈现给AVPlayerLayer。
  3. 我没有收到任何错误,并且找到了一些关于转换CALayers的文档。另外,我将AVPlayerLayer添加到NSView,它仍然是空的,所以我认为2是问题所在。

    我正在使用Apple的AVPlayerDemo的AVPlayerDemoPlaybackViewController的修改版本。我把它变成了NSObject,因为我从中删除了所有的接口代码。

    我在创建AVPlayer时在(void)prepareToPlayAsset:withKeys:方法中创建AVPlayerLayer :(我只是将图层添加到NSView以测试它是否正常工作。)

     if (![self player])
    {
        /* Get a new AVPlayer initialized to play the specified player item. */
        [self setPlayer:[AVPlayer playerWithPlayerItem:self.mPlayerItem]];  
    
        /* Observe the AVPlayer "currentItem" property to find out when any 
         AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did 
         occur.*/
        [self.player addObserver:self 
                      forKeyPath:kCurrentItemKey 
                         options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                         context:AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext];
        mPlaybackView = [AVPlayerLayer playerLayerWithPlayer:self.player];
        [self.theView setWantsLayer:YES];
        [mPlaybackView setFrame:self.theView.layer.bounds];
        [self.theView.layer addSublayer:mPlaybackView];
    }
    

    然后我创建一个NSRunLoop来每秒抓取一次AVPlayerLayer的帧30次:

    framegrabTimer = [NSTimer timerWithTimeInterval:(1/30) target:self selector:@selector(grabFrameFromMovie) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:framegrabTimer forMode:NSDefaultRunLoopMode];
    

    这是我用来抓取框架并将其传递给处理QCRenderer的类的代码:

    -(void)grabFrameFromMovie {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    CGContextRef theContext = CGBitmapContextCreate(NULL, mPlaybackView.frame.size.width, mPlaybackView.frame.size.height, 8, 4*mPlaybackView.frame.size.width, colorSpace, kCGImageAlphaPremultipliedLast);
    [mPlaybackView renderInContext:theContext];
    CGImageRef CGImage = CGBitmapContextCreateImage(theContext);
    NSImage *image = [[NSImage alloc] initWithCGImage:CGImage size:NSMakeSize(mPlaybackView.frame.size.width, mPlaybackView.frame.size.height)];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"AVPlayerLoadedNewFrame" object:[image copy]];
    CGContextRelease(theContext);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(CGImage); }
    

    我无法弄清楚为什么我只是清楚了。非常感谢任何帮助,因为OS X没有足够的AVFoundation文档。

3 个答案:

答案 0 :(得分:0)

它适用于我:

AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:[[[self player] currentItem] asset]];
CGImageRef capture = [gen copyCGImageAtTime:self.player.currentTime actualTime:NULL error:NULL];
NSImage *img = [[NSImage alloc] initWithCGImage:capture size:self.playerView.frame.size];

答案 1 :(得分:0)

您可以将AVPlayerItemVideoOutput添加到AVPlayerItem,然后调用copyPixelBufferForItemTime来查询在指定时间内包含该帧的CVPixelBufferRef对象,下面是示例代码:

df['type'] = np.where(df['model'].isin(dictMobile.keys()), 'mobile','tablet')
print (df)
  assigned to       model    type
0       user1  ipad air 2  tablet
1       user2   galaxy a5  mobile
2       user3    ipad air  tablet

你可以查看苹果公司的官方样本:

Real-timeVideoProcessingUsingAVPlayerItemVideoOutput

答案 2 :(得分:0)

SWIFT 5.2版本:

我认为 rozochkin 的答案是正确的,我发现它真的很有用。我自己进行了测试,它可以正常工作。

我只想发布更新的Swift 5.2版本,以防万一有人需要它。

func getCurrentFrame() -> CGImage? {
    guard let player = self.player, let avPlayerAsset = player.currentItem?.asset else {return nil}
    let assetImageGenerator = AVAssetImageGenerator(asset: avPlayerAsset)
    assetImageGenerator.requestedTimeToleranceAfter = .zero
    assetImageGenerator.requestedTimeToleranceBefore = .zero
    assetImageGenerator.appliesPreferredTrackTransform = true
    let imageRef = try! assetImageGenerator.copyCGImage(at: player.currentTime(), actualTime: nil)
    return imageRef 
}

重要说明:

requestedTimeToleranceAfter requestedTimeToleranceBefore 应该设置为.zero,因为根据源代码,“生成图像的实际时间可能与[...]要求的效率时间”。

appliesPreferredTrackTransform 必须设置为 TRUE (默认为FALSE),否则会出现帧旋转不良。将此属性设置为TRUE,您将获得在播放器中真正看到的内容。