【问题标题】:AVPlayer not rendering to its AVPlayerLayerAVPlayer 未渲染到其 AVPlayerLayer
【发布时间】:2012-03-29 11:51:21
【问题描述】:

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

我已将其缩小为以下两个原因之一:

  1. 我没有正确创建 NSImage。
  2. AVPlayer 未渲染到 AVPlayerLayer。

我没有收到任何错误,并且找到了一些关于转换 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 以每秒 30 次抓取 AVPlayerLayer 的帧:

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 文档。

【问题讨论】:

  • 你好。你有这个问题的运气吗?以某种方式解决?我也想从 AVPlayerLayer 截屏,但到目前为止 renderInContext 对我不起作用。
  • 我不确定它与您的问题有何关系,但timerWithTimeInterval:(1/30) 并没有按照您的想法行事。 1/30 的计算结果为 0,因为两个操作数都是整数,您应该使用 1./30. 来获得 0.0333333 的双精度值。

标签: macos avfoundation calayer avplayer


【解决方案1】:

它对我有用:

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

【讨论】:

    【解决方案2】:

    您可以在AVPlayerItem中添加一个AVPlayerItemVideoOutput,然后调用copyPixelBufferForItemTime来查询包含指定时间帧的CVPixelBufferRef对象,示例代码如下:

    NSDictionary *pixBuffAttributes = @{                                                                                      
        (id)kCVPixelBufferWidthKey:@(nWidth),                                 
        (id)kCVPixelBufferHeightKey:@(nHeight),                                  
        (id)kCVPixelBufferCGImageCompatibilityKey:@YES,
    };
    m_output = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:pixBuffAttributes];
    
    ...
    
    m_buffer = [m_output copyPixelBufferForItemTime:time itemTimeForDisplay:NULL];
    
    CVPixelBufferLockBaseAddress(m_buffer, 0);
    auto *buffer = CVPixelBufferGetBaseAddress(m_buffer);
    frame->width = CVPixelBufferGetWidth(m_buffer);
    frame->height = CVPixelBufferGetHeight(m_buffer);
    frame->widthbytes = CVPixelBufferGetBytesPerRow(m_buffer);
    frame->bufferlen = frame->widthbytes * (uint32)CVPixelBufferGetHeight(m_buffer);
    
    auto &videoInfo = m_info.video;
    CGDataProviderRef dp = CGDataProviderCreateWithData(nullptr, buffer, frame->bufferlen, nullptr);
    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
    m_image = CGImageCreate(frame->width,
                            frame->height,
                            8,
                            videoInfo.pixelbits,
                            frame->widthbytes,
                            cs,
                            kCGImageAlphaNoneSkipFirst,
                            dp,
                            nullptr,
                            true,
                            kCGRenderingIntentDefault);
    CGColorSpaceRelease(cs);
    CGDataProviderRelease(dp);
    

    您可以查看苹果的官方样品:

    Real-timeVideoProcessingUsingAVPlayerItemVideoOutput

    【讨论】:

      【解决方案3】:

      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 
      }
      

      重要提示:

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

      appliesPreferredTrackTransform 必须设置为 TRUE(默认为 FALSE),否则你会得到一个糟糕的旋转帧。将此属性设置为 TRUE 后,您将获得您在播放器中真正看到的内容。

      【讨论】:

        猜你喜欢
        • 2023-03-30
        • 2015-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多