1.simulcast+SVC打開
std::string str[] = {"low", "medium", "high"};
std::string msid[] = {"l", "m", "h"};
//double pri = 0.8;
//添加初始化參數,在此處設置時域層數,push 多少webrtc::RtpEncodingParameters就是多少層simulcast
webrtc::RtpTransceiverInit rtpTI;
for (int i = 3; i >= 1; i--) {
webrtc::RtpEncodingParameters videoEncoding;
videoEncoding.rid = str[i-1];
//videoEncoding.max_bitrate_bps = 3 * i * 100 * 1000;
//videoEncoding.bitrate_priority = pri;
videoEncoding.num_temporal_layers = 3;
rtpTI.send_encodings.push_back(videoEncoding);
//pri -= 0.2;
rtpTI.stream_ids.push_back(msid[i-1]); //這個是SDP中msid參數的名字
}
//單層simulcast的時候設置時域層數
//rtpTI.stream_ids.push_back("cam");
//webrtc::RtpEncodingParameters videoEncoding;
//videoEncoding.rid = str[2];
//videoEncoding.num_temporal_layers = 3;
//rtpTI.send_encodings.push_back(videoEncoding);
auto ret = peer_connection_->AddTransceiver(video_track_,rtpTI);
//這個可以獲取當前設置的參數
webrtc::RtpParameters para =
peer_connection_->GetSenders()[1]->GetParameters();
2.設置編碼優先順序(編碼選擇)
src/media/engine/internal_encoder_factory.cc
std::vector<SdpVideoFormat> InternalEncoderFactory::GetSupportedFormats()
const {
std::vector<SdpVideoFormat> supported_codecs;
//for (const webrtc::SdpVideoFormat& format : webrtc::SupportedH264Codecs())
// supported_codecs.push_back(format);
// for (const webrtc::SdpVideoFormat& format : webrtc::SupportedVP9Codecs())
// supported_codecs.push_back(format);
supported_codecs.push_back(SdpVideoFormat(cricket::kVp8CodecName));
return supported_codecs;
}
3.設置H264可通過AddTransceiver參數初始化支持SVC
增加對H264的支持:
src/media/engine/webrtc_video_engine.cc
bool IsTemporalLayersSupported(const std::string& codec_name) {
return absl::EqualsIgnoreCase(codec_name, kVp8CodecName) ||
absl::EqualsIgnoreCase(codec_name, kVp9CodecName) ||
absl::EqualsIgnoreCase(codec_name, kH264CodecName);
}
4.關於simulcast和svc的常量參數設置
src/api/video/video_codec_constans.h
enum : int { kMaxEncoderBuffers = 8 };
enum : int { kMaxSimulcastStreams = 3 };
enum : int { kMaxSpatialLayers = 5 };
enum : int { kMaxTemporalStreams = 4 };
5.simulcast
media_session.cc
static bool AddStreamParams(){
...
//這是對應的流的信息
StreamParams stream_param =
sender.rids.empty()
?
// Signal SSRCs and legacy simulcast (if requested).
//老版本planb. rids為空,使用num_simulcast_layer來創建,內部調用GenerateSsrcs
CreateStreamParamsForNewSenderWithSsrcs(
sender, rtcp_cname, include_rtx_streams,
include_flexfec_stream, ssrc_generator)
:
// Signal RIDs and spec-compliant simulcast (if requested).
CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
...
}
AddTransceiver只能在webrtc::SdpSemantics::kUnifiedPlan模式下,這個在CreatePeerConnection時設置進去,目前設置完simulcast參數后,數據未推上去,應該是服務端暫未支持。
webrtc::SdpSemantics::kPlanB模式下對應的是AddTrack,但是設置simulcast層數是在CreateOffer設置進去,webrtc::PeerConnectionInterface::RTCOfferAnswerOptions.num_simulcast_layers,但是此時根據抓包和斷點看到的是只有2層,RTCOfferAnswerOptions里面默認是兩層。
注:webrtc會根據當前視頻的分辨率,以及預設的常量來決定實際的simulcast層數,例如640x480-位於(960,540)和(640,320),所以參數是設置為(640,320),最多2層simulcast
void Conductor::ConnectToPeer(int peer_id) {
//RTC_DCHECK(peer_id_ == -1);
//RTC_DCHECK(peer_id != -1);
if (peer_connection_.get()) {
main_wnd_->MessageBox(
"Error", "We only support connecting to one peer at a time", true);
return;
}
if (InitializePeerConnection()) {
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options =
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions();
options.num_simulcast_layers = 3;
peer_id_ = peer_id;
peer_connection_->CreateOffer(
this, options);
} else {
main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
}
}
src/media/engine/simulcast.cc
// These tables describe from which resolution we can use how many
// simulcast layers at what bitrates (maximum, target, and minimum).
// Important!! Keep this table from high resolution to low resolution.
// clang-format off
const SimulcastFormat kSimulcastFormats[] = {
{1920, 1080, 3, 5000, 4000, 800},
{1280, 720, 3, 2500, 2500, 600},
{960, 540, 3, 1200, 1200, 350},
{640, 360, 2, 700, 500, 150},
{480, 270, 2, 450, 350, 150},
{320, 180, 1, 200, 150, 30},
{0, 0, 1, 200, 150, 30}
};
FindSimulcastFormatIndex:
{
...
for (uint32_t i = 0; i < arraysize(kSimulcastFormats); ++i) {
if (width * height >=
kSimulcastFormats[i].width * kSimulcastFormats[i].height) {
return i;
}
}
...
}
ReconfigureEncoder
↓
CreateEncoderStreams
↓
GetSimulcastConfig
↓
LimitSimulcastLayerCount
↓
FindSimulcastFormatIndex
6.simulcast碼率設置
編碼相關的設置在src/video/video_stream_encoder.cc
-ReconfigureEncoder
ReconfigureEncoder
↓
CreateEncoderStreams
↓
GetSimulcastConfig
↓
GetNormalSimulcastLayers 設置寬高碼率,同時會設置默認時域層
DefaultNumberOfTemporalLayers
FindSimulcastMaxBitrateBps
FindSimulcastTargetBitrateBps
FindSimulcastMinBitrateBps
kDefaultVideoMaxFramerate = 60
計算simulcast碼率的時候使用雙線性插值:
const int total_pixels_up =
kSimulcastFormats[index - 1].width * kSimulcastFormats[index - 1].height;
const int total_pixels_down =
kSimulcastFormats[index].width * kSimulcastFormats[index].height;
const int total_pixels = width * height;
const float rate = (total_pixels_up - total_pixels) /
static_cast<float>(total_pixels_up - total_pixels_down);
SimulcastFormat res;
res.width = width;
res.height = height;
res.max_layers = kSimulcastFormats[index].max_layers;
res.max_bitrate_kbps =
kSimulcastFormats[index - 1].max_bitrate_kbps * (1.0 - rate) +
kSimulcastFormats[index].max_bitrate_kbps * rate;
在設置0層simulcast時,如果打開了kUseBaseHeavyVP8TL3RateAllocationFieldTrial("WebRTC-UseBaseHeavyVP8TL3RateAllocation"),最大碼率和目標碼率將會乘以系數以適應0時域層
// If alternative temporal rate allocation is selected, adjust the
// bitrate of the lowest simulcast stream so that absolute bitrate for
// the base temporal layer matches the bitrate for the base temporal
// layer with the default 3 simulcast streams. Otherwise we risk a
// higher threshold for receiving a feed at all.
if (num_temporal_layers == 3) {
if (webrtc::field_trial::IsEnabled(
kUseBaseHeavyVP8TL3RateAllocationFieldTrial)) {
// Base heavy allocation increases TL0 bitrate from 40% to 60%.
rate_factor = 0.4 / 0.6;
}
} else {
rate_factor =
webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(3, 0) /
webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
num_temporal_layers, 0);
}
static const float
kLayerRateAllocation[kMaxTemporalStreams][kMaxTemporalStreams] = {
{1.0f, 1.0f, 1.0f, 1.0f}, // 1 layer
{0.6f, 1.0f, 1.0f, 1.0f}, // 2 layers {60%, 40%}
{0.4f, 0.6f, 1.0f, 1.0f}, // 3 layers {40%, 20%, 40%}
{0.25f, 0.4f, 0.6f, 1.0f} // 4 layers {25%, 15%, 20%, 40%}
};
static const float kBaseHeavy3TlRateAllocation[kMaxTemporalStreams] = {
0.6f, 0.8f, 1.0f, 1.0f // 3 layers {60%, 20%, 20%}
};
7.時域層碼率設置
ReconfigureEncoder
↓
EncoderSimulcastProxy::InitEncode
↓
LibvpxVp8Encoder::InitEncode
↓
SimulcastRateAllocator::Allocate
↓
SimulcastRateAllocator::GetTemporalRateAllocation 獲取對應simulcast層數的時域層比率乘以目標碼率
時域層的所有碼率總和是當前simulcast層的目標碼率
std::vector<uint32_t> SimulcastRateAllocator::DefaultTemporalLayerAllocation(
int bitrate_kbps,
int max_bitrate_kbps,
int simulcast_id) const {
const size_t num_temporal_layers = NumTemporalStreams(simulcast_id);
std::vector<uint32_t> bitrates;
for (size_t i = 0; i < num_temporal_layers; ++i) {
float layer_bitrate =
bitrate_kbps * GetTemporalRateAllocation(num_temporal_layers, i);
bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
}
// Allocation table is of aggregates, transform to individual rates.
uint32_t sum = 0;
for (size_t i = 0; i < num_temporal_layers; ++i) {
uint32_t layer_bitrate = bitrates[i];
RTC_DCHECK_LE(sum, bitrates[i]);
bitrates[i] -= sum;
sum = layer_bitrate;
if (sum >= static_cast<uint32_t>(bitrate_kbps)) {
// Sum adds up; any subsequent layers will be 0.
bitrates.resize(i + 1);
break;
}
}
return bitrates;
}