Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "H264FmtpParser.h"
#include "api/video_codecs/h264_profile_level_id.h"
#include "mozilla/CheckedInt.h"
#include "nsContentTypeParser.h"
namespace mozilla {
static Maybe<H264_PROFILE> MapWebrtcProfile(webrtc::H264Profile aProfile) {
switch (aProfile) {
case webrtc::H264Profile::kProfileConstrainedBaseline:
case webrtc::H264Profile::kProfileBaseline:
// Constrained Baseline to Baseline here (bug 2040726).
return Some(H264_PROFILE::H264_PROFILE_BASE);
case webrtc::H264Profile::kProfileMain:
return Some(H264_PROFILE::H264_PROFILE_MAIN);
case webrtc::H264Profile::kProfileConstrainedHigh:
case webrtc::H264Profile::kProfileHigh:
return Some(H264_PROFILE::H264_PROFILE_HIGH);
case webrtc::H264Profile::kProfilePredictiveHigh444:
return Nothing();
}
return Nothing();
}
static Maybe<H264_LEVEL> MapWebrtcLevel(webrtc::H264Level aLevel) {
switch (aLevel) {
case webrtc::H264Level::kLevel1_b:
return Some(H264_LEVEL::H264_LEVEL_1_b);
case webrtc::H264Level::kLevel1:
return Some(H264_LEVEL::H264_LEVEL_1);
case webrtc::H264Level::kLevel1_1:
return Some(H264_LEVEL::H264_LEVEL_1_1);
case webrtc::H264Level::kLevel1_2:
return Some(H264_LEVEL::H264_LEVEL_1_2);
case webrtc::H264Level::kLevel1_3:
return Some(H264_LEVEL::H264_LEVEL_1_3);
case webrtc::H264Level::kLevel2:
return Some(H264_LEVEL::H264_LEVEL_2);
case webrtc::H264Level::kLevel2_1:
return Some(H264_LEVEL::H264_LEVEL_2_1);
case webrtc::H264Level::kLevel2_2:
return Some(H264_LEVEL::H264_LEVEL_2_2);
case webrtc::H264Level::kLevel3:
return Some(H264_LEVEL::H264_LEVEL_3);
case webrtc::H264Level::kLevel3_1:
return Some(H264_LEVEL::H264_LEVEL_3_1);
case webrtc::H264Level::kLevel3_2:
return Some(H264_LEVEL::H264_LEVEL_3_2);
case webrtc::H264Level::kLevel4:
return Some(H264_LEVEL::H264_LEVEL_4);
case webrtc::H264Level::kLevel4_1:
return Some(H264_LEVEL::H264_LEVEL_4_1);
case webrtc::H264Level::kLevel4_2:
return Some(H264_LEVEL::H264_LEVEL_4_2);
case webrtc::H264Level::kLevel5:
return Some(H264_LEVEL::H264_LEVEL_5);
case webrtc::H264Level::kLevel5_1:
return Some(H264_LEVEL::H264_LEVEL_5_1);
case webrtc::H264Level::kLevel5_2:
return Some(H264_LEVEL::H264_LEVEL_5_2);
}
return Nothing();
}
H264FmtpParams ParseH264Fmtp(const nsACString& aMimeString) {
H264FmtpParams out;
nsContentTypeParser parser((NS_ConvertUTF8toUTF16(aMimeString)));
nsAutoString profileLevelId;
if (NS_SUCCEEDED(parser.GetParameter("profile-level-id", profileLevelId))) {
NS_ConvertUTF16toUTF8 narrow(profileLevelId);
auto parsed = webrtc::ParseH264ProfileLevelId(narrow.get());
Maybe<H264_PROFILE> profile;
Maybe<H264_LEVEL> level;
if (parsed) {
profile = MapWebrtcProfile(parsed->profile);
level = MapWebrtcLevel(parsed->level);
}
if (profile && level) {
out.mProfileLevel = H264ProfileLevel{*profile, *level};
} else {
out.mProfileLevel = Err(H264FmtpParseError::Invalid);
}
}
nsAutoString packetizationMode;
if (NS_SUCCEEDED(
parser.GetParameter("packetization-mode", packetizationMode))) {
nsresult rv;
int32_t mode = packetizationMode.ToInteger(&rv);
if (NS_SUCCEEDED(rv) && mode >= 0 && mode <= 2) {
out.mPacketizationMode = static_cast<uint32_t>(mode);
} else {
out.mPacketizationMode = Err(H264FmtpParseError::Invalid);
}
}
return out;
}
bool H264LevelFits(H264_LEVEL aLevel, uint32_t aWidth, uint32_t aHeight,
double aFramerate) {
struct H264LevelConstraint {
H264_LEVEL mLevel;
uint32_t mMaxMacroblocksPerFrame;
uint32_t mMaxMacroblocksPerSecond;
};
// H.264 Annex A Table A-1. Mirrors libwebrtc's kLevelConstraints.
static constexpr H264LevelConstraint kH264LevelConstraints[] = {
{H264_LEVEL::H264_LEVEL_1, 99, 1485},
{H264_LEVEL::H264_LEVEL_1_b, 99, 1485},
{H264_LEVEL::H264_LEVEL_1_1, 396, 3000},
{H264_LEVEL::H264_LEVEL_1_2, 396, 6000},
{H264_LEVEL::H264_LEVEL_1_3, 396, 11880},
{H264_LEVEL::H264_LEVEL_2, 396, 11880},
{H264_LEVEL::H264_LEVEL_2_1, 792, 19800},
{H264_LEVEL::H264_LEVEL_2_2, 1620, 20250},
{H264_LEVEL::H264_LEVEL_3, 1620, 40500},
{H264_LEVEL::H264_LEVEL_3_1, 3600, 108000},
{H264_LEVEL::H264_LEVEL_3_2, 5120, 216000},
{H264_LEVEL::H264_LEVEL_4, 8192, 245760},
{H264_LEVEL::H264_LEVEL_4_1, 8192, 245760},
{H264_LEVEL::H264_LEVEL_4_2, 8704, 522240},
{H264_LEVEL::H264_LEVEL_5, 22080, 589824},
{H264_LEVEL::H264_LEVEL_5_1, 36864, 983040},
{H264_LEVEL::H264_LEVEL_5_2, 36864, 2073600},
};
for (const auto& c : kH264LevelConstraints) {
if (c.mLevel != aLevel) {
continue;
}
// Calculate macroblocks per frame
const CheckedInt<uint32_t> mbs =
((CheckedInt<uint32_t>(aWidth) + 15) / 16) *
((CheckedInt<uint32_t>(aHeight) + 15) / 16);
if (!mbs.isValid() || mbs.value() > c.mMaxMacroblocksPerFrame) {
return false;
}
if (static_cast<double>(mbs.value()) * aFramerate >
static_cast<double>(c.mMaxMacroblocksPerSecond)) {
return false;
}
return true;
}
return false;
}
} // namespace mozilla