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.
5 #include "base/at_exit.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"
25 using media::VideoEncodeAccelerator;
30 const media::VideoFrame::Format kInputFormat = media::VideoFrame::I420;
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;
56 // The syntax of multiple test streams is:
57 // test-stream1;test-stream2;test-stream3
58 // The syntax of each test stream is:
59 // "in_filename:width:height:out_filename:requested_bitrate:requested_framerate
60 // :requested_subsequent_bitrate:requested_subsequent_framerate"
61 // - |in_filename| must be an I420 (YUV planar) raw stream
62 // (see http://www.fourcc.org/yuv.php#IYUV).
63 // - |width| and |height| are in pixels.
64 // - |profile| to encode into (values of media::VideoCodecProfile).
65 // - |out_filename| filename to save the encoded stream to (optional).
66 // Output stream is saved for the simple encode test only.
67 // Further parameters are optional (need to provide preceding positional
68 // parameters if a specific subsequent parameter is required):
69 // - |requested_bitrate| requested bitrate in bits per second.
70 // - |requested_framerate| requested initial framerate.
71 // - |requested_subsequent_bitrate| bitrate to switch to in the middle of the
73 // - |requested_subsequent_framerate| framerate to switch to in the middle
75 // Bitrate is only forced for tests that test bitrate.
76 const char* g_default_in_filename = "sync_192p20_frames.yuv";
77 const char* g_default_in_parameters = ":320:192:1:out.h264:200000";
78 base::FilePath::StringType* g_test_stream_data;
82 : requested_bitrate(0),
83 requested_framerate(0),
84 requested_subsequent_bitrate(0),
85 requested_subsequent_framerate(0) {}
89 base::MemoryMappedFile input_file;
90 media::VideoCodecProfile requested_profile;
91 std::string out_filename;
92 unsigned int requested_bitrate;
93 unsigned int requested_framerate;
94 unsigned int requested_subsequent_bitrate;
95 unsigned int requested_subsequent_framerate;
98 // Parse |data| into its constituent parts, set the various output fields
99 // accordingly, read in video stream, and store them to |test_streams|.
100 static void ParseAndReadTestStreamData(const base::FilePath::StringType& data,
101 ScopedVector<TestStream>* test_streams) {
102 // Split the string to individual test stream data.
103 std::vector<base::FilePath::StringType> test_streams_data;
104 base::SplitString(data, ';', &test_streams_data);
105 CHECK_GE(test_streams_data.size(), 1U) << data;
107 // Parse each test stream data and read the input file.
108 for (size_t index = 0; index < test_streams_data.size(); ++index) {
109 std::vector<base::FilePath::StringType> fields;
110 base::SplitString(test_streams_data[index], ':', &fields);
111 CHECK_GE(fields.size(), 4U) << data;
112 CHECK_LE(fields.size(), 9U) << data;
113 TestStream* test_stream = new TestStream();
115 base::FilePath::StringType filename = fields[0];
117 CHECK(base::StringToInt(fields[1], &width));
118 CHECK(base::StringToInt(fields[2], &height));
119 test_stream->size = gfx::Size(width, height);
120 CHECK(!test_stream->size.IsEmpty());
122 CHECK(base::StringToInt(fields[3], &profile));
123 CHECK_GT(profile, media::VIDEO_CODEC_PROFILE_UNKNOWN);
124 CHECK_LE(profile, media::VIDEO_CODEC_PROFILE_MAX);
125 test_stream->requested_profile =
126 static_cast<media::VideoCodecProfile>(profile);
128 if (fields.size() >= 5 && !fields[4].empty())
129 test_stream->out_filename = fields[4];
131 if (fields.size() >= 6 && !fields[5].empty())
132 CHECK(base::StringToUint(fields[5], &test_stream->requested_bitrate));
134 if (fields.size() >= 7 && !fields[6].empty())
135 CHECK(base::StringToUint(fields[6], &test_stream->requested_framerate));
137 if (fields.size() >= 8 && !fields[7].empty()) {
138 CHECK(base::StringToUint(fields[7],
139 &test_stream->requested_subsequent_bitrate));
142 if (fields.size() >= 9 && !fields[8].empty()) {
143 CHECK(base::StringToUint(fields[8],
144 &test_stream->requested_subsequent_framerate));
147 CHECK(test_stream->input_file.Initialize(base::FilePath(filename)));
148 test_streams->push_back(test_stream);
152 // Set default parameters of |test_streams| and update the parameters according
153 // to |mid_stream_bitrate_switch| and |mid_stream_framerate_switch|.
154 static void UpdateTestStreamData(bool mid_stream_bitrate_switch,
155 bool mid_stream_framerate_switch,
156 ScopedVector<TestStream>* test_streams) {
157 for (size_t i = 0; i < test_streams->size(); i++) {
158 TestStream* test_stream = (*test_streams)[i];
159 // Use defaults for bitrate/framerate if they are not provided.
160 if (test_stream->requested_bitrate == 0)
161 test_stream->requested_bitrate = kDefaultBitrate;
163 if (test_stream->requested_framerate == 0)
164 test_stream->requested_framerate = kDefaultFramerate;
166 // If bitrate/framerate switch is requested, use the subsequent values if
167 // provided, or, if not, calculate them from their initial values using
168 // the default ratios.
169 // Otherwise, if a switch is not requested, keep the initial values.
170 if (mid_stream_bitrate_switch) {
171 if (test_stream->requested_subsequent_bitrate == 0) {
172 test_stream->requested_subsequent_bitrate =
173 test_stream->requested_bitrate * kDefaultSubsequentBitrateRatio;
176 test_stream->requested_subsequent_bitrate =
177 test_stream->requested_bitrate;
179 if (test_stream->requested_subsequent_bitrate == 0)
180 test_stream->requested_subsequent_bitrate = 1;
182 if (mid_stream_framerate_switch) {
183 if (test_stream->requested_subsequent_framerate == 0) {
184 test_stream->requested_subsequent_framerate =
185 test_stream->requested_framerate * kDefaultSubsequentFramerateRatio;
188 test_stream->requested_subsequent_framerate =
189 test_stream->requested_framerate;
191 if (test_stream->requested_subsequent_framerate == 0)
192 test_stream->requested_subsequent_framerate = 1;
206 // Performs basic, codec-specific sanity checks on the stream buffers passed
207 // to ProcessStreamBuffer(): whether we've seen keyframes before non-keyframes,
208 // correct sequences of H.264 NALUs (SPS before PPS and before slices), etc.
209 // Calls given FrameFoundCallback when a complete frame is found while
211 class StreamValidator {
213 // To be called when a complete frame is found while processing a stream
214 // buffer, passing true if the frame is a keyframe. Returns false if we
215 // are not interested in more frames and further processing should be aborted.
216 typedef base::Callback<bool(bool)> FrameFoundCallback;
218 virtual ~StreamValidator() {}
220 // Provide a StreamValidator instance for the given |profile|.
221 static scoped_ptr<StreamValidator> Create(media::VideoCodecProfile profile,
222 const FrameFoundCallback& frame_cb);
224 // Process and verify contents of a bitstream buffer.
225 virtual void ProcessStreamBuffer(const uint8* stream, size_t size) = 0;
228 explicit StreamValidator(const FrameFoundCallback& frame_cb)
229 : frame_cb_(frame_cb) {}
231 FrameFoundCallback frame_cb_;
234 class H264Validator : public StreamValidator {
236 explicit H264Validator(const FrameFoundCallback& frame_cb)
237 : StreamValidator(frame_cb),
242 void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
245 // Set to true when encoder provides us with the corresponding NALU type.
251 void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
252 media::H264Parser h264_parser;
253 h264_parser.SetStream(stream, size);
256 media::H264NALU nalu;
257 media::H264Parser::Result result;
259 result = h264_parser.AdvanceToNextNALU(&nalu);
260 if (result == media::H264Parser::kEOStream)
263 ASSERT_EQ(result, media::H264Parser::kOk);
265 bool keyframe = false;
267 switch (nalu.nal_unit_type) {
268 case media::H264NALU::kIDRSlice:
269 ASSERT_TRUE(seen_sps_);
270 ASSERT_TRUE(seen_pps_);
271 seen_idr_ = keyframe = true;
273 case media::H264NALU::kNonIDRSlice:
274 ASSERT_TRUE(seen_idr_);
275 if (!frame_cb_.Run(keyframe))
279 case media::H264NALU::kSPS:
283 case media::H264NALU::kPPS:
284 ASSERT_TRUE(seen_sps_);
294 class VP8Validator : public StreamValidator {
296 explicit VP8Validator(const FrameFoundCallback& frame_cb)
297 : StreamValidator(frame_cb),
298 seen_keyframe_(false) {}
300 void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
303 // Have we already got a keyframe in the stream?
307 void VP8Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
308 bool keyframe = !(stream[0] & 0x01);
310 seen_keyframe_ = true;
312 EXPECT_TRUE(seen_keyframe_);
314 frame_cb_.Run(keyframe);
315 // TODO(posciak): We could be getting more frames in the buffer, but there is
316 // no simple way to detect this. We'd need to parse the frames and go through
317 // partition numbers/sizes. For now assume one frame per buffer.
321 scoped_ptr<StreamValidator> StreamValidator::Create(
322 media::VideoCodecProfile profile,
323 const FrameFoundCallback& frame_cb) {
324 scoped_ptr<StreamValidator> validator;
326 if (profile >= media::H264PROFILE_MIN &&
327 profile <= media::H264PROFILE_MAX) {
328 validator.reset(new H264Validator(frame_cb));
329 } else if (profile >= media::VP8PROFILE_MIN &&
330 profile <= media::VP8PROFILE_MAX) {
331 validator.reset(new VP8Validator(frame_cb));
333 LOG(FATAL) << "Unsupported profile: " << profile;
336 return validator.Pass();
339 class VEAClient : public VideoEncodeAccelerator::Client {
341 VEAClient(const TestStream& test_stream,
342 ClientStateNotification<ClientState>* note,
344 unsigned int keyframe_period,
347 virtual ~VEAClient();
348 void CreateEncoder();
349 void DestroyEncoder();
351 // Return the number of encoded frames per second.
352 double frames_per_second();
354 // VideoDecodeAccelerator::Client implementation.
355 void RequireBitstreamBuffers(unsigned int input_count,
356 const gfx::Size& input_coded_size,
357 size_t output_buffer_size) OVERRIDE;
358 void BitstreamBufferReady(int32 bitstream_buffer_id,
360 bool key_frame) OVERRIDE;
361 void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
364 bool has_encoder() { return encoder_.get(); }
366 void SetState(ClientState new_state);
368 // Set current stream parameters to given |bitrate| at |framerate|.
369 void SetStreamParameters(unsigned int bitrate, unsigned int framerate);
371 // Called when encoder is done with a VideoFrame.
372 void InputNoLongerNeededCallback(int32 input_id);
374 // Ensure encoder has at least as many inputs as it asked for
375 // via RequireBitstreamBuffers().
376 void FeedEncoderWithInputs();
378 // Provide the encoder with a new output buffer.
379 void FeedEncoderWithOutput(base::SharedMemory* shm);
381 // Feed the encoder with num_required_input_buffers_ of black frames to force
382 // it to encode and return all inputs that came before this, effectively
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);
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();
397 // Test codec performance, failing the test if we are currently running
398 // the performance test.
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);
406 scoped_ptr<VideoEncodeAccelerator> encoder_;
408 const TestStream& test_stream_;
409 // Used to notify another thread about the state. VEAClient does not own this.
410 ClientStateNotification<ClientState>* note_;
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_;
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_;
423 // Current offset into input stream.
424 off_t pos_in_input_stream_;
425 // Calculated from input_coded_size_, in bytes.
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_;
432 // Precalculated number of frames in the stream.
433 unsigned int num_frames_in_stream_;
435 // Number of encoded frames we've got from the encoder thus far.
436 unsigned int num_encoded_frames_;
438 // Frames since last bitrate verification.
439 unsigned int num_frames_since_last_check_;
441 // True if received a keyframe while processing current bitstream buffer.
442 bool seen_keyframe_in_this_buffer_;
444 // True if we are to save the encoded stream to a file.
447 // Request a keyframe every keyframe_period_ frames.
448 const unsigned int keyframe_period_;
450 // Frame number for which we requested a keyframe.
451 unsigned int keyframe_requested_at_;
453 // True if we are asking encoder for a particular bitrate.
456 // Current requested bitrate.
457 unsigned int current_requested_bitrate_;
459 // Current expected framerate.
460 unsigned int current_framerate_;
462 // Byte size of the encoded stream (for bitrate calculation) since last
463 // time we checked bitrate.
464 size_t encoded_stream_size_since_last_check_;
466 // If true, verify performance at the end of the test.
469 scoped_ptr<StreamValidator> validator_;
471 // The time when the encoder has initialized.
472 base::TimeTicks encoder_initialized_time_;
474 // The time when the last encoded frame is ready.
475 base::TimeTicks last_frame_ready_time_;
477 // All methods of this class should be run on the same thread.
478 base::ThreadChecker thread_checker_;
481 VEAClient::VEAClient(const TestStream& test_stream,
482 ClientStateNotification<ClientState>* note,
484 unsigned int keyframe_period,
487 : state_(CS_CREATED),
488 test_stream_(test_stream),
491 next_output_buffer_id_(0),
492 pos_in_input_stream_(0),
493 input_buffer_size_(0),
494 num_required_input_buffers_(0),
495 output_buffer_size_(0),
496 num_frames_in_stream_(0),
497 num_encoded_frames_(0),
498 num_frames_since_last_check_(0),
499 seen_keyframe_in_this_buffer_(false),
500 save_to_file_(save_to_file),
501 keyframe_period_(keyframe_period),
502 keyframe_requested_at_(kMaxFrameNum),
503 force_bitrate_(force_bitrate),
504 current_requested_bitrate_(0),
505 current_framerate_(0),
506 encoded_stream_size_since_last_check_(0),
507 test_perf_(test_perf) {
508 if (keyframe_period_)
509 CHECK_LT(kMaxKeyframeDelay, keyframe_period_);
511 validator_ = StreamValidator::Create(
512 test_stream_.requested_profile,
513 base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
515 CHECK(validator_.get());
518 CHECK(!test_stream_.out_filename.empty());
519 base::FilePath out_filename(test_stream_.out_filename);
520 // This creates or truncates out_filename.
521 // Without it, AppendToFile() will not work.
522 EXPECT_EQ(0, base::WriteFile(out_filename, NULL, 0));
525 thread_checker_.DetachFromThread();
528 VEAClient::~VEAClient() { CHECK(!has_encoder()); }
530 void VEAClient::CreateEncoder() {
531 DCHECK(thread_checker_.CalledOnValidThread());
532 CHECK(!has_encoder());
534 scoped_ptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
535 encoder_.reset(new V4L2VideoEncodeAccelerator(device.Pass()));
536 SetState(CS_ENCODER_SET);
538 DVLOG(1) << "Profile: " << test_stream_.requested_profile
539 << ", initial bitrate: " << test_stream_.requested_bitrate;
540 if (!encoder_->Initialize(kInputFormat,
542 test_stream_.requested_profile,
543 test_stream_.requested_bitrate,
545 DLOG(ERROR) << "VideoEncodeAccelerator::Initialize() failed";
550 SetStreamParameters(test_stream_.requested_bitrate,
551 test_stream_.requested_framerate);
552 SetState(CS_INITIALIZED);
553 encoder_initialized_time_ = base::TimeTicks::Now();
556 void VEAClient::DestroyEncoder() {
557 DCHECK(thread_checker_.CalledOnValidThread());
560 encoder_.release()->Destroy();
563 double VEAClient::frames_per_second() {
564 base::TimeDelta duration = last_frame_ready_time_ - encoder_initialized_time_;
565 return num_encoded_frames_ / duration.InSecondsF();
568 void VEAClient::RequireBitstreamBuffers(unsigned int input_count,
569 const gfx::Size& input_coded_size,
570 size_t output_size) {
571 DCHECK(thread_checker_.CalledOnValidThread());
572 ASSERT_EQ(state_, CS_INITIALIZED);
573 SetState(CS_ENCODING);
575 // TODO(posciak): For now we only support input streams that meet encoder
576 // size requirements exactly (i.e. coded size == visible size).
577 input_coded_size_ = input_coded_size;
578 ASSERT_EQ(input_coded_size_, test_stream_.size);
580 input_buffer_size_ = media::VideoFrame::AllocationSize(kInputFormat,
582 CHECK_GT(input_buffer_size_, 0UL);
584 // ARM performs CPU cache management with CPU cache line granularity. We thus
585 // need to ensure our buffers are CPU cache line-aligned (64 byte-aligned).
586 // Otherwise newer kernels will refuse to accept them, and on older kernels
587 // we'll be treating ourselves to random corruption.
588 // Since we are just mmapping and passing chunks of the input file, to ensure
589 // alignment, if the starting virtual addresses of the frames in it were not
590 // 64 byte-aligned, we'd have to use a separate set of input buffers and copy
591 // the frames into them before sending to the encoder. It would have been an
592 // overkill here though, because, for now at least, we only test resolutions
593 // that result in proper alignment, and it would have also interfered with
594 // performance testing. So just assert that the frame size is a multiple of
595 // 64 bytes. This ensures all frames start at 64-byte boundary, because
596 // MemoryMappedFile should be mmapp()ed at virtual page start as well.
597 ASSERT_EQ(input_buffer_size_ & 63, 0)
598 << "Frame size has to be a multiple of 64 bytes";
599 ASSERT_EQ(reinterpret_cast<off_t>(test_stream_.input_file.data()) & 63, 0)
600 << "Mapped file should be mapped at a 64 byte boundary";
602 num_required_input_buffers_ = input_count;
603 ASSERT_GT(num_required_input_buffers_, 0UL);
605 num_frames_in_stream_ = test_stream_.input_file.length() / input_buffer_size_;
606 CHECK_GT(num_frames_in_stream_, 0UL);
607 CHECK_LE(num_frames_in_stream_, kMaxFrameNum);
608 CHECK_EQ(num_frames_in_stream_ * input_buffer_size_,
609 test_stream_.input_file.length());
611 output_buffer_size_ = output_size;
612 ASSERT_GT(output_buffer_size_, 0UL);
614 for (unsigned int i = 0; i < kNumOutputBuffers; ++i) {
615 base::SharedMemory* shm = new base::SharedMemory();
616 CHECK(shm->CreateAndMapAnonymous(output_buffer_size_));
617 output_shms_.push_back(shm);
618 FeedEncoderWithOutput(shm);
621 FeedEncoderWithInputs();
624 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id,
627 DCHECK(thread_checker_.CalledOnValidThread());
628 ASSERT_LE(payload_size, output_buffer_size_);
630 IdToSHM::iterator it = output_buffers_at_client_.find(bitstream_buffer_id);
631 ASSERT_NE(it, output_buffers_at_client_.end());
632 base::SharedMemory* shm = it->second;
633 output_buffers_at_client_.erase(it);
635 if (state_ == CS_FINISHED)
638 encoded_stream_size_since_last_check_ += payload_size;
640 const uint8* stream_ptr = static_cast<const uint8*>(shm->memory());
641 if (payload_size > 0)
642 validator_->ProcessStreamBuffer(stream_ptr, payload_size);
644 EXPECT_EQ(key_frame, seen_keyframe_in_this_buffer_);
645 seen_keyframe_in_this_buffer_ = false;
648 int size = base::checked_cast<int>(payload_size);
649 EXPECT_EQ(base::AppendToFile(
650 base::FilePath::FromUTF8Unsafe(test_stream_.out_filename),
651 static_cast<char*>(shm->memory()),
656 FeedEncoderWithOutput(shm);
659 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error) {
660 DCHECK(thread_checker_.CalledOnValidThread());
664 void VEAClient::SetState(ClientState new_state) {
665 DVLOG(4) << "Changing state " << state_ << "->" << new_state;
666 note_->Notify(new_state);
670 void VEAClient::SetStreamParameters(unsigned int bitrate,
671 unsigned int framerate) {
672 current_requested_bitrate_ = bitrate;
673 current_framerate_ = framerate;
674 CHECK_GT(current_requested_bitrate_, 0UL);
675 CHECK_GT(current_framerate_, 0UL);
676 encoder_->RequestEncodingParametersChange(current_requested_bitrate_,
678 DVLOG(1) << "Switched parameters to " << current_requested_bitrate_
679 << " bps @ " << current_framerate_ << " FPS";
682 void VEAClient::InputNoLongerNeededCallback(int32 input_id) {
683 std::set<int32>::iterator it = inputs_at_client_.find(input_id);
684 ASSERT_NE(it, inputs_at_client_.end());
685 inputs_at_client_.erase(it);
686 FeedEncoderWithInputs();
689 scoped_refptr<media::VideoFrame> VEAClient::PrepareInputFrame(off_t position) {
690 CHECK_LE(position + input_buffer_size_, test_stream_.input_file.length());
693 const_cast<uint8*>(test_stream_.input_file.data() + position);
695 CHECK_GT(current_framerate_, 0);
696 scoped_refptr<media::VideoFrame> frame =
697 media::VideoFrame::WrapExternalYuvData(
700 gfx::Rect(test_stream_.size),
702 input_coded_size_.width(),
703 input_coded_size_.width() / 2,
704 input_coded_size_.width() / 2,
706 frame_data + input_coded_size_.GetArea(),
707 frame_data + (input_coded_size_.GetArea() * 5 / 4),
708 base::TimeDelta().FromMilliseconds(
709 next_input_id_ * base::Time::kMillisecondsPerSecond /
711 media::BindToCurrentLoop(
712 base::Bind(&VEAClient::InputNoLongerNeededCallback,
713 base::Unretained(this),
716 CHECK(inputs_at_client_.insert(next_input_id_).second);
722 void VEAClient::FeedEncoderWithInputs() {
726 if (state_ != CS_ENCODING)
729 while (inputs_at_client_.size() <
730 num_required_input_buffers_ + kNumExtraInputFrames) {
731 size_t bytes_left = test_stream_.input_file.length() - pos_in_input_stream_;
732 if (bytes_left < input_buffer_size_) {
733 DCHECK_EQ(bytes_left, 0UL);
738 bool force_keyframe = false;
739 if (keyframe_period_ && next_input_id_ % keyframe_period_ == 0) {
740 keyframe_requested_at_ = next_input_id_;
741 force_keyframe = true;
744 scoped_refptr<media::VideoFrame> video_frame =
745 PrepareInputFrame(pos_in_input_stream_);
746 pos_in_input_stream_ += input_buffer_size_;
748 encoder_->Encode(video_frame, force_keyframe);
752 void VEAClient::FeedEncoderWithOutput(base::SharedMemory* shm) {
756 if (state_ != CS_ENCODING && state_ != CS_FINISHING)
759 base::SharedMemoryHandle dup_handle;
760 CHECK(shm->ShareToProcess(base::Process::Current().handle(), &dup_handle));
762 media::BitstreamBuffer bitstream_buffer(
763 next_output_buffer_id_++, dup_handle, output_buffer_size_);
764 CHECK(output_buffers_at_client_.insert(std::make_pair(bitstream_buffer.id(),
766 encoder_->UseOutputBitstreamBuffer(bitstream_buffer);
769 void VEAClient::FlushEncoder() {
770 ASSERT_EQ(state_, CS_ENCODING);
771 SetState(CS_FINISHING);
773 // Feed the encoder with an additional set of num_required_input_buffers_
774 // to flush it, using the first frame in the input stream. The resulting
775 // encoded frames will be ignored.
776 for (unsigned int i = 0; i < num_required_input_buffers_; ++i) {
777 scoped_refptr<media::VideoFrame> frame = PrepareInputFrame(0);
778 encoder_->Encode(frame, false);
782 bool VEAClient::HandleEncodedFrame(bool keyframe) {
783 // This would be a bug in the test, which should not ignore false
784 // return value from this method.
785 CHECK_LE(num_encoded_frames_, num_frames_in_stream_);
787 ++num_encoded_frames_;
788 ++num_frames_since_last_check_;
790 last_frame_ready_time_ = base::TimeTicks::Now();
792 // Got keyframe, reset keyframe detection regardless of whether we
793 // got a frame in time or not.
794 keyframe_requested_at_ = kMaxFrameNum;
795 seen_keyframe_in_this_buffer_ = true;
798 // Because the keyframe behavior requirements are loose, we give
799 // the encoder more freedom here. It could either deliver a keyframe
800 // immediately after we requested it, which could be for a frame number
801 // before the one we requested it for (if the keyframe request
802 // is asynchronous, i.e. not bound to any concrete frame, and because
803 // the pipeline can be deeper than one frame), at that frame, or after.
804 // So the only constraints we put here is that we get a keyframe not
805 // earlier than we requested one (in time), and not later than
806 // kMaxKeyframeDelay frames after the frame, for which we requested
807 // it, comes back encoded.
808 EXPECT_LE(num_encoded_frames_, keyframe_requested_at_ + kMaxKeyframeDelay);
810 if (num_encoded_frames_ == num_frames_in_stream_ / 2) {
811 VerifyStreamProperties();
812 if (test_stream_.requested_subsequent_bitrate !=
813 current_requested_bitrate_ ||
814 test_stream_.requested_subsequent_framerate != current_framerate_) {
815 SetStreamParameters(test_stream_.requested_subsequent_bitrate,
816 test_stream_.requested_subsequent_framerate);
818 } else if (num_encoded_frames_ == num_frames_in_stream_) {
820 VerifyStreamProperties();
821 SetState(CS_FINISHED);
828 void VEAClient::VerifyPerf() {
829 double measured_fps = frames_per_second();
830 LOG(INFO) << "Measured encoder FPS: " << measured_fps;
832 EXPECT_GE(measured_fps, kMinPerfFPS);
835 void VEAClient::VerifyStreamProperties() {
836 CHECK_GT(num_frames_since_last_check_, 0UL);
837 CHECK_GT(encoded_stream_size_since_last_check_, 0UL);
838 unsigned int bitrate = encoded_stream_size_since_last_check_ * 8 *
839 current_framerate_ / num_frames_since_last_check_;
840 DVLOG(1) << "Current chunk's bitrate: " << bitrate
841 << " (expected: " << current_requested_bitrate_
842 << " @ " << current_framerate_ << " FPS,"
843 << " num frames in chunk: " << num_frames_since_last_check_;
845 num_frames_since_last_check_ = 0;
846 encoded_stream_size_since_last_check_ = 0;
848 if (force_bitrate_) {
850 current_requested_bitrate_,
851 kBitrateTolerance * current_requested_bitrate_);
856 // - Number of concurrent encoders.
857 // - If true, save output to file (provided an output filename was supplied).
858 // - Force a keyframe every n frames.
859 // - Force bitrate; the actual required value is provided as a property
860 // of the input stream, because it depends on stream type/resolution/etc.
861 // - If true, measure performance.
862 // - If true, switch bitrate mid-stream.
863 // - If true, switch framerate mid-stream.
864 class VideoEncodeAcceleratorTest
865 : public ::testing::TestWithParam<
866 Tuple7<int, bool, int, bool, bool, bool, bool> > {};
868 TEST_P(VideoEncodeAcceleratorTest, TestSimpleEncode) {
869 const int num_concurrent_encoders = GetParam().a;
870 const bool save_to_file = GetParam().b;
871 const unsigned int keyframe_period = GetParam().c;
872 const bool force_bitrate = GetParam().d;
873 const bool test_perf = GetParam().e;
874 const bool mid_stream_bitrate_switch = GetParam().f;
875 const bool mid_stream_framerate_switch = GetParam().g;
877 // Initialize the test streams.
878 ScopedVector<TestStream> test_streams;
879 ParseAndReadTestStreamData(*g_test_stream_data, &test_streams);
880 UpdateTestStreamData(
881 mid_stream_bitrate_switch, mid_stream_framerate_switch, &test_streams);
883 ScopedVector<ClientStateNotification<ClientState> > notes;
884 // The clients can only be deleted after the encoder threads are stopped.
885 ScopedVector<VEAClient> clients;
886 ScopedVector<base::Thread> encoder_threads;
888 // Create all the encoders.
889 for (int i = 0; i < num_concurrent_encoders; i++) {
890 int test_stream_index = i % test_streams.size();
891 // Disregard save_to_file if we didn't get an output filename.
892 bool encoder_save_to_file =
894 !test_streams[test_stream_index]->out_filename.empty());
896 notes.push_back(new ClientStateNotification<ClientState>());
897 clients.push_back(new VEAClient(*test_streams[test_stream_index],
899 encoder_save_to_file,
904 // Initialize the encoder thread.
905 char thread_name[32];
906 sprintf(thread_name, "EncoderThread%d", i);
907 base::Thread* encoder_thread = new base::Thread(thread_name);
908 encoder_thread->Start();
909 encoder_thread->message_loop()->PostTask(
911 base::Bind(&VEAClient::CreateEncoder,
912 base::Unretained(clients.back())));
913 encoder_threads.push_back(encoder_thread);
916 // Wait all the encoders to finish.
917 for (int i = 0; i < num_concurrent_encoders; i++) {
918 ASSERT_EQ(notes[i]->Wait(), CS_ENCODER_SET);
919 ASSERT_EQ(notes[i]->Wait(), CS_INITIALIZED);
920 ASSERT_EQ(notes[i]->Wait(), CS_ENCODING);
921 ASSERT_EQ(notes[i]->Wait(), CS_FINISHING);
922 ASSERT_EQ(notes[i]->Wait(), CS_FINISHED);
923 encoder_threads[i]->message_loop()->PostTask(
925 base::Bind(&VEAClient::DestroyEncoder, base::Unretained(clients[i])));
926 encoder_threads[i]->Stop();
930 INSTANTIATE_TEST_CASE_P(
932 VideoEncodeAcceleratorTest,
933 ::testing::Values(MakeTuple(1, true, 0, false, false, false, false)));
935 INSTANTIATE_TEST_CASE_P(
937 VideoEncodeAcceleratorTest,
938 ::testing::Values(MakeTuple(1, false, 0, false, true, false, false)));
940 INSTANTIATE_TEST_CASE_P(
942 VideoEncodeAcceleratorTest,
943 ::testing::Values(MakeTuple(1, false, 10, false, false, false, false)));
945 INSTANTIATE_TEST_CASE_P(
947 VideoEncodeAcceleratorTest,
948 ::testing::Values(MakeTuple(1, false, 0, true, false, false, false)));
950 INSTANTIATE_TEST_CASE_P(
951 MidStreamParamSwitchBitrate,
952 VideoEncodeAcceleratorTest,
953 ::testing::Values(MakeTuple(1, false, 0, true, false, true, false)));
955 INSTANTIATE_TEST_CASE_P(
956 MidStreamParamSwitchFPS,
957 VideoEncodeAcceleratorTest,
958 ::testing::Values(MakeTuple(1, false, 0, true, false, false, true)));
960 INSTANTIATE_TEST_CASE_P(
961 MidStreamParamSwitchBitrateAndFPS,
962 VideoEncodeAcceleratorTest,
963 ::testing::Values(MakeTuple(1, false, 0, true, false, true, true)));
965 INSTANTIATE_TEST_CASE_P(
967 VideoEncodeAcceleratorTest,
968 ::testing::Values(MakeTuple(3, false, 0, false, false, false, false),
969 MakeTuple(3, false, 0, true, false, true, true)));
971 // TODO(posciak): more tests:
972 // - async FeedEncoderWithOutput
973 // - out-of-order return of outputs to encoder
974 // - multiple encoders + decoders
975 // - mid-stream encoder_->Destroy()
978 } // namespace content
980 int main(int argc, char** argv) {
981 testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args.
982 CommandLine::Init(argc, argv);
984 base::ShadowingAtExitManager at_exit_manager;
985 scoped_ptr<base::FilePath::StringType> test_stream_data(
986 new base::FilePath::StringType(
987 media::GetTestDataFilePath(content::g_default_in_filename).value() +
988 content::g_default_in_parameters));
989 content::g_test_stream_data = test_stream_data.get();
991 // Needed to enable DVLOG through --vmodule.
992 logging::LoggingSettings settings;
993 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
994 CHECK(logging::InitLogging(settings));
996 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
999 CommandLine::SwitchMap switches = cmd_line->GetSwitches();
1000 for (CommandLine::SwitchMap::const_iterator it = switches.begin();
1001 it != switches.end();
1003 if (it->first == "test_stream_data") {
1004 test_stream_data->assign(it->second.c_str());
1007 if (it->first == "v" || it->first == "vmodule")
1009 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
1012 return RUN_ALL_TESTS();