webrtc_m130/webrtc/video/video_send_stream.cc
Peter Boström 45553aefac Remove VideoEngine interface usage from new API.
Instantiates ProcessThread/ChannelGroup inside Call instead of using
VideoEngine or ViEBase. This removes the need for ViEChannelManager,
ViEInputManager and other ViESharedData completely.

Some interface headers are still referenced due to external interfaces
being defined there. Upon interface removal these will be moved to
implementation headers.

BUG=1695
R=mflodman@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/50849005

Cr-Commit-Position: refs/heads/master@{#9160}
2015-05-08 11:54:39 +00:00

542 lines
20 KiB
C++

/*
* Copyright (c) 2013 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 "webrtc/video/video_send_stream.h"
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/trace_event.h"
#include "webrtc/video_engine/encoder_state_feedback.h"
#include "webrtc/video_engine/vie_capturer.h"
#include "webrtc/video_engine/vie_channel.h"
#include "webrtc/video_engine/vie_channel_group.h"
#include "webrtc/video_engine/vie_defines.h"
#include "webrtc/video_engine/vie_encoder.h"
#include "webrtc/video_send_stream.h"
namespace webrtc {
std::string
VideoSendStream::Config::EncoderSettings::ToString() const {
std::stringstream ss;
ss << "{payload_name: " << payload_name;
ss << ", payload_type: " << payload_type;
ss << ", encoder: " << (encoder != nullptr ? "(VideoEncoder)" : "nullptr");
ss << '}';
return ss.str();
}
std::string VideoSendStream::Config::Rtp::Rtx::ToString()
const {
std::stringstream ss;
ss << "{ssrcs: [";
for (size_t i = 0; i < ssrcs.size(); ++i) {
ss << ssrcs[i];
if (i != ssrcs.size() - 1)
ss << ", ";
}
ss << ']';
ss << ", payload_type: " << payload_type;
ss << '}';
return ss.str();
}
std::string VideoSendStream::Config::Rtp::ToString() const {
std::stringstream ss;
ss << "{ssrcs: [";
for (size_t i = 0; i < ssrcs.size(); ++i) {
ss << ssrcs[i];
if (i != ssrcs.size() - 1)
ss << ", ";
}
ss << ']';
ss << ", max_packet_size: " << max_packet_size;
ss << ", extensions: [";
for (size_t i = 0; i < extensions.size(); ++i) {
ss << extensions[i].ToString();
if (i != extensions.size() - 1)
ss << ", ";
}
ss << ']';
ss << ", nack: {rtp_history_ms: " << nack.rtp_history_ms << '}';
ss << ", fec: " << fec.ToString();
ss << ", rtx: " << rtx.ToString();
ss << ", c_name: " << c_name;
ss << '}';
return ss.str();
}
std::string VideoSendStream::Config::ToString() const {
std::stringstream ss;
ss << "{encoder_settings: " << encoder_settings.ToString();
ss << ", rtp: " << rtp.ToString();
ss << ", pre_encode_callback: "
<< (pre_encode_callback != nullptr ? "(I420FrameCallback)" : "nullptr");
ss << ", post_encode_callback: " << (post_encode_callback != nullptr
? "(EncodedFrameObserver)"
: "nullptr");
ss << "local_renderer: " << (local_renderer != nullptr ? "(VideoRenderer)"
: "nullptr");
ss << ", render_delay_ms: " << render_delay_ms;
ss << ", target_delay_ms: " << target_delay_ms;
ss << ", suspend_below_min_bitrate: " << (suspend_below_min_bitrate ? "on"
: "off");
ss << '}';
return ss.str();
}
namespace internal {
VideoSendStream::VideoSendStream(
newapi::Transport* transport,
CpuOveruseObserver* overuse_observer,
int num_cpu_cores,
ProcessThread* module_process_thread,
ChannelGroup* channel_group,
int channel_id,
const VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config,
const std::map<uint32_t, RtpState>& suspended_ssrcs)
: transport_adapter_(transport),
encoded_frame_proxy_(config.post_encode_callback),
config_(config),
suspended_ssrcs_(suspended_ssrcs),
module_process_thread_(module_process_thread),
channel_group_(channel_group),
channel_id_(channel_id),
use_config_bitrate_(true),
stats_proxy_(Clock::GetRealTimeClock(), config) {
CHECK(channel_group->CreateSendChannel(channel_id_, 0, num_cpu_cores, true));
vie_channel_ = channel_group_->GetChannel(channel_id_);
vie_encoder_ = channel_group_->GetEncoder(channel_id_);
DCHECK(!config_.rtp.ssrcs.empty());
for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
const std::string& extension = config_.rtp.extensions[i].name;
int id = config_.rtp.extensions[i].id;
// One-byte-extension local identifiers are in the range 1-14 inclusive.
DCHECK_GE(id, 1);
DCHECK_LE(id, 14);
if (extension == RtpExtension::kTOffset) {
CHECK_EQ(0, vie_channel_->SetSendTimestampOffsetStatus(true, id));
} else if (extension == RtpExtension::kAbsSendTime) {
CHECK_EQ(0, vie_channel_->SetSendAbsoluteSendTimeStatus(true, id));
} else if (extension == RtpExtension::kVideoRotation) {
CHECK_EQ(0, vie_channel_->SetSendVideoRotationStatus(true, id));
} else {
RTC_NOTREACHED() << "Registering unsupported RTP extension.";
}
}
// TODO(pbos): Consider configuring REMB in Call.
channel_group_->SetChannelRembStatus(true, false, vie_channel_);
// Enable NACK, FEC or both.
bool enable_protection_nack = false;
bool enable_protection_fec = false;
if (config_.rtp.fec.red_payload_type != -1) {
enable_protection_fec = true;
DCHECK(config_.rtp.fec.ulpfec_payload_type != -1);
if (config_.rtp.nack.rtp_history_ms > 0) {
enable_protection_nack = true;
vie_channel_->SetHybridNACKFECStatus(
true, static_cast<unsigned char>(config_.rtp.fec.red_payload_type),
static_cast<unsigned char>(config_.rtp.fec.ulpfec_payload_type));
} else {
vie_channel_->SetFECStatus(
true, static_cast<unsigned char>(config_.rtp.fec.red_payload_type),
static_cast<unsigned char>(config_.rtp.fec.ulpfec_payload_type));
}
// TODO(changbin): Should set RTX for RED mapping in RTP sender in future.
} else {
enable_protection_nack = config_.rtp.nack.rtp_history_ms > 0;
vie_channel_->SetNACKStatus(config_.rtp.nack.rtp_history_ms > 0);
}
vie_encoder_->UpdateProtectionMethod(enable_protection_nack,
enable_protection_fec);
ConfigureSsrcs();
char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
DCHECK_LT(config_.rtp.c_name.length(),
static_cast<size_t>(ViERTP_RTCP::KMaxRTCPCNameLength));
strncpy(rtcp_cname, config_.rtp.c_name.c_str(), sizeof(rtcp_cname) - 1);
rtcp_cname[sizeof(rtcp_cname) - 1] = '\0';
vie_channel_->SetRTCPCName(rtcp_cname);
vie_capturer_ = ViECapturer::CreateViECapturer(module_process_thread_);
CHECK_EQ(0, vie_capturer_->RegisterFrameCallback(channel_id_, vie_encoder_));
vie_channel_->RegisterSendTransport(&transport_adapter_);
// 28 to match packet overhead in ModuleRtpRtcpImpl.
vie_channel_->SetMTU(
static_cast<unsigned int>(config_.rtp.max_packet_size + 28));
DCHECK(config.encoder_settings.encoder != nullptr);
DCHECK_GE(config.encoder_settings.payload_type, 0);
DCHECK_LE(config.encoder_settings.payload_type, 127);
// TODO(pbos): Wire up codec internal-source setting or remove setting.
CHECK_EQ(0, vie_encoder_->RegisterExternalEncoder(
config.encoder_settings.encoder,
config.encoder_settings.payload_type, false));
CHECK(ReconfigureVideoEncoder(encoder_config));
if (overuse_observer) {
vie_capturer_->RegisterCpuOveruseObserver(overuse_observer);
}
// Registered regardless of monitoring, used for stats.
vie_capturer_->RegisterCpuOveruseMetricsObserver(&stats_proxy_);
vie_channel_->RegisterSendSideDelayObserver(&stats_proxy_);
vie_encoder_->RegisterSendStatisticsProxy(&stats_proxy_);
vie_encoder_->RegisterPreEncodeCallback(config_.pre_encode_callback);
if (config_.post_encode_callback)
vie_encoder_->RegisterPostEncodeImageCallback(&encoded_frame_proxy_);
if (config_.suspend_below_min_bitrate) {
vie_encoder_->SuspendBelowMinBitrate();
// Must enable pacing when enabling SuspendBelowMinBitrate. Otherwise, no
// padding will be sent when the video is suspended so the video will be
// unable to recover.
// TODO(pbos): Pacing should probably be enabled outside of VideoSendStream.
vie_channel_->SetTransmissionSmoothingStatus(true);
}
vie_channel_->RegisterSendChannelRtcpStatisticsCallback(&stats_proxy_);
vie_channel_->RegisterSendChannelRtpStatisticsCallback(&stats_proxy_);
vie_channel_->RegisterRtcpPacketTypeCounterObserver(&stats_proxy_);
vie_channel_->RegisterSendBitrateObserver(&stats_proxy_);
vie_channel_->RegisterSendFrameCountObserver(&stats_proxy_);
vie_encoder_->RegisterCodecObserver(&stats_proxy_);
}
VideoSendStream::~VideoSendStream() {
vie_encoder_->RegisterCodecObserver(nullptr);
vie_channel_->RegisterSendFrameCountObserver(nullptr);
vie_channel_->RegisterSendBitrateObserver(nullptr);
vie_channel_->RegisterRtcpPacketTypeCounterObserver(nullptr);
vie_channel_->RegisterSendChannelRtpStatisticsCallback(nullptr);
vie_channel_->RegisterSendChannelRtcpStatisticsCallback(nullptr);
vie_encoder_->RegisterPreEncodeCallback(nullptr);
vie_encoder_->RegisterPostEncodeImageCallback(nullptr);
vie_channel_->DeregisterSendTransport();
vie_capturer_->RegisterCpuOveruseObserver(nullptr);
vie_capturer_->DeregisterFrameCallback(vie_encoder_);
delete vie_capturer_;
vie_encoder_->DeRegisterExternalEncoder(
config_.encoder_settings.payload_type);
channel_group_->DeleteChannel(channel_id_);
}
void VideoSendStream::IncomingCapturedFrame(const I420VideoFrame& frame) {
// TODO(pbos): Local rendering should not be done on the capture thread.
if (config_.local_renderer != nullptr)
config_.local_renderer->RenderFrame(frame, 0);
stats_proxy_.OnIncomingFrame();
vie_capturer_->IncomingFrame(frame);
}
VideoSendStreamInput* VideoSendStream::Input() { return this; }
void VideoSendStream::Start() {
transport_adapter_.Enable();
vie_encoder_->Pause();
if (vie_channel_->StartSend() == 0) {
// Was not already started, trigger a keyframe.
vie_encoder_->SendKeyFrame();
}
vie_encoder_->Restart();
vie_channel_->StartReceive();
}
void VideoSendStream::Stop() {
// TODO(pbos): Make sure the encoder stops here.
vie_channel_->StopSend();
vie_channel_->StopReceive();
transport_adapter_.Disable();
}
bool VideoSendStream::ReconfigureVideoEncoder(
const VideoEncoderConfig& config) {
TRACE_EVENT0("webrtc", "VideoSendStream::(Re)configureVideoEncoder");
LOG(LS_INFO) << "(Re)configureVideoEncoder: " << config.ToString();
const std::vector<VideoStream>& streams = config.streams;
DCHECK(!streams.empty());
DCHECK_GE(config_.rtp.ssrcs.size(), streams.size());
VideoCodec video_codec;
memset(&video_codec, 0, sizeof(video_codec));
if (config_.encoder_settings.payload_name == "VP8") {
video_codec.codecType = kVideoCodecVP8;
} else if (config_.encoder_settings.payload_name == "VP9") {
video_codec.codecType = kVideoCodecVP9;
} else if (config_.encoder_settings.payload_name == "H264") {
video_codec.codecType = kVideoCodecH264;
} else {
video_codec.codecType = kVideoCodecGeneric;
}
switch (config.content_type) {
case VideoEncoderConfig::ContentType::kRealtimeVideo:
video_codec.mode = kRealtimeVideo;
break;
case VideoEncoderConfig::ContentType::kScreen:
video_codec.mode = kScreensharing;
if (config.streams.size() == 1 &&
config.streams[0].temporal_layer_thresholds_bps.size() == 1) {
video_codec.targetBitrate =
config.streams[0].temporal_layer_thresholds_bps[0] / 1000;
}
break;
}
if (video_codec.codecType == kVideoCodecVP8) {
video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
} else if (video_codec.codecType == kVideoCodecVP9) {
video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
} else if (video_codec.codecType == kVideoCodecH264) {
video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
}
if (video_codec.codecType == kVideoCodecVP8) {
if (config.encoder_specific_settings != nullptr) {
video_codec.codecSpecific.VP8 = *reinterpret_cast<const VideoCodecVP8*>(
config.encoder_specific_settings);
}
video_codec.codecSpecific.VP8.numberOfTemporalLayers =
static_cast<unsigned char>(
streams.back().temporal_layer_thresholds_bps.size() + 1);
} else if (video_codec.codecType == kVideoCodecVP9) {
if (config.encoder_specific_settings != nullptr) {
video_codec.codecSpecific.VP9 = *reinterpret_cast<const VideoCodecVP9*>(
config.encoder_specific_settings);
}
video_codec.codecSpecific.VP9.numberOfTemporalLayers =
static_cast<unsigned char>(
streams.back().temporal_layer_thresholds_bps.size() + 1);
} else if (video_codec.codecType == kVideoCodecH264) {
if (config.encoder_specific_settings != nullptr) {
video_codec.codecSpecific.H264 = *reinterpret_cast<const VideoCodecH264*>(
config.encoder_specific_settings);
}
} else {
// TODO(pbos): Support encoder_settings codec-agnostically.
DCHECK(config.encoder_specific_settings == nullptr)
<< "Encoder-specific settings for codec type not wired up.";
}
strncpy(video_codec.plName,
config_.encoder_settings.payload_name.c_str(),
kPayloadNameSize - 1);
video_codec.plName[kPayloadNameSize - 1] = '\0';
video_codec.plType = config_.encoder_settings.payload_type;
video_codec.numberOfSimulcastStreams =
static_cast<unsigned char>(streams.size());
video_codec.minBitrate = streams[0].min_bitrate_bps / 1000;
DCHECK_LE(streams.size(), static_cast<size_t>(kMaxSimulcastStreams));
for (size_t i = 0; i < streams.size(); ++i) {
SimulcastStream* sim_stream = &video_codec.simulcastStream[i];
DCHECK_GT(streams[i].width, 0u);
DCHECK_GT(streams[i].height, 0u);
DCHECK_GT(streams[i].max_framerate, 0);
// Different framerates not supported per stream at the moment.
DCHECK_EQ(streams[i].max_framerate, streams[0].max_framerate);
DCHECK_GE(streams[i].min_bitrate_bps, 0);
DCHECK_GE(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
DCHECK_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
DCHECK_GE(streams[i].max_qp, 0);
sim_stream->width = static_cast<unsigned short>(streams[i].width);
sim_stream->height = static_cast<unsigned short>(streams[i].height);
sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
sim_stream->qpMax = streams[i].max_qp;
sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
streams[i].temporal_layer_thresholds_bps.size() + 1);
video_codec.width = std::max(video_codec.width,
static_cast<unsigned short>(streams[i].width));
video_codec.height = std::max(
video_codec.height, static_cast<unsigned short>(streams[i].height));
video_codec.minBitrate =
std::min(video_codec.minBitrate,
static_cast<unsigned int>(streams[i].min_bitrate_bps / 1000));
video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000;
video_codec.qpMax = std::max(video_codec.qpMax,
static_cast<unsigned int>(streams[i].max_qp));
}
// Set to zero to not update the bitrate controller from ViEEncoder, as
// the bitrate controller is already set from Call.
video_codec.startBitrate = 0;
DCHECK_GT(streams[0].max_framerate, 0);
video_codec.maxFramerate = streams[0].max_framerate;
if (!SetSendCodec(video_codec))
return false;
DCHECK_GE(config.min_transmit_bitrate_bps, 0);
vie_encoder_->SetMinTransmitBitrate(config.min_transmit_bitrate_bps / 1000);
encoder_config_ = config;
use_config_bitrate_ = false;
return true;
}
bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
return vie_channel_->ReceivedRTCPPacket(packet, length) == 0;
}
VideoSendStream::Stats VideoSendStream::GetStats() {
return stats_proxy_.GetStats();
}
void VideoSendStream::ConfigureSsrcs() {
vie_channel_->SetSSRC(config_.rtp.ssrcs.front(), kViEStreamTypeNormal, 0);
for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.ssrcs[i];
vie_channel_->SetSSRC(ssrc, kViEStreamTypeNormal,
static_cast<unsigned char>(i));
RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
if (it != suspended_ssrcs_.end())
vie_channel_->SetRtpStateForSsrc(ssrc, it->second);
}
if (config_.rtp.rtx.ssrcs.empty()) {
return;
}
// Set up RTX.
DCHECK_EQ(config_.rtp.rtx.ssrcs.size(), config_.rtp.ssrcs.size());
for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
vie_channel_->SetSSRC(config_.rtp.rtx.ssrcs[i], kViEStreamTypeRtx,
static_cast<unsigned char>(i));
RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
if (it != suspended_ssrcs_.end())
vie_channel_->SetRtpStateForSsrc(ssrc, it->second);
}
DCHECK_GE(config_.rtp.rtx.payload_type, 0);
vie_channel_->SetRtxSendPayloadType(config_.rtp.rtx.payload_type,
config_.encoder_settings.payload_type);
}
std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const {
std::map<uint32_t, RtpState> rtp_states;
for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.ssrcs[i];
rtp_states[ssrc] = vie_channel_->GetRtpStateForSsrc( ssrc);
}
for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
rtp_states[ssrc] = vie_channel_->GetRtpStateForSsrc(ssrc);
}
return rtp_states;
}
void VideoSendStream::SignalNetworkState(Call::NetworkState state) {
// When network goes up, enable RTCP status before setting transmission state.
// When it goes down, disable RTCP afterwards. This ensures that any packets
// sent due to the network state changed will not be dropped.
if (state == Call::kNetworkUp)
vie_channel_->SetRTCPMode(kRtcpCompound);
vie_encoder_->SetNetworkTransmissionState(state == Call::kNetworkUp);
if (state == Call::kNetworkDown)
vie_channel_->SetRTCPMode(kRtcpOff);
}
int64_t VideoSendStream::GetRtt() const {
webrtc::RtcpStatistics rtcp_stats;
uint16_t frac_lost;
uint32_t cumulative_lost;
uint32_t extended_max_sequence_number;
uint32_t jitter;
int64_t rtt_ms;
if (vie_channel_->GetSendRtcpStatistics(&frac_lost, &cumulative_lost,
&extended_max_sequence_number,
&jitter, &rtt_ms) == 0) {
return rtt_ms;
}
return -1;
}
bool VideoSendStream::SetSendCodec(VideoCodec video_codec) {
if (video_codec.maxBitrate == 0) {
// Unset max bitrate -> cap to one bit per pixel.
video_codec.maxBitrate =
(video_codec.width * video_codec.height * video_codec.maxFramerate) /
1000;
}
if (video_codec.minBitrate < kViEMinCodecBitrate)
video_codec.minBitrate = kViEMinCodecBitrate;
if (video_codec.maxBitrate < kViEMinCodecBitrate)
video_codec.maxBitrate = kViEMinCodecBitrate;
// Stop the media flow while reconfiguring.
vie_encoder_->Pause();
if (vie_encoder_->SetEncoder(video_codec) != 0) {
LOG(LS_ERROR) << "Failed to set encoder.";
return false;
}
if (vie_channel_->SetSendCodec(video_codec, false) != 0) {
LOG(LS_ERROR) << "Failed to set send codec.";
return false;
}
// Not all configured SSRCs have to be utilized (simulcast senders don't have
// to send on all SSRCs at once etc.)
std::vector<uint32_t> used_ssrcs = config_.rtp.ssrcs;
used_ssrcs.resize(static_cast<size_t>(video_codec.numberOfSimulcastStreams));
// Update used SSRCs.
vie_encoder_->SetSsrcs(used_ssrcs);
EncoderStateFeedback* encoder_state_feedback =
channel_group_->GetEncoderStateFeedback();
encoder_state_feedback->UpdateSsrcs(used_ssrcs, vie_encoder_);
// Update the protection mode, we might be switching NACK/FEC.
vie_encoder_->UpdateProtectionMethod(vie_encoder_->nack_enabled(),
vie_channel_->IsSendingFecEnabled());
// Restart the media flow
vie_encoder_->Restart();
return true;
}
} // namespace internal
} // namespace webrtc