Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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
#include "MediaMIMETypes.h"
#include "gtest/gtest.h"
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/media/webrtc/CodecInfo.h"
using namespace mozilla;
using mozilla::WebrtcCodecInfo;
constexpr const char* kVideoTypes[] = {
"video/av1",
"video/h264",
"video/vp8",
"video/vp9",
};
constexpr const char* kAudioTypes[] = {
"audio/g722",
"audio/opus",
"audio/pcma",
"audio/pcmu",
};
constexpr const char* kNonCanonicalMimeTypes[] = {
"video/avc",
"video/avc1",
"video/avc3",
};
class MOZ_RAII ScopedPrefSetter {
public:
ScopedPrefSetter(const char* aPrefName, bool aValue)
: mPrefName(aPrefName),
mOriginalValue(Preferences::GetBool(aPrefName, false)) {
MOZ_ASSERT(NS_IsMainThread());
Preferences::SetBool(mPrefName, aValue);
}
~ScopedPrefSetter() {
MOZ_ASSERT(NS_IsMainThread());
Preferences::SetBool(mPrefName, mOriginalValue);
}
ScopedPrefSetter(const ScopedPrefSetter&) = delete;
ScopedPrefSetter& operator=(const ScopedPrefSetter&) = delete;
ScopedPrefSetter(ScopedPrefSetter&&) = delete;
ScopedPrefSetter& operator=(ScopedPrefSetter&&) = delete;
private:
const char* mPrefName;
const bool mOriginalValue;
};
class WebRTCCodecInfoTest : public testing::Test {
protected:
void SetUp() override {
// Ensure gfxVars is initialized before tests run.
MOZ_ASSERT(NS_IsMainThread());
if (!gfx::gfxVars::IsInitialized()) {
gfx::gfxVars::Initialize();
}
}
// Returns false if the MIME string is unparseable or unsupported.
static bool SupportsSWEncode(const WebrtcCodecInfo& aInfo,
const char* aMime) {
Maybe<MediaExtendedMIMEType> mime = MakeMediaExtendedMIMEType(aMime);
return mime && aInfo.CheckEncodeType(*mime) &&
(!mime->Type().HasVideoMajorType() ||
SupportsVideoMimeEncodeForWebrtc(*mime).contains(
media::EncodeSupport::SoftwareEncode));
}
static bool SupportsSWDecode(const WebrtcCodecInfo& aInfo,
const char* aMime) {
Maybe<MediaExtendedMIMEType> mime = MakeMediaExtendedMIMEType(aMime);
return mime && aInfo.CheckDecodeType(*mime) &&
(!mime->Type().HasVideoMajorType() ||
SupportsVideoMimeDecodeForWebrtc(*mime).contains(
media::DecodeSupport::SoftwareDecode));
}
static bool SupportsHWEncode(const WebrtcCodecInfo& aInfo,
const char* aMime) {
Maybe<MediaExtendedMIMEType> mime = MakeMediaExtendedMIMEType(aMime);
return mime && aInfo.CheckEncodeType(*mime) &&
mime->Type().HasVideoMajorType() &&
SupportsVideoMimeEncodeForWebrtc(*mime).contains(
media::EncodeSupport::HardwareEncode);
}
static bool SupportsHWDecode(const WebrtcCodecInfo& aInfo,
const char* aMime) {
Maybe<MediaExtendedMIMEType> mime = MakeMediaExtendedMIMEType(aMime);
return mime && aInfo.CheckDecodeType(*mime) &&
mime->Type().HasVideoMajorType() &&
SupportsVideoMimeDecodeForWebrtc(*mime).contains(
media::DecodeSupport::HardwareDecode);
}
// Helper function used to verify audio encode/decode is still working for use
// in the video pref tests.
static void TestAudioDecodeEncodeSWHW(const WebrtcCodecInfo* aCodecInfo) {
for (const auto& type : kAudioTypes) {
EXPECT_TRUE(SupportsSWDecode(*aCodecInfo, type))
<< "Type failed: " << type;
EXPECT_TRUE(SupportsSWEncode(*aCodecInfo, type))
<< "Type failed: " << type;
EXPECT_FALSE(SupportsHWDecode(*aCodecInfo, type))
<< "Type failed: " << type;
EXPECT_FALSE(SupportsHWEncode(*aCodecInfo, type))
<< "Type failed: " << type;
}
}
};
// Test that invalid MIME types return false for all support queries.
TEST_F(WebRTCCodecInfoTest, InvalidMimeType) {
const auto codecInfo = WebrtcCodecInfo::Create();
EXPECT_FALSE(SupportsSWDecode(*codecInfo, ""));
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "foobar"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video/fake"));
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "/"));
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "/fake"));
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "fake/"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video fake"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video/no; fake"));
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "\xe6\xbc\xa2\xe5\xad\x97"));
}
// Test that RTP supplementary codecs are filtered as per WPTs. See:
TEST_F(WebRTCCodecInfoTest, FilterRTPSupplementaryCodecs) {
const auto codecInfo = WebrtcCodecInfo::Create();
// RTX - Retransmission (RFC 4588)
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "video/rtx"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video/rtx"));
// RED - Redundant encoding (RFC 2198)
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "video/red"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video/red"));
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "audio/red"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "audio/red"));
// ULPFEC - Forward error correction (RFC 5109)
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "video/ulpfec"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video/ulpfec"));
// FlexFEC - Flexible forward error correction
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "video/flexfec-03"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "video/flexfec-03"));
// Telephone-event - DTMF tones (RFC 4733)
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "audio/telephone-event"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "audio/telephone-event"));
// CN - Comfort Noise
EXPECT_FALSE(SupportsSWEncode(*codecInfo, "audio/CN"));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, "audio/CN"));
}
// Test that audio MIME types are rejected when querying video support
// and vice versa.
TEST_F(WebRTCCodecInfoTest, AudioVideoMismatch) {
const auto codecInfo = WebrtcCodecInfo::Create();
const auto swapMajorType = [](const char* aMime) -> std::string {
std::string s(aMime);
if (s.starts_with("video/")) {
s.replace(0, 5, "audio");
} else if (s.starts_with("audio/")) {
s.replace(0, 5, "video");
}
return s;
};
for (const auto& type : kVideoTypes) {
const auto swapped = swapMajorType(type);
EXPECT_FALSE(SupportsSWDecode(*codecInfo, swapped.c_str()))
<< "Type failed: " << swapped;
EXPECT_FALSE(SupportsSWEncode(*codecInfo, swapped.c_str()))
<< "Type failed: " << swapped;
}
for (const auto& type : kAudioTypes) {
const auto swapped = swapMajorType(type);
EXPECT_FALSE(SupportsSWDecode(*codecInfo, swapped.c_str()))
<< "Type failed: " << swapped;
EXPECT_FALSE(SupportsSWEncode(*codecInfo, swapped.c_str()))
<< "Type failed: " << swapped;
}
}
// Test that common WebRTC video codecs are supported (software)
TEST_F(WebRTCCodecInfoTest, CommonVideoCodecsSupported) {
const auto codecInfo = WebrtcCodecInfo::Create();
for (const auto& type : kVideoTypes) {
EXPECT_TRUE(SupportsSWDecode(*codecInfo, type)) << "Type failed: " << type;
EXPECT_TRUE(SupportsSWEncode(*codecInfo, type)) << "Type failed: " << type;
}
}
// Test that common WebRTC audio codecs are supported.
TEST_F(WebRTCCodecInfoTest, CommonAudioCodecsSupported) {
const auto codecInfo = WebrtcCodecInfo::Create();
TestAudioDecodeEncodeSWHW(codecInfo.get());
}
// Test that non-canonical WebRTC H264 MIME types are rejected.
TEST_F(WebRTCCodecInfoTest, H264NonCanonicalMimeTypes) {
const auto codecInfo = WebrtcCodecInfo::Create();
for (const auto& type : kNonCanonicalMimeTypes) {
EXPECT_FALSE(SupportsSWEncode(*codecInfo, type));
EXPECT_FALSE(SupportsSWDecode(*codecInfo, type));
}
}
// Test case insensitivity of MIME type parsing.
TEST_F(WebRTCCodecInfoTest, CaseInsensitiveMimeTypes) {
const auto codecInfo = WebrtcCodecInfo::Create();
for (const auto& type : kVideoTypes) {
nsAutoCString upper(type);
ToUpperCase(upper);
EXPECT_TRUE(SupportsSWEncode(*codecInfo, upper.get()))
<< "Type failed: " << upper.get();
EXPECT_TRUE(SupportsSWDecode(*codecInfo, upper.get()))
<< "Type failed: " << upper.get();
}
for (const auto& type : kAudioTypes) {
nsAutoCString upper(type);
ToUpperCase(upper);
EXPECT_TRUE(SupportsSWEncode(*codecInfo, upper.get()))
<< "Type failed: " << upper.get();
EXPECT_TRUE(SupportsSWDecode(*codecInfo, upper.get()))
<< "Type failed: " << upper.get();
}
}
// Test that H.264-specific WebRTC pref blocks HW encode/decode if false
TEST_F(WebRTCCodecInfoTest, H264HWBlockedByWebRTCPref) {
const ScopedPrefSetter h264Pref("media.webrtc.hw.h264.enabled", false);
const auto codecInfo = WebrtcCodecInfo::Create();
EXPECT_FALSE(SupportsHWDecode(*codecInfo, "video/h264"));
EXPECT_FALSE(SupportsHWEncode(*codecInfo, "video/h264"));
// SW should still work
EXPECT_TRUE(SupportsSWDecode(*codecInfo, "video/h264"));
EXPECT_TRUE(SupportsSWEncode(*codecInfo, "video/h264"));
// Audio shouldn't be affected
TestAudioDecodeEncodeSWHW(codecInfo.get());
}
// Test that AV1 WebRTC pref disables AV1 support if false
TEST_F(WebRTCCodecInfoTest, AV1BlockedByWebRTCPref) {
const ScopedPrefSetter av1Pref("media.webrtc.codec.video.av1.enabled", false);
const auto codecInfo = WebrtcCodecInfo::Create();
for (const auto& type : kVideoTypes) {
if (strcmp(type, "video/av1") == 0) {
EXPECT_FALSE(SupportsSWDecode(*codecInfo, type)) << "Type: " << type;
EXPECT_FALSE(SupportsSWEncode(*codecInfo, type)) << "Type: " << type;
} else {
EXPECT_TRUE(SupportsSWDecode(*codecInfo, type)) << "Type: " << type;
EXPECT_TRUE(SupportsSWEncode(*codecInfo, type)) << "Type: " << type;
}
}
// Audio shouldn't be affected
TestAudioDecodeEncodeSWHW(codecInfo.get());
}
// Test that VP9 WebRTC pref disables VP9 support if false
TEST_F(WebRTCCodecInfoTest, VP9BlockedByWebRTCPref) {
const ScopedPrefSetter vp9Pref("media.peerconnection.video.vp9_enabled",
false);
const auto codecInfo = WebrtcCodecInfo::Create();
for (const auto& type : kVideoTypes) {
if (strcmp(type, "video/vp9") == 0) {
EXPECT_FALSE(SupportsSWDecode(*codecInfo, type)) << "Type: " << type;
EXPECT_FALSE(SupportsSWEncode(*codecInfo, type)) << "Type: " << type;
} else {
EXPECT_TRUE(SupportsSWDecode(*codecInfo, type)) << "Type: " << type;
EXPECT_TRUE(SupportsSWEncode(*codecInfo, type)) << "Type: " << type;
}
}
// Audio shouldn't be affected
TestAudioDecodeEncodeSWHW(codecInfo.get());
}
// Test that the H.264 baseline disable pref does not affect non-baseline H264.
// Note: baseline profiles specifically cannot be verified here because
TEST_F(WebRTCCodecInfoTest, H264BaselineBlockedByWebRTCPref) {
const ScopedPrefSetter baselinePref(
"media.navigator.video.disable_h264_baseline", true);
const auto codecInfo = WebrtcCodecInfo::Create();
// Non-baseline H264 should still be supported
EXPECT_TRUE(SupportsSWDecode(*codecInfo, "video/h264"));
EXPECT_TRUE(SupportsSWEncode(*codecInfo, "video/h264"));
// Audio shouldn't be affected
TestAudioDecodeEncodeSWHW(codecInfo.get());
}