1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "testing/gtest/include/gtest/gtest.h" 12 #include "webrtc/base/checks.h" 13 #include "webrtc/base/scoped_ptr.h" 14 #include "webrtc/common_video/include/video_image.h" 15 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 16 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" 17 #include "webrtc/system_wrappers/include/tick_util.h" 18 #include "webrtc/test/testsupport/fileutils.h" 19 #include "webrtc/test/testsupport/metrics/video_metrics.h" 20 #include "webrtc/tools/simple_command_line_parser.h" 21 #include "webrtc/video_frame.h" 22 23 class Vp8SequenceCoderEncodeCallback : public webrtc::EncodedImageCallback { 24 public: 25 explicit Vp8SequenceCoderEncodeCallback(FILE* encoded_file) 26 : encoded_file_(encoded_file), encoded_bytes_(0) {} 27 ~Vp8SequenceCoderEncodeCallback(); 28 int Encoded(const webrtc::EncodedImage& encoded_image, 29 const webrtc::CodecSpecificInfo* codecSpecificInfo, 30 const webrtc::RTPFragmentationHeader*); 31 // Returns the encoded image. 32 webrtc::EncodedImage encoded_image() { return encoded_image_; } 33 size_t encoded_bytes() { return encoded_bytes_; } 34 35 private: 36 webrtc::EncodedImage encoded_image_; 37 FILE* encoded_file_; 38 size_t encoded_bytes_; 39 }; 40 41 Vp8SequenceCoderEncodeCallback::~Vp8SequenceCoderEncodeCallback() { 42 delete[] encoded_image_._buffer; 43 encoded_image_._buffer = NULL; 44 } 45 int Vp8SequenceCoderEncodeCallback::Encoded( 46 const webrtc::EncodedImage& encoded_image, 47 const webrtc::CodecSpecificInfo* codecSpecificInfo, 48 const webrtc::RTPFragmentationHeader* fragmentation) { 49 if (encoded_image_._size < encoded_image._size) { 50 delete[] encoded_image_._buffer; 51 encoded_image_._buffer = NULL; 52 encoded_image_._buffer = new uint8_t[encoded_image._size]; 53 encoded_image_._size = encoded_image._size; 54 } 55 memcpy(encoded_image_._buffer, encoded_image._buffer, encoded_image._size); 56 encoded_image_._length = encoded_image._length; 57 if (encoded_file_ != NULL) { 58 if (fwrite(encoded_image._buffer, 1, encoded_image._length, 59 encoded_file_) != encoded_image._length) { 60 return -1; 61 } 62 } 63 encoded_bytes_ += encoded_image_._length; 64 return 0; 65 } 66 67 // TODO(mikhal): Add support for varying the frame size. 68 class Vp8SequenceCoderDecodeCallback : public webrtc::DecodedImageCallback { 69 public: 70 explicit Vp8SequenceCoderDecodeCallback(FILE* decoded_file) 71 : decoded_file_(decoded_file) {} 72 int32_t Decoded(webrtc::VideoFrame& frame) override; 73 int32_t Decoded(webrtc::VideoFrame& frame, int64_t decode_time_ms) override { 74 RTC_NOTREACHED(); 75 return -1; 76 } 77 bool DecodeComplete(); 78 79 private: 80 FILE* decoded_file_; 81 }; 82 83 int Vp8SequenceCoderDecodeCallback::Decoded(webrtc::VideoFrame& image) { 84 EXPECT_EQ(0, webrtc::PrintVideoFrame(image, decoded_file_)); 85 return 0; 86 } 87 88 int SequenceCoder(webrtc::test::CommandLineParser* parser) { 89 int width = strtol((parser->GetFlag("w")).c_str(), NULL, 10); 90 int height = strtol((parser->GetFlag("h")).c_str(), NULL, 10); 91 int framerate = strtol((parser->GetFlag("f")).c_str(), NULL, 10); 92 93 if (width <= 0 || height <= 0 || framerate <= 0) { 94 fprintf(stderr, "Error: Resolution cannot be <= 0!\n"); 95 return -1; 96 } 97 int target_bitrate = strtol((parser->GetFlag("b")).c_str(), NULL, 10); 98 if (target_bitrate <= 0) { 99 fprintf(stderr, "Error: Bit-rate cannot be <= 0!\n"); 100 return -1; 101 } 102 103 // SetUp 104 // Open input file. 105 std::string encoded_file_name = parser->GetFlag("encoded_file"); 106 FILE* encoded_file = fopen(encoded_file_name.c_str(), "wb"); 107 if (encoded_file == NULL) { 108 fprintf(stderr, "Error: Cannot open encoded file\n"); 109 return -1; 110 } 111 std::string input_file_name = parser->GetFlag("input_file"); 112 FILE* input_file = fopen(input_file_name.c_str(), "rb"); 113 if (input_file == NULL) { 114 fprintf(stderr, "Error: Cannot open input file\n"); 115 return -1; 116 } 117 // Open output file. 118 std::string output_file_name = parser->GetFlag("output_file"); 119 FILE* output_file = fopen(output_file_name.c_str(), "wb"); 120 if (output_file == NULL) { 121 fprintf(stderr, "Error: Cannot open output file\n"); 122 return -1; 123 } 124 125 // Get range of frames: will encode num_frames following start_frame). 126 int start_frame = strtol((parser->GetFlag("start_frame")).c_str(), NULL, 10); 127 int num_frames = strtol((parser->GetFlag("num_frames")).c_str(), NULL, 10); 128 129 // Codec SetUp. 130 webrtc::VideoCodec inst; 131 memset(&inst, 0, sizeof(inst)); 132 webrtc::VP8Encoder* encoder = webrtc::VP8Encoder::Create(); 133 webrtc::VP8Decoder* decoder = webrtc::VP8Decoder::Create(); 134 inst.codecType = webrtc::kVideoCodecVP8; 135 inst.codecSpecific.VP8.feedbackModeOn = false; 136 inst.codecSpecific.VP8.denoisingOn = true; 137 inst.maxFramerate = framerate; 138 inst.startBitrate = target_bitrate; 139 inst.maxBitrate = 8000; 140 inst.width = width; 141 inst.height = height; 142 143 if (encoder->InitEncode(&inst, 1, 1440) < 0) { 144 fprintf(stderr, "Error: Cannot initialize vp8 encoder\n"); 145 return -1; 146 } 147 EXPECT_EQ(0, decoder->InitDecode(&inst, 1)); 148 webrtc::VideoFrame input_frame; 149 size_t length = webrtc::CalcBufferSize(webrtc::kI420, width, height); 150 rtc::scoped_ptr<uint8_t[]> frame_buffer(new uint8_t[length]); 151 152 int half_width = (width + 1) / 2; 153 // Set and register callbacks. 154 Vp8SequenceCoderEncodeCallback encoder_callback(encoded_file); 155 encoder->RegisterEncodeCompleteCallback(&encoder_callback); 156 Vp8SequenceCoderDecodeCallback decoder_callback(output_file); 157 decoder->RegisterDecodeCompleteCallback(&decoder_callback); 158 // Read->Encode->Decode sequence. 159 // num_frames = -1 implies unlimited encoding (entire sequence). 160 int64_t starttime = webrtc::TickTime::MillisecondTimestamp(); 161 int frame_cnt = 1; 162 int frames_processed = 0; 163 input_frame.CreateEmptyFrame(width, height, width, half_width, half_width); 164 while (!feof(input_file) && 165 (num_frames == -1 || frames_processed < num_frames)) { 166 if (fread(frame_buffer.get(), 1, length, input_file) != length) 167 continue; 168 if (frame_cnt >= start_frame) { 169 webrtc::ConvertToI420(webrtc::kI420, frame_buffer.get(), 0, 0, width, 170 height, 0, webrtc::kVideoRotation_0, &input_frame); 171 encoder->Encode(input_frame, NULL, NULL); 172 decoder->Decode(encoder_callback.encoded_image(), false, NULL); 173 ++frames_processed; 174 } 175 ++frame_cnt; 176 } 177 printf("\nProcessed %d frames\n", frames_processed); 178 int64_t endtime = webrtc::TickTime::MillisecondTimestamp(); 179 int64_t totalExecutionTime = endtime - starttime; 180 printf("Total execution time: %.2lf ms\n", 181 static_cast<double>(totalExecutionTime)); 182 double actual_bit_rate = 183 8.0 * encoder_callback.encoded_bytes() / (frame_cnt / inst.maxFramerate); 184 printf("Actual bitrate: %f kbps\n", actual_bit_rate / 1000); 185 webrtc::test::QualityMetricsResult psnr_result, ssim_result; 186 EXPECT_EQ(0, webrtc::test::I420MetricsFromFiles( 187 input_file_name.c_str(), output_file_name.c_str(), 188 inst.width, inst.height, &psnr_result, &ssim_result)); 189 printf("PSNR avg: %f[dB], min: %f[dB]\nSSIM avg: %f, min: %f\n", 190 psnr_result.average, psnr_result.min, ssim_result.average, 191 ssim_result.min); 192 return frame_cnt; 193 } 194 195 int main(int argc, char** argv) { 196 std::string program_name = argv[0]; 197 std::string usage = 198 "Encode and decodes a video sequence, and writes" 199 "results to a file.\n" 200 "Example usage:\n" + 201 program_name + 202 " functionality" 203 " --w=352 --h=288 --input_file=input.yuv --output_file=output.yuv " 204 " Command line flags:\n" 205 " - width(int): The width of the input file. Default: 352\n" 206 " - height(int): The height of the input file. Default: 288\n" 207 " - input_file(string): The YUV file to encode." 208 " Default: foreman.yuv\n" 209 " - encoded_file(string): The vp8 encoded file (encoder output)." 210 " Default: vp8_encoded.vp8\n" 211 " - output_file(string): The yuv decoded file (decoder output)." 212 " Default: vp8_decoded.yuv\n." 213 " - start_frame - frame number in which encoding will begin. Default: 0" 214 " - num_frames - Number of frames to be processed. " 215 " Default: -1 (entire sequence)."; 216 217 webrtc::test::CommandLineParser parser; 218 219 // Init the parser and set the usage message. 220 parser.Init(argc, argv); 221 parser.SetUsageMessage(usage); 222 223 // Reset flags. 224 parser.SetFlag("w", "352"); 225 parser.SetFlag("h", "288"); 226 parser.SetFlag("f", "30"); 227 parser.SetFlag("b", "500"); 228 parser.SetFlag("start_frame", "0"); 229 parser.SetFlag("num_frames", "-1"); 230 parser.SetFlag("output_file", webrtc::test::OutputPath() + "vp8_decoded.yuv"); 231 parser.SetFlag("encoded_file", 232 webrtc::test::OutputPath() + "vp8_encoded.vp8"); 233 parser.SetFlag("input_file", 234 webrtc::test::ResourcePath("foreman_cif", "yuv")); 235 parser.SetFlag("help", "false"); 236 237 parser.ProcessFlags(); 238 if (parser.GetFlag("help") == "true") { 239 parser.PrintUsageMessage(); 240 exit(EXIT_SUCCESS); 241 } 242 parser.PrintEnteredFlags(); 243 244 return SequenceCoder(&parser); 245 } 246