Bug 1244913 - resolution-based bitrates for each simulcast layer, scaleResolutionDownBy, and working maxBitrate in unicast. r=bwc,jesup

MozReview-Commit-ID: 347J1ElsOEx
This commit is contained in:
Jan-Ivar Bruaroey 2016-02-12 19:56:56 -05:00
parent ad805f4ccc
commit 9f731f009b
6 changed files with 93 additions and 75 deletions

View File

@ -1092,6 +1092,9 @@ RTCPeerConnection.prototype = {
var encodings = parameters.encodings || [];
encodings.reduce((uniqueRids, encoding) => {
if (encoding.scaleResolutionDownBy < 1.0) {
throw new this._win.RangeError("scaleResolutionDownBy must be >= 1.0");
}
if (!encoding.rid && encodings.length > 1) {
throw new this._win.DOMException("Missing rid", "TypeError");
}

View File

@ -23,7 +23,8 @@ public:
maxPps(0),
maxMbps(0),
maxCpb(0),
maxDpb(0)
maxDpb(0),
scaleDownBy(1.0)
{}
bool operator==(const EncodingConstraints& constraints) const
@ -40,6 +41,7 @@ public:
uint32_t maxMbps; // macroblocks per second
uint32_t maxCpb; // coded picture buffer size
uint32_t maxDpb; // decoded picture buffer size
double scaleDownBy; // To preserve resolution
};
} // namespace mozilla

View File

@ -682,6 +682,8 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
#endif
video_codec.qpMax = 56;
video_codec.numberOfSimulcastStreams = 1;
video_codec.simulcastStream[0].jsScaleDownBy =
codecConfig->mEncodingConstraints.scaleDownBy;
video_codec.mode = mCodecMode;
codecFound = true;
@ -979,6 +981,12 @@ WebrtcVideoConduit::ConfigureRecvMediaCodecs(
return kMediaConduitNoError;
}
template<typename T>
T MinIgnoreZero(const T& a, const T& b)
{
return std::min(a? a:b, b? b:a);
}
struct ResolutionAndBitrateLimits {
uint32_t resolution_in_mb;
uint16_t min_bitrate;
@ -986,7 +994,7 @@ struct ResolutionAndBitrateLimits {
uint16_t max_bitrate;
};
#define MB_OF(w,h) ((unsigned int)((((w)>>4))*((unsigned int)((h)>>4))))
#define MB_OF(w,h) ((unsigned int)((((w+15)>>4))*((unsigned int)((h+15)>>4))))
// For now, try to set the max rates well above the knee in the curve.
// Chosen somewhat arbitrarily; it's hard to find good data oriented for
@ -1007,24 +1015,25 @@ static ResolutionAndBitrateLimits kResolutionAndBitrateLimits[] = {
void
WebrtcVideoConduit::SelectBitrates(unsigned short width,
unsigned short height,
unsigned int cap,
mozilla::Atomic<int32_t, mozilla::Relaxed>& aLastFramerateTenths,
unsigned int& out_start,
unsigned int& out_min,
unsigned int& out_start,
unsigned int& out_max)
{
// max bandwidth should be proportional (not linearly!) to resolution, and
// proportional (perhaps linearly, or close) to current frame rate.
unsigned int fs, mb_width, mb_height;
mb_width = (width + 15) >> 4;
mb_height = (height + 15) >> 4;
fs = mb_width * mb_height;
unsigned int fs = MB_OF(width, height);
for (ResolutionAndBitrateLimits resAndLimits : kResolutionAndBitrateLimits) {
if (fs > resAndLimits.resolution_in_mb) {
out_min = resAndLimits.min_bitrate;
out_start = resAndLimits.start_bitrate;
out_max = resAndLimits.max_bitrate;
if (fs > resAndLimits.resolution_in_mb &&
// pick the highest range where at least start rate is within cap
// (or if we're at the end of the array).
(!cap || resAndLimits.start_bitrate <= cap ||
resAndLimits.resolution_in_mb == 0)) {
out_min = MinIgnoreZero((unsigned int)resAndLimits.min_bitrate, cap);
out_start = MinIgnoreZero((unsigned int)resAndLimits.start_bitrate, cap);
out_max = MinIgnoreZero((unsigned int)resAndLimits.max_bitrate, cap);
break;
}
}
@ -1069,19 +1078,16 @@ static void ConstrainPreservingAspectRatioExact(uint32_t max_fs,
unsigned short* width,
unsigned short* height)
{
unsigned int mb_width = (*width + 15) >> 4;
unsigned int mb_height = (*height + 15) >> 4;
// We could try to pick a better starting divisor, but it won't make any real
// performance difference.
for (size_t d = 1; d < std::min(mb_width, mb_height); ++d) {
if ((mb_width % d) || (mb_height % d)) {
for (size_t d = 1; d < std::min(*width, *height); ++d) {
if ((*width % d) || (*height % d)) {
continue; // Not divisible
}
if ((mb_width*mb_height)/(d*d) <= max_fs) {
*width = 16 * mb_width / d;
*height = 16 * mb_height / d;
if (((*width) * (*height))/(d*d) <= max_fs) {
*width /= d;
*height /= d;
return;
}
}
@ -1278,53 +1284,54 @@ WebrtcVideoConduit::ReconfigureSendCodec(unsigned short width,
vie_codec.width = width;
vie_codec.height = height;
vie_codec.maxFramerate = mSendingFramerate;
SelectBitrates(vie_codec.width, vie_codec.height,
SelectBitrates(vie_codec.width, vie_codec.height, 0,
mLastFramerateTenths,
vie_codec.startBitrate,
vie_codec.minBitrate,
vie_codec.startBitrate,
vie_codec.maxBitrate);
// TODO: If/when we begin supporting width/height constraints on simulcast
// streams, for each such constraint we will need to choose a resolution
// that is the same aspect ratio as all other streams. This requires us to
// store the original constraints somewhere.
for (size_t i = vie_codec.numberOfSimulcastStreams; i > 0; --i) {
webrtc::SimulcastStream& stream(vie_codec.simulcastStream[i - 1]);
if (stream.maxBitrate && (stream.maxBitrate < vie_codec.minBitrate)) {
// This stream cannot do full resolution with good quality. Need to
// scale down.
stream.width = 0;
stream.height = 0;
uint32_t max_fs_in_mb = kResolutionAndBitrateLimits[0].resolution_in_mb;
for (ResolutionAndBitrateLimits resAndLimits :
kResolutionAndBitrateLimits) {
if (resAndLimits.min_bitrate < stream.maxBitrate) {
// Use the resolution from the _previous_ entry
unsigned short adjusted_width = width;
unsigned short adjusted_height = height;
// webrtc.org won't tolerate simulcast unless every stream is
// exactly the same aspect ratio
ConstrainPreservingAspectRatioExact(max_fs_in_mb,
&adjusted_width,
&adjusted_height);
stream.width = adjusted_width;
stream.height = adjusted_height;
break;
}
max_fs_in_mb = resAndLimits.resolution_in_mb;
stream.width = width;
stream.height = height;
MOZ_ASSERT(stream.jsScaleDownBy >= 1.0);
uint32_t new_width = uint32_t(width / stream.jsScaleDownBy);
uint32_t new_height = uint32_t(height / stream.jsScaleDownBy);
// TODO: If two layers are similar, only alloc bits to one (Bug 1249859)
if (new_width != width || new_height != height) {
if (vie_codec.numberOfSimulcastStreams == 1) {
// Use less strict scaling in unicast. That way 320x240 / 3 = 106x79.
ConstrainPreservingAspectRatio(new_width, new_height,
&stream.width, &stream.height);
} else {
// webrtc.org supposedly won't tolerate simulcast unless every stream
// is exactly the same aspect ratio. 320x240 / 3 = 80x60.
ConstrainPreservingAspectRatioExact(new_width*new_height,
&stream.width, &stream.height);
}
} else {
stream.width = width;
stream.height = height;
}
// webrtc.org also gets upset if the last simulcast stream has a
// different resolution than the vie_codec
// Give each layer default appropriate bandwidth limits based on the
// resolution/framerate of that layer
SelectBitrates(stream.width, stream.height, stream.jsMaxBitrate,
mLastFramerateTenths,
stream.minBitrate,
stream.targetBitrate,
stream.maxBitrate);
vie_codec.minBitrate = std::min(stream.minBitrate, vie_codec.minBitrate);
vie_codec.startBitrate += stream.targetBitrate;
vie_codec.maxBitrate = std::max(stream.maxBitrate, vie_codec.maxBitrate);
// webrtc.org expects the last, highest fidelity, simulcast stream to
// always have the same resolution as vie_codec
if (i == vie_codec.numberOfSimulcastStreams) {
vie_codec.width = stream.width;
vie_codec.height = stream.height;
}
}
if (vie_codec.numberOfSimulcastStreams != 0) {
vie_codec.startBitrate /= vie_codec.numberOfSimulcastStreams;
}
if ((err = mPtrViECodec->SetSendCodec(mChannel, vie_codec)) != 0)
{
CSFLogError(logTag, "%s: SetSendCodec(%ux%u) failed, err %d",
@ -1787,18 +1794,6 @@ WebrtcVideoConduit::DeliverI420Frame(const webrtc::I420VideoFrame& webrtc_frame)
return -1;
}
template<typename T>
T MinIgnoreZero(const T& a, const T& b)
{
if (!a) {
return b;
} else if (!b) {
return a;
} else {
return std::min(a, b);
}
}
/**
* Copy the codec passed into Conduit's database
*/
@ -1895,14 +1890,24 @@ WebrtcVideoConduit::CodecConfigToWebRTCCodec(const VideoCodecConfig* codecInfo,
stream.height,
(unsigned short)encoding.constraints.maxHeight);
if (encoding.constraints.maxBr) {
// webrtc.org uses kbps, we use bps
stream.maxBitrate = encoding.constraints.maxBr/1000;
stream.minBitrate = MinIgnoreZero(stream.minBitrate, stream.maxBitrate);
stream.targetBitrate = MinIgnoreZero(stream.targetBitrate,
stream.maxBitrate);
}
// webrtc.org uses kbps, we use bps
stream.jsMaxBitrate = encoding.constraints.maxBr/1000;
stream.jsScaleDownBy = encoding.constraints.scaleDownBy;
MOZ_ASSERT(stream.jsScaleDownBy >= 1.0);
uint32_t width = stream.width? stream.width : 640;
uint32_t height = stream.height? stream.height : 480;
uint32_t new_width = uint32_t(width / stream.jsScaleDownBy);
uint32_t new_height = uint32_t(height / stream.jsScaleDownBy);
if (new_width != width || new_height != height) {
// Estimate. Overridden on first frame.
SelectBitrates(new_width, new_height, stream.jsMaxBitrate,
mLastFramerateTenths,
stream.minBitrate,
stream.targetBitrate,
stream.maxBitrate);
}
// webrtc.org expects simulcast streams to be ordered by increasing
// fidelity, our jsep code does the opposite.
cinst.simulcastStream[codecInfo->mSimulcastEncodings.size()-i-1] = stream;

View File

@ -141,14 +141,16 @@ public:
/**
* Function to set the encoding bitrate limits based on incoming frame size and rate
* @param width, height: dimensions of the frame
* @param cap: user-enforced max bitrate, or 0
* @param aLastFramerateTenths: holds the current input framerate
* @param out_start, out_min, out_max: bitrate results
*/
void SelectBitrates(unsigned short width,
unsigned short height,
unsigned int cap,
mozilla::Atomic<int32_t, mozilla::Relaxed>& aLastFramerateTenths,
unsigned int& out_start,
unsigned int& out_min,
unsigned int& out_start,
unsigned int& out_max);
/**

View File

@ -2415,6 +2415,7 @@ PeerConnectionImpl::SetParameters(MediaStreamTrack& aTrack,
if (encoding.mMaxBitrate.WasPassed()) {
constraint.constraints.maxBr = encoding.mMaxBitrate.Value();
}
constraint.constraints.scaleDownBy = encoding.mScaleResolutionDownBy;
constraints.push_back(constraint);
}
}
@ -2449,6 +2450,7 @@ PeerConnectionImpl::GetParameters(MediaStreamTrack& aTrack,
RTCRtpEncodingParameters encoding;
encoding.mRid.Construct(NS_ConvertASCIItoUTF16(constraint.rid.c_str()));
encoding.mMaxBitrate.Construct(constraint.constraints.maxBr);
encoding.mScaleResolutionDownBy = constraint.constraints.scaleDownBy;
aOutParameters.mEncodings.Value().AppendElement(Move(encoding), fallible);
}
return NS_OK;

View File

@ -690,6 +690,8 @@ struct SimulcastStream {
unsigned int minBitrate; // kilobits/sec.
unsigned int qpMax; // minimum quality
char rid[kRIDSize];
unsigned int jsMaxBitrate; // user-controlled max bitrate
double jsScaleDownBy; // user-controlled downscale
bool operator==(const SimulcastStream& other) const {
return width == other.width &&
@ -699,7 +701,9 @@ struct SimulcastStream {
targetBitrate == other.targetBitrate &&
minBitrate == other.minBitrate &&
qpMax == other.qpMax &&
strcmp(rid, other.rid) == 0;
strcmp(rid, other.rid) == 0 &&
jsMaxBitrate == other.jsMaxBitrate &&
jsScaleDownBy == other.jsScaleDownBy;
}
bool operator!=(const SimulcastStream& other) const {