Add VP9 profile negotiation to SDP

This CL adds VP9 profile information in SDP. It adds the necessary fields and
enums to codec containers.

Additional profiles will be followed.

Bug: webrtc:9376
Change-Id: I78574714f06f8087262a71dd64c01f31a229dd54
Reviewed-on: https://webrtc-review.googlesource.com/81960
Reviewed-by: Taylor (left Google) <deadbeef@webrtc.org>
Reviewed-by: Niklas Enbom <niklas.enbom@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Emircan Uysaler <emircan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23810}
This commit is contained in:
Emircan Uysaler 2018-06-28 10:59:02 -07:00 committed by Commit Bot
parent 0ea751539e
commit 98badbcd9f
14 changed files with 223 additions and 35 deletions

View File

@ -53,6 +53,20 @@ rtc_source_set("rtc_media_config") {
]
}
rtc_source_set("rtc_vp9_profile") {
sources = [
"base/vp9_profile.cc",
"base/vp9_profile.h",
]
deps = [
"..:webrtc_common",
"../api/video_codecs:video_codecs_api",
"../rtc_base:rtc_base_approved",
"//third_party/abseil-cpp/absl/types:optional",
]
}
rtc_static_library("rtc_media_base") {
visibility = [ "*" ]
defines = []
@ -109,6 +123,7 @@ rtc_static_library("rtc_media_base") {
deps += [
":rtc_h264_profile_id",
":rtc_media_config",
":rtc_vp9_profile",
"..:webrtc_common",
"../api:libjingle_peerconnection_api",
"../api/audio_codecs:audio_codecs_api",
@ -551,6 +566,7 @@ if (rtc_include_tests) {
":rtc_media",
":rtc_media_base",
":rtc_media_tests_utils",
":rtc_vp9_profile",
"../api:create_simulcast_test_fixture_api",
"../api:libjingle_peerconnection_api",
"../api:mock_video_codec_factory",

View File

@ -13,6 +13,7 @@
#include <algorithm>
#include "media/base/h264_profile_level_id.h"
#include "media/base/vp9_profile.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/stringencode.h"
@ -270,6 +271,8 @@ bool VideoCodec::Matches(const VideoCodec& other) const {
if (CodecNamesEq(name.c_str(), kH264CodecName))
return webrtc::H264::IsSameH264Profile(params, other.params) &&
IsSameH264PacketizationMode(params, other.params);
if (CodecNamesEq(name.c_str(), kVp9CodecName))
return webrtc::IsSameVP9Profile(params, other.params);
return true;
}
@ -386,9 +389,12 @@ bool IsSameCodec(const std::string& name1,
// If different names (case insensitive), then not same formats.
if (!CodecNamesEq(name1, name2))
return false;
// For every format besides H264, comparing names is enough.
return !CodecNamesEq(name1.c_str(), kH264CodecName) ||
webrtc::H264::IsSameH264Profile(params1, params2);
// For every format besides H264 and VP9, comparing names is enough.
if (CodecNamesEq(name1.c_str(), kH264CodecName))
return webrtc::H264::IsSameH264Profile(params1, params2);
if (CodecNamesEq(name1.c_str(), kVp9CodecName))
return webrtc::IsSameVP9Profile(params1, params2);
return true;
}
} // namespace cricket

View File

@ -9,6 +9,8 @@
*/
#include "media/base/codec.h"
#include "media/base/vp9_profile.h"
#include "rtc_base/gunit.h"
using cricket::AudioCodec;
@ -186,6 +188,36 @@ TEST(CodecTest, TestVideoCodecMatches) {
EXPECT_FALSE(c1.Matches(VideoCodec(95, "V")));
}
// VP9 codecs compare profile information.
TEST(CodecTest, TestVP9CodecMatches) {
const char kProfile0[] = "0";
const char kProfile2[] = "2";
VideoCodec c_no_profile(95, cricket::kVp9CodecName);
VideoCodec c_profile0(95, cricket::kVp9CodecName);
c_profile0.params[webrtc::kVP9FmtpProfileId] = kProfile0;
EXPECT_TRUE(c_profile0.Matches(c_no_profile));
{
VideoCodec c_profile0_eq(95, cricket::kVp9CodecName);
c_profile0_eq.params[webrtc::kVP9FmtpProfileId] = kProfile0;
EXPECT_TRUE(c_profile0.Matches(c_profile0_eq));
}
{
VideoCodec c_profile2(95, cricket::kVp9CodecName);
c_profile2.params[webrtc::kVP9FmtpProfileId] = kProfile2;
EXPECT_FALSE(c_profile0.Matches(c_profile2));
EXPECT_FALSE(c_no_profile.Matches(c_profile2));
}
{
VideoCodec c_no_profile_eq(95, cricket::kVp9CodecName);
EXPECT_TRUE(c_no_profile.Matches(c_no_profile_eq));
}
}
// Matching H264 codecs also need to have matching profile-level-id and
// packetization-mode.
TEST(CodecTest, TestH264CodecMatches) {

63
media/base/vp9_profile.cc Normal file
View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "media/base/vp9_profile.h"
#include "rtc_base/string_to_number.h"
namespace webrtc {
// Profile information for VP9 video.
const char kVP9FmtpProfileId[] = "x-google-profile-id";
std::string VP9ProfileToString(VP9Profile profile) {
switch (profile) {
case VP9Profile::kProfile0:
return "0";
case VP9Profile::kProfile2:
return "2";
}
return "0";
}
absl::optional<VP9Profile> StringToVP9Profile(const std::string& str) {
const absl::optional<int> i = rtc::StringToNumber<int>(str);
if (!i.has_value())
return absl::nullopt;
switch (i.value()) {
case 0:
return VP9Profile::kProfile0;
case 2:
return VP9Profile::kProfile2;
default:
return absl::nullopt;
}
return absl::nullopt;
}
absl::optional<VP9Profile> ParseSdpForVP9Profile(
const SdpVideoFormat::Parameters& params) {
const auto profile_it = params.find(kVP9FmtpProfileId);
if (profile_it == params.end())
return VP9Profile::kProfile0;
const std::string& profile_str = profile_it->second;
return StringToVP9Profile(profile_str);
}
bool IsSameVP9Profile(const SdpVideoFormat::Parameters& params1,
const SdpVideoFormat::Parameters& params2) {
const rtc::Optional<VP9Profile> profile = ParseSdpForVP9Profile(params1);
const rtc::Optional<VP9Profile> other_profile =
ParseSdpForVP9Profile(params2);
return profile && other_profile && profile == other_profile;
}
} // namespace webrtc

53
media/base/vp9_profile.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MEDIA_BASE_VP9_PROFILE_H_
#define MEDIA_BASE_VP9_PROFILE_H_
#include <map>
#include <string>
#include "absl/types/optional.h"
#include "api/video_codecs/sdp_video_format.h"
#include "common_types.h" // NOLINT(build/include)
namespace webrtc {
// Profile information for VP9 video.
extern const char kVP9FmtpProfileId[];
enum class VP9Profile {
kProfile0,
kProfile2,
};
// Helper functions to convert VP9Profile to std::string. Returns "0" by
// default.
std::string VP9ProfileToString(VP9Profile profile);
// Helper functions to convert std::string to VP9Profile. Returns null if given
// an invalid profile string.
absl::optional<VP9Profile> StringToVP9Profile(const std::string& str);
// Parse profile that is represented as a string of single digit contained in an
// SDP key-value map. A default profile(kProfile0) will be returned if the
// profile key is missing. Nothing will be returned if the key is present but
// the string is invalid.
absl::optional<VP9Profile> ParseSdpForVP9Profile(
const SdpVideoFormat::Parameters& params);
// Returns true if the parameters have the same VP9 profile, or neither contains
// VP9 profile.
bool IsSameVP9Profile(const SdpVideoFormat::Parameters& params1,
const SdpVideoFormat::Parameters& params2);
} // namespace webrtc
#endif // MEDIA_BASE_VP9_PROFILE_H_

View File

@ -24,8 +24,8 @@ std::vector<SdpVideoFormat> InternalDecoderFactory::GetSupportedFormats()
const {
std::vector<SdpVideoFormat> formats;
formats.push_back(SdpVideoFormat(cricket::kVp8CodecName));
if (VP9Decoder::IsSupported())
formats.push_back(SdpVideoFormat(cricket::kVp9CodecName));
for (const SdpVideoFormat& format : SupportedVP9Codecs())
formats.push_back(format);
for (const SdpVideoFormat& h264_format : SupportedH264Codecs())
formats.push_back(h264_format);
return formats;
@ -35,15 +35,10 @@ std::unique_ptr<VideoDecoder> InternalDecoderFactory::CreateVideoDecoder(
const SdpVideoFormat& format) {
if (cricket::CodecNamesEq(format.name, cricket::kVp8CodecName))
return VP8Decoder::Create();
if (cricket::CodecNamesEq(format.name, cricket::kVp9CodecName)) {
RTC_DCHECK(VP9Decoder::IsSupported());
if (cricket::CodecNamesEq(format.name, cricket::kVp9CodecName))
return VP9Decoder::Create();
}
if (cricket::CodecNamesEq(format.name, cricket::kH264CodecName))
return H264Decoder::Create();
RTC_LOG(LS_ERROR) << "Trying to create decoder for unsupported format";
return nullptr;
}

View File

@ -24,12 +24,10 @@ std::vector<SdpVideoFormat> InternalEncoderFactory::GetSupportedFormats()
const {
std::vector<SdpVideoFormat> supported_codecs;
supported_codecs.push_back(SdpVideoFormat(cricket::kVp8CodecName));
if (webrtc::VP9Encoder::IsSupported())
supported_codecs.push_back(SdpVideoFormat(cricket::kVp9CodecName));
for (const webrtc::SdpVideoFormat& format : webrtc::SupportedVP9Codecs())
supported_codecs.push_back(format);
for (const webrtc::SdpVideoFormat& format : webrtc::SupportedH264Codecs())
supported_codecs.push_back(format);
return supported_codecs;
}
@ -45,13 +43,10 @@ std::unique_ptr<VideoEncoder> InternalEncoderFactory::CreateVideoEncoder(
const SdpVideoFormat& format) {
if (cricket::CodecNamesEq(format.name, cricket::kVp8CodecName))
return VP8Encoder::Create();
if (cricket::CodecNamesEq(format.name, cricket::kVp9CodecName))
return VP9Encoder::Create();
if (cricket::CodecNamesEq(format.name, cricket::kH264CodecName))
return H264Encoder::Create(cricket::VideoCodec(format));
RTC_LOG(LS_ERROR) << "Trying to created encoder of unsupported format "
<< format.name;
return nullptr;

View File

@ -472,7 +472,10 @@ rtc_static_library("webrtc_vp9") {
":webrtc_vp9_helpers",
"..:module_api",
"../..:webrtc_common",
"../../api/video_codecs:video_codecs_api",
"../../common_video",
"../../media:rtc_media_base",
"../../media:rtc_vp9_profile",
"../../rtc_base:checks",
"../../rtc_base:rtc_base",
"../../system_wrappers",

View File

@ -0,0 +1,3 @@
include_rules = [
"+media/base",
]

View File

@ -13,22 +13,31 @@
#define MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_
#include <memory>
#include <vector>
#include "api/video_codecs/sdp_video_format.h"
#include "media/base/codec.h"
#include "modules/video_coding/include/video_codec_interface.h"
namespace webrtc {
// Returns a vector with all supported internal VP9 profiles that we can
// negotiate in SDP, in order of preference.
std::vector<SdpVideoFormat> SupportedVP9Codecs();
class VP9Encoder : public VideoEncoder {
public:
static bool IsSupported();
// Deprecated. Returns default implementation using VP9 Profile 0.
// TODO(emircan): Remove once this is no longer used.
static std::unique_ptr<VP9Encoder> Create();
// Parses VP9 Profile from |codec| and returns the appropriate implementation.
static std::unique_ptr<VP9Encoder> Create(const cricket::VideoCodec& codec);
~VP9Encoder() override {}
};
class VP9Decoder : public VideoDecoder {
public:
static bool IsSupported();
static std::unique_ptr<VP9Decoder> Create();
~VP9Decoder() override {}

View File

@ -52,12 +52,19 @@ int GetCpuSpeed(int width, int height) {
#endif
}
bool VP9Encoder::IsSupported() {
return true;
std::vector<SdpVideoFormat> SupportedVP9Codecs() {
return {SdpVideoFormat(
cricket::kVp9CodecName,
{{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}})};
}
std::unique_ptr<VP9Encoder> VP9Encoder::Create() {
return rtc::MakeUnique<VP9EncoderImpl>();
return rtc::MakeUnique<VP9EncoderImpl>(cricket::VideoCodec());
}
std::unique_ptr<VP9Encoder> VP9Encoder::Create(
const cricket::VideoCodec& codec) {
return rtc::MakeUnique<VP9EncoderImpl>(codec);
}
void VP9EncoderImpl::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
@ -66,9 +73,11 @@ void VP9EncoderImpl::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
enc->GetEncodedLayerFrame(pkt);
}
VP9EncoderImpl::VP9EncoderImpl()
VP9EncoderImpl::VP9EncoderImpl(const cricket::VideoCodec& codec)
: encoded_image_(),
encoded_complete_callback_(nullptr),
profile_(
ParseSdpForVP9Profile(codec.params).value_or(VP9Profile::kProfile0)),
inited_(false),
timestamp_(0),
cpu_speed_(3),
@ -88,6 +97,7 @@ VP9EncoderImpl::VP9EncoderImpl()
is_flexible_mode_(false) {
memset(&codec_, 0, sizeof(codec_));
memset(&svc_params_, 0, sizeof(vpx_svc_extra_cfg_t));
RTC_DCHECK(VP9Profile::kProfile0 == profile_);
}
VP9EncoderImpl::~VP9EncoderImpl() {
@ -960,10 +970,6 @@ const char* VP9EncoderImpl::ImplementationName() const {
return "libvpx";
}
bool VP9Decoder::IsSupported() {
return true;
}
std::unique_ptr<VP9Decoder> VP9Decoder::Create() {
return rtc::MakeUnique<VP9DecoderImpl>();
}

View File

@ -17,6 +17,8 @@
#include <vector>
#include "modules/video_coding/codecs/vp9/include/vp9.h"
#include "media/base/vp9_profile.h"
#include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
#include "rtc_base/rate_statistics.h"
@ -28,7 +30,7 @@ namespace webrtc {
class VP9EncoderImpl : public VP9Encoder {
public:
VP9EncoderImpl();
explicit VP9EncoderImpl(const cricket::VideoCodec& codec);
virtual ~VP9EncoderImpl();
@ -94,6 +96,7 @@ class VP9EncoderImpl : public VP9Encoder {
CodecSpecificInfo codec_specific_;
EncodedImageCallback* encoded_complete_callback_;
VideoCodec codec_;
const VP9Profile profile_;
bool inited_;
int64_t timestamp_;
int cpu_speed_;

View File

@ -14,12 +14,14 @@
#endif // !defined(RTC_DISABLE_VP9)
#include "modules/video_coding/codecs/vp9/include/vp9.h"
#include "api/video_codecs/sdp_video_format.h"
#include "rtc_base/checks.h"
namespace webrtc {
bool VP9Encoder::IsSupported() {
return false;
std::vector<SdpVideoFormat> SupportedVP9Codecs() {
return std::vector<SdpVideoFormat>();
}
std::unique_ptr<VP9Encoder> VP9Encoder::Create() {
@ -27,8 +29,10 @@ std::unique_ptr<VP9Encoder> VP9Encoder::Create() {
return nullptr;
}
bool VP9Decoder::IsSupported() {
return false;
std::unique_ptr<VP9Encoder> VP9Encoder::Create(
const cricket::VideoCodec& codec) {
RTC_NOTREACHED();
return nullptr;
}
std::unique_ptr<VP9Decoder> VP9Decoder::Create() {

View File

@ -25,7 +25,7 @@ static jlong JNI_VP9Encoder_CreateEncoder(JNIEnv* jni,
static jboolean JNI_VP9Encoder_IsSupported(JNIEnv* jni,
const JavaParamRef<jclass>&) {
return VP9Encoder::IsSupported();
return !SupportedVP9Codecs().empty();
}
static jlong JNI_VP9Decoder_CreateDecoder(JNIEnv* jni,
@ -35,7 +35,7 @@ static jlong JNI_VP9Decoder_CreateDecoder(JNIEnv* jni,
static jboolean JNI_VP9Decoder_IsSupported(JNIEnv* jni,
const JavaParamRef<jclass>&) {
return VP9Decoder::IsSupported();
return !SupportedVP9Codecs().empty();
}
} // namespace jni