<!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  &lt;eric.carlson@apple.com&gt;
+
+        [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  &lt;zalan@apple.com&gt;
</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&lt;NSError&gt;);
</ins><span class="cx"> 
</span><ins>+    enum class InterruptionReason { None, VideoNotAllowedInBackground, AudioInUse, VideoInUse, VideoNotAllowedInSideBySide };
+    void captureSessionBeginInterruption(RetainPtr&lt;NSNotification&gt;);
+    void captureSessionEndInterruption(RetainPtr&lt;NSNotification&gt;);
+
</ins><span class="cx">     AVCaptureSession *session() const { return m_session.get(); }
</span><span class="cx"> 
</span><span class="cx">     const RealtimeMediaSourceSettings&amp; settings() const final;
</span><span class="lines">@@ -98,7 +104,8 @@
</span><span class="cx">     RefPtr&lt;RealtimeMediaSourceCapabilities&gt; m_capabilities;
</span><span class="cx">     RetainPtr&lt;AVCaptureSession&gt; m_session;
</span><span class="cx">     RetainPtr&lt;AVCaptureDevice&gt; 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 &lt;AVFoundation/AVCaptureInput.h&gt;
</span><span class="cx"> #import &lt;AVFoundation/AVCaptureOutput.h&gt;
</span><span class="cx"> #import &lt;AVFoundation/AVCaptureSession.h&gt;
</span><ins>+#import &lt;AVFoundation/AVError.h&gt;
</ins><span class="cx"> #import &lt;objc/runtime.h&gt;
</span><span class="cx"> #import &lt;wtf/MainThread.h&gt;
</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&lt;AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate&gt;
</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&lt;NSString*&gt;* 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&lt;int&gt;(InterruptionReason::VideoNotAllowedInBackground) == static_cast&lt;int&gt;(AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground), &quot;InterruptionReason::VideoNotAllowedInBackground is not AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground as expected&quot;);
+    static_assert(static_cast&lt;int&gt;(InterruptionReason::VideoNotAllowedInSideBySide) == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps, &quot;InterruptionReason::VideoNotAllowedInSideBySide is not AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps as expected&quot;);
+    static_assert(static_cast&lt;int&gt;(InterruptionReason::VideoInUse) == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient, &quot;InterruptionReason::VideoInUse is not AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient as expected&quot;);
+    static_assert(static_cast&lt;int&gt;(InterruptionReason::AudioInUse) == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient, &quot;InterruptionReason::AudioInUse is not AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient as expected&quot;);
+#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&lt;NSError&gt; 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&lt;NSNotification&gt; notification)
+{
+    m_interruption = static_cast&lt;AVMediaCaptureSource::InterruptionReason&gt;([notification.get().userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]);
+}
+
+void AVMediaCaptureSource::captureSessionEndInterruption(RetainPtr&lt;NSNotification&gt;)
+{
+    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&lt;NSString*&gt;* sessionKVOProperties()
</ins><span class="cx"> {
</span><span class="cx">     static NSArray* keys = [@[@&quot;running&quot;] 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-&gt;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-&gt;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-&gt;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-&gt;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, &quot;WebCoreAVMediaCaptureSourceObserver::sessionRuntimeError(%p) - error = %s&quot;, self, [[error localizedDescription] UTF8String]);
+
+    if (m_callback)
+        m_callback-&gt;captureSessionRuntimeError(error);
+}
+
+-(void)beginSessionInterrupted:(NSNotification*)notification
+{
+    LOG(Media, &quot;WebCoreAVMediaCaptureSourceObserver::beginSessionInterrupted(%p) - reason = %d&quot;, self, [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]);
+
+    if (m_callback)
+        m_callback-&gt;captureSessionBeginInterruption(notification);
+}
+
+- (void)endSessionInterrupted:(NSNotification*)notification
+{
+    LOG(Media, &quot;WebCoreAVMediaCaptureSourceObserver::endSessionInterrupted(%p) &quot;, self);
+
+    if (m_callback)
+        m_callback-&gt;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&lt;Float64&gt;(highestFrameRateRange, range.maxFrameRate);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if ([videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset1280x720]) {
</del><ins>+        if (AVCaptureSessionPreset1280x720 &amp;&amp; [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 &amp;&amp; [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 &amp;&amp; [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 &amp;&amp; [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 &amp;&amp; [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 &amp;&amp; [preset isEqualToString:AVCaptureSessionPreset1280x720])
</ins><span class="cx">         return { 1280, 720 };
</span><span class="cx"> 
</span><del>-    if ([preset isEqualToString:AVCaptureSessionPreset960x540])
</del><ins>+    if (AVCaptureSessionPreset960x540 &amp;&amp; [preset isEqualToString:AVCaptureSessionPreset960x540])
</ins><span class="cx">         return { 960, 540 };
</span><span class="cx"> 
</span><del>-    if ([preset isEqualToString:AVCaptureSessionPreset640x480])
</del><ins>+    if (AVCaptureSessionPreset640x480 &amp;&amp; [preset isEqualToString:AVCaptureSessionPreset640x480])
</ins><span class="cx">         return { 640, 480 };
</span><span class="cx"> 
</span><del>-    if ([preset isEqualToString:AVCaptureSessionPreset352x288])
</del><ins>+    if (AVCaptureSessionPreset352x288 &amp;&amp; [preset isEqualToString:AVCaptureSessionPreset352x288])
</ins><span class="cx">         return { 352, 288 };
</span><span class="cx"> 
</span><del>-    if ([preset isEqualToString:AVCaptureSessionPreset320x240])
</del><ins>+    if (AVCaptureSessionPreset320x240 &amp;&amp; [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) &amp;&amp; (!height || height.value() == 720))
</del><ins>+    if ((!width || width.value() == 1280) &amp;&amp; (!height || height.value() == 720) &amp;&amp; AVCaptureSessionPreset1280x720)
</ins><span class="cx">         return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset1280x720] ? AVCaptureSessionPreset1280x720 : nil;
</span><span class="cx"> 
</span><del>-    if ((!width || width.value() == 960) &amp;&amp; (!height || height.value() == 540 ))
</del><ins>+    if ((!width || width.value() == 960) &amp;&amp; (!height || height.value() == 540) &amp;&amp; AVCaptureSessionPreset960x540)
</ins><span class="cx">         return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset960x540] ? AVCaptureSessionPreset960x540 : nil;
</span><span class="cx"> 
</span><del>-    if ((!width || width.value() == 640) &amp;&amp; (!height || height.value() == 480 ))
</del><ins>+    if ((!width || width.value() == 640) &amp;&amp; (!height || height.value() == 480 ) &amp;&amp; AVCaptureSessionPreset640x480)
</ins><span class="cx">         return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480] ? AVCaptureSessionPreset640x480 : nil;
</span><span class="cx"> 
</span><del>-    if ((!width || width.value() == 352) &amp;&amp; (!height || height.value() == 288 ))
</del><ins>+    if ((!width || width.value() == 352) &amp;&amp; (!height || height.value() == 288 ) &amp;&amp; AVCaptureSessionPreset352x288)
</ins><span class="cx">         return [videoDevice supportsAVCaptureSessionPreset:AVCaptureSessionPreset352x288] ? AVCaptureSessionPreset352x288 : nil;
</span><span class="cx"> 
</span><del>-    if ((!width || width.value() == 320) &amp;&amp; (!height || height.value() == 240 ))
</del><ins>+    if ((!width || width.value() == 320) &amp;&amp; (!height || height.value() == 240 ) &amp;&amp; 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>