diff --git a/webrtc/common_types.h b/webrtc/common_types.h index f7e48402ab..20fa858cb2 100644 --- a/webrtc/common_types.h +++ b/webrtc/common_types.h @@ -12,7 +12,7 @@ #define WEBRTC_COMMON_TYPES_H_ #include - +#include #include #include "webrtc/typedefs.h" @@ -37,7 +37,7 @@ #define RTP_PAYLOAD_NAME_SIZE 32 -#if defined(WEBRTC_WIN) +#if defined(WEBRTC_WIN) || defined(WIN32) // Compares two strings without regard to case. #define STR_CASE_CMP(s1, s2) ::_stricmp(s1, s2) // Compares characters of two strings without regard to case. @@ -272,14 +272,26 @@ class FrameCountObserver { // ================================================================== // Each codec supported can be described by this structure. -struct CodecInst -{ - int pltype; - char plname[RTP_PAYLOAD_NAME_SIZE]; - int plfreq; - int pacsize; - int channels; - int rate; // bits/sec unlike {start,min,max}Bitrate elsewhere in this file! +struct CodecInst { + int pltype; + char plname[RTP_PAYLOAD_NAME_SIZE]; + int plfreq; + int pacsize; + int channels; + int rate; // bits/sec unlike {start,min,max}Bitrate elsewhere in this file! + + bool operator==(const CodecInst& other) const { + return pltype == other.pltype && + (STR_CASE_CMP(plname, other.plname) == 0) && + plfreq == other.plfreq && + pacsize == other.pacsize && + channels == other.channels && + rate == other.rate; + } + + bool operator!=(const CodecInst& other) const { + return !(*this == other); + } }; // RTP @@ -541,18 +553,34 @@ enum VP8ResilienceMode { }; // VP8 specific -struct VideoCodecVP8 -{ - bool pictureLossIndicationOn; - bool feedbackModeOn; - VideoCodecComplexity complexity; - VP8ResilienceMode resilience; - unsigned char numberOfTemporalLayers; - bool denoisingOn; - bool errorConcealmentOn; - bool automaticResizeOn; - bool frameDroppingOn; - int keyFrameInterval; +struct VideoCodecVP8 { + bool pictureLossIndicationOn; + bool feedbackModeOn; + VideoCodecComplexity complexity; + VP8ResilienceMode resilience; + unsigned char numberOfTemporalLayers; + bool denoisingOn; + bool errorConcealmentOn; + bool automaticResizeOn; + bool frameDroppingOn; + int keyFrameInterval; + + bool operator==(const VideoCodecVP8& other) const { + return pictureLossIndicationOn == other.pictureLossIndicationOn && + feedbackModeOn == other.feedbackModeOn && + complexity == other.complexity && + resilience == other.resilience && + numberOfTemporalLayers == other.numberOfTemporalLayers && + denoisingOn == other.denoisingOn && + errorConcealmentOn == other.errorConcealmentOn && + automaticResizeOn == other.automaticResizeOn && + frameDroppingOn == other.frameDroppingOn && + keyFrameInterval == other.keyFrameInterval; + } + + bool operator!=(const VideoCodecVP8& other) const { + return !(*this == other); + } }; // Video codec types @@ -574,15 +602,28 @@ union VideoCodecUnion // Simulcast is when the same stream is encoded multiple times with different // settings such as resolution. -struct SimulcastStream -{ - unsigned short width; - unsigned short height; - unsigned char numberOfTemporalLayers; - unsigned int maxBitrate; // kilobits/sec. - unsigned int targetBitrate; // kilobits/sec. - unsigned int minBitrate; // kilobits/sec. - unsigned int qpMax; // minimum quality +struct SimulcastStream { + unsigned short width; + unsigned short height; + unsigned char numberOfTemporalLayers; + unsigned int maxBitrate; // kilobits/sec. + unsigned int targetBitrate; // kilobits/sec. + unsigned int minBitrate; // kilobits/sec. + unsigned int qpMax; // minimum quality + + bool operator==(const SimulcastStream& other) const { + return width == other.width && + height == other.height && + numberOfTemporalLayers == other.numberOfTemporalLayers && + maxBitrate == other.maxBitrate && + targetBitrate == other.targetBitrate && + minBitrate == other.minBitrate && + qpMax == other.qpMax; + } + + bool operator!=(const SimulcastStream& other) const { + return !(*this == other); + } }; enum VideoCodecMode { @@ -591,31 +632,63 @@ enum VideoCodecMode { }; // Common video codec properties -struct VideoCodec -{ - VideoCodecType codecType; - char plName[kPayloadNameSize]; - unsigned char plType; +struct VideoCodec { + VideoCodecType codecType; + char plName[kPayloadNameSize]; + unsigned char plType; - unsigned short width; - unsigned short height; + unsigned short width; + unsigned short height; - unsigned int startBitrate; // kilobits/sec. - unsigned int maxBitrate; // kilobits/sec. - unsigned int minBitrate; // kilobits/sec. - unsigned char maxFramerate; + unsigned int startBitrate; // kilobits/sec. + unsigned int maxBitrate; // kilobits/sec. + unsigned int minBitrate; // kilobits/sec. + unsigned char maxFramerate; - VideoCodecUnion codecSpecific; + VideoCodecUnion codecSpecific; - unsigned int qpMax; - unsigned char numberOfSimulcastStreams; - SimulcastStream simulcastStream[kMaxSimulcastStreams]; + unsigned int qpMax; + unsigned char numberOfSimulcastStreams; + SimulcastStream simulcastStream[kMaxSimulcastStreams]; - VideoCodecMode mode; + VideoCodecMode mode; - // When using an external encoder/decoder this allows to pass - // extra options without requiring webrtc to be aware of them. - Config* extra_options; + // When using an external encoder/decoder this allows to pass + // extra options without requiring webrtc to be aware of them. + Config* extra_options; +}; + +// TODO(mallinath) - Remove this and push these two methods inside VideoCodec. +// This is done to handle operator== defined in fakewebrtcvideoengine.h +// This modification allows us to commit this CL directly and not with libjingle +// push. +struct VideoCodecDerived : public VideoCodec { + bool operator==(const VideoCodec& other) const { + bool ret = codecType == other.codecType && + (STR_CASE_CMP(plName, other.plName) == 0) && + plType == other.plType && + width == other.width && + height == other.height && + startBitrate == other.startBitrate && + maxBitrate == other.maxBitrate && + minBitrate == other.minBitrate && + maxFramerate == other.maxFramerate && + qpMax == other.qpMax && + numberOfSimulcastStreams == other.numberOfSimulcastStreams && + mode == other.mode; + if (ret && codecType == kVideoCodecVP8) { + ret &= (codecSpecific.VP8 == other.codecSpecific.VP8); + } + + for (unsigned char i = 0; i < other.numberOfSimulcastStreams && ret; ++i) { + ret &= (simulcastStream[i] == other.simulcastStream[i]); + } + return ret; + } + + bool operator!=(const VideoCodec& other) const { + return !(*this == other); + } }; // Bandwidth over-use detector options. These are used to drive diff --git a/webrtc/video_engine/video_engine_core.gypi b/webrtc/video_engine/video_engine_core.gypi index 14d5fb6b9e..57cdecd8fa 100644 --- a/webrtc/video_engine/video_engine_core.gypi +++ b/webrtc/video_engine/video_engine_core.gypi @@ -131,6 +131,7 @@ 'encoder_state_feedback_unittest.cc', 'overuse_frame_detector_unittest.cc', 'stream_synchronization_unittest.cc', + 'vie_codec_unittest.cc', 'vie_remb_unittest.cc', ], 'conditions': [ diff --git a/webrtc/video_engine/vie_codec_unittest.cc b/webrtc/video_engine/vie_codec_unittest.cc new file mode 100644 index 0000000000..31cec05a1f --- /dev/null +++ b/webrtc/video_engine/vie_codec_unittest.cc @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2014 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 "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_types.h" + +namespace webrtc { + +// Builds VP8 codec with 0 simulcast streams. +void BuildVP8Codec(webrtc::VideoCodec* video_codec) { + video_codec->codecType = kVideoCodecVP8; + strncpy(video_codec->plName, "VP8", 4); + video_codec->plType = 100; + video_codec->width = 1280; + video_codec->height = 720; + + video_codec->startBitrate = 1000; // kbps + video_codec->maxBitrate = 2000; // kbps + video_codec->minBitrate = 1000; // kbps + video_codec->maxFramerate = 30; + + video_codec->qpMax = 50; + video_codec->numberOfSimulcastStreams = 0; + video_codec->mode = kRealtimeVideo; + + // Set VP8 codec specific info. + video_codec->codecSpecific.VP8.pictureLossIndicationOn = true; + video_codec->codecSpecific.VP8.feedbackModeOn = true; + video_codec->codecSpecific.VP8.complexity = kComplexityNormal; + video_codec->codecSpecific.VP8.resilience = kResilienceOff; + video_codec->codecSpecific.VP8.numberOfTemporalLayers = 0; + video_codec->codecSpecific.VP8.denoisingOn = true; + video_codec->codecSpecific.VP8.errorConcealmentOn = true; + video_codec->codecSpecific.VP8.automaticResizeOn = true; + video_codec->codecSpecific.VP8.frameDroppingOn = true; + video_codec->codecSpecific.VP8.keyFrameInterval = 200; +} + + +void SetSimulcastSettings(webrtc::VideoCodec* video_codec) { + // Simulcast settings. + video_codec->numberOfSimulcastStreams = 1; + video_codec->simulcastStream[0].width = 320; + video_codec->simulcastStream[0].height = 180; + video_codec->simulcastStream[0].numberOfTemporalLayers = 0; + video_codec->simulcastStream[0].maxBitrate = 100; + video_codec->simulcastStream[0].targetBitrate = 100; + video_codec->simulcastStream[0].minBitrate = 0; + video_codec->simulcastStream[0].qpMax = video_codec->qpMax; +} + + +// This test compares two VideoCodecInst objects except codec specific and +// simulcast streams. +TEST(ViECodecTest, TestCompareCodecs) { + VideoCodecDerived codec1, codec2; + memset(&codec1, 0, sizeof(VideoCodec)); + memset(&codec2, 0, sizeof(VideoCodec)); + + BuildVP8Codec(&codec1); + BuildVP8Codec(&codec2); + + EXPECT_TRUE(codec1 == codec2); + EXPECT_FALSE(codec1 != codec2); + + // plname is case insensitive. + strncpy(codec2.plName, "vp8", 4); + EXPECT_TRUE(codec1 == codec2); + + codec2.codecType = kVideoCodecUnknown; + EXPECT_FALSE(codec1 == codec2); + + // Modify pltype. + BuildVP8Codec(&codec2); + codec2.plType = 101; + EXPECT_FALSE(codec1 == codec2); + + // Modifing height and width. + BuildVP8Codec(&codec2); + codec2.width = 640; + codec2.height = 480; + EXPECT_FALSE(codec1 == codec2); + + // Modify framerate, default value is 30. + BuildVP8Codec(&codec2); + codec2.maxFramerate = 15; + EXPECT_FALSE(codec1 == codec2); + + // Modifying startBitrate, default value is 1000 kbps. + BuildVP8Codec(&codec2); + codec2.startBitrate = 2000; + EXPECT_FALSE(codec1 == codec2); + // maxBitrate + BuildVP8Codec(&codec2); + codec2.startBitrate = 3000; + EXPECT_FALSE(codec1 == codec2); + // minBirate + BuildVP8Codec(&codec2); + codec2.startBitrate = 500; + EXPECT_FALSE(codec1 == codec2); + + // Modify qpMax. + BuildVP8Codec(&codec2); + codec2.qpMax = 100; + EXPECT_FALSE(codec1 == codec2); + + // Modify mode + BuildVP8Codec(&codec2); + codec2.mode = kScreensharing; + EXPECT_FALSE(codec1 == codec2); +} + +// Test VP8 specific comparision. +TEST(ViECodecTest, TestCompareVP8CodecSpecific) { + VideoCodecDerived codec1, codec2; + memset(&codec1, 0, sizeof(VideoCodec)); + memset(&codec2, 0, sizeof(VideoCodec)); + + BuildVP8Codec(&codec1); + BuildVP8Codec(&codec2); + EXPECT_TRUE(codec1 == codec2); + + // pictureLossIndicationOn + codec2.codecSpecific.VP8.pictureLossIndicationOn = false; + EXPECT_FALSE(codec1 == codec2); + + // feedbackModeOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.feedbackModeOn = false; + EXPECT_FALSE(codec1 == codec2); + + // complexity + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.complexity = kComplexityHigh; + EXPECT_FALSE(codec1 == codec2); + + // resilience + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.resilience = kResilientStream; + EXPECT_FALSE(codec1 == codec2); + + // numberOfTemporalLayers + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.numberOfTemporalLayers = 2; + EXPECT_FALSE(codec1 == codec2); + + // denoisingOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.denoisingOn = false; + EXPECT_FALSE(codec1 == codec2); + + // errorConcealmentOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.errorConcealmentOn = false; + EXPECT_FALSE(codec1 == codec2); + + // pictureLossIndicationOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.automaticResizeOn = false; + EXPECT_FALSE(codec1 == codec2); + + // frameDroppingOn + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.frameDroppingOn = false; + EXPECT_FALSE(codec1 == codec2); + + // keyFrameInterval + BuildVP8Codec(&codec2); + codec2.codecSpecific.VP8.keyFrameInterval = 100; + EXPECT_FALSE(codec1 == codec2); +} + +// This test compares simulcast stream information in VideoCodec. +TEST(ViECodecTest, TestCompareSimulcastStreams) { + VideoCodecDerived codec1, codec2; + memset(&codec1, 0, sizeof(VideoCodec)); + memset(&codec2, 0, sizeof(VideoCodec)); + + BuildVP8Codec(&codec1); + BuildVP8Codec(&codec2); + // Set simulacast settings. + SetSimulcastSettings(&codec1); + SetSimulcastSettings(&codec2); + EXPECT_TRUE(codec1 == codec2); + + // Modify number of streams. + codec2.numberOfSimulcastStreams = 2; + EXPECT_FALSE(codec1 == codec2); + + // Resetting steram count. + codec2.numberOfSimulcastStreams = 1; + // Modify height and width in codec2. + codec2.simulcastStream[0].width = 640; + codec2.simulcastStream[0].height = 480; + EXPECT_FALSE(codec1 == codec2); + + // numberOfTemporalLayers + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].numberOfTemporalLayers = 2; + EXPECT_FALSE(codec1 == codec2); + + // maxBitrate + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].maxBitrate = 1000; + EXPECT_FALSE(codec1 == codec2); + + // targetBitrate + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].targetBitrate = 1000; + EXPECT_FALSE(codec1 == codec2); + + // minBitrate + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].minBitrate = 50; + EXPECT_FALSE(codec1 == codec2); + + // qpMax + SetSimulcastSettings(&codec2); + codec2.simulcastStream[0].qpMax = 100; + EXPECT_FALSE(codec1 == codec2); +} + +} // namespace webrtc diff --git a/webrtc/voice_engine/voe_codec_unittest.cc b/webrtc/voice_engine/voe_codec_unittest.cc index baa4f37ee3..7e440b2ada 100644 --- a/webrtc/voice_engine/voe_codec_unittest.cc +++ b/webrtc/voice_engine/voe_codec_unittest.cc @@ -169,6 +169,56 @@ TEST_F(VoECodecTest, DISABLED_ON_ANDROID(DualStreamRemoveSecondaryCodec)) { EXPECT_EQ(-1, voe_codec_->GetSecondarySendCodec(channel_, my_codec)); } +TEST(VoECodecInst, TestCompareCodecInstances) { + CodecInst codec1, codec2; + memset(&codec1, 0, sizeof(CodecInst)); + memset(&codec2, 0, sizeof(CodecInst)); + + codec1.pltype = 101; + strncpy(codec1.plname, "isac", 4); + codec1.plfreq = 8000; + codec1.pacsize = 110; + codec1.channels = 1; + codec1.rate = 8000; + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Compare two codecs now. + EXPECT_TRUE(codec1 == codec2); + EXPECT_FALSE(codec1 != codec2); + + // Changing pltype. + codec2.pltype = 102; + EXPECT_FALSE(codec1 == codec2); + EXPECT_TRUE(codec1 != codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // payload name should be case insensitive. + strncpy(codec2.plname, "ISAC", 4); + EXPECT_TRUE(codec1 == codec2); + + // Test modifying the |plfreq| + codec2.plfreq = 16000; + EXPECT_FALSE(codec1 == codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Test modifying the |pacsize|. + codec2.pacsize = 440; + EXPECT_FALSE(codec1 == codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Test modifying the |channels|. + codec2.channels = 2; + EXPECT_FALSE(codec1 == codec2); + + // Reset to codec2 to codec1 state. + memcpy(&codec2, &codec1, sizeof(CodecInst)); + // Test modifying the |rate|. + codec2.rate = 0; + EXPECT_FALSE(codec1 == codec2); +} + } // namespace } // namespace voe } // namespace webrtc