[iOS] Unset active media capture source when stopped capturing
[WebKit-https.git] / Source / WebCore / platform / mediastream / mac / RealtimeOutgoingVideoSource.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted, provided that the following conditions
6  * are required to be met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Inc. nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "RealtimeOutgoingVideoSource.h"
31
32 #if USE(LIBWEBRTC)
33
34 #include <webrtc/common_video/include/corevideo_frame_buffer.h>
35 #include <webrtc/common_video/libyuv/include/webrtc_libyuv.h>
36 #include <webrtc/media/base/videoframe.h>
37 #include <wtf/MainThread.h>
38
39 #include "CoreMediaSoftLink.h"
40 #include "CoreVideoSoftLink.h"
41
42 namespace WebCore {
43
44 RealtimeOutgoingVideoSource::RealtimeOutgoingVideoSource(Ref<RealtimeMediaSource>&& videoSource)
45     : m_videoSource(WTFMove(videoSource))
46     , m_blackFrameTimer(*this, &RealtimeOutgoingVideoSource::sendOneBlackFrame)
47 {
48     m_videoSource->addObserver(*this);
49     setSizeFromSource();
50 }
51
52 bool RealtimeOutgoingVideoSource::setSource(Ref<RealtimeMediaSource>&& newSource)
53 {
54     if (!m_initialSettings)
55         m_initialSettings = m_videoSource->settings();
56
57     auto newSettings = newSource->settings();
58
59     if (m_initialSettings->width() < newSettings.width() || m_initialSettings->height() < newSettings.height())
60         return false;
61
62     m_videoSource->removeObserver(*this);
63     m_videoSource = WTFMove(newSource);
64     m_videoSource->addObserver(*this);
65
66     setSizeFromSource();
67     m_muted = m_videoSource->muted();
68     m_enabled = m_videoSource->enabled();
69
70     return true;
71 }
72
73 void RealtimeOutgoingVideoSource::stop()
74 {
75     m_videoSource->removeObserver(*this);
76     m_blackFrameTimer.stop();
77     m_isStopped = true;
78 }
79
80 void RealtimeOutgoingVideoSource::sourceMutedChanged()
81 {
82     ASSERT(m_muted != m_videoSource->muted());
83
84     m_muted = m_videoSource->muted();
85
86     if (m_muted && m_sinks.size() && m_enabled)
87         sendBlackFrame();
88 }
89
90 void RealtimeOutgoingVideoSource::sourceEnabledChanged()
91 {
92     ASSERT(m_enabled != m_videoSource->enabled());
93
94     m_enabled = m_videoSource->enabled();
95
96     if (!m_enabled && m_sinks.size() && !m_muted)
97         sendBlackFrame();
98 }
99
100 void RealtimeOutgoingVideoSource::setSizeFromSource()
101 {
102     const auto& settings = m_videoSource->settings();
103     m_width = settings.width();
104     m_height = settings.height();
105 }
106
107 bool RealtimeOutgoingVideoSource::GetStats(Stats*)
108 {
109     return false;
110 }
111
112 void RealtimeOutgoingVideoSource::AddOrUpdateSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink, const rtc::VideoSinkWants&)
113 {
114     // FIXME: support sinkWants
115     if (!m_sinks.contains(sink))
116         m_sinks.append(sink);
117 }
118
119 void RealtimeOutgoingVideoSource::RemoveSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink)
120 {
121     m_sinks.removeFirst(sink);
122 }
123
124 void RealtimeOutgoingVideoSource::sendBlackFrame()
125 {
126     if (!m_blackFrame) {
127         auto frame = m_bufferPool.CreateBuffer(m_width, m_height);
128         frame->SetToBlack();
129         m_blackFrame = WTFMove(frame);
130     }
131     sendOneBlackFrame();
132     // FIXME: We should not need to send two black frames but VTB requires that so we are sure a black frame is sent over the wire.
133     m_blackFrameTimer.startOneShot(0_s);
134 }
135
136 void RealtimeOutgoingVideoSource::sendOneBlackFrame()
137 {
138     sendFrame(rtc::scoped_refptr<webrtc::VideoFrameBuffer>(m_blackFrame));
139 }
140
141 void RealtimeOutgoingVideoSource::sendFrame(rtc::scoped_refptr<webrtc::VideoFrameBuffer>&& buffer)
142 {
143     webrtc::VideoFrame frame(buffer, 0, 0, m_currentRotation);
144     for (auto* sink : m_sinks)
145         sink->OnFrame(frame);
146 }
147
148 void RealtimeOutgoingVideoSource::videoSampleAvailable(MediaSample& sample)
149 {
150     if (!m_sinks.size())
151         return;
152
153     if (m_muted || !m_enabled)
154         return;
155
156     switch (sample.videoRotation()) {
157     case MediaSample::VideoRotation::None:
158         m_currentRotation = webrtc::kVideoRotation_0;
159         break;
160     case MediaSample::VideoRotation::UpsideDown:
161         m_currentRotation = webrtc::kVideoRotation_180;
162         break;
163     case MediaSample::VideoRotation::Right:
164         m_currentRotation = webrtc::kVideoRotation_90;
165         break;
166     case MediaSample::VideoRotation::Left:
167         m_currentRotation = webrtc::kVideoRotation_270;
168         break;
169     }
170
171     ASSERT(sample.platformSample().type == PlatformSample::CMSampleBufferType);
172     auto pixelBuffer = static_cast<CVPixelBufferRef>(CMSampleBufferGetImageBuffer(sample.platformSample().sample.cmSampleBuffer));
173     auto pixelFormatType = CVPixelBufferGetPixelFormatType(pixelBuffer);
174
175     if (pixelFormatType == kCVPixelFormatType_420YpCbCr8Planar || pixelFormatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
176         sendFrame(new rtc::RefCountedObject<webrtc::CoreVideoFrameBuffer>(pixelBuffer));
177         return;
178     }
179
180     CVPixelBufferLockBaseAddress(pixelBuffer, 0);
181     auto* source = reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
182
183     auto newBuffer = m_bufferPool.CreateBuffer(m_width, m_height);
184     if (pixelFormatType == kCVPixelFormatType_32BGRA)
185         webrtc::ConvertToI420(webrtc::kARGB, source, 0, 0, m_width, m_height, 0, webrtc::kVideoRotation_0, newBuffer);
186     else {
187         ASSERT(pixelFormatType == kCVPixelFormatType_32ARGB);
188         webrtc::ConvertToI420(webrtc::kBGRA, source, 0, 0, m_width, m_height, 0, webrtc::kVideoRotation_0, newBuffer);
189     }
190     CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
191     sendFrame(WTFMove(newBuffer));
192 }
193
194 } // namespace WebCore
195
196 #endif // USE(LIBWEBRTC)