sdp: parse and serialize non-key=value fmtp lines

some codecs like RED and telephone-event have fmtp lines which
do not conform to the list-of-key=value convention. Add support
for parsing and serializing this by setting the name to the empty
string.

BUG=webrtc:11640

Change-Id: Ie3ef7c98f756940f97d27a39af0574aa37949f74
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/178120
Commit-Queue: Taylor <deadbeef@webrtc.org>
Reviewed-by: Justin Uberti <juberti@webrtc.org>
Reviewed-by: Taylor <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31609}
This commit is contained in:
Philipp Hancke 2020-07-01 21:07:32 +02:00 committed by Commit Bot
parent 27398d6632
commit f2a4ec19d1
3 changed files with 52 additions and 15 deletions

View File

@ -67,6 +67,8 @@ struct RTC_EXPORT Codec {
int id;
std::string name;
int clockrate;
// Non key-value parameters such as the telephone-event "015" are
// represented using an empty string as key, i.e. {"": "0-15"}.
CodecParameterMap params;
FeedbackParams feedback_params;

View File

@ -1762,8 +1762,13 @@ void WriteRtcpFbHeader(int payload_type, rtc::StringBuilder* os) {
void WriteFmtpParameter(const std::string& parameter_name,
const std::string& parameter_value,
rtc::StringBuilder* os) {
// fmtp parameters: |parameter_name|=|parameter_value|
*os << parameter_name << kSdpDelimiterEqual << parameter_value;
if (parameter_name == "") {
// RFC 2198 and RFC 4733 don't use key-value pairs.
*os << parameter_value;
} else {
// fmtp parameters: |parameter_name|=|parameter_value|
*os << parameter_name << kSdpDelimiterEqual << parameter_value;
}
}
bool IsFmtpParam(const std::string& name) {
@ -3603,8 +3608,10 @@ bool ParseFmtpParam(const std::string& line,
std::string* value,
SdpParseError* error) {
if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, parameter, value)) {
ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
return false;
// Support for non-key-value lines like RFC 2198 or RFC 4733.
*parameter = "";
*value = line;
return true;
}
// a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
return true;
@ -3622,7 +3629,7 @@ bool ParseFmtpAttributes(const std::string& line,
std::string line_payload;
std::string line_params;
// RFC 5576
// https://tools.ietf.org/html/rfc4566#section-6
// a=fmtp:<format> <format specific parameters>
// At least two fields, whereas the second one is any of the optional
// parameters.
@ -3651,17 +3658,15 @@ bool ParseFmtpAttributes(const std::string& line,
cricket::CodecParameterMap codec_params;
for (auto& iter : fields) {
if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
// Only fmtps with equals are currently supported. Other fmtp types
// should be ignored. Unknown fmtps do not constitute an error.
continue;
}
std::string name;
std::string value;
if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
return false;
}
if (codec_params.find(name) != codec_params.end()) {
RTC_LOG(LS_INFO) << "Overwriting duplicate fmtp parameter with key \""
<< name << "\".";
}
codec_params[name] = value;
}

View File

@ -1293,8 +1293,7 @@ class WebRtcSdpTest : public ::testing::Test {
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32",
"dummy_session_params"));
audio->set_protocol(cricket::kMediaProtocolSavpf);
AudioCodec opus(111, "opus", 48000, 0, 2);
audio->AddCodec(opus);
audio->AddCodec(AudioCodec(111, "opus", 48000, 0, 2));
audio->AddCodec(AudioCodec(103, "ISAC", 16000, 0, 1));
audio->AddCodec(AudioCodec(104, "ISAC", 32000, 0, 1));
return audio;
@ -1934,13 +1933,14 @@ class WebRtcSdpTest : public ::testing::Test {
// description.
"a=msid-semantic: WMS\r\n"
// Pl type 111 preferred.
"m=audio 9 RTP/SAVPF 111 104 103\r\n"
"m=audio 9 RTP/SAVPF 111 104 103 105\r\n"
// Pltype 111 listed before 103 and 104 in the map.
"a=rtpmap:111 opus/48000/2\r\n"
// Pltype 103 listed before 104.
"a=rtpmap:103 ISAC/16000\r\n"
"a=rtpmap:104 ISAC/32000\r\n"
"a=fmtp:111 0-15,66,70\r\n"
"a=rtpmap:105 telephone-event/8000\r\n"
"a=fmtp:105 0-15,66,70\r\n"
"a=fmtp:111 ";
std::ostringstream os;
os << "minptime=" << params.min_ptime << "; stereo=" << params.stereo
@ -1987,6 +1987,14 @@ class WebRtcSdpTest : public ::testing::Test {
VerifyCodecParameter(codec.params, "maxptime", params.max_ptime);
}
cricket::AudioCodec dtmf = acd->codecs()[3];
EXPECT_EQ("telephone-event", dtmf.name);
EXPECT_EQ(105, dtmf.id);
EXPECT_EQ(3u,
dtmf.params.size()); // ptime and max_ptime count as parameters.
EXPECT_EQ(dtmf.params.begin()->first, "");
EXPECT_EQ(dtmf.params.begin()->second, "0-15,66,70");
const VideoContentDescription* vcd =
GetFirstVideoContentDescription(jdesc_output->description());
ASSERT_TRUE(vcd);
@ -3592,6 +3600,28 @@ TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithPTimeAndMaxPTime) {
EXPECT_EQ(sdp_with_fmtp, message);
}
TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithTelephoneEvent) {
AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
cricket::AudioCodecs codecs = acd->codecs();
cricket::AudioCodec dtmf(105, "telephone-event", 8000, 0, 1);
dtmf.params[""] = "0-15";
codecs.push_back(dtmf);
acd->set_codecs(codecs);
ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
jdesc_.session_version()));
std::string message = webrtc::SdpSerialize(jdesc_);
std::string sdp_with_fmtp = kSdpFullString;
InjectAfter("m=audio 2345 RTP/SAVPF 111 103 104", " 105", &sdp_with_fmtp);
InjectAfter(
"a=rtpmap:104 ISAC/32000\r\n",
"a=rtpmap:105 telephone-event/8000\r\n" // No comma here. String merging!
"a=fmtp:105 0-15\r\n",
&sdp_with_fmtp);
EXPECT_EQ(sdp_with_fmtp, message);
}
TEST_F(WebRtcSdpTest, SerializeVideoFmtp) {
VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);