<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[212844] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/212844">212844</a></dd>
<dt>Author</dt> <dd>eric.carlson@apple.com</dd>
<dt>Date</dt> <dd>2017-02-22 12:06:57 -0800 (Wed, 22 Feb 2017)</dd>
</dl>
<h3>Log Message</h3>
<pre>[MediaStream iOS] Respond to capture interruptions and notifications
https://bugs.webkit.org/show_bug.cgi?id=168610
Reviewed by Jer Noble.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaStreamAVFObjC::ensureLayer): Drive-by cleanup: don't create
a sample buffer display layer unless there is an active video track.
* platform/mediastream/mac/AVMediaCaptureSource.h:
* platform/mediastream/mac/AVMediaCaptureSource.mm:
(WebCore::AVMediaCaptureSource::AVMediaCaptureSource): Add static_asserts to ensure that
WebCore::InterruptionReason values map directly to AVCaptureSessionInterruptionReason values.
(WebCore::AVMediaCaptureSource::~AVMediaCaptureSource): Minor cleanup.
(WebCore::AVMediaCaptureSource::startProducingData): Start listening for notifications before
starting the session.
(WebCore::AVMediaCaptureSource::stopProducingData): Remove the notification observers before
stopping the session.
(WebCore::AVMediaCaptureSource::setupSession): Minor style cleanup.
(WebCore::AVMediaCaptureSource::captureSessionRuntimeError): New. When the error is AVErrorMediaServicesWereReset,
sometimes caused by a mediaserverd crash, try to restart the session one time.
(WebCore::AVMediaCaptureSource::captureSessionBeginInterruption): Store the interruption reason.
(WebCore::AVMediaCaptureSource::captureSessionEndInterruption): Try to restart the session if
it was interrupted because the app went into multi-app layout mode.
(WebCore::sessionKVOProperties): Drive-by cleanup.
(-[WebCoreAVMediaCaptureSourceObserver initWithCallback:]):
(-[WebCoreAVMediaCaptureSourceObserver disconnect]):
(-[WebCoreAVMediaCaptureSourceObserver addNotificationObservers]):
(-[WebCoreAVMediaCaptureSourceObserver removeNotificationObservers]):
(-[WebCoreAVMediaCaptureSourceObserver sessionRuntimeError:]):
(-[WebCoreAVMediaCaptureSourceObserver beginSessionInterrupted:]):
(-[WebCoreAVMediaCaptureSourceObserver endSessionInterrupted:]):
* platform/mediastream/mac/AVVideoCaptureSource.mm:
(WebCore::AVVideoCaptureSource::initializeCapabilities): Not all AVCaptureSession preset strings
are available on all platforms so load with SOFT_LINK_POINTER_OPTIONAL and NULL check before use.
(WebCore::sizeForPreset): Ditto.
(WebCore::AVVideoCaptureSource::bestSessionPresetForVideoDimensions): Ditto.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacAVMediaCaptureSourceh">trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacAVMediaCaptureSourcemm">trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacAVVideoCaptureSourcemm">trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (212843 => 212844)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-22 19:33:02 UTC (rev 212843)
+++ trunk/Source/WebCore/ChangeLog        2017-02-22 20:06:57 UTC (rev 212844)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2017-02-22 Eric Carlson <eric.carlson@apple.com>
+
+ [MediaStream iOS] Respond to capture interruptions and notifications
+ https://bugs.webkit.org/show_bug.cgi?id=168610
+
+ Reviewed by Jer Noble.
+
+ * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm:
+ (WebCore::MediaPlayerPrivateMediaStreamAVFObjC::ensureLayer): Drive-by cleanup: don't create
+ a sample buffer display layer unless there is an active video track.
+
+ * platform/mediastream/mac/AVMediaCaptureSource.h:
+ * platform/mediastream/mac/AVMediaCaptureSource.mm:
+ (WebCore::AVMediaCaptureSource::AVMediaCaptureSource): Add static_asserts to ensure that
+ WebCore::InterruptionReason values map directly to AVCaptureSessionInterruptionReason values.
+ (WebCore::AVMediaCaptureSource::~AVMediaCaptureSource): Minor cleanup.
+ (WebCore::AVMediaCaptureSource::startProducingData): Start listening for notifications before
+ starting the session.
+ (WebCore::AVMediaCaptureSource::stopProducingData): Remove the notification observers before
+ stopping the session.
+ (WebCore::AVMediaCaptureSource::setupSession): Minor style cleanup.
+ (WebCore::AVMediaCaptureSource::captureSessionRuntimeError): New. When the error is AVErrorMediaServicesWereReset,
+ sometimes caused by a mediaserverd crash, try to restart the session one time.
+ (WebCore::AVMediaCaptureSource::captureSessionBeginInterruption): Store the interruption reason.
+ (WebCore::AVMediaCaptureSource::captureSessionEndInterruption): Try to restart the session if
+ it was interrupted because the app went into multi-app layout mode.
+ (WebCore::sessionKVOProperties): Drive-by cleanup.
+ (-[WebCoreAVMediaCaptureSourceObserver initWithCallback:]):
+ (-[WebCoreAVMediaCaptureSourceObserver disconnect]):
+ (-[WebCoreAVMediaCaptureSourceObserver addNotificationObservers]):
+ (-[WebCoreAVMediaCaptureSourceObserver removeNotificationObservers]):
+ (-[WebCoreAVMediaCaptureSourceObserver sessionRuntimeError:]):
+ (-[WebCoreAVMediaCaptureSourceObserver beginSessionInterrupted:]):
+ (-[WebCoreAVMediaCaptureSourceObserver endSessionInterrupted:]):
+
+ * platform/mediastream/mac/AVVideoCaptureSource.mm:
+ (WebCore::AVVideoCaptureSource::initializeCapabilities): Not all AVCaptureSession preset strings
+ are available on all platforms so load with SOFT_LINK_POINTER_OPTIONAL and NULL check before use.
+ (WebCore::sizeForPreset): Ditto.
+ (WebCore::AVVideoCaptureSource::bestSessionPresetForVideoDimensions): Ditto.
+
</ins><span class="cx"> 2017-02-22 Zalan Bujtas <zalan@apple.com>
</span><span class="cx">
</span><span class="cx"> Simple line layout: ensureLineBoxes for paginated content.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacAVMediaCaptureSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h (212843 => 212844)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h        2017-02-22 19:33:02 UTC (rev 212843)
+++ trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h        2017-02-22 20:06:57 UTC (rev 212844)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> OBJC_CLASS AVCaptureOutput;
</span><span class="cx"> OBJC_CLASS AVCaptureSession;
</span><span class="cx"> OBJC_CLASS AVCaptureVideoDataOutput;
</span><ins>+OBJC_CLASS NSError;
</ins><span class="cx"> OBJC_CLASS WebCoreAVMediaCaptureSourceObserver;
</span><span class="cx">
</span><span class="cx"> typedef struct opaqueCMSampleBuffer *CMSampleBufferRef;
</span><span class="lines">@@ -53,8 +54,13 @@
</span><span class="cx">
</span><span class="cx"> virtual void captureOutputDidOutputSampleBufferFromConnection(AVCaptureOutput*, CMSampleBufferRef, AVCaptureConnection*) = 0;
</span><span class="cx">
</span><del>- virtual void captureSessionIsRunningDidChange(bool);
</del><ins>+ void captureSessionIsRunningDidChange(bool);
+ void captureSessionRuntimeError(RetainPtr<NSError>);
</ins><span class="cx">
</span><ins>+ enum class InterruptionReason { None, VideoNotAllowedInBackground, AudioInUse, VideoInUse, VideoNotAllowedInSideBySide };
+ void captureSessionBeginInterruption(RetainPtr<NSNotification>);
+ void captureSessionEndInterruption(RetainPtr<NSNotification>);
+
</ins><span class="cx"> AVCaptureSession *session() const { return m_session.get(); }
</span><span class="cx">
</span><span class="cx"> const RealtimeMediaSourceSettings& settings() const final;
</span><span class="lines">@@ -98,7 +104,8 @@
</span><span class="cx"> RefPtr<RealtimeMediaSourceCapabilities> m_capabilities;
</span><span class="cx"> RetainPtr<AVCaptureSession> m_session;
</span><span class="cx"> RetainPtr<AVCaptureDevice> m_device;
</span><del>- bool m_isRunning { false};
</del><ins>+ InterruptionReason m_interruption { InterruptionReason::None };
+ bool m_isRunning { false };
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacAVMediaCaptureSourcemm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm (212843 => 212844)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm        2017-02-22 19:33:02 UTC (rev 212843)
+++ trunk/Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm        2017-02-22 20:06:57 UTC (rev 212844)
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> #import <AVFoundation/AVCaptureInput.h>
</span><span class="cx"> #import <AVFoundation/AVCaptureOutput.h>
</span><span class="cx"> #import <AVFoundation/AVCaptureSession.h>
</span><ins>+#import <AVFoundation/AVError.h>
</ins><span class="cx"> #import <objc/runtime.h>
</span><span class="cx"> #import <wtf/MainThread.h>
</span><span class="cx">
</span><span class="lines">@@ -72,21 +73,25 @@
</span><span class="cx"> SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
</span><span class="cx"> SOFT_LINK_POINTER(AVFoundation, AVMediaTypeMuxed, NSString *)
</span><span class="cx"> SOFT_LINK_POINTER(AVFoundation, AVMediaTypeVideo, NSString *)
</span><del>-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset1280x720, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset640x480, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset352x288, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPresetLow, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionDidStopRunningNotification, NSString *)
</del><span class="cx">
</span><span class="cx"> #define AVMediaTypeAudio getAVMediaTypeAudio()
</span><span class="cx"> #define AVMediaTypeMuxed getAVMediaTypeMuxed()
</span><span class="cx"> #define AVMediaTypeVideo getAVMediaTypeVideo()
</span><del>-#define AVCaptureSessionPreset1280x720 getAVCaptureSessionPreset1280x720()
-#define AVCaptureSessionPreset640x480 getAVCaptureSessionPreset640x480()
-#define AVCaptureSessionPreset352x288 getAVCaptureSessionPreset352x288()
-#define AVCaptureSessionPresetLow getAVCaptureSessionPresetLow()
-#define AVCaptureSessionDidStopRunningNotification getAVCaptureSessionDidStopRunningNotification()
</del><span class="cx">
</span><ins>+#if PLATFORM(IOS)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionRuntimeErrorNotification, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionWasInterruptedNotification, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionInterruptionEndedNotification, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionInterruptionReasonKey, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionErrorKey, NSString *)
+
+#define AVCaptureSessionRuntimeErrorNotification getAVCaptureSessionRuntimeErrorNotification()
+#define AVCaptureSessionWasInterruptedNotification getAVCaptureSessionWasInterruptedNotification()
+#define AVCaptureSessionInterruptionEndedNotification getAVCaptureSessionInterruptionEndedNotification()
+#define AVCaptureSessionInterruptionReasonKey getAVCaptureSessionInterruptionReasonKey()
+#define AVCaptureSessionErrorKey getAVCaptureSessionErrorKey()
+#endif
+
</ins><span class="cx"> using namespace WebCore;
</span><span class="cx">
</span><span class="cx"> @interface WebCoreAVMediaCaptureSourceObserver : NSObject<AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>
</span><span class="lines">@@ -96,13 +101,20 @@
</span><span class="cx">
</span><span class="cx"> -(id)initWithCallback:(AVMediaCaptureSource*)callback;
</span><span class="cx"> -(void)disconnect;
</span><del>--(void)captureOutput:(AVCaptureOutputType *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnectionType *)connection;
--(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context;
</del><ins>+-(void)addNotificationObservers;
+-(void)removeNotificationObservers;
+-(void)captureOutput:(AVCaptureOutputType*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnectionType*)connection;
+-(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context;
+#if PLATFORM(IOS)
+-(void)sessionRuntimeError:(NSNotification*)notification;
+-(void)beginSessionInterrupted:(NSNotification*)notification;
+-(void)endSessionInterrupted:(NSNotification*)notification;
+#endif
</ins><span class="cx"> @end
</span><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><del>-static NSArray* sessionKVOProperties();
</del><ins>+static NSArray<NSString*>* sessionKVOProperties();
</ins><span class="cx">
</span><span class="cx"> static dispatch_queue_t globaAudioCaptureSerialQueue()
</span><span class="cx"> {
</span><span class="lines">@@ -130,6 +142,13 @@
</span><span class="cx"> , m_objcObserver(adoptNS([[WebCoreAVMediaCaptureSourceObserver alloc] initWithCallback:this]))
</span><span class="cx"> , m_device(device)
</span><span class="cx"> {
</span><ins>+#if PLATFORM(IOS)
+ static_assert(static_cast<int>(InterruptionReason::VideoNotAllowedInBackground) == static_cast<int>(AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground), "InterruptionReason::VideoNotAllowedInBackground is not AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground as expected");
+ static_assert(static_cast<int>(InterruptionReason::VideoNotAllowedInSideBySide) == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps, "InterruptionReason::VideoNotAllowedInSideBySide is not AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps as expected");
+ static_assert(static_cast<int>(InterruptionReason::VideoInUse) == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient, "InterruptionReason::VideoInUse is not AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient as expected");
+ static_assert(static_cast<int>(InterruptionReason::AudioInUse) == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient, "InterruptionReason::AudioInUse is not AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient as expected");
+#endif
+
</ins><span class="cx"> setPersistentID(device.uniqueID);
</span><span class="cx"> setMuted(true);
</span><span class="cx"> }
</span><span class="lines">@@ -138,11 +157,15 @@
</span><span class="cx"> {
</span><span class="cx"> [m_objcObserver disconnect];
</span><span class="cx">
</span><del>- if (m_session) {
- for (NSString *keyName in sessionKVOProperties())
- [m_session removeObserver:m_objcObserver.get() forKeyPath:keyName];
</del><ins>+ if (!m_session)
+ return;
+
+ for (NSString *keyName in sessionKVOProperties())
+ [m_session removeObserver:m_objcObserver.get() forKeyPath:keyName];
+
+ if ([m_session isRunning])
</ins><span class="cx"> [m_session stopRunning];
</span><del>- }
</del><ins>+
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void AVMediaCaptureSource::startProducingData()
</span><span class="lines">@@ -152,7 +175,8 @@
</span><span class="cx">
</span><span class="cx"> if ([m_session isRunning])
</span><span class="cx"> return;
</span><del>-
</del><ins>+
+ [m_objcObserver addNotificationObservers];
</ins><span class="cx"> [m_session startRunning];
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -161,6 +185,7 @@
</span><span class="cx"> if (!m_session || ![m_session isRunning])
</span><span class="cx"> return;
</span><span class="cx">
</span><ins>+ [m_objcObserver removeNotificationObservers];
</ins><span class="cx"> [m_session stopRunning];
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -223,7 +248,7 @@
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> m_session = adoptNS([allocAVCaptureSessionInstance() init]);
</span><del>- for (NSString *keyName in sessionKVOProperties())
</del><ins>+ for (NSString* keyName in sessionKVOProperties())
</ins><span class="cx"> [m_session addObserver:m_objcObserver.get() forKeyPath:keyName options:NSKeyValueObservingOptionNew context:(void *)nil];
</span><span class="cx">
</span><span class="cx"> [m_session beginConfiguration];
</span><span class="lines">@@ -253,6 +278,35 @@
</span><span class="cx"> });
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+#if PLATFORM(IOS)
+void AVMediaCaptureSource::captureSessionRuntimeError(RetainPtr<NSError> error)
+{
+ if (!m_isRunning || error.get().code != AVErrorMediaServicesWereReset)
+ return;
+
+ // Try to restart the session, but reset m_isRunning immediately so if it fails we won't try again.
+ [m_session startRunning];
+ m_isRunning = [m_session isRunning];
+}
+
+void AVMediaCaptureSource::captureSessionBeginInterruption(RetainPtr<NSNotification> notification)
+{
+ m_interruption = static_cast<AVMediaCaptureSource::InterruptionReason>([notification.get().userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]);
+}
+
+void AVMediaCaptureSource::captureSessionEndInterruption(RetainPtr<NSNotification>)
+{
+ InterruptionReason reason = m_interruption;
+
+ m_interruption = InterruptionReason::None;
+ if (reason != InterruptionReason::VideoNotAllowedInSideBySide || m_isRunning)
+ return;
+
+ [m_session startRunning];
+ m_isRunning = [m_session isRunning];
+}
+#endif
+
</ins><span class="cx"> void AVMediaCaptureSource::setVideoSampleBufferDelegate(AVCaptureVideoDataOutputType* videoOutput)
</span><span class="cx"> {
</span><span class="cx"> [videoOutput setSampleBufferDelegate:m_objcObserver.get() queue:globaVideoCaptureSerialQueue()];
</span><span class="lines">@@ -269,7 +323,7 @@
</span><span class="cx"> return nullptr;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-NSArray* sessionKVOProperties()
</del><ins>+NSArray<NSString*>* sessionKVOProperties()
</ins><span class="cx"> {
</span><span class="cx"> static NSArray* keys = [@[@"running"] retain];
</span><span class="cx"> return keys;
</span><span class="lines">@@ -286,6 +340,7 @@
</span><span class="cx"> return nil;
</span><span class="cx">
</span><span class="cx"> m_callback = callback;
</span><ins>+
</ins><span class="cx"> return self;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -293,10 +348,33 @@
</span><span class="cx"> {
</span><span class="cx"> [NSObject cancelPreviousPerformRequestsWithTarget:self];
</span><span class="cx"> m_callback = 0;
</span><ins>+ [self removeNotificationObservers];
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-- (void)captureOutput:(AVCaptureOutputType *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnectionType *)connection
</del><ins>+- (void)addNotificationObservers
</ins><span class="cx"> {
</span><ins>+#if PLATFORM(IOS)
+ ASSERT(m_callback);
+
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ AVCaptureSessionType* session = m_callback->session();
+
+ [center addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:session];
+ [center addObserver:self selector:@selector(beginSessionInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:session];
+ [center addObserver:self selector:@selector(endSessionInterrupted:) name:AVCaptureSessionInterruptionEndedNotification object:session];
+#endif
+}
+
+- (void)removeNotificationObservers
+{
+#if PLATFORM(IOS)
+ ASSERT(m_callback);
+ [[NSNotificationCenter defaultCenter] removeObserver:m_callback->session()];
+#endif
+}
+
+- (void)captureOutput:(AVCaptureOutputType*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnectionType*)connection
+{
</ins><span class="cx"> if (!m_callback)
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="lines">@@ -303,7 +381,7 @@
</span><span class="cx"> m_callback->captureOutputDidOutputSampleBufferFromConnection(captureOutput, sampleBuffer, connection);
</span><span class="cx"> }
</span><span class="cx">
</span><del>--(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context
</del><ins>+- (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
</ins><span class="cx"> {
</span><span class="cx"> UNUSED_PARAM(object);
</span><span class="cx"> UNUSED_PARAM(context);
</span><span class="lines">@@ -328,6 +406,33 @@
</span><span class="cx"> m_callback->captureSessionIsRunningDidChange([newValue boolValue]);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+#if PLATFORM(IOS)
+- (void)sessionRuntimeError:(NSNotification*)notification
+{
+ NSError *error = notification.userInfo[AVCaptureSessionErrorKey];
+ LOG(Media, "WebCoreAVMediaCaptureSourceObserver::sessionRuntimeError(%p) - error = %s", self, [[error localizedDescription] UTF8String]);
+
+ if (m_callback)
+ m_callback->captureSessionRuntimeError(error);
+}
+
+-(void)beginSessionInterrupted:(NSNotification*)notification
+{
+ LOG(Media, "WebCoreAVMediaCaptureSourceObserver::beginSessionInterrupted(%p) - reason = %d", self, [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]);
+
+ if (m_callback)
+ m_callback->captureSessionBeginInterruption(notification);
+}
+
+- (void)endSessionInterrupted:(NSNotification*)notification
+{
+ LOG(Media, "WebCoreAVMediaCaptureSourceObserver::endSessionInterrupted(%p) ", self);
+
+ if (m_callback)
+ m_callback->captureSessionEndInterruption(notification);
+}
+#endif
+
</ins><span class="cx"> @end
</span><span class="cx">
</span><span class="cx"> #endif // ENABLE(MEDIA_STREAM)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacAVVideoCaptureSourcemm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm (212843 => 212844)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm        2017-02-22 19:33:02 UTC (rev 212843)
+++ trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm        2017-02-22 20:06:57 UTC (rev 212844)
</span><span class="lines">@@ -84,12 +84,12 @@
</span><span class="cx"> #define AVCaptureVideoPreviewLayer getAVCaptureVideoPreviewLayerClass()
</span><span class="cx"> #define AVFrameRateRange getAVFrameRateRangeClass()
</span><span class="cx">
</span><del>-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset1280x720, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset960x540, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset640x480, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset352x288, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset320x240, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPresetLow, NSString *)
</del><ins>+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionPreset1280x720, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionPreset960x540, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionPreset640x480, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionPreset352x288, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionPreset320x240, NSString *)
+SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionPresetLow, NSString *)
</ins><span class="cx">
</span><span class="cx"> #define AVCaptureSessionPreset1280x720 getAVCaptureSessionPreset1280x720()
</span><span class="cx"> #define AVCaptureSessionPreset960x540 getAVCaptureSessionPreset960x540()
</span><span class="lines">@@ -164,27 +164,27 @@
</span><span class="cx"> highestFrameRateRange = std::max<Float64>(highestFrameRateRange, range.maxFrameRate);
</span><span class="cx"> }
</span><span class="cx">
</span><del>- if ([videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset1280x720]) {
</del><ins>+ if (AVCaptureSessionPreset1280x720 && [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset1280x720]) {
</ins><span class="cx"> updateSizeMinMax(minimumWidth, maximumWidth, 1280);
</span><span class="cx"> updateSizeMinMax(minimumHeight, maximumHeight, 720);
</span><span class="cx"> updateAspectRatioMinMax(minimumAspectRatio, maximumAspectRatio, 1280.0 / 720);
</span><span class="cx"> }
</span><del>- if ([videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset960x540]) {
</del><ins>+ if (AVCaptureSessionPreset960x540 && [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset960x540]) {
</ins><span class="cx"> updateSizeMinMax(minimumWidth, maximumWidth, 960);
</span><span class="cx"> updateSizeMinMax(minimumHeight, maximumHeight, 540);
</span><span class="cx"> updateAspectRatioMinMax(minimumAspectRatio, maximumAspectRatio, 960 / 540);
</span><span class="cx"> }
</span><del>- if ([videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
</del><ins>+ if (AVCaptureSessionPreset640x480 && [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
</ins><span class="cx"> updateSizeMinMax(minimumWidth, maximumWidth, 640);
</span><span class="cx"> updateSizeMinMax(minimumHeight, maximumHeight, 480);
</span><span class="cx"> updateAspectRatioMinMax(minimumAspectRatio, maximumAspectRatio, 640 / 480);
</span><span class="cx"> }
</span><del>- if ([videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset352x288]) {
</del><ins>+ if (AVCaptureSessionPreset352x288 && [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset352x288]) {
</ins><span class="cx"> updateSizeMinMax(minimumWidth, maximumWidth, 352);
</span><span class="cx"> updateSizeMinMax(minimumHeight, maximumHeight, 288);
</span><span class="cx"> updateAspectRatioMinMax(minimumAspectRatio, maximumAspectRatio, 352 / 288);
</span><span class="cx"> }
</span><del>- if ([videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset320x240]) {
</del><ins>+ if (AVCaptureSessionPreset320x240 && [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset320x240]) {
</ins><span class="cx"> updateSizeMinMax(minimumWidth, maximumWidth, 320);
</span><span class="cx"> updateSizeMinMax(minimumHeight, maximumHeight, 240);
</span><span class="cx"> updateAspectRatioMinMax(minimumAspectRatio, maximumAspectRatio, 320 / 240);
</span><span class="lines">@@ -239,19 +239,19 @@
</span><span class="cx"> if (!preset)
</span><span class="cx"> return { };
</span><span class="cx">
</span><del>- if ([preset isEqualToString:AVCaptureSessionPreset1280x720])
</del><ins>+ if (AVCaptureSessionPreset1280x720 && [preset isEqualToString:AVCaptureSessionPreset1280x720])
</ins><span class="cx"> return { 1280, 720 };
</span><span class="cx">
</span><del>- if ([preset isEqualToString:AVCaptureSessionPreset960x540])
</del><ins>+ if (AVCaptureSessionPreset960x540 && [preset isEqualToString:AVCaptureSessionPreset960x540])
</ins><span class="cx"> return { 960, 540 };
</span><span class="cx">
</span><del>- if ([preset isEqualToString:AVCaptureSessionPreset640x480])
</del><ins>+ if (AVCaptureSessionPreset640x480 && [preset isEqualToString:AVCaptureSessionPreset640x480])
</ins><span class="cx"> return { 640, 480 };
</span><span class="cx">
</span><del>- if ([preset isEqualToString:AVCaptureSessionPreset352x288])
</del><ins>+ if (AVCaptureSessionPreset352x288 && [preset isEqualToString:AVCaptureSessionPreset352x288])
</ins><span class="cx"> return { 352, 288 };
</span><span class="cx">
</span><del>- if ([preset isEqualToString:AVCaptureSessionPreset320x240])
</del><ins>+ if (AVCaptureSessionPreset320x240 && [preset isEqualToString:AVCaptureSessionPreset320x240])
</ins><span class="cx"> return { 320, 240 };
</span><span class="cx">
</span><span class="cx"> return { };
</span><span class="lines">@@ -495,19 +495,19 @@
</span><span class="cx"> return nil;
</span><span class="cx">
</span><span class="cx"> AVCaptureDeviceTypedef *videoDevice = device();
</span><del>- if ((!width || width.value() == 1280) && (!height || height.value() == 720))
</del><ins>+ if ((!width || width.value() == 1280) && (!height || height.value() == 720) && AVCaptureSessionPreset1280x720)
</ins><span class="cx"> return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset1280x720] ? AVCaptureSessionPreset1280x720 : nil;
</span><span class="cx">
</span><del>- if ((!width || width.value() == 960) && (!height || height.value() == 540 ))
</del><ins>+ if ((!width || width.value() == 960) && (!height || height.value() == 540) && AVCaptureSessionPreset960x540)
</ins><span class="cx"> return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset960x540] ? AVCaptureSessionPreset960x540 : nil;
</span><span class="cx">
</span><del>- if ((!width || width.value() == 640) && (!height || height.value() == 480 ))
</del><ins>+ if ((!width || width.value() == 640) && (!height || height.value() == 480 ) && AVCaptureSessionPreset640x480)
</ins><span class="cx"> return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480] ? AVCaptureSessionPreset640x480 : nil;
</span><span class="cx">
</span><del>- if ((!width || width.value() == 352) && (!height || height.value() == 288 ))
</del><ins>+ if ((!width || width.value() == 352) && (!height || height.value() == 288 ) && AVCaptureSessionPreset352x288)
</ins><span class="cx"> return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset352x288] ? AVCaptureSessionPreset352x288 : nil;
</span><span class="cx">
</span><del>- if ((!width || width.value() == 320) && (!height || height.value() == 240 ))
</del><ins>+ if ((!width || width.value() == 320) && (!height || height.value() == 240 ) && AVCaptureSessionPreset320x240)
</ins><span class="cx"> return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset320x240] ? AVCaptureSessionPreset320x240 : nil;
</span><span class="cx">
</span><span class="cx"> return nil;
</span></span></pre>
</div>
</div>
</body>
</html>