Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h" | 11 #include "webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h" |
| 12 | 12 |
| 13 #include <algorithm> | 13 #include <algorithm> |
| 14 | 14 |
| 15 // NOTE(ajm): Path provided by gyp. | 15 // NOTE(ajm): Path provided by gyp. |
| 16 #include "libyuv/scale.h" // NOLINT | 16 #include "libyuv/scale.h" // NOLINT |
| 17 | 17 |
| 18 #include "webrtc/base/checks.h" | 18 #include "webrtc/base/checks.h" |
| 19 #include "webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h" | 19 #include "webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h" |
| 20 #include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h" | |
| 20 #include "webrtc/system_wrappers/include/clock.h" | 21 #include "webrtc/system_wrappers/include/clock.h" |
| 21 | 22 |
| 22 namespace { | 23 namespace { |
| 23 | 24 |
| 24 const unsigned int kDefaultMinQp = 2; | 25 const unsigned int kDefaultMinQp = 2; |
| 25 const unsigned int kDefaultMaxQp = 56; | 26 const unsigned int kDefaultMaxQp = 56; |
| 26 // Max qp for lowest spatial resolution when doing simulcast. | 27 // Max qp for lowest spatial resolution when doing simulcast. |
| 27 const unsigned int kLowestResMaxQp = 45; | 28 const unsigned int kLowestResMaxQp = 45; |
| 28 | 29 |
| 29 uint32_t SumStreamTargetBitrate(int streams, const webrtc::VideoCodec& codec) { | |
| 30 uint32_t bitrate_sum = 0; | |
| 31 for (int i = 0; i < streams; ++i) { | |
| 32 bitrate_sum += codec.simulcastStream[i].targetBitrate; | |
| 33 } | |
| 34 return bitrate_sum; | |
| 35 } | |
| 36 | |
| 37 uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) { | 30 uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) { |
| 38 uint32_t bitrate_sum = 0; | 31 uint32_t bitrate_sum = 0; |
| 39 for (int i = 0; i < streams; ++i) { | 32 for (int i = 0; i < streams; ++i) { |
| 40 bitrate_sum += codec.simulcastStream[i].maxBitrate; | 33 bitrate_sum += codec.simulcastStream[i].maxBitrate; |
| 41 } | 34 } |
| 42 return bitrate_sum; | 35 return bitrate_sum; |
| 43 } | 36 } |
| 44 | 37 |
| 45 int NumberOfStreams(const webrtc::VideoCodec& codec) { | 38 int NumberOfStreams(const webrtc::VideoCodec& codec) { |
| 46 int streams = | 39 int streams = |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 85 inst->numberOfSimulcastStreams > 1) { | 78 inst->numberOfSimulcastStreams > 1) { |
| 86 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 79 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 87 } | 80 } |
| 88 if (inst->codecSpecific.VP8.automaticResizeOn && | 81 if (inst->codecSpecific.VP8.automaticResizeOn && |
| 89 inst->numberOfSimulcastStreams > 1) { | 82 inst->numberOfSimulcastStreams > 1) { |
| 90 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 83 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 91 } | 84 } |
| 92 return WEBRTC_VIDEO_CODEC_OK; | 85 return WEBRTC_VIDEO_CODEC_OK; |
| 93 } | 86 } |
| 94 | 87 |
| 95 // TL1 FrameDropper's max time to drop frames. | |
| 96 const float kTl1MaxTimeToDropFrames = 20.0f; | |
| 97 | |
| 98 struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory { | 88 struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory { |
| 99 ScreenshareTemporalLayersFactory() | 89 ScreenshareTemporalLayersFactory() {} |
| 100 : tl1_frame_dropper_(kTl1MaxTimeToDropFrames) {} | |
| 101 | |
| 102 virtual ~ScreenshareTemporalLayersFactory() {} | 90 virtual ~ScreenshareTemporalLayersFactory() {} |
| 103 | 91 |
| 104 virtual webrtc::TemporalLayers* Create(int num_temporal_layers, | 92 virtual webrtc::TemporalLayers* Create(int num_temporal_layers, |
| 105 uint8_t initial_tl0_pic_idx) const { | 93 uint8_t initial_tl0_pic_idx) const { |
| 106 return new webrtc::ScreenshareLayers(num_temporal_layers, rand(), | 94 return new webrtc::ScreenshareLayers(num_temporal_layers, rand(), |
| 107 webrtc::Clock::GetRealTimeClock()); | 95 webrtc::Clock::GetRealTimeClock()); |
| 108 } | 96 } |
| 109 | |
| 110 mutable webrtc::FrameDropper tl0_frame_dropper_; | |
| 111 mutable webrtc::FrameDropper tl1_frame_dropper_; | |
| 112 }; | 97 }; |
| 113 | 98 |
| 114 // An EncodedImageCallback implementation that forwards on calls to a | 99 // An EncodedImageCallback implementation that forwards on calls to a |
| 115 // SimulcastEncoderAdapter, but with the stream index it's registered with as | 100 // SimulcastEncoderAdapter, but with the stream index it's registered with as |
| 116 // the first parameter to Encoded. | 101 // the first parameter to Encoded. |
| 117 class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback { | 102 class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback { |
| 118 public: | 103 public: |
| 119 AdapterEncodedImageCallback(webrtc::SimulcastEncoderAdapter* adapter, | 104 AdapterEncodedImageCallback(webrtc::SimulcastEncoderAdapter* adapter, |
| 120 size_t stream_idx) | 105 size_t stream_idx) |
| 121 : adapter_(adapter), stream_idx_(stream_idx) {} | 106 : adapter_(adapter), stream_idx_(stream_idx) {} |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 132 webrtc::SimulcastEncoderAdapter* const adapter_; | 117 webrtc::SimulcastEncoderAdapter* const adapter_; |
| 133 const size_t stream_idx_; | 118 const size_t stream_idx_; |
| 134 }; | 119 }; |
| 135 | 120 |
| 136 } // namespace | 121 } // namespace |
| 137 | 122 |
| 138 namespace webrtc { | 123 namespace webrtc { |
| 139 | 124 |
| 140 SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory) | 125 SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory) |
| 141 : factory_(factory), | 126 : factory_(factory), |
| 142 encoded_complete_callback_(NULL), | 127 encoded_complete_callback_(nullptr), |
| 143 implementation_name_("SimulcastEncoderAdapter") { | 128 implementation_name_("SimulcastEncoderAdapter") { |
| 144 memset(&codec_, 0, sizeof(webrtc::VideoCodec)); | 129 memset(&codec_, 0, sizeof(webrtc::VideoCodec)); |
| 130 rate_allocator_.reset(new SimulcastRateAllocator(codec_)); | |
| 145 } | 131 } |
| 146 | 132 |
| 147 SimulcastEncoderAdapter::~SimulcastEncoderAdapter() { | 133 SimulcastEncoderAdapter::~SimulcastEncoderAdapter() { |
| 148 Release(); | 134 Release(); |
| 149 } | 135 } |
| 150 | 136 |
| 151 int SimulcastEncoderAdapter::Release() { | 137 int SimulcastEncoderAdapter::Release() { |
| 152 // TODO(pbos): Keep the last encoder instance but call ::Release() on it, then | 138 // TODO(pbos): Keep the last encoder instance but call ::Release() on it, then |
| 153 // re-use this instance in ::InitEncode(). This means that changing | 139 // re-use this instance in ::InitEncode(). This means that changing |
| 154 // resolutions doesn't require reallocation of the first encoder, but only | 140 // resolutions doesn't require reallocation of the first encoder, but only |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 182 } | 168 } |
| 183 | 169 |
| 184 int number_of_streams = NumberOfStreams(*inst); | 170 int number_of_streams = NumberOfStreams(*inst); |
| 185 const bool doing_simulcast = (number_of_streams > 1); | 171 const bool doing_simulcast = (number_of_streams > 1); |
| 186 | 172 |
| 187 if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) { | 173 if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) { |
| 188 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 174 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| 189 } | 175 } |
| 190 | 176 |
| 191 codec_ = *inst; | 177 codec_ = *inst; |
| 178 rate_allocator_.reset(new SimulcastRateAllocator(codec_)); | |
| 179 std::vector<uint32_t> start_bitrates = | |
| 180 rate_allocator_->GetAllocation(codec_.startBitrate); | |
| 192 | 181 |
| 193 // Special mode when screensharing on a single stream. | 182 // Special mode when screensharing on a single stream. |
| 194 if (number_of_streams == 1 && inst->mode == kScreensharing) { | 183 if (number_of_streams == 1 && inst->mode == kScreensharing) { |
| 195 screensharing_tl_factory_.reset(new ScreenshareTemporalLayersFactory()); | 184 screensharing_tl_factory_.reset(new ScreenshareTemporalLayersFactory()); |
| 196 codec_.codecSpecific.VP8.tl_factory = screensharing_tl_factory_.get(); | 185 codec_.codecSpecific.VP8.tl_factory = screensharing_tl_factory_.get(); |
| 197 } | 186 } |
| 198 | 187 |
| 199 std::string implementation_name; | 188 std::string implementation_name; |
| 200 // Create |number_of_streams| of encoder instances and init them. | 189 // Create |number_of_streams| of encoder instances and init them. |
| 201 for (int i = 0; i < number_of_streams; ++i) { | 190 for (int i = 0; i < number_of_streams; ++i) { |
| 202 VideoCodec stream_codec; | 191 VideoCodec stream_codec; |
| 203 bool send_stream = true; | 192 bool send_stream = true; |
| 204 if (!doing_simulcast) { | 193 if (!doing_simulcast) { |
| 205 stream_codec = codec_; | 194 stream_codec = codec_; |
| 206 stream_codec.numberOfSimulcastStreams = 1; | 195 stream_codec.numberOfSimulcastStreams = 1; |
| 207 } else { | 196 } else { |
| 208 bool highest_resolution_stream = (i == (number_of_streams - 1)); | 197 bool highest_resolution_stream = (i == (number_of_streams - 1)); |
| 209 PopulateStreamCodec(&codec_, i, number_of_streams, | 198 uint32_t start_bitrate_kbps = start_bitrates[i]; |
| 199 if (!start_bitrate_kbps) { | |
|
noahric
2016/08/29 21:32:09
Prefer == 0 for integral values. Most of libjingle
sprang_webrtc
2016/08/30 13:27:24
Done.
| |
| 200 // Set max bitrate of previous encoder, but don't send it. | |
| 201 // We need this in order for the multi-encoder to work. | |
| 202 start_bitrate_kbps = codec_.simulcastStream[i - 1].maxBitrate; | |
| 203 } | |
| 204 PopulateStreamCodec(&codec_, i, start_bitrate_kbps, | |
| 210 highest_resolution_stream, &stream_codec, | 205 highest_resolution_stream, &stream_codec, |
| 211 &send_stream); | 206 &send_stream); |
| 212 } | 207 } |
| 213 | 208 |
| 214 // TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl. | 209 // TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl. |
| 215 if (stream_codec.qpMax < kDefaultMinQp) { | 210 if (stream_codec.qpMax < kDefaultMinQp) { |
| 216 stream_codec.qpMax = kDefaultMaxQp; | 211 stream_codec.qpMax = kDefaultMaxQp; |
| 217 } | 212 } |
| 218 | 213 |
| 219 VideoEncoder* encoder = factory_->Create(); | 214 VideoEncoder* encoder = factory_->Create(); |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 365 } | 360 } |
| 366 if (new_bitrate_kbit < codec_.minBitrate) { | 361 if (new_bitrate_kbit < codec_.minBitrate) { |
| 367 new_bitrate_kbit = codec_.minBitrate; | 362 new_bitrate_kbit = codec_.minBitrate; |
| 368 } | 363 } |
| 369 if (codec_.numberOfSimulcastStreams > 0 && | 364 if (codec_.numberOfSimulcastStreams > 0 && |
| 370 new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) { | 365 new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) { |
| 371 new_bitrate_kbit = codec_.simulcastStream[0].minBitrate; | 366 new_bitrate_kbit = codec_.simulcastStream[0].minBitrate; |
| 372 } | 367 } |
| 373 codec_.maxFramerate = new_framerate; | 368 codec_.maxFramerate = new_framerate; |
| 374 | 369 |
| 375 bool send_stream = true; | 370 std::vector<uint32_t> stream_bitrates = |
| 376 uint32_t stream_bitrate = 0; | 371 rate_allocator_->GetAllocation(new_bitrate_kbit); |
| 377 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) { | 372 for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) { |
| 378 stream_bitrate = GetStreamBitrate(stream_idx, streaminfos_.size(), | 373 uint32_t stream_bitrate = stream_bitrates[stream_idx]; |
| 379 new_bitrate_kbit, &send_stream); | 374 bool send_stream = stream_bitrate > 0; |
| 375 if (!send_stream) { | |
| 376 // Not enough bitrate for this stream. | |
| 377 // Return our max bitrate of |stream_idx| - 1, but we don't send it. We | |
| 378 // need to keep this resolution coding in order for the multi-encoder to | |
| 379 // work. | |
|
noahric
2016/08/29 21:32:09
I see this comment goes waaaay back in webrtc (in
sprang_webrtc
2016/08/30 13:27:24
Gotcha, thanks.
vp8_impl.cc already has it's own
noahric
2016/08/30 18:14:33
Yup! By inspection, at least, I don't expect probl
| |
| 380 stream_bitrate = stream_bitrates[stream_idx - 1]; | |
| 381 } | |
| 380 // Need a key frame if we have not sent this stream before. | 382 // Need a key frame if we have not sent this stream before. |
| 381 if (send_stream && !streaminfos_[stream_idx].send_stream) { | 383 if (send_stream && !streaminfos_[stream_idx].send_stream) { |
| 382 streaminfos_[stream_idx].key_frame_request = true; | 384 streaminfos_[stream_idx].key_frame_request = true; |
| 383 } | 385 } |
| 384 streaminfos_[stream_idx].send_stream = send_stream; | 386 streaminfos_[stream_idx].send_stream = send_stream; |
| 385 | 387 |
| 386 // TODO(holmer): This is a temporary hack for screensharing, where we | 388 // TODO(holmer): This is a temporary hack for screensharing, where we |
| 387 // interpret the startBitrate as the encoder target bitrate. This is | 389 // interpret the startBitrate as the encoder target bitrate. This is |
| 388 // to allow for a different max bitrate, so if the codec can't meet | 390 // to allow for a different max bitrate, so if the codec can't meet |
| 389 // the target we still allow it to overshoot up to the max before dropping | 391 // the target we still allow it to overshoot up to the max before dropping |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 410 const CodecSpecificInfo* codecSpecificInfo, | 412 const CodecSpecificInfo* codecSpecificInfo, |
| 411 const RTPFragmentationHeader* fragmentation) { | 413 const RTPFragmentationHeader* fragmentation) { |
| 412 CodecSpecificInfo stream_codec_specific = *codecSpecificInfo; | 414 CodecSpecificInfo stream_codec_specific = *codecSpecificInfo; |
| 413 CodecSpecificInfoVP8* vp8Info = &(stream_codec_specific.codecSpecific.VP8); | 415 CodecSpecificInfoVP8* vp8Info = &(stream_codec_specific.codecSpecific.VP8); |
| 414 vp8Info->simulcastIdx = stream_idx; | 416 vp8Info->simulcastIdx = stream_idx; |
| 415 | 417 |
| 416 return encoded_complete_callback_->OnEncodedImage( | 418 return encoded_complete_callback_->OnEncodedImage( |
| 417 encodedImage, &stream_codec_specific, fragmentation); | 419 encodedImage, &stream_codec_specific, fragmentation); |
| 418 } | 420 } |
| 419 | 421 |
| 420 uint32_t SimulcastEncoderAdapter::GetStreamBitrate( | |
| 421 int stream_idx, | |
| 422 size_t total_number_of_streams, | |
| 423 uint32_t new_bitrate_kbit, | |
| 424 bool* send_stream) const { | |
| 425 if (total_number_of_streams == 1) { | |
| 426 *send_stream = true; | |
| 427 return new_bitrate_kbit; | |
| 428 } | |
| 429 | |
| 430 // The bitrate needed to start sending this stream is given by the | |
| 431 // minimum bitrate allowed for encoding this stream, plus the sum target | |
| 432 // rates of all lower streams. | |
| 433 uint32_t sum_target_lower_streams = | |
| 434 SumStreamTargetBitrate(stream_idx, codec_); | |
| 435 uint32_t bitrate_to_send_this_layer = | |
| 436 codec_.simulcastStream[stream_idx].minBitrate + sum_target_lower_streams; | |
| 437 if (new_bitrate_kbit >= bitrate_to_send_this_layer) { | |
| 438 // We have enough bandwidth to send this stream. | |
| 439 *send_stream = true; | |
| 440 // Bitrate for this stream is the new bitrate (|new_bitrate_kbit|) minus the | |
| 441 // sum target rates of the lower streams, and capped to a maximum bitrate. | |
| 442 // The maximum cap depends on whether we send the next higher stream. | |
| 443 // If we will be sending the next higher stream, |max_rate| is given by | |
| 444 // current stream's |targetBitrate|, otherwise it's capped by |maxBitrate|. | |
| 445 if (stream_idx < codec_.numberOfSimulcastStreams - 1) { | |
| 446 unsigned int max_rate = codec_.simulcastStream[stream_idx].maxBitrate; | |
| 447 if (new_bitrate_kbit >= | |
| 448 SumStreamTargetBitrate(stream_idx + 1, codec_) + | |
| 449 codec_.simulcastStream[stream_idx + 1].minBitrate) { | |
| 450 max_rate = codec_.simulcastStream[stream_idx].targetBitrate; | |
| 451 } | |
| 452 return std::min(new_bitrate_kbit - sum_target_lower_streams, max_rate); | |
| 453 } else { | |
| 454 // For the highest stream (highest resolution), the |targetBitRate| and | |
| 455 // |maxBitrate| are not used. Any excess bitrate (above the targets of | |
| 456 // all lower streams) is given to this (highest resolution) stream. | |
| 457 return new_bitrate_kbit - sum_target_lower_streams; | |
| 458 } | |
| 459 } else { | |
| 460 // Not enough bitrate for this stream. | |
| 461 // Return our max bitrate of |stream_idx| - 1, but we don't send it. We need | |
| 462 // to keep this resolution coding in order for the multi-encoder to work. | |
| 463 *send_stream = false; | |
| 464 return codec_.simulcastStream[stream_idx - 1].maxBitrate; | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 void SimulcastEncoderAdapter::PopulateStreamCodec( | 422 void SimulcastEncoderAdapter::PopulateStreamCodec( |
| 469 const webrtc::VideoCodec* inst, | 423 const webrtc::VideoCodec* inst, |
| 470 int stream_index, | 424 int stream_index, |
| 471 size_t total_number_of_streams, | 425 uint32_t start_bitrate_kbps, |
| 472 bool highest_resolution_stream, | 426 bool highest_resolution_stream, |
| 473 webrtc::VideoCodec* stream_codec, | 427 webrtc::VideoCodec* stream_codec, |
| 474 bool* send_stream) { | 428 bool* send_stream) { |
| 475 *stream_codec = *inst; | 429 *stream_codec = *inst; |
| 476 | 430 |
| 477 // Stream specific settings. | 431 // Stream specific settings. |
| 478 stream_codec->codecSpecific.VP8.numberOfTemporalLayers = | 432 stream_codec->codecSpecific.VP8.numberOfTemporalLayers = |
| 479 inst->simulcastStream[stream_index].numberOfTemporalLayers; | 433 inst->simulcastStream[stream_index].numberOfTemporalLayers; |
| 480 stream_codec->numberOfSimulcastStreams = 0; | 434 stream_codec->numberOfSimulcastStreams = 0; |
| 481 stream_codec->width = inst->simulcastStream[stream_index].width; | 435 stream_codec->width = inst->simulcastStream[stream_index].width; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 493 // kComplexityHigher, which maps to cpu_used = -4. | 447 // kComplexityHigher, which maps to cpu_used = -4. |
| 494 int pixels_per_frame = stream_codec->width * stream_codec->height; | 448 int pixels_per_frame = stream_codec->width * stream_codec->height; |
| 495 if (pixels_per_frame < 352 * 288) { | 449 if (pixels_per_frame < 352 * 288) { |
| 496 stream_codec->codecSpecific.VP8.complexity = webrtc::kComplexityHigher; | 450 stream_codec->codecSpecific.VP8.complexity = webrtc::kComplexityHigher; |
| 497 } | 451 } |
| 498 // Turn off denoising for all streams but the highest resolution. | 452 // Turn off denoising for all streams but the highest resolution. |
| 499 stream_codec->codecSpecific.VP8.denoisingOn = false; | 453 stream_codec->codecSpecific.VP8.denoisingOn = false; |
| 500 } | 454 } |
| 501 // TODO(ronghuawu): what to do with targetBitrate. | 455 // TODO(ronghuawu): what to do with targetBitrate. |
| 502 | 456 |
| 503 int stream_bitrate = GetStreamBitrate(stream_index, total_number_of_streams, | 457 stream_codec->startBitrate = start_bitrate_kbps; |
| 504 inst->startBitrate, send_stream); | |
| 505 stream_codec->startBitrate = stream_bitrate; | |
| 506 } | 458 } |
| 507 | 459 |
| 508 bool SimulcastEncoderAdapter::Initialized() const { | 460 bool SimulcastEncoderAdapter::Initialized() const { |
| 509 return !streaminfos_.empty(); | 461 return !streaminfos_.empty(); |
| 510 } | 462 } |
| 511 | 463 |
| 512 void SimulcastEncoderAdapter::OnDroppedFrame() { | 464 void SimulcastEncoderAdapter::OnDroppedFrame() { |
| 513 streaminfos_[0].encoder->OnDroppedFrame(); | 465 streaminfos_[0].encoder->OnDroppedFrame(); |
| 514 } | 466 } |
| 515 | 467 |
| 516 bool SimulcastEncoderAdapter::SupportsNativeHandle() const { | 468 bool SimulcastEncoderAdapter::SupportsNativeHandle() const { |
| 517 // We should not be calling this method before streaminfos_ are configured. | 469 // We should not be calling this method before streaminfos_ are configured. |
| 518 RTC_DCHECK(!streaminfos_.empty()); | 470 RTC_DCHECK(!streaminfos_.empty()); |
| 519 for (const auto& streaminfo : streaminfos_) { | 471 for (const auto& streaminfo : streaminfos_) { |
| 520 if (!streaminfo.encoder->SupportsNativeHandle()) | 472 if (!streaminfo.encoder->SupportsNativeHandle()) |
| 521 return false; | 473 return false; |
| 522 } | 474 } |
| 523 return true; | 475 return true; |
| 524 } | 476 } |
| 525 | 477 |
| 526 const char* SimulcastEncoderAdapter::ImplementationName() const { | 478 const char* SimulcastEncoderAdapter::ImplementationName() const { |
| 527 return implementation_name_.c_str(); | 479 return implementation_name_.c_str(); |
| 528 } | 480 } |
| 529 | 481 |
| 530 } // namespace webrtc | 482 } // namespace webrtc |
| OLD | NEW |