#include "webrtc/video/video_send_stream.h"
+#include <algorithm>
+#include <sstream>
#include <string>
#include <vector>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/video_engine/include/vie_base.h"
#include "webrtc/video_engine/include/vie_capture.h"
#include "webrtc/video_engine/include/vie_codec.h"
#include "webrtc/video_send_stream.h"
namespace webrtc {
-namespace internal {
+std::string
+VideoSendStream::Config::EncoderSettings::ToString() const {
+ std::stringstream ss;
+ ss << "{payload_name: " << payload_name;
+ ss << ", payload_type: " << payload_type;
+ if (encoder != NULL)
+ ss << ", encoder: " << (encoder != NULL ? "(encoder)" : "NULL");
+ 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;
+ if (min_transmit_bitrate_bps != 0)
+ ss << ", min_transmit_bitrate_bps: " << min_transmit_bitrate_bps;
-VideoSendStream::VideoSendStream(newapi::Transport* transport,
- CpuOveruseObserver* overuse_observer,
- webrtc::VideoEngine* video_engine,
- const VideoSendStream::Config& config,
- int base_channel)
+ ss << ", extensions: {";
+ for (size_t i = 0; i < extensions.size(); ++i) {
+ ss << extensions[i].ToString();
+ if (i != extensions.size() - 1)
+ ss << "}, {";
+ }
+ ss << '}';
+
+ if (nack.rtp_history_ms != 0)
+ ss << ", nack.rtp_history_ms: " << nack.rtp_history_ms;
+ if (fec.ulpfec_payload_type != -1 || fec.red_payload_type != -1)
+ ss << ", fec: " << fec.ToString();
+ if (rtx.payload_type != 0 || !rtx.ssrcs.empty())
+ ss << ", rtx: " << rtx.ToString();
+ if (c_name != "")
+ 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();
+ if (pre_encode_callback != NULL)
+ ss << ", (pre_encode_callback)";
+ if (post_encode_callback != NULL)
+ ss << ", (post_encode_callback)";
+ if (local_renderer != NULL) {
+ ss << ", (local_renderer, render_delay_ms: " << render_delay_ms << ")";
+ }
+ if (target_delay_ms > 0)
+ ss << ", target_delay_ms: " << target_delay_ms;
+ if (suspend_below_min_bitrate)
+ ss << ", suspend_below_min_bitrate: on";
+ ss << '}';
+ return ss.str();
+}
+
+namespace internal {
+VideoSendStream::VideoSendStream(
+ newapi::Transport* transport,
+ CpuOveruseObserver* overuse_observer,
+ webrtc::VideoEngine* video_engine,
+ const VideoSendStream::Config& config,
+ const std::vector<VideoStream> video_streams,
+ const void* encoder_settings,
+ const std::map<uint32_t, RtpState>& suspended_ssrcs,
+ int base_channel,
+ int start_bitrate_bps)
: transport_adapter_(transport),
encoded_frame_proxy_(config.post_encode_callback),
- codec_lock_(CriticalSectionWrapper::CreateCriticalSection()),
config_(config),
+ start_bitrate_bps_(start_bitrate_bps),
+ suspended_ssrcs_(suspended_ssrcs),
external_codec_(NULL),
- channel_(-1) {
+ channel_(-1),
+ stats_proxy_(config) {
video_engine_base_ = ViEBase::GetInterface(video_engine);
video_engine_base_->CreateChannel(channel_, base_channel);
assert(channel_ != -1);
+ assert(start_bitrate_bps_ > 0);
rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
assert(rtp_rtcp_ != NULL);
assert(config_.rtp.ssrcs.size() > 0);
- if (config_.suspend_below_min_bitrate)
- config_.pacing = true;
- rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
assert(config_.rtp.min_transmit_bitrate_bps >= 0);
rtp_rtcp_->SetMinTransmitBitrate(channel_,
rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
}
+ ConfigureSsrcs();
+
char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
assert(config_.rtp.c_name.length() < ViERTP_RTCP::KMaxRTCPCNameLength);
strncpy(rtcp_cname, config_.rtp.c_name.c_str(), sizeof(rtcp_cname) - 1);
}
codec_ = ViECodec::GetInterface(video_engine);
- if (!ReconfigureVideoEncoder(config_.encoder_settings.streams,
- config_.encoder_settings.encoder_settings)) {
+ if (!ReconfigureVideoEncoder(video_streams, encoder_settings))
abort();
- }
if (overuse_observer)
video_engine_base_->RegisterCpuOveruseObserver(channel_, overuse_observer);
+ video_engine_base_->RegisterSendSideDelayObserver(channel_, &stats_proxy_);
+
image_process_ = ViEImageProcess::GetInterface(video_engine);
image_process_->RegisterPreEncodeCallback(channel_,
config_.pre_encode_callback);
&encoded_frame_proxy_);
}
- if (config_.suspend_below_min_bitrate) {
+ if (config_.suspend_below_min_bitrate)
codec_->SuspendBelowMinBitrate(channel_);
- }
-
- stats_proxy_.reset(new SendStatisticsProxy(config, this));
rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_,
- stats_proxy_.get());
+ &stats_proxy_);
rtp_rtcp_->RegisterSendChannelRtpStatisticsCallback(channel_,
- stats_proxy_.get());
- rtp_rtcp_->RegisterSendBitrateObserver(channel_, stats_proxy_.get());
- rtp_rtcp_->RegisterSendFrameCountObserver(channel_, stats_proxy_.get());
+ &stats_proxy_);
+ rtp_rtcp_->RegisterSendBitrateObserver(channel_, &stats_proxy_);
+ rtp_rtcp_->RegisterSendFrameCountObserver(channel_, &stats_proxy_);
- codec_->RegisterEncoderObserver(channel_, *stats_proxy_);
- capture_->RegisterObserver(capture_id_, *stats_proxy_);
+ codec_->RegisterEncoderObserver(channel_, stats_proxy_);
+ capture_->RegisterObserver(capture_id_, stats_proxy_);
}
VideoSendStream::~VideoSendStream() {
capture_->DeregisterObserver(capture_id_);
codec_->DeregisterEncoderObserver(channel_);
- rtp_rtcp_->DeregisterSendFrameCountObserver(channel_, stats_proxy_.get());
- rtp_rtcp_->DeregisterSendBitrateObserver(channel_, stats_proxy_.get());
+ rtp_rtcp_->DeregisterSendFrameCountObserver(channel_, &stats_proxy_);
+ rtp_rtcp_->DeregisterSendBitrateObserver(channel_, &stats_proxy_);
rtp_rtcp_->DeregisterSendChannelRtpStatisticsCallback(channel_,
- stats_proxy_.get());
+ &stats_proxy_);
rtp_rtcp_->DeregisterSendChannelRtcpStatisticsCallback(channel_,
- stats_proxy_.get());
+ &stats_proxy_);
image_process_->DeRegisterPreEncodeCallback(channel_);
rtp_rtcp_->Release();
}
-void VideoSendStream::PutFrame(const I420VideoFrame& frame) {
- input_frame_.CopyFrame(frame);
- SwapFrame(&input_frame_);
-}
-
void VideoSendStream::SwapFrame(I420VideoFrame* frame) {
- // TODO(pbos): Warn if frame is "too far" into the future, or too old. This
- // would help detect if frame's being used without NTP.
- // TO REVIEWER: Is there any good check for this? Should it be
- // skipped?
- if (frame != &input_frame_)
- input_frame_.SwapFrame(frame);
-
// TODO(pbos): Local rendering should not be done on the capture thread.
if (config_.local_renderer != NULL)
- config_.local_renderer->RenderFrame(input_frame_, 0);
+ config_.local_renderer->RenderFrame(*frame, 0);
- external_capture_->SwapFrame(&input_frame_);
+ external_capture_->SwapFrame(frame);
}
VideoSendStreamInput* VideoSendStream::Input() { return this; }
-void VideoSendStream::StartSending() {
+void VideoSendStream::Start() {
transport_adapter_.Enable();
video_engine_base_->StartSend(channel_);
video_engine_base_->StartReceive(channel_);
}
-void VideoSendStream::StopSending() {
+void VideoSendStream::Stop() {
video_engine_base_->StopSend(channel_);
video_engine_base_->StopReceive(channel_);
transport_adapter_.Disable();
bool VideoSendStream::ReconfigureVideoEncoder(
const std::vector<VideoStream>& streams,
- void* encoder_settings) {
+ const void* encoder_settings) {
assert(!streams.empty());
assert(config_.rtp.ssrcs.size() >= streams.size());
- // TODO(pbos): Wire encoder_settings.
- assert(encoder_settings == NULL);
-
- // VideoStreams in config_.encoder_settings need to be locked.
- CriticalSectionScoped crit(codec_lock_.get());
VideoCodec video_codec;
memset(&video_codec, 0, sizeof(video_codec));
- video_codec.codecType =
- (config_.encoder_settings.payload_name == "VP8" ? kVideoCodecVP8
- : kVideoCodecGeneric);
+ if (config_.encoder_settings.payload_name == "VP8") {
+ video_codec.codecType = kVideoCodecVP8;
+ } else if (config_.encoder_settings.payload_name == "H264") {
+ video_codec.codecType = kVideoCodecH264;
+ } else {
+ video_codec.codecType = kVideoCodecGeneric;
+ }
if (video_codec.codecType == kVideoCodecVP8) {
video_codec.codecSpecific.VP8.resilience = kResilientStream;
video_codec.codecSpecific.VP8.automaticResizeOn = false;
video_codec.codecSpecific.VP8.frameDroppingOn = true;
video_codec.codecSpecific.VP8.keyFrameInterval = 3000;
+ } else if (video_codec.codecType == kVideoCodecH264) {
+ video_codec.codecSpecific.H264.profile = kProfileBase;
+ video_codec.codecSpecific.H264.frameDroppingOn = true;
+ video_codec.codecSpecific.H264.keyFrameInterval = 3000;
+ }
+
+ if (video_codec.codecType == kVideoCodecVP8) {
+ if (encoder_settings != NULL) {
+ video_codec.codecSpecific.VP8 =
+ *reinterpret_cast<const VideoCodecVP8*>(encoder_settings);
+ }
+ } else {
+ // TODO(pbos): Support encoder_settings codec-agnostically.
+ assert(encoder_settings == NULL);
}
strncpy(video_codec.plName,
video_codec.qpMax = std::max(video_codec.qpMax,
static_cast<unsigned int>(streams[i].max_qp));
}
+ video_codec.startBitrate =
+ static_cast<unsigned int>(start_bitrate_bps_) / 1000;
if (video_codec.minBitrate < kViEMinCodecBitrate)
video_codec.minBitrate = kViEMinCodecBitrate;
if (video_codec.maxBitrate < kViEMinCodecBitrate)
video_codec.maxBitrate = kViEMinCodecBitrate;
-
- video_codec.startBitrate = 300;
+ if (video_codec.startBitrate < video_codec.minBitrate)
+ video_codec.startBitrate = video_codec.minBitrate;
+ if (video_codec.startBitrate > video_codec.maxBitrate)
+ video_codec.startBitrate = video_codec.maxBitrate;
if (video_codec.startBitrate < video_codec.minBitrate)
video_codec.startBitrate = video_codec.minBitrate;
if (video_codec.startBitrate > video_codec.maxBitrate)
video_codec.startBitrate = video_codec.maxBitrate;
- assert(config_.encoder_settings.streams[0].max_framerate > 0);
- video_codec.maxFramerate = config_.encoder_settings.streams[0].max_framerate;
+ assert(streams[0].max_framerate > 0);
+ video_codec.maxFramerate = streams[0].max_framerate;
- if (codec_->SetSendCodec(channel_, video_codec) != 0)
- return false;
+ return codec_->SetSendCodec(channel_, video_codec) == 0;
+}
+bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
+ return network_->ReceivedRTCPPacket(
+ channel_, packet, static_cast<int>(length)) == 0;
+}
+
+VideoSendStream::Stats VideoSendStream::GetStats() const {
+ return stats_proxy_.GetStats();
+}
+
+void VideoSendStream::ConfigureSsrcs() {
for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
- rtp_rtcp_->SetLocalSSRC(channel_,
- config_.rtp.ssrcs[i],
- kViEStreamTypeNormal,
- static_cast<unsigned char>(i));
+ uint32_t ssrc = config_.rtp.ssrcs[i];
+ rtp_rtcp_->SetLocalSSRC(
+ channel_, ssrc, kViEStreamTypeNormal, static_cast<unsigned char>(i));
+ RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
+ if (it != suspended_ssrcs_.end())
+ rtp_rtcp_->SetRtpStateForSsrc(channel_, ssrc, it->second);
}
- config_.encoder_settings.streams = streams;
- config_.encoder_settings.encoder_settings = encoder_settings;
-
- if (config_.rtp.rtx.ssrcs.empty())
- return true;
+ if (config_.rtp.rtx.ssrcs.empty()) {
+ assert(!config_.rtp.rtx.pad_with_redundant_payloads);
+ return;
+ }
// Set up RTX.
assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
- for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
+ for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
+ uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
rtp_rtcp_->SetLocalSSRC(channel_,
config_.rtp.rtx.ssrcs[i],
kViEStreamTypeRtx,
static_cast<unsigned char>(i));
+ RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
+ if (it != suspended_ssrcs_.end())
+ rtp_rtcp_->SetRtpStateForSsrc(channel_, ssrc, it->second);
}
- if (config_.rtp.rtx.payload_type != 0)
- rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
-
- return true;
-}
+ if (config_.rtp.rtx.pad_with_redundant_payloads) {
+ rtp_rtcp_->SetPadWithRedundantPayloads(channel_, true);
+ }
-bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
- return network_->ReceivedRTCPPacket(
- channel_, packet, static_cast<int>(length)) == 0;
+ assert(config_.rtp.rtx.payload_type >= 0);
+ rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
}
-VideoSendStream::Stats VideoSendStream::GetStats() const {
- return stats_proxy_->GetStats();
-}
+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] = rtp_rtcp_->GetRtpStateForSsrc(channel_, ssrc);
+ }
-bool VideoSendStream::GetSendSideDelay(VideoSendStream::Stats* stats) {
- return codec_->GetSendSideDelay(
- channel_, &stats->avg_delay_ms, &stats->max_delay_ms);
-}
+ for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
+ uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
+ rtp_states[ssrc] = rtp_rtcp_->GetRtpStateForSsrc(channel_, ssrc);
+ }
-std::string VideoSendStream::GetCName() {
- char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
- rtp_rtcp_->GetRTCPCName(channel_, rtcp_cname);
- return rtcp_cname;
+ return rtp_states;
}
} // namespace internal