7526fdad34933bf7bb55eb951ca4cad6f83edca1
[platform/framework/web/crosswalk.git] / src / content / common / gpu / media / video_encode_accelerator_unittest.cc
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 "content/common/gpu/media/exynos_video_encode_accelerator.h"
16 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
17 #include "media/base/bind_to_current_loop.h"
18 #include "media/base/bitstream_buffer.h"
19 #include "media/base/test_data_util.h"
20 #include "media/filters/h264_parser.h"
21 #include "media/video/video_encode_accelerator.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 using media::VideoEncodeAccelerator;
25
26 namespace content {
27 namespace {
28
29 const media::VideoFrame::Format kInputFormat = media::VideoFrame::I420;
30
31 // Arbitrarily chosen to add some depth to the pipeline.
32 const unsigned int kNumOutputBuffers = 4;
33 const unsigned int kNumExtraInputFrames = 4;
34 // Maximum delay between requesting a keyframe and receiving one, in frames.
35 // Arbitrarily chosen as a reasonable requirement.
36 const unsigned int kMaxKeyframeDelay = 4;
37 // Value to use as max frame number for keyframe detection.
38 const unsigned int kMaxFrameNum =
39     std::numeric_limits<unsigned int>::max() - kMaxKeyframeDelay;
40 const uint32 kDefaultBitrate = 2000000;
41 // Tolerance factor for how encoded bitrate can differ from requested bitrate.
42 const double kBitrateTolerance = 0.1;
43 const uint32 kDefaultFPS = 30;
44
45 // The syntax of each test stream is:
46 // "in_filename:width:height:out_filename:requested_bitrate"
47 // - |in_filename| must be an I420 (YUV planar) raw stream
48 //   (see http://www.fourcc.org/yuv.php#IYUV).
49 // - |width| and |height| are in pixels.
50 // - |profile| to encode into (values of media::VideoCodecProfile).
51 // - |out_filename| filename to save the encoded stream to (optional).
52 //   Output stream is saved for the simple encode test only.
53 // - |requested_bitrate| requested bitrate in bits per second (optional).
54 //   Bitrate is only forced for tests that test bitrate.
55 base::FilePath::StringType test_stream_data =
56     media::GetTestDataFilePath("sync_192p20_frames.yuv").value() +
57     ":320:192:1:out.h264:200000";
58
59 struct TestStream {
60   TestStream() : requested_bitrate(0) {}
61   ~TestStream() {}
62
63   gfx::Size size;
64   base::MemoryMappedFile input_file;
65   media::VideoCodecProfile requested_profile;
66   std::string out_filename;
67   unsigned int requested_bitrate;
68 };
69
70 static void ParseAndReadTestStreamData(base::FilePath::StringType data,
71                                        TestStream* test_stream) {
72   std::vector<base::FilePath::StringType> fields;
73   base::SplitString(data, ':', &fields);
74   CHECK_GE(fields.size(), 4U) << data;
75   CHECK_LE(fields.size(), 6U) << data;
76
77   base::FilePath::StringType filename = fields[0];
78   int width, height;
79   CHECK(base::StringToInt(fields[1], &width));
80   CHECK(base::StringToInt(fields[2], &height));
81   test_stream->size = gfx::Size(width, height);
82   CHECK(!test_stream->size.IsEmpty());
83   int profile;
84   CHECK(base::StringToInt(fields[3], &profile));
85   CHECK_GT(profile, media::VIDEO_CODEC_PROFILE_UNKNOWN);
86   CHECK_LE(profile, media::VIDEO_CODEC_PROFILE_MAX);
87   test_stream->requested_profile =
88       static_cast<media::VideoCodecProfile>(profile);
89   if (fields.size() >= 5 && !fields[4].empty())
90     test_stream->out_filename = fields[4];
91   if (fields.size() >= 6 && !fields[5].empty())
92     CHECK(base::StringToUint(fields[5], &test_stream->requested_bitrate));
93
94   CHECK(test_stream->input_file.Initialize(base::FilePath(filename)));
95 }
96
97 enum ClientState {
98   CS_CREATED,
99   CS_ENCODER_SET,
100   CS_INITIALIZED,
101   CS_ENCODING,
102   CS_FINISHING,
103   CS_FINISHED,
104   CS_ERROR,
105 };
106
107 // Performs basic, codec-specific sanity checks on the stream buffers passed
108 // to ProcessStreamBuffer(): whether we've seen keyframes before non-keyframes,
109 // correct sequences of H.264 NALUs (SPS before PPS and before slices), etc.
110 // Calls given FrameFoundCallback when a complete frame is found while
111 // processing.
112 class StreamValidator {
113  public:
114   // To be called when a complete frame is found while processing a stream
115   // buffer, passing true if the frame is a keyframe. Returns false if we
116   // are not interested in more frames and further processing should be aborted.
117   typedef base::Callback<bool(bool)> FrameFoundCallback;
118
119   virtual ~StreamValidator() {}
120
121   // Provide a StreamValidator instance for the given |profile|.
122   static scoped_ptr<StreamValidator> Create(media::VideoCodecProfile profile,
123                                             const FrameFoundCallback& frame_cb);
124
125   // Process and verify contents of a bitstream buffer.
126   virtual void ProcessStreamBuffer(const uint8* stream, size_t size) = 0;
127
128  protected:
129   explicit StreamValidator(const FrameFoundCallback& frame_cb)
130       : frame_cb_(frame_cb) {}
131
132   FrameFoundCallback frame_cb_;
133 };
134
135 class H264Validator : public StreamValidator {
136  public:
137   explicit H264Validator(const FrameFoundCallback& frame_cb)
138       : StreamValidator(frame_cb),
139         seen_sps_(false),
140         seen_pps_(false),
141         seen_idr_(false) {}
142
143   void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
144
145  private:
146   // Set to true when encoder provides us with the corresponding NALU type.
147   bool seen_sps_;
148   bool seen_pps_;
149   bool seen_idr_;
150 };
151
152 void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
153   media::H264Parser h264_parser;
154   h264_parser.SetStream(stream, size);
155
156   while (1) {
157     media::H264NALU nalu;
158     media::H264Parser::Result result;
159
160     result = h264_parser.AdvanceToNextNALU(&nalu);
161     if (result == media::H264Parser::kEOStream)
162       break;
163
164     ASSERT_EQ(result, media::H264Parser::kOk);
165
166     bool keyframe = false;
167
168     switch (nalu.nal_unit_type) {
169       case media::H264NALU::kIDRSlice:
170         ASSERT_TRUE(seen_sps_);
171         ASSERT_TRUE(seen_pps_);
172         seen_idr_ = keyframe = true;
173         // fallthrough
174       case media::H264NALU::kNonIDRSlice:
175         ASSERT_TRUE(seen_idr_);
176         if (!frame_cb_.Run(keyframe))
177           return;
178         break;
179
180       case media::H264NALU::kSPS:
181         seen_sps_ = true;
182         break;
183
184       case media::H264NALU::kPPS:
185         ASSERT_TRUE(seen_sps_);
186         seen_pps_ = true;
187         break;
188
189       default:
190         break;
191     }
192   }
193 }
194
195 class VP8Validator : public StreamValidator {
196  public:
197   explicit VP8Validator(const FrameFoundCallback& frame_cb)
198       : StreamValidator(frame_cb),
199         seen_keyframe_(false) {}
200
201   void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
202
203  private:
204   // Have we already got a keyframe in the stream?
205   bool seen_keyframe_;
206 };
207
208 void VP8Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
209   bool keyframe = !(stream[0] & 0x01);
210   if (keyframe)
211     seen_keyframe_ = true;
212
213   EXPECT_TRUE(seen_keyframe_);
214
215   frame_cb_.Run(keyframe);
216   // TODO(posciak): We could be getting more frames in the buffer, but there is
217   // no simple way to detect this. We'd need to parse the frames and go through
218   // partition numbers/sizes. For now assume one frame per buffer.
219 }
220
221 // static
222 scoped_ptr<StreamValidator> StreamValidator::Create(
223     media::VideoCodecProfile profile,
224     const FrameFoundCallback& frame_cb) {
225   scoped_ptr<StreamValidator> validator;
226
227   if (profile >= media::H264PROFILE_MIN &&
228       profile <= media::H264PROFILE_MAX) {
229     validator.reset(new H264Validator(frame_cb));
230   } else if (profile >= media::VP8PROFILE_MIN &&
231              profile <= media::VP8PROFILE_MAX) {
232     validator.reset(new VP8Validator(frame_cb));
233   } else {
234     LOG(FATAL) << "Unsupported profile: " << profile;
235   }
236
237   return validator.Pass();
238 }
239
240 class VEAClient : public VideoEncodeAccelerator::Client {
241  public:
242   VEAClient(const TestStream& test_stream,
243             ClientStateNotification<ClientState>* note,
244             bool save_to_file,
245             unsigned int keyframe_period,
246             bool force_bitrate);
247   virtual ~VEAClient();
248   void CreateEncoder();
249   void DestroyEncoder();
250
251   // VideoDecodeAccelerator::Client implementation.
252   void NotifyInitializeDone() OVERRIDE;
253   void RequireBitstreamBuffers(unsigned int input_count,
254                                const gfx::Size& input_coded_size,
255                                size_t output_buffer_size) OVERRIDE;
256   void BitstreamBufferReady(int32 bitstream_buffer_id,
257                             size_t payload_size,
258                             bool key_frame) OVERRIDE;
259   void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
260
261  private:
262   bool has_encoder() { return encoder_.get(); }
263
264   void SetState(ClientState new_state);
265   // Called before starting encode to set initial configuration of the encoder.
266   void SetInitialConfiguration();
267   // Called when encoder is done with a VideoFrame.
268   void InputNoLongerNeededCallback(int32 input_id);
269   // Ensure encoder has at least as many inputs as it asked for
270   // via RequireBitstreamBuffers().
271   void FeedEncoderWithInputs();
272   // Provide the encoder with a new output buffer.
273   void FeedEncoderWithOutput(base::SharedMemory* shm);
274   // Feed the encoder with num_required_input_buffers_ of black frames to force
275   // it to encode and return all inputs that came before this, effectively
276   // flushing it.
277   void FlushEncoder();
278
279   // Called on finding a complete frame (with |keyframe| set to true for
280   // keyframes) in the stream, to perform codec-independent, per-frame checks
281   // and accounting. Returns false once we have collected all frames we needed.
282   bool HandleEncodedFrame(bool keyframe);
283
284   // Perform any checks required at the end of the stream, called after
285   // receiving the last frame from the encoder.
286   void ChecksAtFinish();
287
288   // Prepare and return a frame wrapping the data at |position| bytes in
289   // the input stream, ready to be sent to encoder.
290   scoped_refptr<media::VideoFrame> PrepareInputFrame(off_t position);
291
292   ClientState state_;
293   scoped_ptr<VideoEncodeAccelerator> encoder_;
294
295   const TestStream& test_stream_;
296   ClientStateNotification<ClientState>* note_;
297
298   // Ids assigned to VideoFrames (start at 1 for easy comparison with
299   // num_encoded_frames_).
300   std::set<int32> inputs_at_client_;
301   int32 next_input_id_;
302
303   // Ids for output BitstreamBuffers.
304   typedef std::map<int32, base::SharedMemory*> IdToSHM;
305   ScopedVector<base::SharedMemory> output_shms_;
306   IdToSHM output_buffers_at_client_;
307   int32 next_output_buffer_id_;
308
309   // Current offset into input stream.
310   off_t pos_in_input_stream_;
311   // Calculated from input_coded_size_, in bytes.
312   size_t input_buffer_size_;
313   gfx::Size input_coded_size_;
314   // Requested by encoder.
315   unsigned int num_required_input_buffers_;
316   size_t output_buffer_size_;
317
318   // Precalculated number of frames in the stream.
319   unsigned int num_frames_in_stream_;
320   // Number of encoded frames we've got from the encoder thus far.
321   unsigned int num_encoded_frames_;
322
323   // True if received a keyframe while processing current bitstream buffer.
324   bool seen_keyframe_in_this_buffer_;
325
326   // True if we are to save the encoded stream to a file.
327   bool save_to_file_;
328
329   // Request a keyframe every keyframe_period_ frames.
330   const unsigned int keyframe_period_;
331
332   // Frame number for which we requested a keyframe.
333   unsigned int keyframe_requested_at_;
334
335   // True if we are asking encoder for a particular bitrate.
336   bool force_bitrate_;
337
338   // Byte size of the encoded stream (for bitrate calculation).
339   size_t encoded_stream_size_;
340
341   scoped_ptr<StreamValidator> validator_;
342
343   // All methods of this class should be run on the same thread.
344   base::ThreadChecker thread_checker_;
345 };
346
347 VEAClient::VEAClient(const TestStream& test_stream,
348                      ClientStateNotification<ClientState>* note,
349                      bool save_to_file,
350                      unsigned int keyframe_period,
351                      bool force_bitrate)
352     : state_(CS_CREATED),
353       test_stream_(test_stream),
354       note_(note),
355       next_input_id_(1),
356       next_output_buffer_id_(0),
357       pos_in_input_stream_(0),
358       input_buffer_size_(0),
359       num_required_input_buffers_(0),
360       output_buffer_size_(0),
361       num_frames_in_stream_(0),
362       num_encoded_frames_(0),
363       seen_keyframe_in_this_buffer_(false),
364       save_to_file_(save_to_file),
365       keyframe_period_(keyframe_period),
366       keyframe_requested_at_(kMaxFrameNum),
367       force_bitrate_(force_bitrate),
368       encoded_stream_size_(0) {
369   if (keyframe_period_)
370     CHECK_LT(kMaxKeyframeDelay, keyframe_period_);
371
372   validator_ = StreamValidator::Create(
373       test_stream_.requested_profile,
374       base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
375
376   CHECK(validator_.get());
377
378   if (save_to_file_) {
379     CHECK(!test_stream_.out_filename.empty());
380     base::FilePath out_filename(test_stream_.out_filename);
381     // This creates or truncates out_filename.
382     // Without it, AppendToFile() will not work.
383     EXPECT_EQ(0, base::WriteFile(out_filename, NULL, 0));
384   }
385
386   thread_checker_.DetachFromThread();
387 }
388
389 VEAClient::~VEAClient() { CHECK(!has_encoder()); }
390
391 void VEAClient::CreateEncoder() {
392   DCHECK(thread_checker_.CalledOnValidThread());
393   CHECK(!has_encoder());
394
395   encoder_.reset(new ExynosVideoEncodeAccelerator());
396
397   SetState(CS_ENCODER_SET);
398   DVLOG(1) << "Profile: " << test_stream_.requested_profile
399            << ", requested bitrate: " << test_stream_.requested_bitrate;
400   encoder_->Initialize(kInputFormat,
401                        test_stream_.size,
402                        test_stream_.requested_profile,
403                        test_stream_.requested_bitrate,
404                        this);
405 }
406
407 void VEAClient::DestroyEncoder() {
408   DCHECK(thread_checker_.CalledOnValidThread());
409   if (!has_encoder())
410     return;
411   encoder_.release()->Destroy();
412 }
413
414 void VEAClient::NotifyInitializeDone() {
415   DCHECK(thread_checker_.CalledOnValidThread());
416   SetInitialConfiguration();
417   SetState(CS_INITIALIZED);
418 }
419
420 void VEAClient::RequireBitstreamBuffers(unsigned int input_count,
421                                         const gfx::Size& input_coded_size,
422                                         size_t output_size) {
423   DCHECK(thread_checker_.CalledOnValidThread());
424   ASSERT_EQ(state_, CS_INITIALIZED);
425   SetState(CS_ENCODING);
426
427   // TODO(posciak): For now we only support input streams that meet encoder
428   // size requirements exactly (i.e. coded size == visible size).
429   input_coded_size_ = input_coded_size;
430   ASSERT_EQ(input_coded_size_, test_stream_.size);
431
432   input_buffer_size_ = media::VideoFrame::AllocationSize(kInputFormat,
433                                                          input_coded_size_);
434   CHECK_GT(input_buffer_size_, 0UL);
435
436   // ARM performs CPU cache management with CPU cache line granularity. We thus
437   // need to ensure our buffers are CPU cache line-aligned (64 byte-aligned).
438   // Otherwise newer kernels will refuse to accept them, and on older kernels
439   // we'll be treating ourselves to random corruption.
440   // Since we are just mmapping and passing chunks of the input file, to ensure
441   // alignment, if the starting virtual addresses of the frames in it were not
442   // 64 byte-aligned, we'd have to use a separate set of input buffers and copy
443   // the frames into them before sending to the encoder. It would have been an
444   // overkill here though, because, for now at least, we only test resolutions
445   // that result in proper alignment, and it would have also interfered with
446   // performance testing. So just assert that the frame size is a multiple of
447   // 64 bytes. This ensures all frames start at 64-byte boundary, because
448   // MemoryMappedFile should be mmapp()ed at virtual page start as well.
449   ASSERT_EQ(input_buffer_size_ & 63, 0)
450       << "Frame size has to be a multiple of 64 bytes";
451   ASSERT_EQ(reinterpret_cast<off_t>(test_stream_.input_file.data()) & 63, 0)
452       << "Mapped file should be mapped at a 64 byte boundary";
453
454   num_required_input_buffers_ = input_count;
455   ASSERT_GT(num_required_input_buffers_, 0UL);
456
457   num_frames_in_stream_ = test_stream_.input_file.length() / input_buffer_size_;
458   CHECK_GT(num_frames_in_stream_, 0UL);
459   CHECK_LE(num_frames_in_stream_, kMaxFrameNum);
460   CHECK_EQ(num_frames_in_stream_ * input_buffer_size_,
461            test_stream_.input_file.length());
462
463   output_buffer_size_ = output_size;
464   ASSERT_GT(output_buffer_size_, 0UL);
465
466   for (unsigned int i = 0; i < kNumOutputBuffers; ++i) {
467     base::SharedMemory* shm = new base::SharedMemory();
468     CHECK(shm->CreateAndMapAnonymous(output_buffer_size_));
469     output_shms_.push_back(shm);
470     FeedEncoderWithOutput(shm);
471   }
472
473   FeedEncoderWithInputs();
474 }
475
476 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id,
477                                      size_t payload_size,
478                                      bool key_frame) {
479   DCHECK(thread_checker_.CalledOnValidThread());
480   ASSERT_LE(payload_size, output_buffer_size_);
481
482   IdToSHM::iterator it = output_buffers_at_client_.find(bitstream_buffer_id);
483   ASSERT_NE(it, output_buffers_at_client_.end());
484   base::SharedMemory* shm = it->second;
485   output_buffers_at_client_.erase(it);
486
487   if (state_ == CS_FINISHED)
488     return;
489
490   encoded_stream_size_ += payload_size;
491
492   const uint8* stream_ptr = static_cast<const uint8*>(shm->memory());
493   if (payload_size > 0)
494     validator_->ProcessStreamBuffer(stream_ptr, payload_size);
495
496   EXPECT_EQ(key_frame, seen_keyframe_in_this_buffer_);
497   seen_keyframe_in_this_buffer_ = false;
498
499   if (save_to_file_) {
500     int size = base::checked_cast<int>(payload_size);
501     EXPECT_EQ(base::AppendToFile(
502                   base::FilePath::FromUTF8Unsafe(test_stream_.out_filename),
503                   static_cast<char*>(shm->memory()),
504                   size),
505               size);
506   }
507
508   FeedEncoderWithOutput(shm);
509 }
510
511 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error) {
512   DCHECK(thread_checker_.CalledOnValidThread());
513   SetState(CS_ERROR);
514 }
515
516 void VEAClient::SetState(ClientState new_state) {
517   note_->Notify(new_state);
518   state_ = new_state;
519 }
520
521 void VEAClient::SetInitialConfiguration() {
522   if (force_bitrate_) {
523     CHECK_GT(test_stream_.requested_bitrate, 0UL);
524     encoder_->RequestEncodingParametersChange(test_stream_.requested_bitrate,
525                                               kDefaultFPS);
526   }
527 }
528
529 void VEAClient::InputNoLongerNeededCallback(int32 input_id) {
530   std::set<int32>::iterator it = inputs_at_client_.find(input_id);
531   ASSERT_NE(it, inputs_at_client_.end());
532   inputs_at_client_.erase(it);
533   FeedEncoderWithInputs();
534 }
535
536 scoped_refptr<media::VideoFrame> VEAClient::PrepareInputFrame(off_t position) {
537   CHECK_LE(position + input_buffer_size_, test_stream_.input_file.length());
538
539   uint8* frame_data =
540       const_cast<uint8*>(test_stream_.input_file.data() + position);
541
542   scoped_refptr<media::VideoFrame> frame =
543       media::VideoFrame::WrapExternalYuvData(
544           kInputFormat,
545           input_coded_size_,
546           gfx::Rect(test_stream_.size),
547           test_stream_.size,
548           input_coded_size_.width(),
549           input_coded_size_.width() / 2,
550           input_coded_size_.width() / 2,
551           frame_data,
552           frame_data + input_coded_size_.GetArea(),
553           frame_data + (input_coded_size_.GetArea() * 5 / 4),
554           base::TimeDelta(),
555           media::BindToCurrentLoop(
556               base::Bind(&VEAClient::InputNoLongerNeededCallback,
557                          base::Unretained(this),
558                          next_input_id_)));
559
560   CHECK(inputs_at_client_.insert(next_input_id_).second);
561   ++next_input_id_;
562
563   return frame;
564 }
565
566 void VEAClient::FeedEncoderWithInputs() {
567   if (!has_encoder())
568     return;
569
570   if (state_ != CS_ENCODING)
571     return;
572
573   while (inputs_at_client_.size() <
574          num_required_input_buffers_ + kNumExtraInputFrames) {
575     size_t bytes_left = test_stream_.input_file.length() - pos_in_input_stream_;
576     if (bytes_left < input_buffer_size_) {
577       DCHECK_EQ(bytes_left, 0UL);
578       FlushEncoder();
579       return;
580     }
581
582     bool force_keyframe = false;
583     if (keyframe_period_ && next_input_id_ % keyframe_period_ == 0) {
584       keyframe_requested_at_ = next_input_id_;
585       force_keyframe = true;
586     }
587
588     scoped_refptr<media::VideoFrame> video_frame =
589         PrepareInputFrame(pos_in_input_stream_);
590     pos_in_input_stream_ += input_buffer_size_;
591
592     encoder_->Encode(video_frame, force_keyframe);
593   }
594 }
595
596 void VEAClient::FeedEncoderWithOutput(base::SharedMemory* shm) {
597   if (!has_encoder())
598     return;
599
600   if (state_ != CS_ENCODING && state_ != CS_FINISHING)
601     return;
602
603   base::SharedMemoryHandle dup_handle;
604   CHECK(shm->ShareToProcess(base::Process::Current().handle(), &dup_handle));
605
606   media::BitstreamBuffer bitstream_buffer(
607       next_output_buffer_id_++, dup_handle, output_buffer_size_);
608   CHECK(output_buffers_at_client_.insert(std::make_pair(bitstream_buffer.id(),
609                                                         shm)).second);
610   encoder_->UseOutputBitstreamBuffer(bitstream_buffer);
611 }
612
613 void VEAClient::FlushEncoder() {
614   ASSERT_EQ(state_, CS_ENCODING);
615   SetState(CS_FINISHING);
616
617   // Feed the encoder with an additional set of num_required_input_buffers_
618   // to flush it, using the first frame in the input stream. The resulting
619   // encoded frames will be ignored.
620   for (unsigned int i = 0; i < num_required_input_buffers_; ++i) {
621     scoped_refptr<media::VideoFrame> frame = PrepareInputFrame(0);
622     encoder_->Encode(frame, false);
623   }
624 }
625
626 bool VEAClient::HandleEncodedFrame(bool keyframe) {
627   // This would be a bug in the test, which should not ignore false
628   // return value from this method.
629   CHECK_LE(num_encoded_frames_, num_frames_in_stream_);
630
631   ++num_encoded_frames_;
632   if (keyframe) {
633     // Got keyframe, reset keyframe detection regardless of whether we
634     // got a frame in time or not.
635     keyframe_requested_at_ = kMaxFrameNum;
636     seen_keyframe_in_this_buffer_ = true;
637   }
638
639   // Because the keyframe behavior requirements are loose, we give
640   // the encoder more freedom here. It could either deliver a keyframe
641   // immediately after we requested it, which could be for a frame number
642   // before the one we requested it for (if the keyframe request
643   // is asynchronous, i.e. not bound to any concrete frame, and because
644   // the pipeline can be deeper than one frame), at that frame, or after.
645   // So the only constraints we put here is that we get a keyframe not
646   // earlier than we requested one (in time), and not later than
647   // kMaxKeyframeDelay frames after the frame, for which we requested
648   // it, comes back encoded.
649   EXPECT_LE(num_encoded_frames_, keyframe_requested_at_ + kMaxKeyframeDelay);
650
651   if (num_encoded_frames_ == num_frames_in_stream_) {
652     ChecksAtFinish();
653     SetState(CS_FINISHED);
654     return false;
655   }
656
657   return true;
658 }
659
660 void VEAClient::ChecksAtFinish() {
661   unsigned int bitrate =
662       encoded_stream_size_ * 8 * kDefaultFPS / num_frames_in_stream_;
663   DVLOG(1) << "Final bitrate: " << bitrate
664            << " num frames: " << num_frames_in_stream_;
665   if (force_bitrate_) {
666     EXPECT_NEAR(bitrate,
667                 test_stream_.requested_bitrate,
668                 kBitrateTolerance * test_stream_.requested_bitrate);
669   }
670 }
671
672 // Test parameters:
673 // - If true, save output to file (provided an output filename was supplied).
674 // - Force a keyframe every n frames.
675 // - Force bitrate; the actual required value is provided as a property
676 //   of the input stream, because it depends on stream type/resolution/etc.
677 class VideoEncodeAcceleratorTest
678     : public ::testing::TestWithParam<Tuple3<bool, int, bool> > {};
679
680 TEST_P(VideoEncodeAcceleratorTest, TestSimpleEncode) {
681   const unsigned int keyframe_period = GetParam().b;
682   const bool force_bitrate = GetParam().c;
683
684   TestStream test_stream;
685   ParseAndReadTestStreamData(test_stream_data, &test_stream);
686
687   // Disregard save_to_file if we didn't get an output filename.
688   const bool save_to_file = GetParam().a && !test_stream.out_filename.empty();
689
690   if (test_stream.requested_bitrate == 0)
691     test_stream.requested_bitrate = kDefaultBitrate;
692
693   base::Thread encoder_thread("EncoderThread");
694   encoder_thread.Start();
695
696   ClientStateNotification<ClientState> note;
697   scoped_ptr<VEAClient> client(new VEAClient(test_stream,
698                                              &note,
699                                              save_to_file,
700                                              keyframe_period,
701                                              force_bitrate));
702
703   encoder_thread.message_loop()->PostTask(
704       FROM_HERE,
705       base::Bind(&VEAClient::CreateEncoder, base::Unretained(client.get())));
706
707   ASSERT_EQ(note.Wait(), CS_ENCODER_SET);
708   ASSERT_EQ(note.Wait(), CS_INITIALIZED);
709   ASSERT_EQ(note.Wait(), CS_ENCODING);
710   ASSERT_EQ(note.Wait(), CS_FINISHING);
711   ASSERT_EQ(note.Wait(), CS_FINISHED);
712
713   encoder_thread.message_loop()->PostTask(
714       FROM_HERE,
715       base::Bind(&VEAClient::DestroyEncoder, base::Unretained(client.get())));
716
717   encoder_thread.Stop();
718 }
719
720 INSTANTIATE_TEST_CASE_P(SimpleEncode,
721                         VideoEncodeAcceleratorTest,
722                         ::testing::Values(MakeTuple(true, 0, false)));
723
724 INSTANTIATE_TEST_CASE_P(ForceKeyframes,
725                         VideoEncodeAcceleratorTest,
726                         ::testing::Values(MakeTuple(false, 10, false)));
727
728 INSTANTIATE_TEST_CASE_P(ForceBitrate,
729                         VideoEncodeAcceleratorTest,
730                         ::testing::Values(MakeTuple(false, 0, true)));
731
732 // TODO(posciak): more tests:
733 // - async FeedEncoderWithOutput
734 // - out-of-order return of outputs to encoder
735 // - dynamic, runtime bitrate changes
736 // - multiple encoders
737 // - multiple encoders + decoders
738 // - mid-stream encoder_->Destroy()
739
740 }  // namespace
741 }  // namespace content
742
743 int main(int argc, char** argv) {
744   testing::InitGoogleTest(&argc, argv);  // Removes gtest-specific args.
745   CommandLine::Init(argc, argv);
746
747   // Needed to enable DVLOG through --vmodule.
748   logging::LoggingSettings settings;
749   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
750   CHECK(logging::InitLogging(settings));
751
752   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
753   DCHECK(cmd_line);
754
755   CommandLine::SwitchMap switches = cmd_line->GetSwitches();
756   for (CommandLine::SwitchMap::const_iterator it = switches.begin();
757        it != switches.end();
758        ++it) {
759     if (it->first == "test_stream_data") {
760       content::test_stream_data = it->second.c_str();
761       continue;
762     }
763     if (it->first == "v" || it->first == "vmodule")
764       continue;
765     LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
766   }
767
768   base::ShadowingAtExitManager at_exit_manager;
769
770   return RUN_ALL_TESTS();
771 }