Negotiate H264 profiles in SDP

This CL will start to distinguish H264 profiles during SDP negotiation.
We currently don't look at the H264 profile at all and assume they are
all Constrained Baseline Level 3.1. This CL will start to check profiles
for equality when matching, and will generate the correct answer H264
level.

Each local supported H264 profile needs to be listed explicitly in the
list of local supported codecs, even if they are redundant. For example,
Baseline profile should be listed explicitly even though another profile
that is a superset of Baseline is also listed. The reason for this is to
simplify the code and avoid profile intersection during matching. So
VideoCodec::Matches will check for profile equality, and not check if
one codec is a subset of the other. This also leads to the nice property
that VideoCodec::Matches is symmetric, i.e. iif a.Matches(b) then
b.Matches(a).

BUG=webrtc:6337
TBR=tkchin@webrtc.org

Review-Url: https://codereview.webrtc.org/2483173002
Cr-Commit-Position: refs/heads/master@{#15051}
This commit is contained in:
magjed 2016-11-12 09:53:04 -08:00 committed by Commit bot
parent 13ceeeadfc
commit f823ededce
11 changed files with 71 additions and 20 deletions

View File

@ -1355,7 +1355,7 @@ webrtc::VideoEncoder* MediaCodecVideoEncoderFactory::CreateVideoEncoder(
ALOGW << "No HW video encoder for codec " << codec.name;
return nullptr;
}
if (IsCodecSupported(supported_codecs_, codec)) {
if (FindMatchingCodec(supported_codecs_, codec)) {
ALOGD << "Create HW video encoder for " << codec.name;
const VideoCodecType type = cricket::CodecTypeFromName(codec.name);
return new MediaCodecVideoEncoder(AttachCurrentThreadIfNeeded(), type,

View File

@ -263,6 +263,14 @@ void GenerateProfileLevelIdForAnswer(
const CodecParameterMap& local_supported_params,
const CodecParameterMap& remote_offered_params,
CodecParameterMap* answer_params) {
// If both local and remote haven't set profile-level-id, they are both using
// the default profile. In this case, don't set profile-level-id in answer
// either.
if (!local_supported_params.count(kProfileLevelId) &&
!remote_offered_params.count(kProfileLevelId)) {
return;
}
// Parse profile-level-ids.
const rtc::Optional<ProfileLevelId> local_profile_level_id =
ParseSdpProfileLevelId(local_supported_params);

View File

@ -152,7 +152,7 @@ TEST(H264ProfileLevelId, TestGenerateProfileLevelIdForAnswerEmpty) {
CodecParameterMap answer_params;
GenerateProfileLevelIdForAnswer(CodecParameterMap(), CodecParameterMap(),
&answer_params);
EXPECT_EQ("42e01f", answer_params["profile-level-id"]);
EXPECT_TRUE(answer_params.empty());
}
TEST(H264ProfileLevelId,

View File

@ -17,10 +17,20 @@
#include "webrtc/base/logging.h"
#include "webrtc/base/stringencode.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/common_video/h264/profile_level_id.h"
namespace cricket {
const int kMaxPayloadId = 127;
static bool IsSameH264Profile(const CodecParameterMap& params1,
const CodecParameterMap& params2) {
const rtc::Optional<webrtc::H264::ProfileLevelId> profile_level_id =
webrtc::H264::ParseSdpProfileLevelId(params1);
const rtc::Optional<webrtc::H264::ProfileLevelId> other_profile_level_id =
webrtc::H264::ParseSdpProfileLevelId(params2);
// Compare H264 profiles, but not levels.
return profile_level_id && other_profile_level_id &&
profile_level_id->profile == other_profile_level_id->profile;
}
bool FeedbackParam::operator==(const FeedbackParam& other) const {
return _stricmp(other.id().c_str(), id().c_str()) == 0 &&
@ -218,6 +228,14 @@ bool VideoCodec::operator==(const VideoCodec& c) const {
return Codec::operator==(c);
}
bool VideoCodec::Matches(const VideoCodec& other) const {
if (!Codec::Matches(other))
return false;
if (CodecNamesEq(name.c_str(), kH264CodecName))
return IsSameH264Profile(params, other.params);
return true;
}
VideoCodec VideoCodec::CreateRtxCodec(int rtx_payload_type,
int associated_payload_type) {
VideoCodec rtx_codec(rtx_payload_type, kRtxCodecName);
@ -317,12 +335,19 @@ webrtc::VideoCodecType CodecTypeFromName(const std::string& name) {
return webrtc::kVideoCodecUnknown;
}
bool IsCodecSupported(const std::vector<VideoCodec>& supported_codecs,
const VideoCodec& codec) {
return std::any_of(supported_codecs.begin(), supported_codecs.end(),
[&codec](const VideoCodec& supported_codec) -> bool {
return CodecNamesEq(codec.name, supported_codec.name);
});
const VideoCodec* FindMatchingCodec(
const std::vector<VideoCodec>& supported_codecs,
const VideoCodec& codec) {
for (const VideoCodec& supported_codec : supported_codecs) {
if (!CodecNamesEq(codec.name, supported_codec.name))
continue;
if (CodecNamesEq(codec.name.c_str(), kH264CodecName) &&
!IsSameH264Profile(codec.params, supported_codec.params)) {
continue;
}
return &supported_codec;
}
return nullptr;
}
} // namespace cricket

View File

@ -24,8 +24,6 @@ namespace cricket {
typedef std::map<std::string, std::string> CodecParameterMap;
extern const int kMaxPayloadId;
class FeedbackParam {
public:
FeedbackParam(const std::string& id, const std::string& param)
@ -154,6 +152,11 @@ struct VideoCodec : public Codec {
VideoCodec(VideoCodec&& c);
virtual ~VideoCodec() = default;
// Indicates if this video codec is the same as the other video codec, e.g. if
// they are both VP8 or VP9, or if they are both H264 with the same H264
// profile. H264 levels however are not compared.
bool Matches(const VideoCodec& codec) const;
std::string ToString() const;
VideoCodec& operator=(const VideoCodec& c);
@ -213,8 +216,11 @@ webrtc::VideoCodecType CodecTypeFromName(const std::string& name);
bool HasNack(const Codec& codec);
bool HasRemb(const Codec& codec);
bool HasTransportCc(const Codec& codec);
bool IsCodecSupported(const std::vector<VideoCodec>& supported_codecs,
const VideoCodec& codec);
// Returns the first codec in |supported_codecs| that matches |codec|, or
// nullptr if no codec matches.
const VideoCodec* FindMatchingCodec(
const std::vector<VideoCodec>& supported_codecs,
const VideoCodec& codec);
} // namespace cricket

View File

@ -195,7 +195,7 @@ class FakeWebRtcVideoEncoderFactory : public WebRtcVideoEncoderFactory {
webrtc::VideoEncoder* CreateVideoEncoder(
const cricket::VideoCodec& codec) override {
rtc::CritScope lock(&crit_);
if (!IsCodecSupported(codecs_, codec))
if (!FindMatchingCodec(codecs_, codec))
return nullptr;
FakeWebRtcVideoEncoder* encoder = new FakeWebRtcVideoEncoder();
encoders_.push_back(encoder);

View File

@ -638,7 +638,7 @@ static std::vector<VideoCodec> GetSupportedCodecs(
out << ", ";
}
// Don't add internally-supported codecs twice.
if (IsCodecSupported(supported_codecs, codec))
if (FindMatchingCodec(supported_codecs, codec))
continue;
// External video encoders are given payloads 120-127. This also means that
@ -699,7 +699,12 @@ WebRtcVideoChannel2::SelectSendVideoCodec(
GetSupportedCodecs(external_encoder_factory_);
// Select the first remote codec that is supported locally.
for (const VideoCodecSettings& remote_mapped_codec : remote_mapped_codecs) {
if (IsCodecSupported(local_supported_codecs, remote_mapped_codec.codec))
// For H264, we will limit the encode level to the remote offered level
// regardless if level asymmetry is allowed or not. This is strictly not
// following the spec in https://tools.ietf.org/html/rfc6184#section-8.2.2
// since we should limit the encode level to the lower of local and remote
// level when level asymmetry is not allowed.
if (FindMatchingCodec(local_supported_codecs, remote_mapped_codec.codec))
return rtc::Optional<VideoCodecSettings>(remote_mapped_codec);
}
// No remote codec was supported.
@ -955,7 +960,7 @@ bool WebRtcVideoChannel2::GetChangedRecvParameters(
const std::vector<VideoCodec> local_supported_codecs =
GetSupportedCodecs(external_encoder_factory_);
for (const VideoCodecSettings& mapped_codec : mapped_codecs) {
if (!IsCodecSupported(local_supported_codecs, mapped_codec.codec)) {
if (!FindMatchingCodec(local_supported_codecs, mapped_codec.codec)) {
LOG(LS_ERROR) << "SetRecvParameters called with unsupported video codec: "
<< mapped_codec.codec.ToString();
return false;

View File

@ -2519,7 +2519,8 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectBadPayloadTypes) {
TEST_F(WebRtcVideoChannel2Test, SetSendCodecsAcceptAllValidPayloadTypes) {
cricket::VideoSendParameters parameters;
parameters.codecs.push_back(kVp8Codec);
for (int payload_type = 0; payload_type <= 127; ++payload_type) {
// Only the dynamic payload types are valid for video codecs.
for (int payload_type = 96; payload_type <= 127; ++payload_type) {
parameters.codecs[0].id = payload_type;
EXPECT_TRUE(channel_->SetSendParameters(parameters))
<< "Payload type '" << payload_type << "' rejected.";

View File

@ -1,6 +1,7 @@
include_rules = [
"+webrtc/api",
"+webrtc/base",
"+webrtc/common_video/h264",
"+webrtc/logging/rtc_event_log",
"+webrtc/media",
"+webrtc/p2p",

View File

@ -22,6 +22,7 @@
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/common_video/h264/profile_level_id.h"
#include "webrtc/media/base/cryptoparams.h"
#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/p2p/base/p2pconstants.h"
@ -778,6 +779,10 @@ static void NegotiateCodecs(const std::vector<C>& local_codecs,
RTC_DCHECK(apt_it != theirs.params.end());
negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
}
if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
webrtc::H264::GenerateProfileLevelIdForAnswer(
ours.params, theirs.params, &negotiated.params);
}
negotiated.id = theirs.id;
negotiated.name = theirs.name;
negotiated_codecs->push_back(std::move(negotiated));

View File

@ -47,7 +47,7 @@ VideoToolboxVideoEncoderFactory::~VideoToolboxVideoEncoderFactory() {}
VideoEncoder* VideoToolboxVideoEncoderFactory::CreateVideoEncoder(
const cricket::VideoCodec& codec) {
#if defined(WEBRTC_IOS)
if (IsCodecSupported(supported_codecs_, codec)) {
if (FindMatchingCodec(supported_codecs_, codec)) {
LOG(LS_INFO) << "Creating HW encoder for " << codec.name;
return new H264VideoToolboxEncoder();
}
@ -83,7 +83,7 @@ VideoDecoder* VideoToolboxVideoDecoderFactory::CreateVideoDecoder(
VideoCodecType type) {
const auto codec = cricket::VideoCodec(NameFromCodecType(type));
#if defined(WEBRTC_IOS)
if (IsCodecSupported(supported_codecs_, codec)) {
if (FindMatchingCodec(supported_codecs_, codec)) {
LOG(LS_INFO) << "Creating HW decoder for " << codec.name;
return new H264VideoToolboxDecoder();
}