Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / common / gpu / media / video_encode_accelerator_unittest.cc
blob410c67a64d6c74a1033aac3145b49ed8240aa9db
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"
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/process/process.h"
12 #include "base/safe_numerics.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "content/common/gpu/media/exynos_video_encode_accelerator.h"
16 #include "content/common/gpu/media/h264_parser.h"
17 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
18 #include "media/base/bind_to_loop.h"
19 #include "media/base/bitstream_buffer.h"
20 #include "media/video/video_encode_accelerator.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using media::VideoEncodeAccelerator;
25 namespace content {
26 namespace {
28 // Arbitrarily chosen to add some depth to the pipeline.
29 const unsigned int kNumOutputBuffers = 4;
30 const unsigned int kNumExtraInputFrames = 4;
31 // Maximum delay between requesting a keyframe and receiving one, in frames.
32 // Arbitrarily chosen as a reasonable requirement.
33 const unsigned int kMaxKeyframeDelay = 4;
34 // Value to use as max frame number for keyframe detection.
35 const unsigned int kMaxFrameNum =
36 std::numeric_limits<unsigned int>::max() - kMaxKeyframeDelay;
37 const uint32 kDefaultBitrate = 2000000;
38 // Tolerance factor for how encoded bitrate can differ from requested bitrate.
39 const double kBitrateTolerance = 0.1;
40 const uint32 kDefaultFPS = 30;
42 // The syntax of each test stream is:
43 // "in_filename:width:height:out_filename:requested_bitrate"
44 // - |in_filename| must be an I420 (YUV planar) raw stream
45 // (see http://www.fourcc.org/yuv.php#IYUV).
46 // - |width| and |height| are in pixels.
47 // - |out_filename| filename to save the encoded stream to.
48 // Output stream is only saved in the simple encode test.
49 // - |requested_bitrate| requested bitrate in bits per second (optional).
50 // Bitrate is only forced for tests that test bitrate.
51 const base::FilePath::CharType* test_stream_data =
52 FILE_PATH_LITERAL("sync_192p_20frames.yuv:320:192:out.h264:100000");
54 struct TestStream {
55 explicit TestStream(base::FilePath::StringType filename)
56 : requested_bitrate(0) {}
57 ~TestStream() {}
59 gfx::Size size;
60 base::MemoryMappedFile input_file;
61 std::string out_filename;
62 unsigned int requested_bitrate;
65 static void ParseAndReadTestStreamData(base::FilePath::StringType data,
66 TestStream* test_stream) {
67 std::vector<base::FilePath::StringType> fields;
68 base::SplitString(data, ':', &fields);
69 CHECK_GE(fields.size(), 4U) << data;
70 CHECK_LE(fields.size(), 5U) << data;
72 base::FilePath::StringType filename = fields[0];
73 int width, height;
74 CHECK(base::StringToInt(fields[1], &width));
75 CHECK(base::StringToInt(fields[2], &height));
76 test_stream->size = gfx::Size(width, height);
77 CHECK(!test_stream->size.IsEmpty());
78 test_stream->out_filename = fields[3];
79 if (!fields[4].empty())
80 CHECK(base::StringToUint(fields[4], &test_stream->requested_bitrate));
82 CHECK(test_stream->input_file.Initialize(base::FilePath(filename)));
85 enum ClientState {
86 CS_CREATED,
87 CS_ENCODER_SET,
88 CS_INITIALIZED,
89 CS_ENCODING,
90 CS_FINISHING,
91 CS_FINISHED,
92 CS_ERROR,
95 class VEAClient : public VideoEncodeAccelerator::Client {
96 public:
97 VEAClient(const TestStream& test_stream,
98 ClientStateNotification<ClientState>* note,
99 bool save_to_file,
100 unsigned int keyframe_period,
101 bool force_bitrate);
102 virtual ~VEAClient();
103 void CreateEncoder();
104 void DestroyEncoder();
106 // VideoDecodeAccelerator::Client implementation.
107 void NotifyInitializeDone() OVERRIDE;
108 void RequireBitstreamBuffers(unsigned int input_count,
109 const gfx::Size& input_coded_size,
110 size_t output_buffer_size) OVERRIDE;
111 void BitstreamBufferReady(int32 bitstream_buffer_id,
112 size_t payload_size,
113 bool key_frame) OVERRIDE;
114 void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
116 private:
117 bool has_encoder() { return encoder_.get(); }
119 void SetState(ClientState new_state);
120 // Called before starting encode to set initial configuration of the encoder.
121 void SetInitialConfiguration();
122 // Called when encoder is done with a VideoFrame.
123 void InputNoLongerNeededCallback(int32 input_id);
124 // Ensure encoder has at least as many inputs as it asked for
125 // via RequireBitstreamBuffers().
126 void FeedEncoderWithInputs();
127 // Provide the encoder with a new output buffer.
128 void FeedEncoderWithOutput(base::SharedMemory* shm);
129 // Feed the encoder with num_required_input_buffers_ of black frames to force
130 // it to encode and return all inputs that came before this, effectively
131 // flushing it.
132 void FlushEncoder();
133 // Perform any checks required at the end of the stream, called after
134 // receiving the last frame from the encoder.
135 void ChecksAtFinish();
137 ClientState state_;
138 scoped_ptr<VideoEncodeAccelerator> encoder_;
140 const TestStream& test_stream_;
141 ClientStateNotification<ClientState>* note_;
143 // Ids assigned to VideoFrames (start at 1 for easy comparison with
144 // num_encoded_slices_).
145 std::set<int32> inputs_at_client_;
146 int32 next_input_id_;
148 // Ids for output BitstreamBuffers.
149 typedef std::map<int32, base::SharedMemory*> IdToSHM;
150 ScopedVector<base::SharedMemory> output_shms_;
151 IdToSHM output_buffers_at_client_;
152 int32 next_output_buffer_id_;
154 // Current offset into input stream.
155 off_t pos_in_input_stream_;
156 // Calculated from input_coded_size_, in bytes.
157 size_t input_buffer_size_;
158 gfx::Size input_coded_size_;
159 // Requested by encoder.
160 unsigned int num_required_input_buffers_;
161 size_t output_buffer_size_;
163 // Calculated number of frames in the stream.
164 unsigned int num_frames_in_stream_;
165 // Number of encoded slices we got from encoder thus far.
166 unsigned int num_encoded_slices_;
168 // Set to true when encoder provides us with the corresponding NALU type.
169 bool seen_sps_;
170 bool seen_pps_;
171 bool seen_idr_;
173 // True if we are to save the encoded stream to a file.
174 bool save_to_file_;
175 // Request a keyframe every keyframe_period_ frames.
176 const unsigned int keyframe_period_;
177 // Frame number for which we requested a keyframe.
178 unsigned int keyframe_requested_at_;
179 // True if we are asking encoder for a particular bitrate.
180 bool force_bitrate_;
181 // Byte size of the encoded stream (for bitrate calculation).
182 size_t encoded_stream_size_;
184 content::H264Parser h264_parser_;
186 // All methods of this class should be run on the same thread.
187 base::ThreadChecker thread_checker_;
190 VEAClient::VEAClient(const TestStream& test_stream,
191 ClientStateNotification<ClientState>* note,
192 bool save_to_file,
193 unsigned int keyframe_period,
194 bool force_bitrate)
195 : state_(CS_CREATED),
196 test_stream_(test_stream),
197 note_(note),
198 next_input_id_(1),
199 next_output_buffer_id_(0),
200 pos_in_input_stream_(0),
201 input_buffer_size_(0),
202 num_required_input_buffers_(0),
203 output_buffer_size_(0),
204 num_frames_in_stream_(0),
205 num_encoded_slices_(0),
206 seen_sps_(false),
207 seen_pps_(false),
208 seen_idr_(false),
209 save_to_file_(save_to_file),
210 keyframe_period_(keyframe_period),
211 keyframe_requested_at_(kMaxFrameNum),
212 force_bitrate_(force_bitrate),
213 encoded_stream_size_(0) {
214 if (keyframe_period_)
215 CHECK_LT(kMaxKeyframeDelay, keyframe_period_);
217 if (save_to_file_) {
218 CHECK(!test_stream_.out_filename.empty());
219 base::FilePath out_filename(test_stream_.out_filename);
220 // This creates or truncates out_filename.
221 // Without it, AppendToFile() will not work.
222 EXPECT_EQ(0, file_util::WriteFile(out_filename, NULL, 0));
225 thread_checker_.DetachFromThread();
228 VEAClient::~VEAClient() {
229 CHECK(!has_encoder());
232 void VEAClient::CreateEncoder() {
233 DCHECK(thread_checker_.CalledOnValidThread());
234 CHECK(!has_encoder());
236 encoder_.reset(new ExynosVideoEncodeAccelerator(this));
238 SetState(CS_ENCODER_SET);
239 encoder_->Initialize(media::VideoFrame::I420,
240 test_stream_.size,
241 media::H264PROFILE_MAIN,
242 kDefaultBitrate);
245 void VEAClient::DestroyEncoder() {
246 DCHECK(thread_checker_.CalledOnValidThread());
247 if (!has_encoder())
248 return;
249 encoder_.release()->Destroy();
252 void VEAClient::NotifyInitializeDone() {
253 DCHECK(thread_checker_.CalledOnValidThread());
254 SetInitialConfiguration();
255 SetState(CS_INITIALIZED);
258 static size_t I420ByteSize(const gfx::Size& d) {
259 CHECK((d.width() % 2 == 0) && (d.height() % 2 == 0));
260 return d.width() * d.height() * 3 / 2;
263 void VEAClient::RequireBitstreamBuffers(unsigned int input_count,
264 const gfx::Size& input_coded_size,
265 size_t output_size) {
266 DCHECK(thread_checker_.CalledOnValidThread());
267 ASSERT_EQ(state_, CS_INITIALIZED);
268 SetState(CS_ENCODING);
270 // TODO(posciak): For now we only support input streams that meet encoder
271 // size requirements exactly (i.e. coded size == visible size).
272 input_coded_size_ = input_coded_size;
273 ASSERT_EQ(input_coded_size_, test_stream_.size);
275 num_required_input_buffers_ = input_count;
276 ASSERT_GT(num_required_input_buffers_, 0UL);
278 input_buffer_size_ = I420ByteSize(input_coded_size_);
279 CHECK_GT(input_buffer_size_, 0UL);
281 num_frames_in_stream_ = test_stream_.input_file.length() / input_buffer_size_;
282 CHECK_GT(num_frames_in_stream_, 0UL);
283 CHECK_LE(num_frames_in_stream_, kMaxFrameNum);
284 CHECK_EQ(num_frames_in_stream_ * input_buffer_size_,
285 test_stream_.input_file.length());
287 output_buffer_size_ = output_size;
288 ASSERT_GT(output_buffer_size_, 0UL);
290 for (unsigned int i = 0; i < kNumOutputBuffers; ++i) {
291 base::SharedMemory* shm = new base::SharedMemory();
292 CHECK(shm->CreateAndMapAnonymous(output_buffer_size_));
293 output_shms_.push_back(shm);
294 FeedEncoderWithOutput(shm);
297 FeedEncoderWithInputs();
300 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id,
301 size_t payload_size,
302 bool key_frame) {
303 DCHECK(thread_checker_.CalledOnValidThread());
304 ASSERT_LE(payload_size, output_buffer_size_);
305 EXPECT_GT(payload_size, 0UL);
307 IdToSHM::iterator it = output_buffers_at_client_.find(bitstream_buffer_id);
308 ASSERT_NE(it, output_buffers_at_client_.end());
309 base::SharedMemory* shm = it->second;
310 output_buffers_at_client_.erase(it);
312 if (state_ == CS_FINISHED)
313 return;
315 encoded_stream_size_ += payload_size;
317 h264_parser_.SetStream(static_cast<uint8*>(shm->memory()), payload_size);
319 bool seen_idr_in_this_buffer = false;
321 while (1) {
322 content::H264NALU nalu;
323 content::H264Parser::Result result;
325 result = h264_parser_.AdvanceToNextNALU(&nalu);
326 if (result == content::H264Parser::kEOStream)
327 break;
329 ASSERT_EQ(result, content::H264Parser::kOk);
331 switch (nalu.nal_unit_type) {
332 case content::H264NALU::kIDRSlice:
333 ASSERT_TRUE(seen_sps_);
334 ASSERT_TRUE(seen_pps_);
335 seen_idr_ = seen_idr_in_this_buffer = true;
336 // Got keyframe, reset keyframe detection regardless of whether we
337 // got a frame in time or not.
338 keyframe_requested_at_ = kMaxFrameNum;
339 // fallthrough
340 case content::H264NALU::kNonIDRSlice:
341 ASSERT_TRUE(seen_idr_);
342 ++num_encoded_slices_;
344 // Because the keyframe behavior requirements are loose, we give
345 // the encoder more freedom here. It could either deliver a keyframe
346 // immediately after we requested it, which could be for a frame number
347 // before the one we requested it for (if the keyframe request
348 // is asynchronous, i.e. not bound to any concrete frame, and because
349 // the pipeline can be deeper that one frame), at that frame, or after.
350 // So the only constraints we put here is that we get a keyframe not
351 // earlier than we requested one (in time), and not later than
352 // kMaxKeyframeDelay frames after the frame for which we requested
353 // it comes back as encoded slice.
354 EXPECT_LE(num_encoded_slices_,
355 keyframe_requested_at_ + kMaxKeyframeDelay);
356 break;
357 case content::H264NALU::kSPS:
358 seen_sps_ = true;
359 break;
360 case content::H264NALU::kPPS:
361 ASSERT_TRUE(seen_sps_);
362 seen_pps_ = true;
363 break;
364 default:
365 break;
368 if (num_encoded_slices_ == num_frames_in_stream_) {
369 ASSERT_EQ(state_, CS_FINISHING);
370 ChecksAtFinish();
371 SetState(CS_FINISHED);
372 break;
376 EXPECT_EQ(key_frame, seen_idr_in_this_buffer);
378 if (save_to_file_) {
379 int size = base::checked_numeric_cast<int>(payload_size);
380 EXPECT_EQ(file_util::AppendToFile(
381 base::FilePath::FromUTF8Unsafe(test_stream_.out_filename),
382 static_cast<char*>(shm->memory()),
383 size),
384 size);
387 FeedEncoderWithOutput(shm);
390 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error) {
391 DCHECK(thread_checker_.CalledOnValidThread());
392 SetState(CS_ERROR);
395 void VEAClient::SetState(ClientState new_state) {
396 note_->Notify(new_state);
397 state_ = new_state;
400 void VEAClient::SetInitialConfiguration() {
401 if (force_bitrate_) {
402 CHECK_GT(test_stream_.requested_bitrate, 0UL);
403 encoder_->RequestEncodingParametersChange(test_stream_.requested_bitrate,
404 kDefaultFPS);
408 void VEAClient::InputNoLongerNeededCallback(int32 input_id) {
409 std::set<int32>::iterator it = inputs_at_client_.find(input_id);
410 ASSERT_NE(it, inputs_at_client_.end());
411 inputs_at_client_.erase(it);
412 FeedEncoderWithInputs();
415 void VEAClient::FeedEncoderWithInputs() {
416 if (!has_encoder())
417 return;
419 if (state_ != CS_ENCODING)
420 return;
422 while (inputs_at_client_.size() <
423 num_required_input_buffers_ + kNumExtraInputFrames) {
425 size_t bytes_left = test_stream_.input_file.length() - pos_in_input_stream_;
426 if (bytes_left < input_buffer_size_) {
427 DCHECK_EQ(bytes_left, 0UL);
428 FlushEncoder();
429 return;
432 uint8* frame_data = const_cast<uint8*>(test_stream_.input_file.data() +
433 pos_in_input_stream_);
434 scoped_refptr<media::VideoFrame> video_frame =
435 media::VideoFrame::WrapExternalYuvData(
436 media::VideoFrame::I420,
437 input_coded_size_,
438 gfx::Rect(test_stream_.size),
439 test_stream_.size,
440 input_coded_size_.width(),
441 input_coded_size_.width() / 2,
442 input_coded_size_.width() / 2,
443 frame_data,
444 frame_data + input_coded_size_.GetArea(),
445 frame_data + (input_coded_size_.GetArea() * 5 / 4),
446 base::TimeDelta(),
447 media::BindToCurrentLoop(
448 base::Bind(&VEAClient::InputNoLongerNeededCallback,
449 base::Unretained(this),
450 next_input_id_)));
451 CHECK(inputs_at_client_.insert(next_input_id_).second);
452 pos_in_input_stream_ += input_buffer_size_;
454 bool force_keyframe = false;
455 if (keyframe_period_ && next_input_id_ % keyframe_period_ == 0) {
456 keyframe_requested_at_ = next_input_id_;
457 force_keyframe = true;
459 encoder_->Encode(video_frame, force_keyframe);
460 ++next_input_id_;
464 void VEAClient::FeedEncoderWithOutput(base::SharedMemory* shm) {
465 if (!has_encoder())
466 return;
468 if (state_ != CS_ENCODING && state_ != CS_FINISHING)
469 return;
471 base::SharedMemoryHandle dup_handle;
472 CHECK(shm->ShareToProcess(base::Process::Current().handle(), &dup_handle));
474 media::BitstreamBuffer bitstream_buffer(
475 next_output_buffer_id_++, dup_handle, output_buffer_size_);
476 CHECK(output_buffers_at_client_.insert(
477 std::make_pair(bitstream_buffer.id(), shm)).second);
478 encoder_->UseOutputBitstreamBuffer(bitstream_buffer);
481 void VEAClient::FlushEncoder() {
482 ASSERT_EQ(state_, CS_ENCODING);
483 SetState(CS_FINISHING);
485 // Feed encoder with a set of black frames to flush it.
486 for (unsigned int i = 0; i < num_required_input_buffers_; ++i) {
487 scoped_refptr<media::VideoFrame> frame =
488 media::VideoFrame::CreateBlackFrame(input_coded_size_);
489 CHECK(inputs_at_client_.insert(next_input_id_).second);
490 ++next_input_id_;
491 encoder_->Encode(frame, false);
495 void VEAClient::ChecksAtFinish() {
496 if (force_bitrate_) {
497 EXPECT_NEAR(encoded_stream_size_ * 8 * kDefaultFPS / num_frames_in_stream_,
498 test_stream_.requested_bitrate,
499 kBitrateTolerance * test_stream_.requested_bitrate);
503 // Test parameters:
504 // - If true, save output to file.
505 // - Force keyframe every n frames.
506 // - Force bitrate; the actual required value is provided as a property
507 // of the input stream, because it depends on stream type/resolution/etc.
508 class VideoEncodeAcceleratorTest
509 : public ::testing::TestWithParam<Tuple3<bool, int, bool> > {};
511 TEST_P(VideoEncodeAcceleratorTest, TestSimpleEncode) {
512 const bool save_to_file = GetParam().a;
513 const unsigned int keyframe_period = GetParam().b;
514 bool force_bitrate = GetParam().c;
516 TestStream test_stream(test_stream_data);
517 ParseAndReadTestStreamData(test_stream_data, &test_stream);
518 if (test_stream.requested_bitrate == 0)
519 force_bitrate = false;
521 base::Thread encoder_thread("EncoderThread");
522 encoder_thread.Start();
524 ClientStateNotification<ClientState> note;
525 scoped_ptr<VEAClient> client(new VEAClient(test_stream,
526 &note,
527 save_to_file,
528 keyframe_period,
529 force_bitrate));
531 encoder_thread.message_loop()->PostTask(
532 FROM_HERE,
533 base::Bind(&VEAClient::CreateEncoder, base::Unretained(client.get())));
535 ASSERT_EQ(note.Wait(), CS_ENCODER_SET);
536 ASSERT_EQ(note.Wait(), CS_INITIALIZED);
537 ASSERT_EQ(note.Wait(), CS_ENCODING);
538 ASSERT_EQ(note.Wait(), CS_FINISHING);
539 ASSERT_EQ(note.Wait(), CS_FINISHED);
541 encoder_thread.message_loop()->PostTask(
542 FROM_HERE,
543 base::Bind(&VEAClient::DestroyEncoder, base::Unretained(client.get())));
545 encoder_thread.Stop();
548 INSTANTIATE_TEST_CASE_P(SimpleEncode,
549 VideoEncodeAcceleratorTest,
550 ::testing::Values(MakeTuple(true, 0, false)));
552 INSTANTIATE_TEST_CASE_P(ForceKeyframes,
553 VideoEncodeAcceleratorTest,
554 ::testing::Values(MakeTuple(false, 10, false)));
556 INSTANTIATE_TEST_CASE_P(ForceBitrate,
557 VideoEncodeAcceleratorTest,
558 ::testing::Values(MakeTuple(false, 0, true)));
560 // TODO(posciak): more tests:
561 // - async FeedEncoderWithOutput
562 // - out-of-order return of outputs to encoder
563 // - dynamic, runtime bitrate changes
564 // - multiple encoders
565 // - multiple encoders + decoders
566 // - mid-stream encoder_->Destroy()
568 } // namespace
569 } // namespace content
571 int main(int argc, char** argv) {
572 testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args.
573 CommandLine::Init(argc, argv);
575 // Needed to enable DVLOG through --vmodule.
576 logging::LoggingSettings settings;
577 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
578 settings.dcheck_state =
579 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
580 CHECK(logging::InitLogging(settings));
582 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
583 DCHECK(cmd_line);
585 CommandLine::SwitchMap switches = cmd_line->GetSwitches();
586 for (CommandLine::SwitchMap::const_iterator it = switches.begin();
587 it != switches.end();
588 ++it) {
589 if (it->first == "test_stream_data") {
590 content::test_stream_data = it->second.c_str();
591 continue;
593 if (it->first == "v" || it->first == "vmodule")
594 continue;
595 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
598 base::ShadowingAtExitManager at_exit_manager;
600 return RUN_ALL_TESTS();