1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/at_exit.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/file_util.h"
9 #include "base/files/memory_mapped_file.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/process/process.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/time/time.h"
16 #include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
17 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
18 #include "media/base/bind_to_current_loop.h"
19 #include "media/base/bitstream_buffer.h"
20 #include "media/base/test_data_util.h"
21 #include "media/filters/h264_parser.h"
22 #include "media/video/video_encode_accelerator.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 using media::VideoEncodeAccelerator;
26
27 namespace content {
28 namespace {
29
30 const media::VideoFrame::Format kInputFormat = media::VideoFrame::I420;
31
32 // Arbitrarily chosen to add some depth to the pipeline.
33 const unsigned int kNumOutputBuffers = 4;
34 const unsigned int kNumExtraInputFrames = 4;
35 // Maximum delay between requesting a keyframe and receiving one, in frames.
36 // Arbitrarily chosen as a reasonable requirement.
37 const unsigned int kMaxKeyframeDelay = 4;
38 // Value to use as max frame number for keyframe detection.
39 const unsigned int kMaxFrameNum =
40 std::numeric_limits<unsigned int>::max() - kMaxKeyframeDelay;
41 // Default initial bitrate.
42 const uint32 kDefaultBitrate = 2000000;
43 // Default ratio of requested_subsequent_bitrate to initial_bitrate
44 // (see test parameters below) if one is not provided.
45 const double kDefaultSubsequentBitrateRatio = 2.0;
46 // Default initial framerate.
47 const uint32 kDefaultFramerate = 30;
48 // Default ratio of requested_subsequent_framerate to initial_framerate
49 // (see test parameters below) if one is not provided.
50 const double kDefaultSubsequentFramerateRatio = 0.1;
51 // Tolerance factor for how encoded bitrate can differ from requested bitrate.
52 const double kBitrateTolerance = 0.1;
53 // Minimum required FPS throughput for the basic performance test.
54 const uint32 kMinPerfFPS = 30;
55 // Minimum (arbitrary) number of frames required to enforce bitrate requirements
56 // over. Streams shorter than this may be too short to realistically require
57 // an encoder to be able to converge to the requested bitrate over.
58 // The input stream will be looped as many times as needed in bitrate tests
59 // to reach at least this number of frames before calculating final bitrate.
60 const unsigned int kMinFramesForBitrateTests = 300;
61
62 // The syntax of multiple test streams is:
63 // test-stream1;test-stream2;test-stream3
64 // The syntax of each test stream is:
65 // "in_filename:width:height:out_filename:requested_bitrate:requested_framerate
66 // :requested_subsequent_bitrate:requested_subsequent_framerate"
67 // - |in_filename| must be an I420 (YUV planar) raw stream
68 // (see http://www.fourcc.org/yuv.php#IYUV).
69 // - |width| and |height| are in pixels.
70 // - |profile| to encode into (values of media::VideoCodecProfile).
71 // - |out_filename| filename to save the encoded stream to (optional).
72 // Output stream is saved for the simple encode test only.
73 // Further parameters are optional (need to provide preceding positional
74 // parameters if a specific subsequent parameter is required):
75 // - |requested_bitrate| requested bitrate in bits per second.
76 // - |requested_framerate| requested initial framerate.
77 // - |requested_subsequent_bitrate| bitrate to switch to in the middle of the
78 // stream.
79 // - |requested_subsequent_framerate| framerate to switch to in the middle
80 // of the stream.
81 // Bitrate is only forced for tests that test bitrate.
82 const char* g_default_in_filename = "bear_320x192_40frames.yuv";
83 const char* g_default_in_parameters = ":320:192:1:out.h264:200000";
84 base::FilePath::StringType* g_test_stream_data;
85
86 struct TestStream {
TestStreamcontent::__anon19732b560111::TestStream87 TestStream()
88 : requested_bitrate(0),
89 requested_framerate(0),
90 requested_subsequent_bitrate(0),
91 requested_subsequent_framerate(0) {}
~TestStreamcontent::__anon19732b560111::TestStream92 ~TestStream() {}
93
94 gfx::Size size;
95 base::MemoryMappedFile input_file;
96 media::VideoCodecProfile requested_profile;
97 std::string out_filename;
98 unsigned int requested_bitrate;
99 unsigned int requested_framerate;
100 unsigned int requested_subsequent_bitrate;
101 unsigned int requested_subsequent_framerate;
102 };
103
104 // Parse |data| into its constituent parts, set the various output fields
105 // accordingly, read in video stream, and store them to |test_streams|.
ParseAndReadTestStreamData(const base::FilePath::StringType & data,ScopedVector<TestStream> * test_streams)106 static void ParseAndReadTestStreamData(const base::FilePath::StringType& data,
107 ScopedVector<TestStream>* test_streams) {
108 // Split the string to individual test stream data.
109 std::vector<base::FilePath::StringType> test_streams_data;
110 base::SplitString(data, ';', &test_streams_data);
111 CHECK_GE(test_streams_data.size(), 1U) << data;
112
113 // Parse each test stream data and read the input file.
114 for (size_t index = 0; index < test_streams_data.size(); ++index) {
115 std::vector<base::FilePath::StringType> fields;
116 base::SplitString(test_streams_data[index], ':', &fields);
117 CHECK_GE(fields.size(), 4U) << data;
118 CHECK_LE(fields.size(), 9U) << data;
119 TestStream* test_stream = new TestStream();
120
121 base::FilePath::StringType filename = fields[0];
122 int width, height;
123 CHECK(base::StringToInt(fields[1], &width));
124 CHECK(base::StringToInt(fields[2], &height));
125 test_stream->size = gfx::Size(width, height);
126 CHECK(!test_stream->size.IsEmpty());
127 int profile;
128 CHECK(base::StringToInt(fields[3], &profile));
129 CHECK_GT(profile, media::VIDEO_CODEC_PROFILE_UNKNOWN);
130 CHECK_LE(profile, media::VIDEO_CODEC_PROFILE_MAX);
131 test_stream->requested_profile =
132 static_cast<media::VideoCodecProfile>(profile);
133
134 if (fields.size() >= 5 && !fields[4].empty())
135 test_stream->out_filename = fields[4];
136
137 if (fields.size() >= 6 && !fields[5].empty())
138 CHECK(base::StringToUint(fields[5], &test_stream->requested_bitrate));
139
140 if (fields.size() >= 7 && !fields[6].empty())
141 CHECK(base::StringToUint(fields[6], &test_stream->requested_framerate));
142
143 if (fields.size() >= 8 && !fields[7].empty()) {
144 CHECK(base::StringToUint(fields[7],
145 &test_stream->requested_subsequent_bitrate));
146 }
147
148 if (fields.size() >= 9 && !fields[8].empty()) {
149 CHECK(base::StringToUint(fields[8],
150 &test_stream->requested_subsequent_framerate));
151 }
152
153 CHECK(test_stream->input_file.Initialize(base::FilePath(filename)));
154 test_streams->push_back(test_stream);
155 }
156 }
157
158 // Set default parameters of |test_streams| and update the parameters according
159 // to |mid_stream_bitrate_switch| and |mid_stream_framerate_switch|.
UpdateTestStreamData(bool mid_stream_bitrate_switch,bool mid_stream_framerate_switch,ScopedVector<TestStream> * test_streams)160 static void UpdateTestStreamData(bool mid_stream_bitrate_switch,
161 bool mid_stream_framerate_switch,
162 ScopedVector<TestStream>* test_streams) {
163 for (size_t i = 0; i < test_streams->size(); i++) {
164 TestStream* test_stream = (*test_streams)[i];
165 // Use defaults for bitrate/framerate if they are not provided.
166 if (test_stream->requested_bitrate == 0)
167 test_stream->requested_bitrate = kDefaultBitrate;
168
169 if (test_stream->requested_framerate == 0)
170 test_stream->requested_framerate = kDefaultFramerate;
171
172 // If bitrate/framerate switch is requested, use the subsequent values if
173 // provided, or, if not, calculate them from their initial values using
174 // the default ratios.
175 // Otherwise, if a switch is not requested, keep the initial values.
176 if (mid_stream_bitrate_switch) {
177 if (test_stream->requested_subsequent_bitrate == 0) {
178 test_stream->requested_subsequent_bitrate =
179 test_stream->requested_bitrate * kDefaultSubsequentBitrateRatio;
180 }
181 } else {
182 test_stream->requested_subsequent_bitrate =
183 test_stream->requested_bitrate;
184 }
185 if (test_stream->requested_subsequent_bitrate == 0)
186 test_stream->requested_subsequent_bitrate = 1;
187
188 if (mid_stream_framerate_switch) {
189 if (test_stream->requested_subsequent_framerate == 0) {
190 test_stream->requested_subsequent_framerate =
191 test_stream->requested_framerate * kDefaultSubsequentFramerateRatio;
192 }
193 } else {
194 test_stream->requested_subsequent_framerate =
195 test_stream->requested_framerate;
196 }
197 if (test_stream->requested_subsequent_framerate == 0)
198 test_stream->requested_subsequent_framerate = 1;
199 }
200 }
201
202 enum ClientState {
203 CS_CREATED,
204 CS_ENCODER_SET,
205 CS_INITIALIZED,
206 CS_ENCODING,
207 CS_FINISHED,
208 CS_ERROR,
209 };
210
211 // Performs basic, codec-specific sanity checks on the stream buffers passed
212 // to ProcessStreamBuffer(): whether we've seen keyframes before non-keyframes,
213 // correct sequences of H.264 NALUs (SPS before PPS and before slices), etc.
214 // Calls given FrameFoundCallback when a complete frame is found while
215 // processing.
216 class StreamValidator {
217 public:
218 // To be called when a complete frame is found while processing a stream
219 // buffer, passing true if the frame is a keyframe. Returns false if we
220 // are not interested in more frames and further processing should be aborted.
221 typedef base::Callback<bool(bool)> FrameFoundCallback;
222
~StreamValidator()223 virtual ~StreamValidator() {}
224
225 // Provide a StreamValidator instance for the given |profile|.
226 static scoped_ptr<StreamValidator> Create(media::VideoCodecProfile profile,
227 const FrameFoundCallback& frame_cb);
228
229 // Process and verify contents of a bitstream buffer.
230 virtual void ProcessStreamBuffer(const uint8* stream, size_t size) = 0;
231
232 protected:
StreamValidator(const FrameFoundCallback & frame_cb)233 explicit StreamValidator(const FrameFoundCallback& frame_cb)
234 : frame_cb_(frame_cb) {}
235
236 FrameFoundCallback frame_cb_;
237 };
238
239 class H264Validator : public StreamValidator {
240 public:
H264Validator(const FrameFoundCallback & frame_cb)241 explicit H264Validator(const FrameFoundCallback& frame_cb)
242 : StreamValidator(frame_cb),
243 seen_sps_(false),
244 seen_pps_(false),
245 seen_idr_(false) {}
246
247 void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
248
249 private:
250 // Set to true when encoder provides us with the corresponding NALU type.
251 bool seen_sps_;
252 bool seen_pps_;
253 bool seen_idr_;
254 };
255
ProcessStreamBuffer(const uint8 * stream,size_t size)256 void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
257 media::H264Parser h264_parser;
258 h264_parser.SetStream(stream, size);
259
260 while (1) {
261 media::H264NALU nalu;
262 media::H264Parser::Result result;
263
264 result = h264_parser.AdvanceToNextNALU(&nalu);
265 if (result == media::H264Parser::kEOStream)
266 break;
267
268 ASSERT_EQ(result, media::H264Parser::kOk);
269
270 bool keyframe = false;
271
272 switch (nalu.nal_unit_type) {
273 case media::H264NALU::kIDRSlice:
274 ASSERT_TRUE(seen_sps_);
275 ASSERT_TRUE(seen_pps_);
276 seen_idr_ = keyframe = true;
277 // fallthrough
278 case media::H264NALU::kNonIDRSlice:
279 ASSERT_TRUE(seen_idr_);
280 if (!frame_cb_.Run(keyframe))
281 return;
282 break;
283
284 case media::H264NALU::kSPS:
285 seen_sps_ = true;
286 break;
287
288 case media::H264NALU::kPPS:
289 ASSERT_TRUE(seen_sps_);
290 seen_pps_ = true;
291 break;
292
293 default:
294 break;
295 }
296 }
297 }
298
299 class VP8Validator : public StreamValidator {
300 public:
VP8Validator(const FrameFoundCallback & frame_cb)301 explicit VP8Validator(const FrameFoundCallback& frame_cb)
302 : StreamValidator(frame_cb),
303 seen_keyframe_(false) {}
304
305 void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
306
307 private:
308 // Have we already got a keyframe in the stream?
309 bool seen_keyframe_;
310 };
311
ProcessStreamBuffer(const uint8 * stream,size_t size)312 void VP8Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
313 bool keyframe = !(stream[0] & 0x01);
314 if (keyframe)
315 seen_keyframe_ = true;
316
317 EXPECT_TRUE(seen_keyframe_);
318
319 frame_cb_.Run(keyframe);
320 // TODO(posciak): We could be getting more frames in the buffer, but there is
321 // no simple way to detect this. We'd need to parse the frames and go through
322 // partition numbers/sizes. For now assume one frame per buffer.
323 }
324
325 // static
Create(media::VideoCodecProfile profile,const FrameFoundCallback & frame_cb)326 scoped_ptr<StreamValidator> StreamValidator::Create(
327 media::VideoCodecProfile profile,
328 const FrameFoundCallback& frame_cb) {
329 scoped_ptr<StreamValidator> validator;
330
331 if (profile >= media::H264PROFILE_MIN &&
332 profile <= media::H264PROFILE_MAX) {
333 validator.reset(new H264Validator(frame_cb));
334 } else if (profile >= media::VP8PROFILE_MIN &&
335 profile <= media::VP8PROFILE_MAX) {
336 validator.reset(new VP8Validator(frame_cb));
337 } else {
338 LOG(FATAL) << "Unsupported profile: " << profile;
339 }
340
341 return validator.Pass();
342 }
343
344 class VEAClient : public VideoEncodeAccelerator::Client {
345 public:
346 VEAClient(const TestStream& test_stream,
347 ClientStateNotification<ClientState>* note,
348 bool save_to_file,
349 unsigned int keyframe_period,
350 bool force_bitrate,
351 bool test_perf);
352 virtual ~VEAClient();
353 void CreateEncoder();
354 void DestroyEncoder();
355
356 // Return the number of encoded frames per second.
357 double frames_per_second();
358
359 // VideoDecodeAccelerator::Client implementation.
360 void RequireBitstreamBuffers(unsigned int input_count,
361 const gfx::Size& input_coded_size,
362 size_t output_buffer_size) OVERRIDE;
363 void BitstreamBufferReady(int32 bitstream_buffer_id,
364 size_t payload_size,
365 bool key_frame) OVERRIDE;
366 void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
367
368 private:
has_encoder()369 bool has_encoder() { return encoder_.get(); }
370
371 void SetState(ClientState new_state);
372
373 // Set current stream parameters to given |bitrate| at |framerate|.
374 void SetStreamParameters(unsigned int bitrate, unsigned int framerate);
375
376 // Called when encoder is done with a VideoFrame.
377 void InputNoLongerNeededCallback(int32 input_id);
378
379 // Ensure encoder has at least as many inputs as it asked for
380 // via RequireBitstreamBuffers().
381 void FeedEncoderWithInputs();
382
383 // Provide the encoder with a new output buffer.
384 void FeedEncoderWithOutput(base::SharedMemory* shm);
385
386 // Called on finding a complete frame (with |keyframe| set to true for
387 // keyframes) in the stream, to perform codec-independent, per-frame checks
388 // and accounting. Returns false once we have collected all frames we needed.
389 bool HandleEncodedFrame(bool keyframe);
390
391 // Verify that stream bitrate has been close to current_requested_bitrate_,
392 // assuming current_framerate_ since the last time VerifyStreamProperties()
393 // was called. Fail the test if |force_bitrate_| is true and the bitrate
394 // is not within kBitrateTolerance.
395 void VerifyStreamProperties();
396
397 // Test codec performance, failing the test if we are currently running
398 // the performance test.
399 void VerifyPerf();
400
401 // Prepare and return a frame wrapping the data at |position| bytes in
402 // the input stream, ready to be sent to encoder.
403 scoped_refptr<media::VideoFrame> PrepareInputFrame(off_t position);
404
405 ClientState state_;
406 scoped_ptr<VideoEncodeAccelerator> encoder_;
407
408 const TestStream& test_stream_;
409 // Used to notify another thread about the state. VEAClient does not own this.
410 ClientStateNotification<ClientState>* note_;
411
412 // Ids assigned to VideoFrames (start at 1 for easy comparison with
413 // num_encoded_frames_).
414 std::set<int32> inputs_at_client_;
415 int32 next_input_id_;
416
417 // Ids for output BitstreamBuffers.
418 typedef std::map<int32, base::SharedMemory*> IdToSHM;
419 ScopedVector<base::SharedMemory> output_shms_;
420 IdToSHM output_buffers_at_client_;
421 int32 next_output_buffer_id_;
422
423 // Current offset into input stream.
424 off_t pos_in_input_stream_;
425 // Byte size of an input frame.
426 size_t input_buffer_size_;
427 gfx::Size input_coded_size_;
428 // Requested by encoder.
429 unsigned int num_required_input_buffers_;
430 size_t output_buffer_size_;
431
432 // Precalculated number of frames in the stream.
433 unsigned int num_frames_in_stream_;
434
435 // Number of frames to encode. This may differ from num_frames_in_stream_ if
436 // we need more frames for bitrate tests.
437 unsigned int num_frames_to_encode_;
438
439 // Number of encoded frames we've got from the encoder thus far.
440 unsigned int num_encoded_frames_;
441
442 // Frames since last bitrate verification.
443 unsigned int num_frames_since_last_check_;
444
445 // True if received a keyframe while processing current bitstream buffer.
446 bool seen_keyframe_in_this_buffer_;
447
448 // True if we are to save the encoded stream to a file.
449 bool save_to_file_;
450
451 // Request a keyframe every keyframe_period_ frames.
452 const unsigned int keyframe_period_;
453
454 // Frame number for which we requested a keyframe.
455 unsigned int keyframe_requested_at_;
456
457 // True if we are asking encoder for a particular bitrate.
458 bool force_bitrate_;
459
460 // Current requested bitrate.
461 unsigned int current_requested_bitrate_;
462
463 // Current expected framerate.
464 unsigned int current_framerate_;
465
466 // Byte size of the encoded stream (for bitrate calculation) since last
467 // time we checked bitrate.
468 size_t encoded_stream_size_since_last_check_;
469
470 // If true, verify performance at the end of the test.
471 bool test_perf_;
472
473 scoped_ptr<StreamValidator> validator_;
474
475 // The time when the encoding started.
476 base::TimeTicks encode_start_time_;
477
478 // The time when the last encoded frame is ready.
479 base::TimeTicks last_frame_ready_time_;
480
481 // All methods of this class should be run on the same thread.
482 base::ThreadChecker thread_checker_;
483 };
484
VEAClient(const TestStream & test_stream,ClientStateNotification<ClientState> * note,bool save_to_file,unsigned int keyframe_period,bool force_bitrate,bool test_perf)485 VEAClient::VEAClient(const TestStream& test_stream,
486 ClientStateNotification<ClientState>* note,
487 bool save_to_file,
488 unsigned int keyframe_period,
489 bool force_bitrate,
490 bool test_perf)
491 : state_(CS_CREATED),
492 test_stream_(test_stream),
493 note_(note),
494 next_input_id_(1),
495 next_output_buffer_id_(0),
496 pos_in_input_stream_(0),
497 input_buffer_size_(0),
498 num_required_input_buffers_(0),
499 output_buffer_size_(0),
500 num_frames_in_stream_(0),
501 num_frames_to_encode_(0),
502 num_encoded_frames_(0),
503 num_frames_since_last_check_(0),
504 seen_keyframe_in_this_buffer_(false),
505 save_to_file_(save_to_file),
506 keyframe_period_(keyframe_period),
507 keyframe_requested_at_(kMaxFrameNum),
508 force_bitrate_(force_bitrate),
509 current_requested_bitrate_(0),
510 current_framerate_(0),
511 encoded_stream_size_since_last_check_(0),
512 test_perf_(test_perf) {
513 if (keyframe_period_)
514 CHECK_LT(kMaxKeyframeDelay, keyframe_period_);
515
516 validator_ = StreamValidator::Create(
517 test_stream_.requested_profile,
518 base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
519
520 CHECK(validator_.get());
521
522 if (save_to_file_) {
523 CHECK(!test_stream_.out_filename.empty());
524 base::FilePath out_filename(test_stream_.out_filename);
525 // This creates or truncates out_filename.
526 // Without it, AppendToFile() will not work.
527 EXPECT_EQ(0, base::WriteFile(out_filename, NULL, 0));
528 }
529
530 input_buffer_size_ =
531 media::VideoFrame::AllocationSize(kInputFormat, test_stream.size);
532 CHECK_GT(input_buffer_size_, 0UL);
533
534 // Calculate the number of frames in the input stream by dividing its length
535 // in bytes by frame size in bytes.
536 CHECK_EQ(test_stream_.input_file.length() % input_buffer_size_, 0U)
537 << "Stream byte size is not a product of calculated frame byte size";
538 num_frames_in_stream_ = test_stream_.input_file.length() / input_buffer_size_;
539 CHECK_GT(num_frames_in_stream_, 0UL);
540 CHECK_LE(num_frames_in_stream_, kMaxFrameNum);
541
542 // We may need to loop over the stream more than once if more frames than
543 // provided is required for bitrate tests.
544 if (force_bitrate_ && num_frames_in_stream_ < kMinFramesForBitrateTests) {
545 DVLOG(1) << "Stream too short for bitrate test (" << num_frames_in_stream_
546 << " frames), will loop it to reach " << kMinFramesForBitrateTests
547 << " frames";
548 num_frames_to_encode_ = kMinFramesForBitrateTests;
549 } else {
550 num_frames_to_encode_ = num_frames_in_stream_;
551 }
552
553 thread_checker_.DetachFromThread();
554 }
555
~VEAClient()556 VEAClient::~VEAClient() { CHECK(!has_encoder()); }
557
CreateEncoder()558 void VEAClient::CreateEncoder() {
559 DCHECK(thread_checker_.CalledOnValidThread());
560 CHECK(!has_encoder());
561
562 scoped_ptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
563 encoder_.reset(new V4L2VideoEncodeAccelerator(device.Pass()));
564 SetState(CS_ENCODER_SET);
565
566 DVLOG(1) << "Profile: " << test_stream_.requested_profile
567 << ", initial bitrate: " << test_stream_.requested_bitrate;
568 if (!encoder_->Initialize(kInputFormat,
569 test_stream_.size,
570 test_stream_.requested_profile,
571 test_stream_.requested_bitrate,
572 this)) {
573 DLOG(ERROR) << "VideoEncodeAccelerator::Initialize() failed";
574 SetState(CS_ERROR);
575 return;
576 }
577
578 SetStreamParameters(test_stream_.requested_bitrate,
579 test_stream_.requested_framerate);
580 SetState(CS_INITIALIZED);
581 }
582
DestroyEncoder()583 void VEAClient::DestroyEncoder() {
584 DCHECK(thread_checker_.CalledOnValidThread());
585 if (!has_encoder())
586 return;
587 encoder_.reset();
588 }
589
frames_per_second()590 double VEAClient::frames_per_second() {
591 base::TimeDelta duration = last_frame_ready_time_ - encode_start_time_;
592 return num_encoded_frames_ / duration.InSecondsF();
593 }
594
RequireBitstreamBuffers(unsigned int input_count,const gfx::Size & input_coded_size,size_t output_size)595 void VEAClient::RequireBitstreamBuffers(unsigned int input_count,
596 const gfx::Size& input_coded_size,
597 size_t output_size) {
598 DCHECK(thread_checker_.CalledOnValidThread());
599 ASSERT_EQ(state_, CS_INITIALIZED);
600 SetState(CS_ENCODING);
601
602 // TODO(posciak): For now we only support input streams that meet encoder
603 // size requirements exactly (i.e. coded size == visible size), so that we
604 // can simply mmap the stream file and feed the encoder directly with chunks
605 // of that, instead of memcpying from mmapped file into a separate set of
606 // input buffers that would meet the coded size and alignment requirements.
607 // If/when this is changed, the ARM-specific alignment check below should be
608 // redone as well.
609 input_coded_size_ = input_coded_size;
610 ASSERT_EQ(input_coded_size_, test_stream_.size);
611 #if defined(ARCH_CPU_ARMEL)
612 // ARM performs CPU cache management with CPU cache line granularity. We thus
613 // need to ensure our buffers are CPU cache line-aligned (64 byte-aligned).
614 // Otherwise newer kernels will refuse to accept them, and on older kernels
615 // we'll be treating ourselves to random corruption.
616 // Since we are just mmapping and passing chunks of the input file, to ensure
617 // alignment, if the starting virtual addresses of the frames in it were not
618 // 64 byte-aligned, we'd have to use a separate set of input buffers and copy
619 // the frames into them before sending to the encoder. It would have been an
620 // overkill here though, because, for now at least, we only test resolutions
621 // that result in proper alignment, and it would have also interfered with
622 // performance testing. So just assert that the frame size is a multiple of
623 // 64 bytes. This ensures all frames start at 64-byte boundary, because
624 // MemoryMappedFile should be mmapp()ed at virtual page start as well.
625 ASSERT_EQ(input_buffer_size_ & 63, 0u)
626 << "Frame size has to be a multiple of 64 bytes";
627 ASSERT_EQ(reinterpret_cast<off_t>(test_stream_.input_file.data()) & 63, 0)
628 << "Mapped file should be mapped at a 64 byte boundary";
629 #endif
630
631 num_required_input_buffers_ = input_count;
632 ASSERT_GT(num_required_input_buffers_, 0UL);
633
634 output_buffer_size_ = output_size;
635 ASSERT_GT(output_buffer_size_, 0UL);
636
637 for (unsigned int i = 0; i < kNumOutputBuffers; ++i) {
638 base::SharedMemory* shm = new base::SharedMemory();
639 CHECK(shm->CreateAndMapAnonymous(output_buffer_size_));
640 output_shms_.push_back(shm);
641 FeedEncoderWithOutput(shm);
642 }
643
644 encode_start_time_ = base::TimeTicks::Now();
645 FeedEncoderWithInputs();
646 }
647
BitstreamBufferReady(int32 bitstream_buffer_id,size_t payload_size,bool key_frame)648 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id,
649 size_t payload_size,
650 bool key_frame) {
651 DCHECK(thread_checker_.CalledOnValidThread());
652 ASSERT_LE(payload_size, output_buffer_size_);
653
654 IdToSHM::iterator it = output_buffers_at_client_.find(bitstream_buffer_id);
655 ASSERT_NE(it, output_buffers_at_client_.end());
656 base::SharedMemory* shm = it->second;
657 output_buffers_at_client_.erase(it);
658
659 if (state_ == CS_FINISHED)
660 return;
661
662 encoded_stream_size_since_last_check_ += payload_size;
663
664 const uint8* stream_ptr = static_cast<const uint8*>(shm->memory());
665 if (payload_size > 0)
666 validator_->ProcessStreamBuffer(stream_ptr, payload_size);
667
668 EXPECT_EQ(key_frame, seen_keyframe_in_this_buffer_);
669 seen_keyframe_in_this_buffer_ = false;
670
671 if (save_to_file_) {
672 int size = base::checked_cast<int>(payload_size);
673 EXPECT_EQ(base::AppendToFile(
674 base::FilePath::FromUTF8Unsafe(test_stream_.out_filename),
675 static_cast<char*>(shm->memory()),
676 size),
677 size);
678 }
679
680 FeedEncoderWithOutput(shm);
681 }
682
NotifyError(VideoEncodeAccelerator::Error error)683 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error) {
684 DCHECK(thread_checker_.CalledOnValidThread());
685 SetState(CS_ERROR);
686 }
687
SetState(ClientState new_state)688 void VEAClient::SetState(ClientState new_state) {
689 DVLOG(4) << "Changing state " << state_ << "->" << new_state;
690 note_->Notify(new_state);
691 state_ = new_state;
692 }
693
SetStreamParameters(unsigned int bitrate,unsigned int framerate)694 void VEAClient::SetStreamParameters(unsigned int bitrate,
695 unsigned int framerate) {
696 current_requested_bitrate_ = bitrate;
697 current_framerate_ = framerate;
698 CHECK_GT(current_requested_bitrate_, 0UL);
699 CHECK_GT(current_framerate_, 0UL);
700 encoder_->RequestEncodingParametersChange(current_requested_bitrate_,
701 current_framerate_);
702 DVLOG(1) << "Switched parameters to " << current_requested_bitrate_
703 << " bps @ " << current_framerate_ << " FPS";
704 }
705
InputNoLongerNeededCallback(int32 input_id)706 void VEAClient::InputNoLongerNeededCallback(int32 input_id) {
707 std::set<int32>::iterator it = inputs_at_client_.find(input_id);
708 ASSERT_NE(it, inputs_at_client_.end());
709 inputs_at_client_.erase(it);
710 FeedEncoderWithInputs();
711 }
712
PrepareInputFrame(off_t position)713 scoped_refptr<media::VideoFrame> VEAClient::PrepareInputFrame(off_t position) {
714 CHECK_LE(position + input_buffer_size_, test_stream_.input_file.length());
715
716 uint8* frame_data =
717 const_cast<uint8*>(test_stream_.input_file.data() + position);
718
719 CHECK_GT(current_framerate_, 0U);
720 scoped_refptr<media::VideoFrame> frame =
721 media::VideoFrame::WrapExternalYuvData(
722 kInputFormat,
723 input_coded_size_,
724 gfx::Rect(test_stream_.size),
725 test_stream_.size,
726 input_coded_size_.width(),
727 input_coded_size_.width() / 2,
728 input_coded_size_.width() / 2,
729 frame_data,
730 frame_data + input_coded_size_.GetArea(),
731 frame_data + (input_coded_size_.GetArea() * 5 / 4),
732 base::TimeDelta().FromMilliseconds(
733 next_input_id_ * base::Time::kMillisecondsPerSecond /
734 current_framerate_),
735 media::BindToCurrentLoop(
736 base::Bind(&VEAClient::InputNoLongerNeededCallback,
737 base::Unretained(this),
738 next_input_id_)));
739
740 CHECK(inputs_at_client_.insert(next_input_id_).second);
741 ++next_input_id_;
742
743 return frame;
744 }
745
FeedEncoderWithInputs()746 void VEAClient::FeedEncoderWithInputs() {
747 if (!has_encoder())
748 return;
749
750 if (state_ != CS_ENCODING)
751 return;
752
753 while (inputs_at_client_.size() <
754 num_required_input_buffers_ + kNumExtraInputFrames) {
755 size_t bytes_left = test_stream_.input_file.length() - pos_in_input_stream_;
756 if (bytes_left < input_buffer_size_) {
757 DCHECK_EQ(bytes_left, 0UL);
758 // Rewind if at the end of stream and we are still encoding.
759 // This is to flush the encoder with additional frames from the beginning
760 // of the stream, or if the stream is shorter that the number of frames
761 // we require for bitrate tests.
762 pos_in_input_stream_ = 0;
763 continue;
764 }
765
766 bool force_keyframe = false;
767 if (keyframe_period_ && next_input_id_ % keyframe_period_ == 0) {
768 keyframe_requested_at_ = next_input_id_;
769 force_keyframe = true;
770 }
771
772 scoped_refptr<media::VideoFrame> video_frame =
773 PrepareInputFrame(pos_in_input_stream_);
774 pos_in_input_stream_ += input_buffer_size_;
775
776 encoder_->Encode(video_frame, force_keyframe);
777 }
778 }
779
FeedEncoderWithOutput(base::SharedMemory * shm)780 void VEAClient::FeedEncoderWithOutput(base::SharedMemory* shm) {
781 if (!has_encoder())
782 return;
783
784 if (state_ != CS_ENCODING)
785 return;
786
787 base::SharedMemoryHandle dup_handle;
788 CHECK(shm->ShareToProcess(base::Process::Current().handle(), &dup_handle));
789
790 media::BitstreamBuffer bitstream_buffer(
791 next_output_buffer_id_++, dup_handle, output_buffer_size_);
792 CHECK(output_buffers_at_client_.insert(std::make_pair(bitstream_buffer.id(),
793 shm)).second);
794 encoder_->UseOutputBitstreamBuffer(bitstream_buffer);
795 }
796
HandleEncodedFrame(bool keyframe)797 bool VEAClient::HandleEncodedFrame(bool keyframe) {
798 // This would be a bug in the test, which should not ignore false
799 // return value from this method.
800 CHECK_LE(num_encoded_frames_, num_frames_to_encode_);
801
802 ++num_encoded_frames_;
803 ++num_frames_since_last_check_;
804
805 last_frame_ready_time_ = base::TimeTicks::Now();
806 if (keyframe) {
807 // Got keyframe, reset keyframe detection regardless of whether we
808 // got a frame in time or not.
809 keyframe_requested_at_ = kMaxFrameNum;
810 seen_keyframe_in_this_buffer_ = true;
811 }
812
813 // Because the keyframe behavior requirements are loose, we give
814 // the encoder more freedom here. It could either deliver a keyframe
815 // immediately after we requested it, which could be for a frame number
816 // before the one we requested it for (if the keyframe request
817 // is asynchronous, i.e. not bound to any concrete frame, and because
818 // the pipeline can be deeper than one frame), at that frame, or after.
819 // So the only constraints we put here is that we get a keyframe not
820 // earlier than we requested one (in time), and not later than
821 // kMaxKeyframeDelay frames after the frame, for which we requested
822 // it, comes back encoded.
823 EXPECT_LE(num_encoded_frames_, keyframe_requested_at_ + kMaxKeyframeDelay);
824
825 if (num_encoded_frames_ == num_frames_to_encode_ / 2) {
826 VerifyStreamProperties();
827 if (test_stream_.requested_subsequent_bitrate !=
828 current_requested_bitrate_ ||
829 test_stream_.requested_subsequent_framerate != current_framerate_) {
830 SetStreamParameters(test_stream_.requested_subsequent_bitrate,
831 test_stream_.requested_subsequent_framerate);
832 }
833 } else if (num_encoded_frames_ == num_frames_to_encode_) {
834 VerifyPerf();
835 VerifyStreamProperties();
836 SetState(CS_FINISHED);
837 return false;
838 }
839
840 return true;
841 }
842
VerifyPerf()843 void VEAClient::VerifyPerf() {
844 double measured_fps = frames_per_second();
845 LOG(INFO) << "Measured encoder FPS: " << measured_fps;
846 if (test_perf_)
847 EXPECT_GE(measured_fps, kMinPerfFPS);
848 }
849
VerifyStreamProperties()850 void VEAClient::VerifyStreamProperties() {
851 CHECK_GT(num_frames_since_last_check_, 0UL);
852 CHECK_GT(encoded_stream_size_since_last_check_, 0UL);
853 unsigned int bitrate = encoded_stream_size_since_last_check_ * 8 *
854 current_framerate_ / num_frames_since_last_check_;
855 DVLOG(1) << "Current chunk's bitrate: " << bitrate
856 << " (expected: " << current_requested_bitrate_
857 << " @ " << current_framerate_ << " FPS,"
858 << " num frames in chunk: " << num_frames_since_last_check_;
859
860 num_frames_since_last_check_ = 0;
861 encoded_stream_size_since_last_check_ = 0;
862
863 if (force_bitrate_) {
864 EXPECT_NEAR(bitrate,
865 current_requested_bitrate_,
866 kBitrateTolerance * current_requested_bitrate_);
867 }
868 }
869
870 // Test parameters:
871 // - Number of concurrent encoders.
872 // - If true, save output to file (provided an output filename was supplied).
873 // - Force a keyframe every n frames.
874 // - Force bitrate; the actual required value is provided as a property
875 // of the input stream, because it depends on stream type/resolution/etc.
876 // - If true, measure performance.
877 // - If true, switch bitrate mid-stream.
878 // - If true, switch framerate mid-stream.
879 class VideoEncodeAcceleratorTest
880 : public ::testing::TestWithParam<
881 Tuple7<int, bool, int, bool, bool, bool, bool> > {};
882
TEST_P(VideoEncodeAcceleratorTest,TestSimpleEncode)883 TEST_P(VideoEncodeAcceleratorTest, TestSimpleEncode) {
884 const size_t num_concurrent_encoders = GetParam().a;
885 const bool save_to_file = GetParam().b;
886 const unsigned int keyframe_period = GetParam().c;
887 const bool force_bitrate = GetParam().d;
888 const bool test_perf = GetParam().e;
889 const bool mid_stream_bitrate_switch = GetParam().f;
890 const bool mid_stream_framerate_switch = GetParam().g;
891
892 // Initialize the test streams.
893 ScopedVector<TestStream> test_streams;
894 ParseAndReadTestStreamData(*g_test_stream_data, &test_streams);
895 UpdateTestStreamData(
896 mid_stream_bitrate_switch, mid_stream_framerate_switch, &test_streams);
897
898 ScopedVector<ClientStateNotification<ClientState> > notes;
899 ScopedVector<VEAClient> clients;
900 base::Thread encoder_thread("EncoderThread");
901 ASSERT_TRUE(encoder_thread.Start());
902
903 // Create all encoders.
904 for (size_t i = 0; i < num_concurrent_encoders; i++) {
905 size_t test_stream_index = i % test_streams.size();
906 // Disregard save_to_file if we didn't get an output filename.
907 bool encoder_save_to_file =
908 (save_to_file &&
909 !test_streams[test_stream_index]->out_filename.empty());
910
911 notes.push_back(new ClientStateNotification<ClientState>());
912 clients.push_back(new VEAClient(*test_streams[test_stream_index],
913 notes.back(),
914 encoder_save_to_file,
915 keyframe_period,
916 force_bitrate,
917 test_perf));
918
919 encoder_thread.message_loop()->PostTask(
920 FROM_HERE,
921 base::Bind(&VEAClient::CreateEncoder,
922 base::Unretained(clients.back())));
923 }
924
925 // All encoders must pass through states in this order.
926 enum ClientState state_transitions[] = {CS_ENCODER_SET, CS_INITIALIZED,
927 CS_ENCODING, CS_FINISHED};
928
929 // Wait for all encoders to go through all states and finish.
930 // Do this by waiting for all encoders to advance to state n before checking
931 // state n+1, to verify that they are able to operate concurrently.
932 // It also simulates the real-world usage better, as the main thread, on which
933 // encoders are created/destroyed, is a single GPU Process ChildThread.
934 // Moreover, we can't have proper multithreading on X11, so this could cause
935 // hard to debug issues there, if there were multiple "ChildThreads".
936 for (size_t state_no = 0; state_no < arraysize(state_transitions); ++state_no)
937 for (size_t i = 0; i < num_concurrent_encoders; i++)
938 ASSERT_EQ(notes[i]->Wait(), state_transitions[state_no]);
939
940 for (size_t i = 0; i < num_concurrent_encoders; ++i) {
941 encoder_thread.message_loop()->PostTask(
942 FROM_HERE,
943 base::Bind(&VEAClient::DestroyEncoder, base::Unretained(clients[i])));
944 }
945
946 // This ensures all tasks have finished.
947 encoder_thread.Stop();
948 }
949
950 INSTANTIATE_TEST_CASE_P(
951 SimpleEncode,
952 VideoEncodeAcceleratorTest,
953 ::testing::Values(MakeTuple(1, true, 0, false, false, false, false)));
954
955 INSTANTIATE_TEST_CASE_P(
956 EncoderPerf,
957 VideoEncodeAcceleratorTest,
958 ::testing::Values(MakeTuple(1, false, 0, false, true, false, false)));
959
960 INSTANTIATE_TEST_CASE_P(
961 ForceKeyframes,
962 VideoEncodeAcceleratorTest,
963 ::testing::Values(MakeTuple(1, false, 10, false, false, false, false)));
964
965 INSTANTIATE_TEST_CASE_P(
966 ForceBitrate,
967 VideoEncodeAcceleratorTest,
968 ::testing::Values(MakeTuple(1, false, 0, true, false, false, false)));
969
970 INSTANTIATE_TEST_CASE_P(
971 MidStreamParamSwitchBitrate,
972 VideoEncodeAcceleratorTest,
973 ::testing::Values(MakeTuple(1, false, 0, true, false, true, false)));
974
975 INSTANTIATE_TEST_CASE_P(
976 MidStreamParamSwitchFPS,
977 VideoEncodeAcceleratorTest,
978 ::testing::Values(MakeTuple(1, false, 0, true, false, false, true)));
979
980 INSTANTIATE_TEST_CASE_P(
981 MidStreamParamSwitchBitrateAndFPS,
982 VideoEncodeAcceleratorTest,
983 ::testing::Values(MakeTuple(1, false, 0, true, false, true, true)));
984
985 INSTANTIATE_TEST_CASE_P(
986 MultipleEncoders,
987 VideoEncodeAcceleratorTest,
988 ::testing::Values(MakeTuple(3, false, 0, false, false, false, false),
989 MakeTuple(3, false, 0, true, false, true, true)));
990
991 // TODO(posciak): more tests:
992 // - async FeedEncoderWithOutput
993 // - out-of-order return of outputs to encoder
994 // - multiple encoders + decoders
995 // - mid-stream encoder_->Destroy()
996
997 } // namespace
998 } // namespace content
999
main(int argc,char ** argv)1000 int main(int argc, char** argv) {
1001 testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args.
1002 CommandLine::Init(argc, argv);
1003
1004 base::ShadowingAtExitManager at_exit_manager;
1005 scoped_ptr<base::FilePath::StringType> test_stream_data(
1006 new base::FilePath::StringType(
1007 media::GetTestDataFilePath(content::g_default_in_filename).value() +
1008 content::g_default_in_parameters));
1009 content::g_test_stream_data = test_stream_data.get();
1010
1011 // Needed to enable DVLOG through --vmodule.
1012 logging::LoggingSettings settings;
1013 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
1014 CHECK(logging::InitLogging(settings));
1015
1016 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1017 DCHECK(cmd_line);
1018
1019 CommandLine::SwitchMap switches = cmd_line->GetSwitches();
1020 for (CommandLine::SwitchMap::const_iterator it = switches.begin();
1021 it != switches.end();
1022 ++it) {
1023 if (it->first == "test_stream_data") {
1024 test_stream_data->assign(it->second.c_str());
1025 continue;
1026 }
1027 if (it->first == "v" || it->first == "vmodule")
1028 continue;
1029 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
1030 }
1031
1032 return RUN_ALL_TESTS();
1033 }
1034