Per | 8e16e61 | 2016-02-11 15:56:51 +0100 | [diff] [blame] | 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 "webrtc/media/base/videobroadcaster.h" |
| 12 | |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 13 | #include <limits> |
| 14 | |
nisse | af91689 | 2017-01-10 07:44:26 -0800 | [diff] [blame] | 15 | #include "webrtc/api/video/i420_buffer.h" |
Edward Lemur | c20978e | 2017-07-06 19:44:34 +0200 | [diff] [blame] | 16 | #include "webrtc/rtc_base/checks.h" |
| 17 | #include "webrtc/rtc_base/logging.h" |
Per | 8e16e61 | 2016-02-11 15:56:51 +0100 | [diff] [blame] | 18 | |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 19 | namespace rtc { |
| 20 | |
| 21 | VideoBroadcaster::VideoBroadcaster() { |
| 22 | thread_checker_.DetachFromThread(); |
| 23 | } |
| 24 | |
| 25 | void VideoBroadcaster::AddOrUpdateSink( |
nisse | acd935b | 2016-11-11 03:55:13 -0800 | [diff] [blame] | 26 | VideoSinkInterface<webrtc::VideoFrame>* sink, |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 27 | const VideoSinkWants& wants) { |
| 28 | RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 29 | RTC_DCHECK(sink != nullptr); |
perkj | f0dcfe2 | 2016-03-10 18:32:00 +0100 | [diff] [blame] | 30 | rtc::CritScope cs(&sinks_and_wants_lock_); |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 31 | VideoSourceBase::AddOrUpdateSink(sink, wants); |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 32 | UpdateWants(); |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 33 | } |
| 34 | |
| 35 | void VideoBroadcaster::RemoveSink( |
nisse | acd935b | 2016-11-11 03:55:13 -0800 | [diff] [blame] | 36 | VideoSinkInterface<webrtc::VideoFrame>* sink) { |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 37 | RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 38 | RTC_DCHECK(sink != nullptr); |
perkj | f0dcfe2 | 2016-03-10 18:32:00 +0100 | [diff] [blame] | 39 | rtc::CritScope cs(&sinks_and_wants_lock_); |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 40 | VideoSourceBase::RemoveSink(sink); |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 41 | UpdateWants(); |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | bool VideoBroadcaster::frame_wanted() const { |
perkj | f0dcfe2 | 2016-03-10 18:32:00 +0100 | [diff] [blame] | 45 | rtc::CritScope cs(&sinks_and_wants_lock_); |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 46 | return !sink_pairs().empty(); |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 47 | } |
| 48 | |
| 49 | VideoSinkWants VideoBroadcaster::wants() const { |
perkj | f0dcfe2 | 2016-03-10 18:32:00 +0100 | [diff] [blame] | 50 | rtc::CritScope cs(&sinks_and_wants_lock_); |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 51 | return current_wants_; |
| 52 | } |
| 53 | |
nisse | acd935b | 2016-11-11 03:55:13 -0800 | [diff] [blame] | 54 | void VideoBroadcaster::OnFrame(const webrtc::VideoFrame& frame) { |
perkj | f0dcfe2 | 2016-03-10 18:32:00 +0100 | [diff] [blame] | 55 | rtc::CritScope cs(&sinks_and_wants_lock_); |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 56 | for (auto& sink_pair : sink_pairs()) { |
nisse | 6f5a6c3 | 2016-09-22 01:25:59 -0700 | [diff] [blame] | 57 | if (sink_pair.wants.rotation_applied && |
| 58 | frame.rotation() != webrtc::kVideoRotation_0) { |
| 59 | // Calls to OnFrame are not synchronized with changes to the sink wants. |
| 60 | // When rotation_applied is set to true, one or a few frames may get here |
| 61 | // with rotation still pending. Protect sinks that don't expect any |
| 62 | // pending rotation. |
| 63 | LOG(LS_VERBOSE) << "Discarding frame with unexpected rotation."; |
| 64 | continue; |
| 65 | } |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 66 | if (sink_pair.wants.black_frames) { |
nisse | acd935b | 2016-11-11 03:55:13 -0800 | [diff] [blame] | 67 | sink_pair.sink->OnFrame(webrtc::VideoFrame( |
Sergey Ulanov | 19ee1e6eb | 2016-08-01 13:35:55 -0700 | [diff] [blame] | 68 | GetBlackFrameBuffer(frame.width(), frame.height()), frame.rotation(), |
nisse | 0934785 | 2016-10-19 00:30:30 -0700 | [diff] [blame] | 69 | frame.timestamp_us())); |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 70 | } else { |
| 71 | sink_pair.sink->OnFrame(frame); |
| 72 | } |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 73 | } |
| 74 | } |
| 75 | |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 76 | void VideoBroadcaster::UpdateWants() { |
| 77 | RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 78 | |
| 79 | VideoSinkWants wants; |
| 80 | wants.rotation_applied = false; |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 81 | for (auto& sink : sink_pairs()) { |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 82 | // wants.rotation_applied == ANY(sink.wants.rotation_applied) |
| 83 | if (sink.wants.rotation_applied) { |
| 84 | wants.rotation_applied = true; |
| 85 | } |
| 86 | // wants.max_pixel_count == MIN(sink.wants.max_pixel_count) |
sprang | c5d62e2 | 2017-04-02 23:53:04 -0700 | [diff] [blame] | 87 | if (sink.wants.max_pixel_count < wants.max_pixel_count) { |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 88 | wants.max_pixel_count = sink.wants.max_pixel_count; |
| 89 | } |
sprang | 84a3759 | 2017-02-10 07:04:27 -0800 | [diff] [blame] | 90 | // Select the minimum requested target_pixel_count, if any, of all sinks so |
| 91 | // that we don't over utilize the resources for any one. |
| 92 | // TODO(sprang): Consider using the median instead, since the limit can be |
| 93 | // expressed by max_pixel_count. |
| 94 | if (sink.wants.target_pixel_count && |
| 95 | (!wants.target_pixel_count || |
| 96 | (*sink.wants.target_pixel_count < *wants.target_pixel_count))) { |
| 97 | wants.target_pixel_count = sink.wants.target_pixel_count; |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 98 | } |
sprang | c5d62e2 | 2017-04-02 23:53:04 -0700 | [diff] [blame] | 99 | // Select the minimum for the requested max framerates. |
| 100 | if (sink.wants.max_framerate_fps < wants.max_framerate_fps) { |
| 101 | wants.max_framerate_fps = sink.wants.max_framerate_fps; |
| 102 | } |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 103 | } |
| 104 | |
sprang | c5d62e2 | 2017-04-02 23:53:04 -0700 | [diff] [blame] | 105 | if (wants.target_pixel_count && |
| 106 | *wants.target_pixel_count >= wants.max_pixel_count) { |
| 107 | wants.target_pixel_count.emplace(wants.max_pixel_count); |
perkj | 2d5f091 | 2016-02-29 00:04:41 -0800 | [diff] [blame] | 108 | } |
| 109 | current_wants_ = wants; |
| 110 | } |
| 111 | |
nisse | efec590 | 2016-06-09 00:31:39 -0700 | [diff] [blame] | 112 | const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& |
| 113 | VideoBroadcaster::GetBlackFrameBuffer(int width, int height) { |
| 114 | if (!black_frame_buffer_ || black_frame_buffer_->width() != width || |
| 115 | black_frame_buffer_->height() != height) { |
| 116 | rtc::scoped_refptr<webrtc::I420Buffer> buffer = |
nisse | af91689 | 2017-01-10 07:44:26 -0800 | [diff] [blame] | 117 | webrtc::I420Buffer::Create(width, height); |
| 118 | webrtc::I420Buffer::SetBlack(buffer.get()); |
nisse | efec590 | 2016-06-09 00:31:39 -0700 | [diff] [blame] | 119 | black_frame_buffer_ = buffer; |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 120 | } |
nisse | efec590 | 2016-06-09 00:31:39 -0700 | [diff] [blame] | 121 | |
| 122 | return black_frame_buffer_; |
perkj | d6c3954 | 2016-03-17 10:35:23 +0100 | [diff] [blame] | 123 | } |
| 124 | |
Per | a509241 | 2016-02-12 13:30:57 +0100 | [diff] [blame] | 125 | } // namespace rtc |