Skip to content

Commit

Permalink
[Cocoa] Adopt -[AVSampleBufferDisplayLayer readyForDisplay]
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=264197
rdar://114112149

Reviewed by Eric Carlson.

Adopted -[AVSampleBufferDisplayLayer readyForDisplay] where available as a replacement for
-[AVSampleBufferDisplayLayer prerollDecodeWithCompletionHandler:]. While here, made
dynamic_objc_cast friendly to soft-linking.

* Source/WTF/wtf/RetainPtr.h:
(WTF::dynamic_objc_cast):
* Source/WebCore/PAL/pal/cocoa/AVFoundationSoftLink.h:
* Source/WebCore/PAL/pal/cocoa/AVFoundationSoftLink.mm:
* Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(-[WebAVSampleBufferErrorListener beginObservingLayer:]):
(-[WebAVSampleBufferErrorListener stopObservingLayer:]):
(-[WebAVSampleBufferErrorListener layerReadyForDisplayChanged:]):
(WebCore::SourceBufferPrivateAVFObjC::layerReadyForDisplayChanged):
(WebCore::SourceBufferPrivateAVFObjC::enqueueSample):
(WebCore::SourceBufferPrivateAVFObjC::enqueueSampleBuffer):
(WebCore::SourceBufferPrivateAVFObjC::bufferWasConsumed): Deleted.

Canonical link: https://commits.webkit.org/270234@main
  • Loading branch information
aestes committed Nov 4, 2023
1 parent cb1d94c commit 671b617
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 31 deletions.
4 changes: 2 additions & 2 deletions Source/WTF/wtf/RetainPtr.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,9 @@ inline CFHashCode safeCFHash(CFTypeRef a)

#ifdef __OBJC__
// FIXME: Move to TypeCastsCocoa.h once all clients include that header.
template<typename T> T* dynamic_objc_cast(id object)
template<typename T> T *dynamic_objc_cast(id object, Class theClass = [T class])
{
if (![object isKindOfClass:[T class]])
if (![object isKindOfClass:theClass])
return nullptr;

return reinterpret_cast<T*>(object);
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/PAL/pal/cocoa/AVFoundationSoftLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,4 +389,9 @@ SOFT_LINK_CONSTANT_FOR_HEADER(PAL, AVFoundation, AVSpeechSynthesisAvailableVoice
SOFT_LINK_CONSTANT_FOR_HEADER(PAL, AVFoundation, AVCaptureMaxAvailableTorchLevel, float)
#define AVCaptureMaxAvailableTorchLevel PAL::get_AVFoundation_AVCaptureMaxAvailableTorchLevel()

#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
SOFT_LINK_CONSTANT_MAY_FAIL_FOR_HEADER(PAL, AVFoundation, AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification, NSNotificationName)
#define AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification PAL::get_AVFoundation_AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification()
#endif

#endif // USE(AVFOUNDATION)
4 changes: 4 additions & 0 deletions Source/WebCore/PAL/pal/cocoa/AVFoundationSoftLink.mm
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,8 @@ static BOOL justReturnsNO()

SOFT_LINK_CONSTANT_FOR_SOURCE_WITH_EXPORT(PAL, AVFoundation, AVCaptureMaxAvailableTorchLevel, float, PAL_EXPORT)

#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
SOFT_LINK_CONSTANT_MAY_FAIL_FOR_SOURCE_WITH_EXPORT(PAL, AVFoundation, AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification, NSNotificationName, PAL_EXPORT)
#endif

#endif // USE(AVFOUNDATION)
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ ALLOW_NEW_API_WITHOUT_GUARDS_END
void setVideoLayer(AVSampleBufferDisplayLayer*);
void setDecompressionSession(WebCoreDecompressionSession*);

void bufferWasConsumed();
void layerReadyForDisplayChanged(AVSampleBufferDisplayLayer *, bool isReadyForDisplay);

#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
SharedBuffer* initData() { return m_initData.get(); }
Expand Down Expand Up @@ -184,6 +184,7 @@ ALLOW_NEW_API_WITHOUT_GUARDS_END

void processPendingTrackChangeTasks();
void enqueueSample(Ref<MediaSampleAVFObjC>&&, uint64_t trackID);
void enqueueSampleBuffer(MediaSampleAVFObjC&);
void didBecomeReadyForMoreSamples(uint64_t trackID);
void appendCompleted();
void destroyStreamDataParser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ @interface AVSampleBufferDisplayLayer (WebCoreSampleBufferKeySession) <AVContent
@interface AVSampleBufferAudioRenderer (WebCoreSampleBufferKeySession) <AVContentKeyRecipient>
@end

#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
// FIXME (117934497): Remove staging code once -[AVSampleBufferDisplayLayer isReadyForDisplay] is available in SDKs used by WebKit builders
@interface AVSampleBufferDisplayLayer (Staging_113656776)
@property (nonatomic, readonly, getter=isReadyForDisplay) BOOL readyForDisplay;
@end
#endif

@interface WebAVSampleBufferErrorListener : NSObject {
WeakPtr<WebCore::SourceBufferPrivateAVFObjC> _parent;
Vector<RetainPtr<AVSampleBufferDisplayLayer>> _layers;
Expand Down Expand Up @@ -137,19 +144,24 @@ - (void)invalidate
_parent = nullptr;
}

- (void)beginObservingLayer:(AVSampleBufferDisplayLayer*)layer
- (void)beginObservingLayer:(AVSampleBufferDisplayLayer *)layer
{
ASSERT(_parent);
ASSERT(!_layers.contains(layer));

_layers.append(layer);
[layer addObserver:self forKeyPath:@"error" options:NSKeyValueObservingOptionNew context:nullptr];
[layer addObserver:self forKeyPath:@"outputObscuredDueToInsufficientExternalProtection" options:NSKeyValueObservingOptionNew context:nullptr];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(layerFailedToDecode:) name:AVSampleBufferDisplayLayerFailedToDecodeNotification object:layer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(layerRequiresFlushToResumeDecodingChanged:) name:AVSampleBufferDisplayLayerRequiresFlushToResumeDecodingDidChangeNotification object:layer];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(layerFailedToDecode:) name:AVSampleBufferDisplayLayerFailedToDecodeNotification object:layer];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(layerRequiresFlushToResumeDecodingChanged:) name:AVSampleBufferDisplayLayerRequiresFlushToResumeDecodingDidChangeNotification object:layer];
#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
// FIXME (117934497): Remove staging code once -[AVSampleBufferDisplayLayer isReadyForDisplay] is available in SDKs used by WebKit builders
if (PAL::canLoad_AVFoundation_AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification())
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(layerReadyForDisplayChanged:) name:AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification object:layer];
#endif
}

- (void)stopObservingLayer:(AVSampleBufferDisplayLayer*)layer
- (void)stopObservingLayer:(AVSampleBufferDisplayLayer *)layer
{
ASSERT(_parent);
ASSERT(_layers.contains(layer));
Expand All @@ -158,7 +170,13 @@ - (void)stopObservingLayer:(AVSampleBufferDisplayLayer*)layer
[layer removeObserver:self forKeyPath:@"outputObscuredDueToInsufficientExternalProtection"];
_layers.remove(_layers.find(layer));

[[NSNotificationCenter defaultCenter] removeObserver:self name:AVSampleBufferDisplayLayerFailedToDecodeNotification object:layer];
[NSNotificationCenter.defaultCenter removeObserver:self name:AVSampleBufferDisplayLayerFailedToDecodeNotification object:layer];
[NSNotificationCenter.defaultCenter removeObserver:self name:AVSampleBufferDisplayLayerRequiresFlushToResumeDecodingDidChangeNotification object:layer];
#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
// FIXME (117934497): Remove staging code once -[AVSampleBufferDisplayLayer isReadyForDisplay] is available in SDKs used by WebKit builders
if (PAL::canLoad_AVFoundation_AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification())
[NSNotificationCenter.defaultCenter removeObserver:self name:AVSampleBufferDisplayLayerReadyForDisplayDidChangeNotification object:layer];
#endif
}

ALLOW_NEW_API_WITHOUT_GUARDS_BEGIN
Expand Down Expand Up @@ -258,6 +276,21 @@ - (void)layerRequiresFlushToResumeDecodingChanged:(NSNotification *)note
});
}

#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
- (void)layerReadyForDisplayChanged:(NSNotification *)notification
{
RetainPtr layer = dynamic_objc_cast<AVSampleBufferDisplayLayer>(notification.object, PAL::getAVSampleBufferDisplayLayerClass());
BOOL isReadyForDisplay = [layer isReadyForDisplay];

ensureOnMainThread([self, protectedSelf = RetainPtr { self }, layer = WTFMove(layer), isReadyForDisplay] {
if (!_layers.contains(layer.get()))
return;
if (RefPtr parent = _parent.get())
parent->layerReadyForDisplayChanged(layer.get(), isReadyForDisplay);
});
}
#endif

- (void)audioRendererWasAutomaticallyFlushed:(NSNotification*)note
{
RetainPtr<AVSampleBufferAudioRenderer> renderer = (AVSampleBufferAudioRenderer *)[note object];
Expand Down Expand Up @@ -1131,6 +1164,16 @@ static bool sampleBufferRenderersSupportKeySession()
m_layerRequiresFlush = requiresFlush;
}

void SourceBufferPrivateAVFObjC::layerReadyForDisplayChanged(AVSampleBufferDisplayLayer *layer, bool isReadyForDisplay)
{
if (layer != m_displayLayer || !isReadyForDisplay)
return;

ALWAYS_LOG(LOGIDENTIFIER);
if (auto player = this->player())
player->setHasAvailableVideoFrame(true);
}

void SourceBufferPrivateAVFObjC::flush(const AtomString& trackIDString)
{
auto trackID = parseIntegerAllowingTrailingJunk<uint64_t>(trackIDString).value_or(0);
Expand Down Expand Up @@ -1298,25 +1341,7 @@ static bool sampleBufferRenderersSupportKeySession()
if (!m_displayLayer)
return;

if (player && !player->hasAvailableVideoFrame() && !sample->isNonDisplaying()) {
DEBUG_LOG(logSiteIdentifier, "adding buffer attachment");

[m_displayLayer enqueueSampleBuffer:platformSample.sample.cmSampleBuffer];
[m_displayLayer prerollDecodeWithCompletionHandler:[this, logSiteIdentifier, weakThis = WeakPtr { *this }] (BOOL success) mutable {
callOnMainThread([this, logSiteIdentifier, weakThis = WTFMove(weakThis), success] () {
if (!weakThis)
return;

if (!success) {
ERROR_LOG(logSiteIdentifier, "prerollDecodeWithCompletionHandler failed");
return;
}

weakThis->bufferWasConsumed();
});
}];
} else
[m_displayLayer enqueueSampleBuffer:platformSample.sample.cmSampleBuffer];
enqueueSampleBuffer(sample.get());

} else {
// AVSampleBufferAudioRenderer will throw an un-documented exception if passed a sample
Expand All @@ -1335,12 +1360,36 @@ static bool sampleBufferRenderersSupportKeySession()
}
}

void SourceBufferPrivateAVFObjC::bufferWasConsumed()
void SourceBufferPrivateAVFObjC::enqueueSampleBuffer(MediaSampleAVFObjC& sample)
{
DEBUG_LOG(LOGIDENTIFIER);
[m_displayLayer enqueueSampleBuffer:sample.platformSample().sample.cmSampleBuffer];

if (auto player = this->player())
player->setHasAvailableVideoFrame(true);
#if HAVE(AVSAMPLEBUFFERDISPLAYLAYER_READYFORDISPLAY)
// FIXME (117934497): Remove staging code once -[AVSampleBufferDisplayLayer isReadyForDisplay] is available in SDKs used by WebKit builders
if ([m_displayLayer respondsToSelector:@selector(isReadyForDisplay)])
return;
#endif

auto player = this->player();
if (!player || player->hasAvailableVideoFrame() || sample.isNonDisplaying())
return;

DEBUG_LOG(LOGIDENTIFIER, "adding buffer attachment");

[m_displayLayer prerollDecodeWithCompletionHandler:[this, weakThis = WeakPtr { *this }, logSiteIdentifier = LOGIDENTIFIER] (BOOL success) mutable {
callOnMainThread([this, weakThis = WTFMove(weakThis), logSiteIdentifier, success] () {
RefPtr protectedThis = weakThis.get();
if (!protectedThis)
return;

if (!success) {
ERROR_LOG(logSiteIdentifier, "prerollDecodeWithCompletionHandler failed");
return;
}

layerReadyForDisplayChanged(m_displayLayer.get(), true);
});
}];
}

bool SourceBufferPrivateAVFObjC::isReadyForMoreSamples(const AtomString& trackIDString)
Expand Down

0 comments on commit 671b617

Please sign in to comment.