56d40a240c5fe261c39b27e2d60c82fad134529a
[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 "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
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
72 //                                  stream.
73 // - |requested_subsequent_framerate| framerate to switch to in the middle
74 //                                    of the stream.
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;
79
80 struct TestStream {
81   TestStream()
82       : requested_bitrate(0),
83         requested_framerate(0),
84         requested_subsequent_bitrate(0),
85         requested_subsequent_framerate(0) {}
86   ~TestStream() {}
87
88   gfx::Size size;
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;
96 };
97
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;
106
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();
114
115     base::FilePath::StringType filename = fields[0];
116     int width, height;
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());
121     int profile;
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);
127
128     if (fields.size() >= 5 && !fields[4].empty())
129       test_stream->out_filename = fields[4];
130
131     if (fields.size() >= 6 && !fields[5].empty())
132       CHECK(base::StringToUint(fields[5], &test_stream->requested_bitrate));
133
134     if (fields.size() >= 7 && !fields[6].empty())
135       CHECK(base::StringToUint(fields[6], &test_stream->requested_framerate));
136
137     if (fields.size() >= 8 && !fields[7].empty()) {
138       CHECK(base::StringToUint(fields[7],
139                                &test_stream->requested_subsequent_bitrate));
140     }
141
142     if (fields.size() >= 9 && !fields[8].empty()) {
143       CHECK(base::StringToUint(fields[8],
144                                &test_stream->requested_subsequent_framerate));
145     }
146
147     CHECK(test_stream->input_file.Initialize(base::FilePath(filename)));
148     test_streams->push_back(test_stream);
149   }
150 }
151
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;
162
163     if (test_stream->requested_framerate == 0)
164       test_stream->requested_framerate = kDefaultFramerate;
165
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;
174       }
175     } else {
176       test_stream->requested_subsequent_bitrate =
177           test_stream->requested_bitrate;
178     }
179     if (test_stream->requested_subsequent_bitrate == 0)
180       test_stream->requested_subsequent_bitrate = 1;
181
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;
186       }
187     } else {
188       test_stream->requested_subsequent_framerate =
189           test_stream->requested_framerate;
190     }
191     if (test_stream->requested_subsequent_framerate == 0)
192       test_stream->requested_subsequent_framerate = 1;
193   }
194 }
195
196 enum ClientState {
197   CS_CREATED,
198   CS_ENCODER_SET,
199   CS_INITIALIZED,
200   CS_ENCODING,
201   CS_FINISHING,
202   CS_FINISHED,
203   CS_ERROR,
204 };
205
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
210 // processing.
211 class StreamValidator {
212  public:
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;
217
218   virtual ~StreamValidator() {}
219
220   // Provide a StreamValidator instance for the given |profile|.
221   static scoped_ptr<StreamValidator> Create(media::VideoCodecProfile profile,
222                                             const FrameFoundCallback& frame_cb);
223
224   // Process and verify contents of a bitstream buffer.
225   virtual void ProcessStreamBuffer(const uint8* stream, size_t size) = 0;
226
227  protected:
228   explicit StreamValidator(const FrameFoundCallback& frame_cb)
229       : frame_cb_(frame_cb) {}
230
231   FrameFoundCallback frame_cb_;
232 };
233
234 class H264Validator : public StreamValidator {
235  public:
236   explicit H264Validator(const FrameFoundCallback& frame_cb)
237       : StreamValidator(frame_cb),
238         seen_sps_(false),
239         seen_pps_(false),
240         seen_idr_(false) {}
241
242   void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
243
244  private:
245   // Set to true when encoder provides us with the corresponding NALU type.
246   bool seen_sps_;
247   bool seen_pps_;
248   bool seen_idr_;
249 };
250
251 void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
252   media::H264Parser h264_parser;
253   h264_parser.SetStream(stream, size);
254
255   while (1) {
256     media::H264NALU nalu;
257     media::H264Parser::Result result;
258
259     result = h264_parser.AdvanceToNextNALU(&nalu);
260     if (result == media::H264Parser::kEOStream)
261       break;
262
263     ASSERT_EQ(result, media::H264Parser::kOk);
264
265     bool keyframe = false;
266
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;
272         // fallthrough
273       case media::H264NALU::kNonIDRSlice:
274         ASSERT_TRUE(seen_idr_);
275         if (!frame_cb_.Run(keyframe))
276           return;
277         break;
278
279       case media::H264NALU::kSPS:
280         seen_sps_ = true;
281         break;
282
283       case media::H264NALU::kPPS:
284         ASSERT_TRUE(seen_sps_);
285         seen_pps_ = true;
286         break;
287
288       default:
289         break;
290     }
291   }
292 }
293
294 class VP8Validator : public StreamValidator {
295  public:
296   explicit VP8Validator(const FrameFoundCallback& frame_cb)
297       : StreamValidator(frame_cb),
298         seen_keyframe_(false) {}
299
300   void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
301
302  private:
303   // Have we already got a keyframe in the stream?
304   bool seen_keyframe_;
305 };
306
307 void VP8Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
308   bool keyframe = !(stream[0] & 0x01);
309   if (keyframe)
310     seen_keyframe_ = true;
311
312   EXPECT_TRUE(seen_keyframe_);
313
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.
318 }
319
320 // static
321 scoped_ptr<StreamValidator> StreamValidator::Create(
322     media::VideoCodecProfile profile,
323     const FrameFoundCallback& frame_cb) {
324   scoped_ptr<StreamValidator> validator;
325
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));
332   } else {
333     LOG(FATAL) << "Unsupported profile: " << profile;
334   }
335
336   return validator.Pass();
337 }
338
339 class VEAClient : public VideoEncodeAccelerator::Client {
340  public:
341   VEAClient(const TestStream& test_stream,
342             ClientStateNotification<ClientState>* note,
343             bool save_to_file,
344             unsigned int keyframe_period,
345             bool force_bitrate,
346             bool test_perf);
347   virtual ~VEAClient();
348   void CreateEncoder();
349   void DestroyEncoder();
350
351   // Return the number of encoded frames per second.
352   double frames_per_second();
353
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,
359                             size_t payload_size,
360                             bool key_frame) OVERRIDE;
361   void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
362
363  private:
364   bool has_encoder() { return encoder_.get(); }
365
366   void SetState(ClientState new_state);
367
368   // Set current stream parameters to given |bitrate| at |framerate|.
369   void SetStreamParameters(unsigned int bitrate, unsigned int framerate);
370
371   // Called when encoder is done with a VideoFrame.
372   void InputNoLongerNeededCallback(int32 input_id);
373
374   // Ensure encoder has at least as many inputs as it asked for
375   // via RequireBitstreamBuffers().
376   void FeedEncoderWithInputs();
377
378   // Provide the encoder with a new output buffer.
379   void FeedEncoderWithOutput(base::SharedMemory* shm);
380
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
383   // flushing it.
384   void FlushEncoder();
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   // 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_;
431
432   // Precalculated number of frames in the stream.
433   unsigned int num_frames_in_stream_;
434
435   // Number of encoded frames we've got from the encoder thus far.
436   unsigned int num_encoded_frames_;
437
438   // Frames since last bitrate verification.
439   unsigned int num_frames_since_last_check_;
440
441   // True if received a keyframe while processing current bitstream buffer.
442   bool seen_keyframe_in_this_buffer_;
443
444   // True if we are to save the encoded stream to a file.
445   bool save_to_file_;
446
447   // Request a keyframe every keyframe_period_ frames.
448   const unsigned int keyframe_period_;
449
450   // Frame number for which we requested a keyframe.
451   unsigned int keyframe_requested_at_;
452
453   // True if we are asking encoder for a particular bitrate.
454   bool force_bitrate_;
455
456   // Current requested bitrate.
457   unsigned int current_requested_bitrate_;
458
459   // Current expected framerate.
460   unsigned int current_framerate_;
461
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_;
465
466   // If true, verify performance at the end of the test.
467   bool test_perf_;
468
469   scoped_ptr<StreamValidator> validator_;
470
471   // The time when the encoder has initialized.
472   base::TimeTicks encoder_initialized_time_;
473
474   // The time when the last encoded frame is ready.
475   base::TimeTicks last_frame_ready_time_;
476
477   // All methods of this class should be run on the same thread.
478   base::ThreadChecker thread_checker_;
479 };
480
481 VEAClient::VEAClient(const TestStream& test_stream,
482                      ClientStateNotification<ClientState>* note,
483                      bool save_to_file,
484                      unsigned int keyframe_period,
485                      bool force_bitrate,
486                      bool test_perf)
487     : state_(CS_CREATED),
488       test_stream_(test_stream),
489       note_(note),
490       next_input_id_(1),
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_);
510
511   validator_ = StreamValidator::Create(
512       test_stream_.requested_profile,
513       base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
514
515   CHECK(validator_.get());
516
517   if (save_to_file_) {
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));
523   }
524
525   thread_checker_.DetachFromThread();
526 }
527
528 VEAClient::~VEAClient() { CHECK(!has_encoder()); }
529
530 void VEAClient::CreateEncoder() {
531   DCHECK(thread_checker_.CalledOnValidThread());
532   CHECK(!has_encoder());
533
534   scoped_ptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
535   encoder_.reset(new V4L2VideoEncodeAccelerator(device.Pass()));
536   SetState(CS_ENCODER_SET);
537
538   DVLOG(1) << "Profile: " << test_stream_.requested_profile
539            << ", initial bitrate: " << test_stream_.requested_bitrate;
540   if (!encoder_->Initialize(kInputFormat,
541                             test_stream_.size,
542                             test_stream_.requested_profile,
543                             test_stream_.requested_bitrate,
544                             this)) {
545     DLOG(ERROR) << "VideoEncodeAccelerator::Initialize() failed";
546     SetState(CS_ERROR);
547     return;
548   }
549
550   SetStreamParameters(test_stream_.requested_bitrate,
551                       test_stream_.requested_framerate);
552   SetState(CS_INITIALIZED);
553   encoder_initialized_time_ = base::TimeTicks::Now();
554 }
555
556 void VEAClient::DestroyEncoder() {
557   DCHECK(thread_checker_.CalledOnValidThread());
558   if (!has_encoder())
559     return;
560   encoder_.release()->Destroy();
561 }
562
563 double VEAClient::frames_per_second() {
564   base::TimeDelta duration = last_frame_ready_time_ - encoder_initialized_time_;
565   return num_encoded_frames_ / duration.InSecondsF();
566 }
567
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);
574
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);
579
580   input_buffer_size_ = media::VideoFrame::AllocationSize(kInputFormat,
581                                                          input_coded_size_);
582   CHECK_GT(input_buffer_size_, 0UL);
583
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";
601
602   num_required_input_buffers_ = input_count;
603   ASSERT_GT(num_required_input_buffers_, 0UL);
604
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());
610
611   output_buffer_size_ = output_size;
612   ASSERT_GT(output_buffer_size_, 0UL);
613
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);
619   }
620
621   FeedEncoderWithInputs();
622 }
623
624 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id,
625                                      size_t payload_size,
626                                      bool key_frame) {
627   DCHECK(thread_checker_.CalledOnValidThread());
628   ASSERT_LE(payload_size, output_buffer_size_);
629
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);
634
635   if (state_ == CS_FINISHED)
636     return;
637
638   encoded_stream_size_since_last_check_ += payload_size;
639
640   const uint8* stream_ptr = static_cast<const uint8*>(shm->memory());
641   if (payload_size > 0)
642     validator_->ProcessStreamBuffer(stream_ptr, payload_size);
643
644   EXPECT_EQ(key_frame, seen_keyframe_in_this_buffer_);
645   seen_keyframe_in_this_buffer_ = false;
646
647   if (save_to_file_) {
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()),
652                   size),
653               size);
654   }
655
656   FeedEncoderWithOutput(shm);
657 }
658
659 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error) {
660   DCHECK(thread_checker_.CalledOnValidThread());
661   SetState(CS_ERROR);
662 }
663
664 void VEAClient::SetState(ClientState new_state) {
665   DVLOG(4) << "Changing state " << state_ << "->" << new_state;
666   note_->Notify(new_state);
667   state_ = new_state;
668 }
669
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_,
677                                             current_framerate_);
678   DVLOG(1) << "Switched parameters to " << current_requested_bitrate_
679            << " bps @ " << current_framerate_ << " FPS";
680 }
681
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();
687 }
688
689 scoped_refptr<media::VideoFrame> VEAClient::PrepareInputFrame(off_t position) {
690   CHECK_LE(position + input_buffer_size_, test_stream_.input_file.length());
691
692   uint8* frame_data =
693       const_cast<uint8*>(test_stream_.input_file.data() + position);
694
695   CHECK_GT(current_framerate_, 0);
696   scoped_refptr<media::VideoFrame> frame =
697       media::VideoFrame::WrapExternalYuvData(
698           kInputFormat,
699           input_coded_size_,
700           gfx::Rect(test_stream_.size),
701           test_stream_.size,
702           input_coded_size_.width(),
703           input_coded_size_.width() / 2,
704           input_coded_size_.width() / 2,
705           frame_data,
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 /
710               current_framerate_),
711           media::BindToCurrentLoop(
712               base::Bind(&VEAClient::InputNoLongerNeededCallback,
713                          base::Unretained(this),
714                          next_input_id_)));
715
716   CHECK(inputs_at_client_.insert(next_input_id_).second);
717   ++next_input_id_;
718
719   return frame;
720 }
721
722 void VEAClient::FeedEncoderWithInputs() {
723   if (!has_encoder())
724     return;
725
726   if (state_ != CS_ENCODING)
727     return;
728
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);
734       FlushEncoder();
735       return;
736     }
737
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;
742     }
743
744     scoped_refptr<media::VideoFrame> video_frame =
745         PrepareInputFrame(pos_in_input_stream_);
746     pos_in_input_stream_ += input_buffer_size_;
747
748     encoder_->Encode(video_frame, force_keyframe);
749   }
750 }
751
752 void VEAClient::FeedEncoderWithOutput(base::SharedMemory* shm) {
753   if (!has_encoder())
754     return;
755
756   if (state_ != CS_ENCODING && state_ != CS_FINISHING)
757     return;
758
759   base::SharedMemoryHandle dup_handle;
760   CHECK(shm->ShareToProcess(base::Process::Current().handle(), &dup_handle));
761
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(),
765                                                         shm)).second);
766   encoder_->UseOutputBitstreamBuffer(bitstream_buffer);
767 }
768
769 void VEAClient::FlushEncoder() {
770   ASSERT_EQ(state_, CS_ENCODING);
771   SetState(CS_FINISHING);
772
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);
779   }
780 }
781
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_);
786
787   ++num_encoded_frames_;
788   ++num_frames_since_last_check_;
789
790   last_frame_ready_time_ = base::TimeTicks::Now();
791   if (keyframe) {
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;
796   }
797
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);
809
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);
817     }
818   } else if (num_encoded_frames_ == num_frames_in_stream_) {
819     VerifyPerf();
820     VerifyStreamProperties();
821     SetState(CS_FINISHED);
822     return false;
823   }
824
825   return true;
826 }
827
828 void VEAClient::VerifyPerf() {
829   double measured_fps = frames_per_second();
830   LOG(INFO) << "Measured encoder FPS: " << measured_fps;
831   if (test_perf_)
832     EXPECT_GE(measured_fps, kMinPerfFPS);
833 }
834
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_;
844
845   num_frames_since_last_check_ = 0;
846   encoded_stream_size_since_last_check_ = 0;
847
848   if (force_bitrate_) {
849     EXPECT_NEAR(bitrate,
850                 current_requested_bitrate_,
851                 kBitrateTolerance * current_requested_bitrate_);
852   }
853 }
854
855 // Test parameters:
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> > {};
867
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;
876
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);
882
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;
887
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 =
893         (save_to_file &&
894          !test_streams[test_stream_index]->out_filename.empty());
895
896     notes.push_back(new ClientStateNotification<ClientState>());
897     clients.push_back(new VEAClient(*test_streams[test_stream_index],
898                                     notes.back(),
899                                     encoder_save_to_file,
900                                     keyframe_period,
901                                     force_bitrate,
902                                     test_perf));
903
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(
910         FROM_HERE,
911         base::Bind(&VEAClient::CreateEncoder,
912                    base::Unretained(clients.back())));
913     encoder_threads.push_back(encoder_thread);
914   }
915
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(
924         FROM_HERE,
925         base::Bind(&VEAClient::DestroyEncoder, base::Unretained(clients[i])));
926     encoder_threads[i]->Stop();
927   }
928 }
929
930 INSTANTIATE_TEST_CASE_P(
931     SimpleEncode,
932     VideoEncodeAcceleratorTest,
933     ::testing::Values(MakeTuple(1, true, 0, false, false, false, false)));
934
935 INSTANTIATE_TEST_CASE_P(
936     EncoderPerf,
937     VideoEncodeAcceleratorTest,
938     ::testing::Values(MakeTuple(1, false, 0, false, true, false, false)));
939
940 INSTANTIATE_TEST_CASE_P(
941     ForceKeyframes,
942     VideoEncodeAcceleratorTest,
943     ::testing::Values(MakeTuple(1, false, 10, false, false, false, false)));
944
945 INSTANTIATE_TEST_CASE_P(
946     ForceBitrate,
947     VideoEncodeAcceleratorTest,
948     ::testing::Values(MakeTuple(1, false, 0, true, false, false, false)));
949
950 INSTANTIATE_TEST_CASE_P(
951     MidStreamParamSwitchBitrate,
952     VideoEncodeAcceleratorTest,
953     ::testing::Values(MakeTuple(1, false, 0, true, false, true, false)));
954
955 INSTANTIATE_TEST_CASE_P(
956     MidStreamParamSwitchFPS,
957     VideoEncodeAcceleratorTest,
958     ::testing::Values(MakeTuple(1, false, 0, true, false, false, true)));
959
960 INSTANTIATE_TEST_CASE_P(
961     MidStreamParamSwitchBitrateAndFPS,
962     VideoEncodeAcceleratorTest,
963     ::testing::Values(MakeTuple(1, false, 0, true, false, true, true)));
964
965 INSTANTIATE_TEST_CASE_P(
966     MultipleEncoders,
967     VideoEncodeAcceleratorTest,
968     ::testing::Values(MakeTuple(3, false, 0, false, false, false, false),
969                       MakeTuple(3, false, 0, true, false, true, true)));
970
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()
976
977 }  // namespace
978 }  // namespace content
979
980 int main(int argc, char** argv) {
981   testing::InitGoogleTest(&argc, argv);  // Removes gtest-specific args.
982   CommandLine::Init(argc, argv);
983
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();
990
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));
995
996   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
997   DCHECK(cmd_line);
998
999   CommandLine::SwitchMap switches = cmd_line->GetSwitches();
1000   for (CommandLine::SwitchMap::const_iterator it = switches.begin();
1001        it != switches.end();
1002        ++it) {
1003     if (it->first == "test_stream_data") {
1004       test_stream_data->assign(it->second.c_str());
1005       continue;
1006     }
1007     if (it->first == "v" || it->first == "vmodule")
1008       continue;
1009     LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
1010   }
1011
1012   return RUN_ALL_TESTS();
1013 }