diff --git a/modules/video_coding/codecs/test/stats.cc b/modules/video_coding/codecs/test/stats.cc index b07a1f77d0..a3199ab6fa 100644 --- a/modules/video_coding/codecs/test/stats.cc +++ b/modules/video_coding/codecs/test/stats.cc @@ -42,6 +42,16 @@ bool LessForBitRate(const FrameStatistic& s1, const FrameStatistic& s2) { return s1.bitrate_kbps < s2.bitrate_kbps; } +bool LessForPsnr(const FrameStatistic& s1, const FrameStatistic& s2) { + RTC_DCHECK_NE(s1.frame_number, s2.frame_number); + return s1.psnr < s2.psnr; +} + +bool LessForSsim(const FrameStatistic& s1, const FrameStatistic& s2) { + RTC_DCHECK_NE(s1.frame_number, s2.frame_number); + return s1.ssim < s2.ssim; +} + } // namespace FrameStatistic* Stats::AddFrame() { @@ -77,6 +87,8 @@ void Stats::PrintSummary() const { size_t num_key_frames = 0; size_t num_delta_frames = 0; int num_encode_failures = 0; + double total_psnr = 0.0; + double total_ssim = 0.0; for (const FrameStatistic& stat : stats_) { total_encoding_time_us += stat.encode_time_us; @@ -92,6 +104,10 @@ void Stats::PrintSummary() const { if (stat.encode_return_code != 0) { ++num_encode_failures; } + if (stat.decoding_successful) { + total_psnr += stat.psnr; + total_ssim += stat.ssim; + } } // Encoding stats. @@ -164,6 +180,24 @@ void Stats::PrintSummary() const { printf(" Max bitrate: %7d kbps (frame %d)\n", frame_it->bitrate_kbps, frame_it->frame_number); + // Quality. + printf("Quality:\n"); + if (decoded_frames.empty()) { + printf("No successfully decoded frames exist in this statistics.\n"); + } else { + frame_it = std::min_element(decoded_frames.begin(), decoded_frames.end(), + LessForPsnr); + printf(" PSNR min: %f (frame %d)\n", frame_it->psnr, + frame_it->frame_number); + printf(" PSNR avg: %f\n", total_psnr / decoded_frames.size()); + + frame_it = std::min_element(decoded_frames.begin(), decoded_frames.end(), + LessForSsim); + printf(" SSIM min: %f (frame %d)\n", frame_it->ssim, + frame_it->frame_number); + printf(" SSIM avg: %f\n", total_ssim / decoded_frames.size()); + } + printf("\n"); printf("Total encoding time : %7d ms.\n", total_encoding_time_us / 1000); printf("Total decoding time : %7d ms.\n", total_decoding_time_us / 1000); diff --git a/modules/video_coding/codecs/test/stats.h b/modules/video_coding/codecs/test/stats.h index fd6fa874b2..02ca641041 100644 --- a/modules/video_coding/codecs/test/stats.h +++ b/modules/video_coding/codecs/test/stats.h @@ -50,6 +50,10 @@ struct FrameStatistic { int packets_dropped = 0; size_t total_packets = 0; size_t manipulated_length = 0; + + // Quality. + float psnr = 0.0; + float ssim = 0.0; }; // Statistics for a sequence of processed frames. This class is not thread safe. diff --git a/modules/video_coding/codecs/test/videoprocessor.cc b/modules/video_coding/codecs/test/videoprocessor.cc index fb79308054..5e20615b5d 100644 --- a/modules/video_coding/codecs/test/videoprocessor.cc +++ b/modules/video_coding/codecs/test/videoprocessor.cc @@ -99,7 +99,6 @@ void ExtractBufferWithSize(const VideoFrame& image, VideoProcessor::VideoProcessor(webrtc::VideoEncoder* encoder, webrtc::VideoDecoder* decoder, FrameReader* analysis_frame_reader, - FrameWriter* analysis_frame_writer, PacketManipulator* packet_manipulator, const TestConfig& config, Stats* stats, @@ -113,7 +112,6 @@ VideoProcessor::VideoProcessor(webrtc::VideoEncoder* encoder, decode_callback_(this), packet_manipulator_(packet_manipulator), analysis_frame_reader_(analysis_frame_reader), - analysis_frame_writer_(analysis_frame_writer), encoded_frame_writer_(encoded_frame_writer), decoded_frame_writer_(decoded_frame_writer), last_inputed_frame_num_(-1), @@ -127,7 +125,6 @@ VideoProcessor::VideoProcessor(webrtc::VideoEncoder* encoder, RTC_DCHECK(decoder); RTC_DCHECK(packet_manipulator); RTC_DCHECK(analysis_frame_reader); - RTC_DCHECK(analysis_frame_writer); RTC_DCHECK(stats); // Setup required callbacks for the encoder and decoder. @@ -158,7 +155,7 @@ VideoProcessor::~VideoProcessor() { void VideoProcessor::ProcessFrame() { RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_); - ++last_inputed_frame_num_; + const int frame_number = ++last_inputed_frame_num_; // Get frame from file. rtc::scoped_refptr buffer( @@ -167,15 +164,13 @@ void VideoProcessor::ProcessFrame() { // Use the frame number as the basis for timestamp to identify frames. Let the // first timestamp be non-zero, to not make the IvfFileWriter believe that we // want to use capture timestamps in the IVF files. - const uint32_t rtp_timestamp = (last_inputed_frame_num_ + 1) * - kRtpClockRateHz / + const uint32_t rtp_timestamp = (frame_number + 1) * kRtpClockRateHz / config_.codec_settings.maxFramerate; - rtp_timestamp_to_frame_num_[rtp_timestamp] = last_inputed_frame_num_; - VideoFrame source_frame(buffer, rtp_timestamp, kNoRenderTime, - webrtc::kVideoRotation_0); + rtp_timestamp_to_frame_num_[rtp_timestamp] = frame_number; + input_frames_[frame_number] = rtc::MakeUnique( + buffer, rtp_timestamp, kNoRenderTime, webrtc::kVideoRotation_0); - std::vector frame_types = - config_.FrameTypeForFrame(last_inputed_frame_num_); + std::vector frame_types = config_.FrameTypeForFrame(frame_number); // Create frame statistics object used for aggregation at end of test run. FrameStatistic* frame_stat = stats_->AddFrame(); @@ -184,7 +179,7 @@ void VideoProcessor::ProcessFrame() { // time recordings should wrap the Encode call as tightly as possible. frame_stat->encode_start_ns = rtc::TimeNanos(); frame_stat->encode_return_code = - encoder_->Encode(source_frame, nullptr, &frame_types); + encoder_->Encode(*input_frames_[frame_number], nullptr, &frame_types); } void VideoProcessor::SetRates(int bitrate_kbps, int framerate_fps) { @@ -222,30 +217,24 @@ void VideoProcessor::FrameEncoded(webrtc::VideoCodecType codec, config_.encoded_frame_checker->CheckEncodedFrame(codec, encoded_image); } - // Check for dropped frames. const int frame_number = rtp_timestamp_to_frame_num_[encoded_image._timeStamp]; + + // Ensure strict monotonicity. + RTC_CHECK_GT(frame_number, last_encoded_frame_num_); + + // Check for dropped frames. bool last_frame_missing = false; if (frame_number > 0) { - RTC_DCHECK_GE(last_encoded_frame_num_, 0); int num_dropped_from_last_encode = frame_number - last_encoded_frame_num_ - 1; RTC_DCHECK_GE(num_dropped_from_last_encode, 0); RTC_CHECK_GE(rate_update_index_, 0); num_dropped_frames_[rate_update_index_] += num_dropped_from_last_encode; - if (num_dropped_from_last_encode > 0) { - // For dropped frames, we write out the last decoded frame to avoid - // getting out of sync for the computation of PSNR and SSIM. - for (int i = 0; i < num_dropped_from_last_encode; i++) { - WriteDecodedFrameToFile(&last_decoded_frame_buffer_); - } - } const FrameStatistic* last_encoded_frame_stat = stats_->GetFrame(last_encoded_frame_num_); last_frame_missing = (last_encoded_frame_stat->manipulated_length == 0); } - // Ensure strict monotonicity. - RTC_CHECK_GT(frame_number, last_encoded_frame_num_); last_encoded_frame_num_ = frame_number; // Update frame statistics. @@ -285,18 +274,12 @@ void VideoProcessor::FrameEncoded(webrtc::VideoCodecType codec, frame_stat->decode_return_code = decoder_->Decode(copied_image, last_frame_missing, nullptr); - if (frame_stat->decode_return_code != WEBRTC_VIDEO_CODEC_OK) { - // Write the last successful frame the output file to avoid getting it out - // of sync with the source file for SSIM and PSNR comparisons. - WriteDecodedFrameToFile(&last_decoded_frame_buffer_); - } - if (encoded_frame_writer_) { RTC_CHECK(encoded_frame_writer_->WriteFrame(encoded_image, codec)); } } -void VideoProcessor::FrameDecoded(const VideoFrame& image) { +void VideoProcessor::FrameDecoded(const VideoFrame& decoded_frame) { RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_); // For the highest measurement accuracy of the decode time, the start/stop @@ -304,46 +287,67 @@ void VideoProcessor::FrameDecoded(const VideoFrame& image) { int64_t decode_stop_ns = rtc::TimeNanos(); // Update frame statistics. - const int frame_number = rtp_timestamp_to_frame_num_[image.timestamp()]; + const int frame_number = + rtp_timestamp_to_frame_num_[decoded_frame.timestamp()]; FrameStatistic* frame_stat = stats_->GetFrame(frame_number); - frame_stat->decoded_width = image.width(); - frame_stat->decoded_height = image.height(); + frame_stat->decoded_width = decoded_frame.width(); + frame_stat->decoded_height = decoded_frame.height(); frame_stat->decode_time_us = GetElapsedTimeMicroseconds(frame_stat->decode_start_ns, decode_stop_ns); frame_stat->decoding_successful = true; + // Ensure strict monotonicity. + RTC_CHECK_GT(frame_number, last_decoded_frame_num_); + // Check if the codecs have resized the frame since previously decoded frame. if (frame_number > 0) { - RTC_CHECK_GE(last_decoded_frame_num_, 0); + if (decoded_frame_writer_ && last_decoded_frame_num_ >= 0) { + // For dropped/lost frames, write out the last decoded frame to make it + // look like a freeze at playback. + const int num_dropped_frames = frame_number - last_decoded_frame_num_; + for (int i = 0; i < num_dropped_frames; i++) { + WriteDecodedFrameToFile(&last_decoded_frame_buffer_); + } + } + // TODO(ssilkin): move to FrameEncoded when webm:1474 is implemented. const FrameStatistic* last_decoded_frame_stat = stats_->GetFrame(last_decoded_frame_num_); - if (image.width() != last_decoded_frame_stat->decoded_width || - image.height() != last_decoded_frame_stat->decoded_height) { + if (decoded_frame.width() != last_decoded_frame_stat->decoded_width || + decoded_frame.height() != last_decoded_frame_stat->decoded_height) { RTC_CHECK_GE(rate_update_index_, 0); ++num_spatial_resizes_[rate_update_index_]; } } - // Ensure strict monotonicity. - RTC_CHECK_GT(frame_number, last_decoded_frame_num_); last_decoded_frame_num_ = frame_number; - // If the frame size is different from the original size, scale back to the - // original size. This is needed for the PSNR and SSIM calculations. - rtc::Buffer buffer; - ExtractBufferWithSize(image, config_.codec_settings.width, - config_.codec_settings.height, &buffer); - WriteDecodedFrameToFile(&buffer); + // Skip quality metrics calculation to not affect CPU usage. + if (!config_.measure_cpu) { + frame_stat->psnr = + I420PSNR(input_frames_[frame_number].get(), &decoded_frame); + frame_stat->ssim = + I420SSIM(input_frames_[frame_number].get(), &decoded_frame); + } - last_decoded_frame_buffer_ = std::move(buffer); + // Delay erasing of input frames by one frame. The current frame might + // still be needed for other simulcast stream or spatial layer. + const int frame_number_to_erase = frame_number - 1; + if (frame_number_to_erase >= 0) { + auto input_frame_erase_to = + input_frames_.lower_bound(frame_number_to_erase); + input_frames_.erase(input_frames_.begin(), input_frame_erase_to); + } + + if (decoded_frame_writer_) { + ExtractBufferWithSize(decoded_frame, config_.codec_settings.width, + config_.codec_settings.height, + &last_decoded_frame_buffer_); + WriteDecodedFrameToFile(&last_decoded_frame_buffer_); + } } void VideoProcessor::WriteDecodedFrameToFile(rtc::Buffer* buffer) { - RTC_DCHECK_EQ(buffer->size(), analysis_frame_writer_->FrameLength()); - RTC_CHECK(analysis_frame_writer_->WriteFrame(buffer->data())); - if (decoded_frame_writer_) { - RTC_DCHECK_EQ(buffer->size(), decoded_frame_writer_->FrameLength()); - RTC_CHECK(decoded_frame_writer_->WriteFrame(buffer->data())); - } + RTC_DCHECK_EQ(buffer->size(), decoded_frame_writer_->FrameLength()); + RTC_CHECK(decoded_frame_writer_->WriteFrame(buffer->data())); } bool VideoProcessor::ExcludeFrame(const EncodedImage& encoded_image) { diff --git a/modules/video_coding/codecs/test/videoprocessor.h b/modules/video_coding/codecs/test/videoprocessor.h index 0960744b01..62a12ef871 100644 --- a/modules/video_coding/codecs/test/videoprocessor.h +++ b/modules/video_coding/codecs/test/videoprocessor.h @@ -62,7 +62,6 @@ class VideoProcessor { VideoProcessor(webrtc::VideoEncoder* encoder, webrtc::VideoDecoder* decoder, FrameReader* analysis_frame_reader, - FrameWriter* analysis_frame_writer, PacketManipulator* packet_manipulator, const TestConfig& config, Stats* stats, @@ -199,10 +198,16 @@ class VideoProcessor { // Fake network. PacketManipulator* const packet_manipulator_; + // Input frames. Used as reference at frame quality evaluation. + // Async codecs might queue frames. To handle that we keep input frame + // and release it after corresponding coded frame is decoded and quality + // measurement is done. + std::map> input_frames_ + RTC_GUARDED_BY(sequence_checker_); + // These (mandatory) file manipulators are used for, e.g., objective PSNR and // SSIM calculations at the end of a test run. FrameReader* const analysis_frame_reader_; - FrameWriter* const analysis_frame_writer_; // These (optional) file writers are used to persistently store the encoded // and decoded bitstreams. The purpose is to give the experimenter an option diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc index 4694f2d8f4..bcfb132110 100644 --- a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc +++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc @@ -51,23 +51,6 @@ const float kInitialBufferSize = 0.5f; const float kOptimalBufferSize = 0.6f; const float kScaleKeyFrameSize = 0.5f; -void VerifyQuality(const QualityMetricsResult& psnr_result, - const QualityMetricsResult& ssim_result, - const QualityThresholds& quality_thresholds) { - EXPECT_GT(psnr_result.average, quality_thresholds.min_avg_psnr); - EXPECT_GT(psnr_result.min, quality_thresholds.min_min_psnr); - EXPECT_GT(ssim_result.average, quality_thresholds.min_avg_ssim); - EXPECT_GT(ssim_result.min, quality_thresholds.min_min_ssim); -} - -void PrintQualityMetrics(const QualityMetricsResult& psnr_result, - const QualityMetricsResult& ssim_result) { - printf("Quality statistics\n==\n"); - printf("PSNR avg: %f, min: %f\n", psnr_result.average, psnr_result.min); - printf("SSIM avg: %f, min: %f\n", ssim_result.average, ssim_result.min); - printf("\n"); -} - bool RunEncodeInRealTime(const TestConfig& config) { if (config.measure_cpu) { return true; @@ -284,10 +267,15 @@ void VideoProcessorIntegrationTest::ProcessFramesAndMaybeVerify( // Calculate and print rate control statistics. rate_update_index = 0; frame_number = 0; + quality_ = QualityMetrics(); ResetRateControlMetrics(rate_update_index, rate_profiles); while (frame_number < num_frames) { UpdateRateControlMetrics(frame_number); + if (quality_thresholds) { + UpdateQualityMetrics(frame_number); + } + if (bs_thresholds) { VerifyBitstream(frame_number, *bs_thresholds); } @@ -310,28 +298,14 @@ void VideoProcessorIntegrationTest::ProcessFramesAndMaybeVerify( VerifyRateControlMetrics(rate_update_index, rc_thresholds, num_dropped_frames, num_spatial_resizes); + if (quality_thresholds) { + VerifyQualityMetrics(*quality_thresholds); + } + // Calculate and print other statistics. EXPECT_EQ(num_frames, static_cast(stats_.size())); stats_.PrintSummary(); cpu_process_time_->Print(); - - // Calculate and print image quality statistics. - // TODO(marpan): Should compute these quality metrics per SetRates update. - QualityMetricsResult psnr_result, ssim_result; - EXPECT_EQ(0, I420MetricsFromFiles(config_.input_filename.c_str(), - config_.output_filename.c_str(), - config_.codec_settings.width, - config_.codec_settings.height, &psnr_result, - &ssim_result)); - if (quality_thresholds) { - VerifyQuality(psnr_result, ssim_result, *quality_thresholds); - } - PrintQualityMetrics(psnr_result, ssim_result); - - // Remove analysis file. - if (remove(config_.output_filename.c_str()) < 0) { - fprintf(stderr, "Failed to remove temporary file!\n"); - } } void VideoProcessorIntegrationTest::CreateEncoderAndDecoder() { @@ -482,8 +456,8 @@ void VideoProcessorIntegrationTest::SetUpAndInitObjects( task_queue->PostTask([this, &sync_event]() { processor_ = rtc::MakeUnique( encoder_.get(), decoder_.get(), analysis_frame_reader_.get(), - analysis_frame_writer_.get(), packet_manipulator_.get(), config_, - &stats_, encoded_frame_writer_.get(), decoded_frame_writer_.get()); + packet_manipulator_.get(), config_, &stats_, + encoded_frame_writer_.get(), decoded_frame_writer_.get()); sync_event.Set(); }); sync_event.Wait(rtc::Event::kForever); @@ -501,9 +475,7 @@ void VideoProcessorIntegrationTest::ReleaseAndCloseObjects( // The VideoProcessor must be destroyed before the codecs. DestroyEncoderAndDecoder(); - // Close the analysis files before we use them for SSIM/PSNR calculations. analysis_frame_reader_->Close(); - analysis_frame_writer_->Close(); // Close visualization files. if (encoded_frame_writer_) { @@ -591,6 +563,19 @@ void VideoProcessorIntegrationTest::VerifyRateControlMetrics( } } +void VideoProcessorIntegrationTest::UpdateQualityMetrics(int frame_number) { + FrameStatistic* frame_stat = stats_.GetFrame(frame_number); + if (frame_stat->decoding_successful) { + ++quality_.num_decoded_frames; + quality_.total_psnr += frame_stat->psnr; + quality_.total_ssim += frame_stat->ssim; + if (frame_stat->psnr < quality_.min_psnr) + quality_.min_psnr = frame_stat->psnr; + if (frame_stat->ssim < quality_.min_ssim) + quality_.min_ssim = frame_stat->ssim; + } +} + void VideoProcessorIntegrationTest::PrintRateControlMetrics( int rate_update_index, const std::vector& num_dropped_frames, @@ -656,6 +641,17 @@ void VideoProcessorIntegrationTest::VerifyBitstream( EXPECT_LE(*(frame_stat->max_nalu_length), bs_thresholds.max_nalu_length); } +void VideoProcessorIntegrationTest::VerifyQualityMetrics( + const QualityThresholds& quality_thresholds) { + EXPECT_GT(quality_.num_decoded_frames, 0); + EXPECT_GT(quality_.total_psnr / quality_.num_decoded_frames, + quality_thresholds.min_avg_psnr); + EXPECT_GT(quality_.min_psnr, quality_thresholds.min_min_psnr); + EXPECT_GT(quality_.total_ssim / quality_.num_decoded_frames, + quality_thresholds.min_avg_ssim); + EXPECT_GT(quality_.min_ssim, quality_thresholds.min_min_ssim); +} + // Reset quantities before each encoder rate update. void VideoProcessorIntegrationTest::ResetRateControlMetrics( int rate_update_index, diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest.h b/modules/video_coding/codecs/test/videoprocessor_integrationtest.h index b32029de33..10677da2bc 100644 --- a/modules/video_coding/codecs/test/videoprocessor_integrationtest.h +++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest.h @@ -12,6 +12,7 @@ #define MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_INTEGRATIONTEST_H_ #include +#include #include #include #include @@ -159,6 +160,14 @@ class VideoProcessorIntegrationTest : public testing::Test { float key_framesize_kbits; }; + struct QualityMetrics { + int num_decoded_frames = 0; + double total_psnr = 0.0; + double total_ssim = 0.0; + double min_psnr = std::numeric_limits::max(); + double min_ssim = std::numeric_limits::max(); + }; + void CreateEncoderAndDecoder(); void DestroyEncoderAndDecoder(); void SetUpAndInitObjects(rtc::TaskQueue* task_queue, @@ -185,6 +194,9 @@ class VideoProcessorIntegrationTest : public testing::Test { void VerifyBitstream(int frame_number, const BitstreamThresholds& bs_thresholds); + void UpdateQualityMetrics(int frame_number); + void VerifyQualityMetrics(const QualityThresholds& quality_thresholds); + void PrintSettings() const; // Codecs. @@ -207,6 +219,8 @@ class VideoProcessorIntegrationTest : public testing::Test { // Rates set for every encoder rate update. TargetRates target_; + + QualityMetrics quality_; }; } // namespace test diff --git a/modules/video_coding/codecs/test/videoprocessor_unittest.cc b/modules/video_coding/codecs/test/videoprocessor_unittest.cc index a3bf9e30fd..1a51df223d 100644 --- a/modules/video_coding/codecs/test/videoprocessor_unittest.cc +++ b/modules/video_coding/codecs/test/videoprocessor_unittest.cc @@ -20,7 +20,6 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/testsupport/mock/mock_frame_reader.h" -#include "test/testsupport/mock/mock_frame_writer.h" #include "test/testsupport/packet_reader.h" #include "test/testsupport/unittest_utils.h" #include "test/video_codec_settings.h" @@ -55,7 +54,7 @@ class VideoProcessorTest : public testing::Test { .WillRepeatedly(Return(kFrameSize)); video_processor_ = rtc::MakeUnique( &encoder_mock_, &decoder_mock_, &frame_reader_mock_, - &frame_writer_mock_, &packet_manipulator_mock_, config_, &stats_, + &packet_manipulator_mock_, config_, &stats_, nullptr /* encoded_frame_writer */, nullptr /* decoded_frame_writer */); } @@ -78,7 +77,6 @@ class VideoProcessorTest : public testing::Test { MockVideoEncoder encoder_mock_; MockVideoDecoder decoder_mock_; MockFrameReader frame_reader_mock_; - MockFrameWriter frame_writer_mock_; MockPacketManipulator packet_manipulator_mock_; Stats stats_; std::unique_ptr video_processor_; diff --git a/test/BUILD.gn b/test/BUILD.gn index 8a1c823928..9a50e1cc79 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -202,7 +202,6 @@ if (rtc_include_tests) { "testsupport/metrics/video_metrics.cc", "testsupport/metrics/video_metrics.h", "testsupport/mock/mock_frame_reader.h", - "testsupport/mock/mock_frame_writer.h", "testsupport/y4m_frame_writer.cc", "testsupport/yuv_frame_reader.cc", "testsupport/yuv_frame_writer.cc", diff --git a/test/testsupport/mock/mock_frame_writer.h b/test/testsupport/mock/mock_frame_writer.h deleted file mode 100644 index 2f6602dd3f..0000000000 --- a/test/testsupport/mock/mock_frame_writer.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2011 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 TEST_TESTSUPPORT_MOCK_MOCK_FRAME_WRITER_H_ -#define TEST_TESTSUPPORT_MOCK_MOCK_FRAME_WRITER_H_ - -#include "test/testsupport/frame_writer.h" - -#include "test/gmock.h" - -namespace webrtc { -namespace test { - -class MockFrameWriter : public FrameWriter { - public: - MOCK_METHOD0(Init, bool()); - MOCK_METHOD1(WriteFrame, bool(uint8_t* frame_buffer)); - MOCK_METHOD0(Close, void()); - MOCK_METHOD0(FrameLength, size_t()); -}; - -} // namespace test -} // namespace webrtc - -#endif // TEST_TESTSUPPORT_MOCK_MOCK_FRAME_WRITER_H_