Reland with fixes for failing chromium tests. Propagate VideoFrame::UpdateRect to encoder Accumulate it in all places where frames can be dropped before they reach the encoder. Reset UpdateRect in VideoBroadcaster if frame the previous frame is dropped. No accumulation is done here since it's supposed to be a brief occasion then configuration have changed. Original Reviewed-on: https://webrtc-review.googlesource.com/c/123102 Bug: webrtc:10310 Change-Id: I18be73f47f227d6392bf9cb220b549ced225714f Reviewed-on: https://webrtc-review.googlesource.com/c/123230 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26738}
149 lines
5.1 KiB
C++
149 lines
5.1 KiB
C++
/*
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "media/base/video_broadcaster.h"
|
|
|
|
#include <vector>
|
|
|
|
#include "absl/types/optional.h"
|
|
#include "api/video/i420_buffer.h"
|
|
#include "api/video/video_rotation.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/logging.h"
|
|
|
|
namespace rtc {
|
|
|
|
VideoBroadcaster::VideoBroadcaster() = default;
|
|
VideoBroadcaster::~VideoBroadcaster() = default;
|
|
|
|
void VideoBroadcaster::AddOrUpdateSink(
|
|
VideoSinkInterface<webrtc::VideoFrame>* sink,
|
|
const VideoSinkWants& wants) {
|
|
RTC_DCHECK(sink != nullptr);
|
|
rtc::CritScope cs(&sinks_and_wants_lock_);
|
|
if (!FindSinkPair(sink)) {
|
|
// |Sink| is a new sink, which didn't receive previous frame.
|
|
previous_frame_sent_to_all_sinks_ = false;
|
|
}
|
|
VideoSourceBase::AddOrUpdateSink(sink, wants);
|
|
UpdateWants();
|
|
}
|
|
|
|
void VideoBroadcaster::RemoveSink(
|
|
VideoSinkInterface<webrtc::VideoFrame>* sink) {
|
|
RTC_DCHECK(sink != nullptr);
|
|
rtc::CritScope cs(&sinks_and_wants_lock_);
|
|
VideoSourceBase::RemoveSink(sink);
|
|
UpdateWants();
|
|
}
|
|
|
|
bool VideoBroadcaster::frame_wanted() const {
|
|
rtc::CritScope cs(&sinks_and_wants_lock_);
|
|
return !sink_pairs().empty();
|
|
}
|
|
|
|
VideoSinkWants VideoBroadcaster::wants() const {
|
|
rtc::CritScope cs(&sinks_and_wants_lock_);
|
|
return current_wants_;
|
|
}
|
|
|
|
void VideoBroadcaster::OnFrame(const webrtc::VideoFrame& frame) {
|
|
rtc::CritScope cs(&sinks_and_wants_lock_);
|
|
bool current_frame_was_discarded = false;
|
|
for (auto& sink_pair : sink_pairs()) {
|
|
if (sink_pair.wants.rotation_applied &&
|
|
frame.rotation() != webrtc::kVideoRotation_0) {
|
|
// Calls to OnFrame are not synchronized with changes to the sink wants.
|
|
// When rotation_applied is set to true, one or a few frames may get here
|
|
// with rotation still pending. Protect sinks that don't expect any
|
|
// pending rotation.
|
|
RTC_LOG(LS_VERBOSE) << "Discarding frame with unexpected rotation.";
|
|
sink_pair.sink->OnDiscardedFrame();
|
|
current_frame_was_discarded = true;
|
|
continue;
|
|
}
|
|
if (sink_pair.wants.black_frames) {
|
|
webrtc::VideoFrame black_frame =
|
|
webrtc::VideoFrame::Builder()
|
|
.set_video_frame_buffer(
|
|
GetBlackFrameBuffer(frame.width(), frame.height()))
|
|
.set_rotation(frame.rotation())
|
|
.set_timestamp_us(frame.timestamp_us())
|
|
.set_id(frame.id())
|
|
.build();
|
|
sink_pair.sink->OnFrame(black_frame);
|
|
} else if (!previous_frame_sent_to_all_sinks_) {
|
|
// Since last frame was not sent to some sinks, full update is needed.
|
|
webrtc::VideoFrame copy = frame;
|
|
copy.set_update_rect(
|
|
webrtc::VideoFrame::UpdateRect{0, 0, frame.width(), frame.height()});
|
|
sink_pair.sink->OnFrame(copy);
|
|
} else {
|
|
sink_pair.sink->OnFrame(frame);
|
|
}
|
|
}
|
|
previous_frame_sent_to_all_sinks_ = !current_frame_was_discarded;
|
|
}
|
|
|
|
void VideoBroadcaster::OnDiscardedFrame() {
|
|
for (auto& sink_pair : sink_pairs()) {
|
|
sink_pair.sink->OnDiscardedFrame();
|
|
}
|
|
}
|
|
|
|
void VideoBroadcaster::UpdateWants() {
|
|
VideoSinkWants wants;
|
|
wants.rotation_applied = false;
|
|
for (auto& sink : sink_pairs()) {
|
|
// wants.rotation_applied == ANY(sink.wants.rotation_applied)
|
|
if (sink.wants.rotation_applied) {
|
|
wants.rotation_applied = true;
|
|
}
|
|
// wants.max_pixel_count == MIN(sink.wants.max_pixel_count)
|
|
if (sink.wants.max_pixel_count < wants.max_pixel_count) {
|
|
wants.max_pixel_count = sink.wants.max_pixel_count;
|
|
}
|
|
// Select the minimum requested target_pixel_count, if any, of all sinks so
|
|
// that we don't over utilize the resources for any one.
|
|
// TODO(sprang): Consider using the median instead, since the limit can be
|
|
// expressed by max_pixel_count.
|
|
if (sink.wants.target_pixel_count &&
|
|
(!wants.target_pixel_count ||
|
|
(*sink.wants.target_pixel_count < *wants.target_pixel_count))) {
|
|
wants.target_pixel_count = sink.wants.target_pixel_count;
|
|
}
|
|
// Select the minimum for the requested max framerates.
|
|
if (sink.wants.max_framerate_fps < wants.max_framerate_fps) {
|
|
wants.max_framerate_fps = sink.wants.max_framerate_fps;
|
|
}
|
|
}
|
|
|
|
if (wants.target_pixel_count &&
|
|
*wants.target_pixel_count >= wants.max_pixel_count) {
|
|
wants.target_pixel_count.emplace(wants.max_pixel_count);
|
|
}
|
|
current_wants_ = wants;
|
|
}
|
|
|
|
const rtc::scoped_refptr<webrtc::VideoFrameBuffer>&
|
|
VideoBroadcaster::GetBlackFrameBuffer(int width, int height) {
|
|
if (!black_frame_buffer_ || black_frame_buffer_->width() != width ||
|
|
black_frame_buffer_->height() != height) {
|
|
rtc::scoped_refptr<webrtc::I420Buffer> buffer =
|
|
webrtc::I420Buffer::Create(width, height);
|
|
webrtc::I420Buffer::SetBlack(buffer.get());
|
|
black_frame_buffer_ = buffer;
|
|
}
|
|
|
|
return black_frame_buffer_;
|
|
}
|
|
|
|
} // namespace rtc
|