• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "media/base/video_broadcaster.h"
12 
13 #include <vector>
14 
15 #include "absl/types/optional.h"
16 #include "api/video/i420_buffer.h"
17 #include "api/video/video_rotation.h"
18 #include "media/base/video_common.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21 
22 namespace rtc {
23 
24 VideoBroadcaster::VideoBroadcaster() = default;
25 VideoBroadcaster::~VideoBroadcaster() = default;
26 
AddOrUpdateSink(VideoSinkInterface<webrtc::VideoFrame> * sink,const VideoSinkWants & wants)27 void VideoBroadcaster::AddOrUpdateSink(
28     VideoSinkInterface<webrtc::VideoFrame>* sink,
29     const VideoSinkWants& wants) {
30   RTC_DCHECK(sink != nullptr);
31   webrtc::MutexLock lock(&sinks_and_wants_lock_);
32   if (!FindSinkPair(sink)) {
33     // |Sink| is a new sink, which didn't receive previous frame.
34     previous_frame_sent_to_all_sinks_ = false;
35   }
36   VideoSourceBase::AddOrUpdateSink(sink, wants);
37   UpdateWants();
38 }
39 
RemoveSink(VideoSinkInterface<webrtc::VideoFrame> * sink)40 void VideoBroadcaster::RemoveSink(
41     VideoSinkInterface<webrtc::VideoFrame>* sink) {
42   RTC_DCHECK(sink != nullptr);
43   webrtc::MutexLock lock(&sinks_and_wants_lock_);
44   VideoSourceBase::RemoveSink(sink);
45   UpdateWants();
46 }
47 
frame_wanted() const48 bool VideoBroadcaster::frame_wanted() const {
49   webrtc::MutexLock lock(&sinks_and_wants_lock_);
50   return !sink_pairs().empty();
51 }
52 
wants() const53 VideoSinkWants VideoBroadcaster::wants() const {
54   webrtc::MutexLock lock(&sinks_and_wants_lock_);
55   return current_wants_;
56 }
57 
OnFrame(const webrtc::VideoFrame & frame)58 void VideoBroadcaster::OnFrame(const webrtc::VideoFrame& frame) {
59   webrtc::MutexLock lock(&sinks_and_wants_lock_);
60   bool current_frame_was_discarded = false;
61   for (auto& sink_pair : sink_pairs()) {
62     if (sink_pair.wants.rotation_applied &&
63         frame.rotation() != webrtc::kVideoRotation_0) {
64       // Calls to OnFrame are not synchronized with changes to the sink wants.
65       // When rotation_applied is set to true, one or a few frames may get here
66       // with rotation still pending. Protect sinks that don't expect any
67       // pending rotation.
68       RTC_LOG(LS_VERBOSE) << "Discarding frame with unexpected rotation.";
69       sink_pair.sink->OnDiscardedFrame();
70       current_frame_was_discarded = true;
71       continue;
72     }
73     if (sink_pair.wants.black_frames) {
74       webrtc::VideoFrame black_frame =
75           webrtc::VideoFrame::Builder()
76               .set_video_frame_buffer(
77                   GetBlackFrameBuffer(frame.width(), frame.height()))
78               .set_rotation(frame.rotation())
79               .set_timestamp_us(frame.timestamp_us())
80               .set_id(frame.id())
81               .build();
82       sink_pair.sink->OnFrame(black_frame);
83     } else if (!previous_frame_sent_to_all_sinks_ && frame.has_update_rect()) {
84       // Since last frame was not sent to some sinks, no reliable update
85       // information is available, so we need to clear the update rect.
86       webrtc::VideoFrame copy = frame;
87       copy.clear_update_rect();
88       sink_pair.sink->OnFrame(copy);
89     } else {
90       sink_pair.sink->OnFrame(frame);
91     }
92   }
93   previous_frame_sent_to_all_sinks_ = !current_frame_was_discarded;
94 }
95 
OnDiscardedFrame()96 void VideoBroadcaster::OnDiscardedFrame() {
97   for (auto& sink_pair : sink_pairs()) {
98     sink_pair.sink->OnDiscardedFrame();
99   }
100 }
101 
UpdateWants()102 void VideoBroadcaster::UpdateWants() {
103   VideoSinkWants wants;
104   wants.rotation_applied = false;
105   wants.resolution_alignment = 1;
106   for (auto& sink : sink_pairs()) {
107     // wants.rotation_applied == ANY(sink.wants.rotation_applied)
108     if (sink.wants.rotation_applied) {
109       wants.rotation_applied = true;
110     }
111     // wants.max_pixel_count == MIN(sink.wants.max_pixel_count)
112     if (sink.wants.max_pixel_count < wants.max_pixel_count) {
113       wants.max_pixel_count = sink.wants.max_pixel_count;
114     }
115     // Select the minimum requested target_pixel_count, if any, of all sinks so
116     // that we don't over utilize the resources for any one.
117     // TODO(sprang): Consider using the median instead, since the limit can be
118     // expressed by max_pixel_count.
119     if (sink.wants.target_pixel_count &&
120         (!wants.target_pixel_count ||
121          (*sink.wants.target_pixel_count < *wants.target_pixel_count))) {
122       wants.target_pixel_count = sink.wants.target_pixel_count;
123     }
124     // Select the minimum for the requested max framerates.
125     if (sink.wants.max_framerate_fps < wants.max_framerate_fps) {
126       wants.max_framerate_fps = sink.wants.max_framerate_fps;
127     }
128     wants.resolution_alignment = cricket::LeastCommonMultiple(
129         wants.resolution_alignment, sink.wants.resolution_alignment);
130   }
131 
132   if (wants.target_pixel_count &&
133       *wants.target_pixel_count >= wants.max_pixel_count) {
134     wants.target_pixel_count.emplace(wants.max_pixel_count);
135   }
136   current_wants_ = wants;
137 }
138 
139 const rtc::scoped_refptr<webrtc::VideoFrameBuffer>&
GetBlackFrameBuffer(int width,int height)140 VideoBroadcaster::GetBlackFrameBuffer(int width, int height) {
141   if (!black_frame_buffer_ || black_frame_buffer_->width() != width ||
142       black_frame_buffer_->height() != height) {
143     rtc::scoped_refptr<webrtc::I420Buffer> buffer =
144         webrtc::I420Buffer::Create(width, height);
145     webrtc::I420Buffer::SetBlack(buffer.get());
146     black_frame_buffer_ = buffer;
147   }
148 
149   return black_frame_buffer_;
150 }
151 
152 }  // namespace rtc
153