1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "media/base/video_broadcaster.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <vector>
15*d9f75844SAndroid Build Coastguard Worker
16*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
17*d9f75844SAndroid Build Coastguard Worker #include "api/video/i420_buffer.h"
18*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_rotation.h"
19*d9f75844SAndroid Build Coastguard Worker #include "media/base/video_common.h"
20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
22*d9f75844SAndroid Build Coastguard Worker
23*d9f75844SAndroid Build Coastguard Worker namespace rtc {
24*d9f75844SAndroid Build Coastguard Worker
25*d9f75844SAndroid Build Coastguard Worker VideoBroadcaster::VideoBroadcaster() = default;
26*d9f75844SAndroid Build Coastguard Worker VideoBroadcaster::~VideoBroadcaster() = default;
27*d9f75844SAndroid Build Coastguard Worker
AddOrUpdateSink(VideoSinkInterface<webrtc::VideoFrame> * sink,const VideoSinkWants & wants)28*d9f75844SAndroid Build Coastguard Worker void VideoBroadcaster::AddOrUpdateSink(
29*d9f75844SAndroid Build Coastguard Worker VideoSinkInterface<webrtc::VideoFrame>* sink,
30*d9f75844SAndroid Build Coastguard Worker const VideoSinkWants& wants) {
31*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(sink != nullptr);
32*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
33*d9f75844SAndroid Build Coastguard Worker if (!FindSinkPair(sink)) {
34*d9f75844SAndroid Build Coastguard Worker // `Sink` is a new sink, which didn't receive previous frame.
35*d9f75844SAndroid Build Coastguard Worker previous_frame_sent_to_all_sinks_ = false;
36*d9f75844SAndroid Build Coastguard Worker
37*d9f75844SAndroid Build Coastguard Worker if (last_constraints_.has_value()) {
38*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << __func__ << " forwarding stored constraints min_fps "
39*d9f75844SAndroid Build Coastguard Worker << last_constraints_->min_fps.value_or(-1) << " max_fps "
40*d9f75844SAndroid Build Coastguard Worker << last_constraints_->max_fps.value_or(-1);
41*d9f75844SAndroid Build Coastguard Worker sink->OnConstraintsChanged(*last_constraints_);
42*d9f75844SAndroid Build Coastguard Worker }
43*d9f75844SAndroid Build Coastguard Worker }
44*d9f75844SAndroid Build Coastguard Worker VideoSourceBase::AddOrUpdateSink(sink, wants);
45*d9f75844SAndroid Build Coastguard Worker UpdateWants();
46*d9f75844SAndroid Build Coastguard Worker }
47*d9f75844SAndroid Build Coastguard Worker
RemoveSink(VideoSinkInterface<webrtc::VideoFrame> * sink)48*d9f75844SAndroid Build Coastguard Worker void VideoBroadcaster::RemoveSink(
49*d9f75844SAndroid Build Coastguard Worker VideoSinkInterface<webrtc::VideoFrame>* sink) {
50*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(sink != nullptr);
51*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
52*d9f75844SAndroid Build Coastguard Worker VideoSourceBase::RemoveSink(sink);
53*d9f75844SAndroid Build Coastguard Worker UpdateWants();
54*d9f75844SAndroid Build Coastguard Worker }
55*d9f75844SAndroid Build Coastguard Worker
frame_wanted() const56*d9f75844SAndroid Build Coastguard Worker bool VideoBroadcaster::frame_wanted() const {
57*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
58*d9f75844SAndroid Build Coastguard Worker return !sink_pairs().empty();
59*d9f75844SAndroid Build Coastguard Worker }
60*d9f75844SAndroid Build Coastguard Worker
wants() const61*d9f75844SAndroid Build Coastguard Worker VideoSinkWants VideoBroadcaster::wants() const {
62*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
63*d9f75844SAndroid Build Coastguard Worker return current_wants_;
64*d9f75844SAndroid Build Coastguard Worker }
65*d9f75844SAndroid Build Coastguard Worker
OnFrame(const webrtc::VideoFrame & frame)66*d9f75844SAndroid Build Coastguard Worker void VideoBroadcaster::OnFrame(const webrtc::VideoFrame& frame) {
67*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
68*d9f75844SAndroid Build Coastguard Worker bool current_frame_was_discarded = false;
69*d9f75844SAndroid Build Coastguard Worker for (auto& sink_pair : sink_pairs()) {
70*d9f75844SAndroid Build Coastguard Worker if (sink_pair.wants.rotation_applied &&
71*d9f75844SAndroid Build Coastguard Worker frame.rotation() != webrtc::kVideoRotation_0) {
72*d9f75844SAndroid Build Coastguard Worker // Calls to OnFrame are not synchronized with changes to the sink wants.
73*d9f75844SAndroid Build Coastguard Worker // When rotation_applied is set to true, one or a few frames may get here
74*d9f75844SAndroid Build Coastguard Worker // with rotation still pending. Protect sinks that don't expect any
75*d9f75844SAndroid Build Coastguard Worker // pending rotation.
76*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_VERBOSE) << "Discarding frame with unexpected rotation.";
77*d9f75844SAndroid Build Coastguard Worker sink_pair.sink->OnDiscardedFrame();
78*d9f75844SAndroid Build Coastguard Worker current_frame_was_discarded = true;
79*d9f75844SAndroid Build Coastguard Worker continue;
80*d9f75844SAndroid Build Coastguard Worker }
81*d9f75844SAndroid Build Coastguard Worker if (sink_pair.wants.black_frames) {
82*d9f75844SAndroid Build Coastguard Worker webrtc::VideoFrame black_frame =
83*d9f75844SAndroid Build Coastguard Worker webrtc::VideoFrame::Builder()
84*d9f75844SAndroid Build Coastguard Worker .set_video_frame_buffer(
85*d9f75844SAndroid Build Coastguard Worker GetBlackFrameBuffer(frame.width(), frame.height()))
86*d9f75844SAndroid Build Coastguard Worker .set_rotation(frame.rotation())
87*d9f75844SAndroid Build Coastguard Worker .set_timestamp_us(frame.timestamp_us())
88*d9f75844SAndroid Build Coastguard Worker .set_id(frame.id())
89*d9f75844SAndroid Build Coastguard Worker .build();
90*d9f75844SAndroid Build Coastguard Worker sink_pair.sink->OnFrame(black_frame);
91*d9f75844SAndroid Build Coastguard Worker } else if (!previous_frame_sent_to_all_sinks_ && frame.has_update_rect()) {
92*d9f75844SAndroid Build Coastguard Worker // Since last frame was not sent to some sinks, no reliable update
93*d9f75844SAndroid Build Coastguard Worker // information is available, so we need to clear the update rect.
94*d9f75844SAndroid Build Coastguard Worker webrtc::VideoFrame copy = frame;
95*d9f75844SAndroid Build Coastguard Worker copy.clear_update_rect();
96*d9f75844SAndroid Build Coastguard Worker sink_pair.sink->OnFrame(copy);
97*d9f75844SAndroid Build Coastguard Worker } else {
98*d9f75844SAndroid Build Coastguard Worker sink_pair.sink->OnFrame(frame);
99*d9f75844SAndroid Build Coastguard Worker }
100*d9f75844SAndroid Build Coastguard Worker }
101*d9f75844SAndroid Build Coastguard Worker previous_frame_sent_to_all_sinks_ = !current_frame_was_discarded;
102*d9f75844SAndroid Build Coastguard Worker }
103*d9f75844SAndroid Build Coastguard Worker
OnDiscardedFrame()104*d9f75844SAndroid Build Coastguard Worker void VideoBroadcaster::OnDiscardedFrame() {
105*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
106*d9f75844SAndroid Build Coastguard Worker for (auto& sink_pair : sink_pairs()) {
107*d9f75844SAndroid Build Coastguard Worker sink_pair.sink->OnDiscardedFrame();
108*d9f75844SAndroid Build Coastguard Worker }
109*d9f75844SAndroid Build Coastguard Worker }
110*d9f75844SAndroid Build Coastguard Worker
ProcessConstraints(const webrtc::VideoTrackSourceConstraints & constraints)111*d9f75844SAndroid Build Coastguard Worker void VideoBroadcaster::ProcessConstraints(
112*d9f75844SAndroid Build Coastguard Worker const webrtc::VideoTrackSourceConstraints& constraints) {
113*d9f75844SAndroid Build Coastguard Worker webrtc::MutexLock lock(&sinks_and_wants_lock_);
114*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << __func__ << " min_fps "
115*d9f75844SAndroid Build Coastguard Worker << constraints.min_fps.value_or(-1) << " max_fps "
116*d9f75844SAndroid Build Coastguard Worker << constraints.max_fps.value_or(-1) << " broadcasting to "
117*d9f75844SAndroid Build Coastguard Worker << sink_pairs().size() << " sinks.";
118*d9f75844SAndroid Build Coastguard Worker last_constraints_ = constraints;
119*d9f75844SAndroid Build Coastguard Worker for (auto& sink_pair : sink_pairs())
120*d9f75844SAndroid Build Coastguard Worker sink_pair.sink->OnConstraintsChanged(constraints);
121*d9f75844SAndroid Build Coastguard Worker }
122*d9f75844SAndroid Build Coastguard Worker
UpdateWants()123*d9f75844SAndroid Build Coastguard Worker void VideoBroadcaster::UpdateWants() {
124*d9f75844SAndroid Build Coastguard Worker VideoSinkWants wants;
125*d9f75844SAndroid Build Coastguard Worker wants.rotation_applied = false;
126*d9f75844SAndroid Build Coastguard Worker wants.resolution_alignment = 1;
127*d9f75844SAndroid Build Coastguard Worker wants.aggregates.emplace(VideoSinkWants::Aggregates());
128*d9f75844SAndroid Build Coastguard Worker wants.is_active = false;
129*d9f75844SAndroid Build Coastguard Worker
130*d9f75844SAndroid Build Coastguard Worker // TODO(webrtc:14451) : I think it makes sense to always
131*d9f75844SAndroid Build Coastguard Worker // "ignore" encoders that are not active. But that would
132*d9f75844SAndroid Build Coastguard Worker // probably require a controlled roll out with a field trials?
133*d9f75844SAndroid Build Coastguard Worker // To play it safe, only ignore inactive encoders is there is an
134*d9f75844SAndroid Build Coastguard Worker // active encoder using the new api (requested_resolution),
135*d9f75844SAndroid Build Coastguard Worker // this means that there is only a behavioural change when using new
136*d9f75844SAndroid Build Coastguard Worker // api.
137*d9f75844SAndroid Build Coastguard Worker bool ignore_inactive_encoders_old_api = false;
138*d9f75844SAndroid Build Coastguard Worker for (auto& sink : sink_pairs()) {
139*d9f75844SAndroid Build Coastguard Worker if (sink.wants.is_active && sink.wants.requested_resolution.has_value()) {
140*d9f75844SAndroid Build Coastguard Worker ignore_inactive_encoders_old_api = true;
141*d9f75844SAndroid Build Coastguard Worker break;
142*d9f75844SAndroid Build Coastguard Worker }
143*d9f75844SAndroid Build Coastguard Worker }
144*d9f75844SAndroid Build Coastguard Worker
145*d9f75844SAndroid Build Coastguard Worker for (auto& sink : sink_pairs()) {
146*d9f75844SAndroid Build Coastguard Worker if (!sink.wants.is_active &&
147*d9f75844SAndroid Build Coastguard Worker (sink.wants.requested_resolution || ignore_inactive_encoders_old_api)) {
148*d9f75844SAndroid Build Coastguard Worker continue;
149*d9f75844SAndroid Build Coastguard Worker }
150*d9f75844SAndroid Build Coastguard Worker // wants.rotation_applied == ANY(sink.wants.rotation_applied)
151*d9f75844SAndroid Build Coastguard Worker if (sink.wants.rotation_applied) {
152*d9f75844SAndroid Build Coastguard Worker wants.rotation_applied = true;
153*d9f75844SAndroid Build Coastguard Worker }
154*d9f75844SAndroid Build Coastguard Worker // wants.max_pixel_count == MIN(sink.wants.max_pixel_count)
155*d9f75844SAndroid Build Coastguard Worker if (sink.wants.max_pixel_count < wants.max_pixel_count) {
156*d9f75844SAndroid Build Coastguard Worker wants.max_pixel_count = sink.wants.max_pixel_count;
157*d9f75844SAndroid Build Coastguard Worker }
158*d9f75844SAndroid Build Coastguard Worker // Select the minimum requested target_pixel_count, if any, of all sinks so
159*d9f75844SAndroid Build Coastguard Worker // that we don't over utilize the resources for any one.
160*d9f75844SAndroid Build Coastguard Worker // TODO(sprang): Consider using the median instead, since the limit can be
161*d9f75844SAndroid Build Coastguard Worker // expressed by max_pixel_count.
162*d9f75844SAndroid Build Coastguard Worker if (sink.wants.target_pixel_count &&
163*d9f75844SAndroid Build Coastguard Worker (!wants.target_pixel_count ||
164*d9f75844SAndroid Build Coastguard Worker (*sink.wants.target_pixel_count < *wants.target_pixel_count))) {
165*d9f75844SAndroid Build Coastguard Worker wants.target_pixel_count = sink.wants.target_pixel_count;
166*d9f75844SAndroid Build Coastguard Worker }
167*d9f75844SAndroid Build Coastguard Worker // Select the minimum for the requested max framerates.
168*d9f75844SAndroid Build Coastguard Worker if (sink.wants.max_framerate_fps < wants.max_framerate_fps) {
169*d9f75844SAndroid Build Coastguard Worker wants.max_framerate_fps = sink.wants.max_framerate_fps;
170*d9f75844SAndroid Build Coastguard Worker }
171*d9f75844SAndroid Build Coastguard Worker wants.resolution_alignment = cricket::LeastCommonMultiple(
172*d9f75844SAndroid Build Coastguard Worker wants.resolution_alignment, sink.wants.resolution_alignment);
173*d9f75844SAndroid Build Coastguard Worker
174*d9f75844SAndroid Build Coastguard Worker // Pick MAX(requested_resolution) since the actual can be downscaled
175*d9f75844SAndroid Build Coastguard Worker // in encoder instead.
176*d9f75844SAndroid Build Coastguard Worker if (sink.wants.requested_resolution) {
177*d9f75844SAndroid Build Coastguard Worker if (!wants.requested_resolution) {
178*d9f75844SAndroid Build Coastguard Worker wants.requested_resolution = sink.wants.requested_resolution;
179*d9f75844SAndroid Build Coastguard Worker } else {
180*d9f75844SAndroid Build Coastguard Worker wants.requested_resolution->width =
181*d9f75844SAndroid Build Coastguard Worker std::max(wants.requested_resolution->width,
182*d9f75844SAndroid Build Coastguard Worker sink.wants.requested_resolution->width);
183*d9f75844SAndroid Build Coastguard Worker wants.requested_resolution->height =
184*d9f75844SAndroid Build Coastguard Worker std::max(wants.requested_resolution->height,
185*d9f75844SAndroid Build Coastguard Worker sink.wants.requested_resolution->height);
186*d9f75844SAndroid Build Coastguard Worker }
187*d9f75844SAndroid Build Coastguard Worker } else if (sink.wants.is_active) {
188*d9f75844SAndroid Build Coastguard Worker wants.aggregates->any_active_without_requested_resolution = true;
189*d9f75844SAndroid Build Coastguard Worker }
190*d9f75844SAndroid Build Coastguard Worker
191*d9f75844SAndroid Build Coastguard Worker wants.is_active |= sink.wants.is_active;
192*d9f75844SAndroid Build Coastguard Worker }
193*d9f75844SAndroid Build Coastguard Worker
194*d9f75844SAndroid Build Coastguard Worker if (wants.target_pixel_count &&
195*d9f75844SAndroid Build Coastguard Worker *wants.target_pixel_count >= wants.max_pixel_count) {
196*d9f75844SAndroid Build Coastguard Worker wants.target_pixel_count.emplace(wants.max_pixel_count);
197*d9f75844SAndroid Build Coastguard Worker }
198*d9f75844SAndroid Build Coastguard Worker current_wants_ = wants;
199*d9f75844SAndroid Build Coastguard Worker }
200*d9f75844SAndroid Build Coastguard Worker
201*d9f75844SAndroid Build Coastguard Worker const rtc::scoped_refptr<webrtc::VideoFrameBuffer>&
GetBlackFrameBuffer(int width,int height)202*d9f75844SAndroid Build Coastguard Worker VideoBroadcaster::GetBlackFrameBuffer(int width, int height) {
203*d9f75844SAndroid Build Coastguard Worker if (!black_frame_buffer_ || black_frame_buffer_->width() != width ||
204*d9f75844SAndroid Build Coastguard Worker black_frame_buffer_->height() != height) {
205*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<webrtc::I420Buffer> buffer =
206*d9f75844SAndroid Build Coastguard Worker webrtc::I420Buffer::Create(width, height);
207*d9f75844SAndroid Build Coastguard Worker webrtc::I420Buffer::SetBlack(buffer.get());
208*d9f75844SAndroid Build Coastguard Worker black_frame_buffer_ = buffer;
209*d9f75844SAndroid Build Coastguard Worker }
210*d9f75844SAndroid Build Coastguard Worker
211*d9f75844SAndroid Build Coastguard Worker return black_frame_buffer_;
212*d9f75844SAndroid Build Coastguard Worker }
213*d9f75844SAndroid Build Coastguard Worker
214*d9f75844SAndroid Build Coastguard Worker } // namespace rtc
215