AVPlayer не рендерится на свой AVPlayerLayer - PullRequest
2 голосов
/ 13 марта 2012

У меня есть AVPlayerLayer (подкласс CALayer), и мне нужно войти в тип изображения, который может быть передан в QCRenderer (QCRenderer принимает NSImages и CIImages.) Я могу преобразовать CALayer в CGImageRef, и это в NSImage, но содержание всегда ясно.

Я сузил это до одной из двух причин:

  1. Я неправильно создаю NSImage.
  2. AVPlayer не рендерится на AVPlayerLayer.

Я не получаю никаких ошибок, и нашел некоторую документацию по конвертации CALayers. Кроме того, я добавил AVPlayerLayer в NSView, который остается пустым, поэтому я считаю, что проблема заключается в 2.

Я использую модифицированную версию AVPlayerDemo от AVPlayerDemoPlaybackViewController от Apple. Я превратил его в NSObject, так как я удалил весь код интерфейса из него.

Я создаю AVPlayerLayer в (void) prepareToPlayAsset: withKeys: метод при создании AVPlayer: (я только добавляю слой в 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); }

Я не могу понять, почему я только проясняюсь. Любая помощь в этом с благодарностью, так как не хватает документации AVFoundation для OS X.

Ответы [ 2 ]

0 голосов
/ 13 октября 2017

Вы можете добавить AVPlayerItemVideoOutput к AVPlayerItem, а затем вызвать 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);

И вы можете проверить официальный образец Apple:

Обработка видео в реальном времениИспользование AVPlayerItemVideoOutput

0 голосов
/ 03 сентября 2013

это работает для меня:

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