/* 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 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 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 profile; Maybe 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(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 mbs = ((CheckedInt(aWidth) + 15) / 16) * ((CheckedInt(aHeight) + 15) / 16); if (!mbs.isValid() || mbs.value() > c.mMaxMacroblocksPerFrame) { return false; } if (static_cast(mbs.value()) * aFramerate > static_cast(c.mMaxMacroblocksPerSecond)) { return false; } return true; } return false; } } // namespace mozilla