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