|  | /* | 
|  | *  Copyright (c) 2017 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 "modules/video_coding/codecs/test/video_codec_unittest.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "api/test/create_frame_generator.h" | 
|  | #include "api/video_codecs/video_encoder.h" | 
|  | #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" | 
|  | #include "modules/video_coding/include/video_error_codes.h" | 
|  | #include "test/video_codec_settings.h" | 
|  |  | 
|  | static constexpr webrtc::TimeDelta kEncodeTimeout = | 
|  | webrtc::TimeDelta::Millis(100); | 
|  | static constexpr webrtc::TimeDelta kDecodeTimeout = | 
|  | webrtc::TimeDelta::Millis(25); | 
|  | // Set bitrate to get higher quality. | 
|  | static const int kStartBitrate = 300; | 
|  | static const int kMaxBitrate = 4000; | 
|  | static const int kWidth = 176;        // Width of the input image. | 
|  | static const int kHeight = 144;       // Height of the input image. | 
|  | static const int kMaxFramerate = 30;  // Arbitrary value. | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  | const VideoEncoder::Capabilities kCapabilities(false); | 
|  | } | 
|  |  | 
|  | EncodedImageCallback::Result | 
|  | VideoCodecUnitTest::FakeEncodeCompleteCallback::OnEncodedImage( | 
|  | const EncodedImage& frame, | 
|  | const CodecSpecificInfo* codec_specific_info) { | 
|  | MutexLock lock(&test_->encoded_frame_section_); | 
|  | test_->encoded_frames_.push_back(frame); | 
|  | RTC_DCHECK(codec_specific_info); | 
|  | test_->codec_specific_infos_.push_back(*codec_specific_info); | 
|  | if (!test_->wait_for_encoded_frames_threshold_) { | 
|  | test_->encoded_frame_event_.Set(); | 
|  | return Result(Result::OK); | 
|  | } | 
|  |  | 
|  | if (test_->encoded_frames_.size() == | 
|  | test_->wait_for_encoded_frames_threshold_) { | 
|  | test_->wait_for_encoded_frames_threshold_ = 1; | 
|  | test_->encoded_frame_event_.Set(); | 
|  | } | 
|  | return Result(Result::OK); | 
|  | } | 
|  |  | 
|  | void VideoCodecUnitTest::FakeDecodeCompleteCallback::Decoded( | 
|  | VideoFrame& frame, | 
|  | std::optional<int32_t> /* decode_time_ms */, | 
|  | std::optional<uint8_t> qp) { | 
|  | MutexLock lock(&test_->decoded_frame_section_); | 
|  | test_->decoded_frame_.emplace(frame); | 
|  | test_->decoded_qp_ = qp; | 
|  | test_->decoded_frame_event_.Set(); | 
|  | } | 
|  |  | 
|  | void VideoCodecUnitTest::SetUp() { | 
|  | webrtc::test::CodecSettings(kVideoCodecVP8, &codec_settings_); | 
|  | codec_settings_.startBitrate = kStartBitrate; | 
|  | codec_settings_.maxBitrate = kMaxBitrate; | 
|  | codec_settings_.maxFramerate = kMaxFramerate; | 
|  | codec_settings_.width = kWidth; | 
|  | codec_settings_.height = kHeight; | 
|  |  | 
|  | ModifyCodecSettings(&codec_settings_); | 
|  |  | 
|  | input_frame_generator_ = test::CreateSquareFrameGenerator( | 
|  | codec_settings_.width, codec_settings_.height, | 
|  | test::FrameGeneratorInterface::OutputType::kI420, std::optional<int>()); | 
|  |  | 
|  | encoder_ = CreateEncoder(); | 
|  | decoder_ = CreateDecoder(); | 
|  | encoder_->RegisterEncodeCompleteCallback(&encode_complete_callback_); | 
|  | decoder_->RegisterDecodeCompleteCallback(&decode_complete_callback_); | 
|  |  | 
|  | EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, | 
|  | encoder_->InitEncode( | 
|  | &codec_settings_, | 
|  | VideoEncoder::Settings(kCapabilities, 1 /* number of cores */, | 
|  | 0 /* max payload size (unused) */))); | 
|  |  | 
|  | VideoDecoder::Settings decoder_settings; | 
|  | decoder_settings.set_codec_type(codec_settings_.codecType); | 
|  | decoder_settings.set_max_render_resolution( | 
|  | {codec_settings_.width, codec_settings_.height}); | 
|  | EXPECT_TRUE(decoder_->Configure(decoder_settings)); | 
|  | } | 
|  |  | 
|  | void VideoCodecUnitTest::ModifyCodecSettings(VideoCodec* /* codec_settings */) { | 
|  | } | 
|  |  | 
|  | VideoFrame VideoCodecUnitTest::NextInputFrame() { | 
|  | test::FrameGeneratorInterface::VideoFrameData frame_data = | 
|  | input_frame_generator_->NextFrame(); | 
|  | VideoFrame input_frame = VideoFrame::Builder() | 
|  | .set_video_frame_buffer(frame_data.buffer) | 
|  | .set_update_rect(frame_data.update_rect) | 
|  | .build(); | 
|  |  | 
|  | const uint32_t timestamp = | 
|  | last_input_frame_timestamp_ + | 
|  | kVideoPayloadTypeFrequency / codec_settings_.maxFramerate; | 
|  | input_frame.set_rtp_timestamp(timestamp); | 
|  | input_frame.set_timestamp_us(timestamp * (1000 / 90)); | 
|  |  | 
|  | last_input_frame_timestamp_ = timestamp; | 
|  | return input_frame; | 
|  | } | 
|  |  | 
|  | bool VideoCodecUnitTest::WaitForEncodedFrame( | 
|  | EncodedImage* frame, | 
|  | CodecSpecificInfo* codec_specific_info) { | 
|  | std::vector<EncodedImage> frames; | 
|  | std::vector<CodecSpecificInfo> codec_specific_infos; | 
|  | if (!WaitForEncodedFrames(&frames, &codec_specific_infos)) | 
|  | return false; | 
|  | EXPECT_EQ(frames.size(), static_cast<size_t>(1)); | 
|  | EXPECT_EQ(frames.size(), codec_specific_infos.size()); | 
|  | *frame = frames[0]; | 
|  | *codec_specific_info = codec_specific_infos[0]; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void VideoCodecUnitTest::SetWaitForEncodedFramesThreshold(size_t num_frames) { | 
|  | MutexLock lock(&encoded_frame_section_); | 
|  | wait_for_encoded_frames_threshold_ = num_frames; | 
|  | } | 
|  |  | 
|  | bool VideoCodecUnitTest::WaitForEncodedFrames( | 
|  | std::vector<EncodedImage>* frames, | 
|  | std::vector<CodecSpecificInfo>* codec_specific_info) { | 
|  | EXPECT_TRUE(encoded_frame_event_.Wait(kEncodeTimeout)) | 
|  | << "Timed out while waiting for encoded frame."; | 
|  | // This becomes unsafe if there are multiple threads waiting for frames. | 
|  | MutexLock lock(&encoded_frame_section_); | 
|  | EXPECT_FALSE(encoded_frames_.empty()); | 
|  | EXPECT_FALSE(codec_specific_infos_.empty()); | 
|  | EXPECT_EQ(encoded_frames_.size(), codec_specific_infos_.size()); | 
|  | if (!encoded_frames_.empty()) { | 
|  | *frames = encoded_frames_; | 
|  | encoded_frames_.clear(); | 
|  | RTC_DCHECK(!codec_specific_infos_.empty()); | 
|  | *codec_specific_info = codec_specific_infos_; | 
|  | codec_specific_infos_.clear(); | 
|  | return true; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool VideoCodecUnitTest::WaitForDecodedFrame(std::unique_ptr<VideoFrame>* frame, | 
|  | std::optional<uint8_t>* qp) { | 
|  | bool ret = decoded_frame_event_.Wait(kDecodeTimeout); | 
|  | EXPECT_TRUE(ret) << "Timed out while waiting for a decoded frame."; | 
|  | // This becomes unsafe if there are multiple threads waiting for frames. | 
|  | MutexLock lock(&decoded_frame_section_); | 
|  | EXPECT_TRUE(decoded_frame_); | 
|  | if (decoded_frame_) { | 
|  | frame->reset(new VideoFrame(std::move(*decoded_frame_))); | 
|  | *qp = decoded_qp_; | 
|  | decoded_frame_.reset(); | 
|  | return true; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t VideoCodecUnitTest::GetNumEncodedFrames() { | 
|  | MutexLock lock(&encoded_frame_section_); | 
|  | return encoded_frames_.size(); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |