GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / content / common / gpu / media / video_encode_accelerator_unittest.cc
blob73e319afb47afa7875fd62a7416e127e45f98118
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/files/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_handle.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/sys_byteorder.h"
16 #include "base/time/time.h"
17 #include "base/timer/timer.h"
18 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
19 #include "media/base/bind_to_current_loop.h"
20 #include "media/base/bitstream_buffer.h"
21 #include "media/base/test_data_util.h"
22 #include "media/filters/h264_parser.h"
23 #include "media/video/fake_video_encode_accelerator.h"
24 #include "media/video/video_encode_accelerator.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 #if defined(USE_OZONE)
28 #include "ui/ozone/public/ozone_platform.h"
29 #endif
31 #if defined(OS_CHROMEOS)
32 #if defined(ARCH_CPU_ARMEL) || (defined(USE_OZONE) && defined(USE_V4L2_CODEC))
33 #include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
34 #endif
35 #if defined(ARCH_CPU_X86_FAMILY)
36 #include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
37 #endif // defined(ARCH_CPU_X86_FAMILY)
38 #else
39 #error The VideoEncodeAcceleratorUnittest is not supported on this platform.
40 #endif
42 using media::VideoEncodeAccelerator;
44 namespace content {
45 namespace {
47 const media::VideoFrame::Format kInputFormat = media::VideoFrame::I420;
49 // Arbitrarily chosen to add some depth to the pipeline.
50 const unsigned int kNumOutputBuffers = 4;
51 const unsigned int kNumExtraInputFrames = 4;
52 // Maximum delay between requesting a keyframe and receiving one, in frames.
53 // Arbitrarily chosen as a reasonable requirement.
54 const unsigned int kMaxKeyframeDelay = 4;
55 // Value to use as max frame number for keyframe detection.
56 const unsigned int kMaxFrameNum =
57 std::numeric_limits<unsigned int>::max() - kMaxKeyframeDelay;
58 // Default initial bitrate.
59 const uint32 kDefaultBitrate = 2000000;
60 // Default ratio of requested_subsequent_bitrate to initial_bitrate
61 // (see test parameters below) if one is not provided.
62 const double kDefaultSubsequentBitrateRatio = 2.0;
63 // Default initial framerate.
64 const uint32 kDefaultFramerate = 30;
65 // Default ratio of requested_subsequent_framerate to initial_framerate
66 // (see test parameters below) if one is not provided.
67 const double kDefaultSubsequentFramerateRatio = 0.1;
68 // Tolerance factor for how encoded bitrate can differ from requested bitrate.
69 const double kBitrateTolerance = 0.1;
70 // Minimum required FPS throughput for the basic performance test.
71 const uint32 kMinPerfFPS = 30;
72 // Minimum (arbitrary) number of frames required to enforce bitrate requirements
73 // over. Streams shorter than this may be too short to realistically require
74 // an encoder to be able to converge to the requested bitrate over.
75 // The input stream will be looped as many times as needed in bitrate tests
76 // to reach at least this number of frames before calculating final bitrate.
77 const unsigned int kMinFramesForBitrateTests = 300;
79 // The syntax of multiple test streams is:
80 // test-stream1;test-stream2;test-stream3
81 // The syntax of each test stream is:
82 // "in_filename:width:height:out_filename:requested_bitrate:requested_framerate
83 // :requested_subsequent_bitrate:requested_subsequent_framerate"
84 // - |in_filename| must be an I420 (YUV planar) raw stream
85 // (see http://www.fourcc.org/yuv.php#IYUV).
86 // - |width| and |height| are in pixels.
87 // - |profile| to encode into (values of media::VideoCodecProfile).
88 // - |out_filename| filename to save the encoded stream to (optional). The
89 // format for H264 is Annex-B byte stream. The format for VP8 is IVF. Output
90 // stream is saved for the simple encode test only. H264 raw stream and IVF
91 // can be used as input of VDA unittest. H264 raw stream can be played by
92 // "mplayer -fps 25 out.h264" and IVF can be played by mplayer directly.
93 // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
94 // Further parameters are optional (need to provide preceding positional
95 // parameters if a specific subsequent parameter is required):
96 // - |requested_bitrate| requested bitrate in bits per second.
97 // - |requested_framerate| requested initial framerate.
98 // - |requested_subsequent_bitrate| bitrate to switch to in the middle of the
99 // stream.
100 // - |requested_subsequent_framerate| framerate to switch to in the middle
101 // of the stream.
102 // Bitrate is only forced for tests that test bitrate.
103 const char* g_default_in_filename = "bear_320x192_40frames.yuv";
104 const char* g_default_in_parameters = ":320:192:1:out.h264:200000";
106 // Enabled by including a --fake_encoder flag to the command line invoking the
107 // test.
108 bool g_fake_encoder = false;
110 // Environment to store test stream data for all test cases.
111 class VideoEncodeAcceleratorTestEnvironment;
112 VideoEncodeAcceleratorTestEnvironment* g_env;
114 struct IvfFileHeader {
115 char signature[4]; // signature: 'DKIF'
116 uint16_t version; // version (should be 0)
117 uint16_t header_size; // size of header in bytes
118 uint32_t fourcc; // codec FourCC (e.g., 'VP80')
119 uint16_t width; // width in pixels
120 uint16_t height; // height in pixels
121 uint32_t framerate; // frame rate per seconds
122 uint32_t timescale; // time scale. For example, if framerate is 30 and
123 // timescale is 2, the unit of IvfFrameHeader.timestamp
124 // is 2/30 seconds.
125 uint32_t num_frames; // number of frames in file
126 uint32_t unused; // unused
127 } __attribute__((packed));
129 struct IvfFrameHeader {
130 uint32_t frame_size; // Size of frame in bytes (not including the header)
131 uint64_t timestamp; // 64-bit presentation timestamp
132 } __attribute__((packed));
134 // The number of frames to be encoded. This variable is set by the switch
135 // "--num_frames_to_encode". Ignored if 0.
136 int g_num_frames_to_encode = 0;
138 struct TestStream {
139 TestStream()
140 : num_frames(0),
141 aligned_buffer_size(0),
142 requested_bitrate(0),
143 requested_framerate(0),
144 requested_subsequent_bitrate(0),
145 requested_subsequent_framerate(0) {}
146 ~TestStream() {}
148 gfx::Size visible_size;
149 gfx::Size coded_size;
150 unsigned int num_frames;
152 // Original unaligned input file name provided as an argument to the test.
153 // And the file must be an I420 (YUV planar) raw stream.
154 std::string in_filename;
156 // A temporary file used to prepare aligned input buffers of |in_filename|.
157 // The file makes sure starting address of YUV planes are 64 byte-aligned.
158 base::FilePath aligned_in_file;
160 // The memory mapping of |aligned_in_file|
161 base::MemoryMappedFile mapped_aligned_in_file;
163 // Byte size of a frame of |aligned_in_file|.
164 size_t aligned_buffer_size;
166 // Byte size for each aligned plane of a frame
167 std::vector<size_t> aligned_plane_size;
169 std::string out_filename;
170 media::VideoCodecProfile requested_profile;
171 unsigned int requested_bitrate;
172 unsigned int requested_framerate;
173 unsigned int requested_subsequent_bitrate;
174 unsigned int requested_subsequent_framerate;
177 inline static size_t Align64Bytes(size_t value) {
178 return (value + 63) & ~63;
181 // Write |data| of |size| bytes at |offset| bytes into |file|.
182 static bool WriteFile(base::File* file,
183 const off_t offset,
184 const uint8* data,
185 size_t size) {
186 size_t written_bytes = 0;
187 while (written_bytes < size) {
188 int bytes = file->Write(offset + written_bytes,
189 reinterpret_cast<const char*>(data + written_bytes),
190 size - written_bytes);
191 if (bytes <= 0)
192 return false;
193 written_bytes += bytes;
195 return true;
198 static bool IsH264(media::VideoCodecProfile profile) {
199 return profile >= media::H264PROFILE_MIN && profile <= media::H264PROFILE_MAX;
202 static bool IsVP8(media::VideoCodecProfile profile) {
203 return profile >= media::VP8PROFILE_MIN && profile <= media::VP8PROFILE_MAX;
206 // ARM performs CPU cache management with CPU cache line granularity. We thus
207 // need to ensure our buffers are CPU cache line-aligned (64 byte-aligned).
208 // Otherwise newer kernels will refuse to accept them, and on older kernels
209 // we'll be treating ourselves to random corruption.
210 // Since we are just mapping and passing chunks of the input file directly to
211 // the VEA as input frames to avoid copying large chunks of raw data on each
212 // frame and thus affecting performance measurements, we have to prepare a
213 // temporary file with all planes aligned to 64-byte boundaries beforehand.
214 static void CreateAlignedInputStreamFile(const gfx::Size& coded_size,
215 TestStream* test_stream) {
216 // Test case may have many encoders and memory should be prepared once.
217 if (test_stream->coded_size == coded_size &&
218 test_stream->mapped_aligned_in_file.IsValid())
219 return;
221 // All encoders in multiple encoder test reuse the same test_stream, make
222 // sure they requested the same coded_size
223 ASSERT_TRUE(!test_stream->mapped_aligned_in_file.IsValid() ||
224 coded_size == test_stream->coded_size);
225 test_stream->coded_size = coded_size;
227 size_t num_planes = media::VideoFrame::NumPlanes(kInputFormat);
228 std::vector<size_t> padding_sizes(num_planes);
229 std::vector<size_t> coded_bpl(num_planes);
230 std::vector<size_t> visible_bpl(num_planes);
231 std::vector<size_t> visible_plane_rows(num_planes);
233 // Calculate padding in bytes to be added after each plane required to keep
234 // starting addresses of all planes at a 64 byte boudnary. This padding will
235 // be added after each plane when copying to the temporary file.
236 // At the same time we also need to take into account coded_size requested by
237 // the VEA; each row of visible_bpl bytes in the original file needs to be
238 // copied into a row of coded_bpl bytes in the aligned file.
239 for (size_t i = 0; i < num_planes; i++) {
240 size_t size =
241 media::VideoFrame::PlaneAllocationSize(kInputFormat, i, coded_size);
242 test_stream->aligned_plane_size.push_back(Align64Bytes(size));
243 test_stream->aligned_buffer_size += test_stream->aligned_plane_size.back();
245 coded_bpl[i] =
246 media::VideoFrame::RowBytes(i, kInputFormat, coded_size.width());
247 visible_bpl[i] = media::VideoFrame::RowBytes(
248 i, kInputFormat, test_stream->visible_size.width());
249 visible_plane_rows[i] = media::VideoFrame::Rows(
250 i, kInputFormat, test_stream->visible_size.height());
251 size_t padding_rows =
252 media::VideoFrame::Rows(i, kInputFormat, coded_size.height()) -
253 visible_plane_rows[i];
254 padding_sizes[i] = padding_rows * coded_bpl[i] + Align64Bytes(size) - size;
257 base::MemoryMappedFile src_file;
258 CHECK(src_file.Initialize(base::FilePath(test_stream->in_filename)));
259 CHECK(base::CreateTemporaryFile(&test_stream->aligned_in_file));
261 size_t visible_buffer_size = media::VideoFrame::AllocationSize(
262 kInputFormat, test_stream->visible_size);
263 CHECK_EQ(src_file.length() % visible_buffer_size, 0U)
264 << "Stream byte size is not a product of calculated frame byte size";
266 test_stream->num_frames = src_file.length() / visible_buffer_size;
267 uint32 flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
268 base::File::FLAG_READ;
270 // Create a temporary file with coded_size length.
271 base::File dest_file(test_stream->aligned_in_file, flags);
272 CHECK_GT(test_stream->aligned_buffer_size, 0UL);
273 dest_file.SetLength(test_stream->aligned_buffer_size *
274 test_stream->num_frames);
276 const uint8* src = src_file.data();
277 off_t dest_offset = 0;
278 for (size_t frame = 0; frame < test_stream->num_frames; frame++) {
279 for (size_t i = 0; i < num_planes; i++) {
280 // Assert that each plane of frame starts at 64 byte boundary.
281 ASSERT_EQ(dest_offset & 63, 0)
282 << "Planes of frame should be mapped at a 64 byte boundary";
283 for (size_t j = 0; j < visible_plane_rows[i]; j++) {
284 CHECK(WriteFile(&dest_file, dest_offset, src, visible_bpl[i]));
285 src += visible_bpl[i];
286 dest_offset += coded_bpl[i];
288 dest_offset += padding_sizes[i];
291 CHECK(test_stream->mapped_aligned_in_file.Initialize(dest_file.Pass()));
292 // Assert that memory mapped of file starts at 64 byte boundary. So each
293 // plane of frames also start at 64 byte boundary.
294 ASSERT_EQ(
295 reinterpret_cast<off_t>(test_stream->mapped_aligned_in_file.data()) & 63,
297 << "File should be mapped at a 64 byte boundary";
299 CHECK_EQ(test_stream->mapped_aligned_in_file.length() %
300 test_stream->aligned_buffer_size,
302 << "Stream byte size is not a product of calculated frame byte size";
303 CHECK_GT(test_stream->num_frames, 0UL);
304 CHECK_LE(test_stream->num_frames, kMaxFrameNum);
307 // Parse |data| into its constituent parts, set the various output fields
308 // accordingly, read in video stream, and store them to |test_streams|.
309 static void ParseAndReadTestStreamData(const base::FilePath::StringType& data,
310 ScopedVector<TestStream>* test_streams) {
311 // Split the string to individual test stream data.
312 std::vector<base::FilePath::StringType> test_streams_data;
313 base::SplitString(data, ';', &test_streams_data);
314 CHECK_GE(test_streams_data.size(), 1U) << data;
316 // Parse each test stream data and read the input file.
317 for (size_t index = 0; index < test_streams_data.size(); ++index) {
318 std::vector<base::FilePath::StringType> fields;
319 base::SplitString(test_streams_data[index], ':', &fields);
320 CHECK_GE(fields.size(), 4U) << data;
321 CHECK_LE(fields.size(), 9U) << data;
322 TestStream* test_stream = new TestStream();
324 test_stream->in_filename = fields[0];
325 int width, height;
326 CHECK(base::StringToInt(fields[1], &width));
327 CHECK(base::StringToInt(fields[2], &height));
328 test_stream->visible_size = gfx::Size(width, height);
329 CHECK(!test_stream->visible_size.IsEmpty());
330 int profile;
331 CHECK(base::StringToInt(fields[3], &profile));
332 CHECK_GT(profile, media::VIDEO_CODEC_PROFILE_UNKNOWN);
333 CHECK_LE(profile, media::VIDEO_CODEC_PROFILE_MAX);
334 test_stream->requested_profile =
335 static_cast<media::VideoCodecProfile>(profile);
337 if (fields.size() >= 5 && !fields[4].empty())
338 test_stream->out_filename = fields[4];
340 if (fields.size() >= 6 && !fields[5].empty())
341 CHECK(base::StringToUint(fields[5], &test_stream->requested_bitrate));
343 if (fields.size() >= 7 && !fields[6].empty())
344 CHECK(base::StringToUint(fields[6], &test_stream->requested_framerate));
346 if (fields.size() >= 8 && !fields[7].empty()) {
347 CHECK(base::StringToUint(fields[7],
348 &test_stream->requested_subsequent_bitrate));
351 if (fields.size() >= 9 && !fields[8].empty()) {
352 CHECK(base::StringToUint(fields[8],
353 &test_stream->requested_subsequent_framerate));
355 test_streams->push_back(test_stream);
359 enum ClientState {
360 CS_CREATED,
361 CS_ENCODER_SET,
362 CS_INITIALIZED,
363 CS_ENCODING,
364 CS_FINISHED,
365 CS_ERROR,
368 // Performs basic, codec-specific sanity checks on the stream buffers passed
369 // to ProcessStreamBuffer(): whether we've seen keyframes before non-keyframes,
370 // correct sequences of H.264 NALUs (SPS before PPS and before slices), etc.
371 // Calls given FrameFoundCallback when a complete frame is found while
372 // processing.
373 class StreamValidator {
374 public:
375 // To be called when a complete frame is found while processing a stream
376 // buffer, passing true if the frame is a keyframe. Returns false if we
377 // are not interested in more frames and further processing should be aborted.
378 typedef base::Callback<bool(bool)> FrameFoundCallback;
380 virtual ~StreamValidator() {}
382 // Provide a StreamValidator instance for the given |profile|.
383 static scoped_ptr<StreamValidator> Create(media::VideoCodecProfile profile,
384 const FrameFoundCallback& frame_cb);
386 // Process and verify contents of a bitstream buffer.
387 virtual void ProcessStreamBuffer(const uint8* stream, size_t size) = 0;
389 protected:
390 explicit StreamValidator(const FrameFoundCallback& frame_cb)
391 : frame_cb_(frame_cb) {}
393 FrameFoundCallback frame_cb_;
396 class H264Validator : public StreamValidator {
397 public:
398 explicit H264Validator(const FrameFoundCallback& frame_cb)
399 : StreamValidator(frame_cb),
400 seen_sps_(false),
401 seen_pps_(false),
402 seen_idr_(false) {}
404 void ProcessStreamBuffer(const uint8* stream, size_t size) override;
406 private:
407 // Set to true when encoder provides us with the corresponding NALU type.
408 bool seen_sps_;
409 bool seen_pps_;
410 bool seen_idr_;
412 media::H264Parser h264_parser_;
415 void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
416 h264_parser_.SetStream(stream, size);
418 while (1) {
419 media::H264NALU nalu;
420 media::H264Parser::Result result;
422 result = h264_parser_.AdvanceToNextNALU(&nalu);
423 if (result == media::H264Parser::kEOStream)
424 break;
426 ASSERT_EQ(media::H264Parser::kOk, result);
428 bool keyframe = false;
430 switch (nalu.nal_unit_type) {
431 case media::H264NALU::kIDRSlice:
432 ASSERT_TRUE(seen_sps_);
433 ASSERT_TRUE(seen_pps_);
434 seen_idr_ = true;
435 keyframe = true;
436 // fallthrough
437 case media::H264NALU::kNonIDRSlice: {
438 ASSERT_TRUE(seen_idr_);
439 if (!frame_cb_.Run(keyframe))
440 return;
441 break;
444 case media::H264NALU::kSPS: {
445 int sps_id;
446 ASSERT_EQ(media::H264Parser::kOk, h264_parser_.ParseSPS(&sps_id));
447 seen_sps_ = true;
448 break;
451 case media::H264NALU::kPPS: {
452 ASSERT_TRUE(seen_sps_);
453 int pps_id;
454 ASSERT_EQ(media::H264Parser::kOk, h264_parser_.ParsePPS(&pps_id));
455 seen_pps_ = true;
456 break;
459 default:
460 break;
465 class VP8Validator : public StreamValidator {
466 public:
467 explicit VP8Validator(const FrameFoundCallback& frame_cb)
468 : StreamValidator(frame_cb),
469 seen_keyframe_(false) {}
471 void ProcessStreamBuffer(const uint8* stream, size_t size) override;
473 private:
474 // Have we already got a keyframe in the stream?
475 bool seen_keyframe_;
478 void VP8Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
479 bool keyframe = !(stream[0] & 0x01);
480 if (keyframe)
481 seen_keyframe_ = true;
483 EXPECT_TRUE(seen_keyframe_);
485 frame_cb_.Run(keyframe);
486 // TODO(posciak): We could be getting more frames in the buffer, but there is
487 // no simple way to detect this. We'd need to parse the frames and go through
488 // partition numbers/sizes. For now assume one frame per buffer.
491 // static
492 scoped_ptr<StreamValidator> StreamValidator::Create(
493 media::VideoCodecProfile profile,
494 const FrameFoundCallback& frame_cb) {
495 scoped_ptr<StreamValidator> validator;
497 if (IsH264(profile)) {
498 validator.reset(new H264Validator(frame_cb));
499 } else if (IsVP8(profile)) {
500 validator.reset(new VP8Validator(frame_cb));
501 } else {
502 LOG(FATAL) << "Unsupported profile: " << profile;
505 return validator.Pass();
508 class VEAClient : public VideoEncodeAccelerator::Client {
509 public:
510 VEAClient(TestStream* test_stream,
511 ClientStateNotification<ClientState>* note,
512 bool save_to_file,
513 unsigned int keyframe_period,
514 bool force_bitrate,
515 bool test_perf,
516 bool mid_stream_bitrate_switch,
517 bool mid_stream_framerate_switch,
518 bool run_at_fps);
519 ~VEAClient() override;
520 void CreateEncoder();
521 void DestroyEncoder();
523 // Return the number of encoded frames per second.
524 double frames_per_second();
526 // VideoDecodeAccelerator::Client implementation.
527 void RequireBitstreamBuffers(unsigned int input_count,
528 const gfx::Size& input_coded_size,
529 size_t output_buffer_size) override;
530 void BitstreamBufferReady(int32 bitstream_buffer_id,
531 size_t payload_size,
532 bool key_frame) override;
533 void NotifyError(VideoEncodeAccelerator::Error error) override;
535 private:
536 bool has_encoder() { return encoder_.get(); }
538 scoped_ptr<media::VideoEncodeAccelerator> CreateFakeVEA();
539 scoped_ptr<media::VideoEncodeAccelerator> CreateV4L2VEA();
540 scoped_ptr<media::VideoEncodeAccelerator> CreateVaapiVEA();
542 void SetState(ClientState new_state);
544 // Set current stream parameters to given |bitrate| at |framerate|.
545 void SetStreamParameters(unsigned int bitrate, unsigned int framerate);
547 // Called when encoder is done with a VideoFrame.
548 void InputNoLongerNeededCallback(int32 input_id);
550 // Feed the encoder with one input frame.
551 void FeedEncoderWithOneInput();
553 // Provide the encoder with a new output buffer.
554 void FeedEncoderWithOutput(base::SharedMemory* shm);
556 // Called on finding a complete frame (with |keyframe| set to true for
557 // keyframes) in the stream, to perform codec-independent, per-frame checks
558 // and accounting. Returns false once we have collected all frames we needed.
559 bool HandleEncodedFrame(bool keyframe);
561 // Verify that stream bitrate has been close to current_requested_bitrate_,
562 // assuming current_framerate_ since the last time VerifyStreamProperties()
563 // was called. Fail the test if |force_bitrate_| is true and the bitrate
564 // is not within kBitrateTolerance.
565 void VerifyStreamProperties();
567 // Test codec performance, failing the test if we are currently running
568 // the performance test.
569 void VerifyPerf();
571 // Write IVF file header to test_stream_->out_filename.
572 void WriteIvfFileHeader();
574 // Write an IVF frame header to test_stream_->out_filename.
575 void WriteIvfFrameHeader(int frame_index, size_t frame_size);
577 // Prepare and return a frame wrapping the data at |position| bytes in
578 // the input stream, ready to be sent to encoder.
579 scoped_refptr<media::VideoFrame> PrepareInputFrame(off_t position);
581 // Update the parameters according to |mid_stream_bitrate_switch| and
582 // |mid_stream_framerate_switch|.
583 void UpdateTestStreamData(bool mid_stream_bitrate_switch,
584 bool mid_stream_framerate_switch);
586 // Callback function of the |input_timer_|.
587 void OnInputTimer();
589 ClientState state_;
590 scoped_ptr<VideoEncodeAccelerator> encoder_;
592 TestStream* test_stream_;
594 // Used to notify another thread about the state. VEAClient does not own this.
595 ClientStateNotification<ClientState>* note_;
597 // Ids assigned to VideoFrames (start at 1 for easy comparison with
598 // num_encoded_frames_).
599 std::set<int32> inputs_at_client_;
600 int32 next_input_id_;
602 // Ids for output BitstreamBuffers.
603 typedef std::map<int32, base::SharedMemory*> IdToSHM;
604 ScopedVector<base::SharedMemory> output_shms_;
605 IdToSHM output_buffers_at_client_;
606 int32 next_output_buffer_id_;
608 // Current offset into input stream.
609 off_t pos_in_input_stream_;
610 gfx::Size input_coded_size_;
611 // Requested by encoder.
612 unsigned int num_required_input_buffers_;
613 size_t output_buffer_size_;
615 // Number of frames to encode. This may differ from the number of frames in
616 // stream if we need more frames for bitrate tests.
617 unsigned int num_frames_to_encode_;
619 // Number of encoded frames we've got from the encoder thus far.
620 unsigned int num_encoded_frames_;
622 // Frames since last bitrate verification.
623 unsigned int num_frames_since_last_check_;
625 // True if received a keyframe while processing current bitstream buffer.
626 bool seen_keyframe_in_this_buffer_;
628 // True if we are to save the encoded stream to a file.
629 bool save_to_file_;
631 // Request a keyframe every keyframe_period_ frames.
632 const unsigned int keyframe_period_;
634 // Number of keyframes requested by now.
635 unsigned int num_keyframes_requested_;
637 // Next keyframe expected before next_keyframe_at_ + kMaxKeyframeDelay.
638 unsigned int next_keyframe_at_;
640 // True if we are asking encoder for a particular bitrate.
641 bool force_bitrate_;
643 // Current requested bitrate.
644 unsigned int current_requested_bitrate_;
646 // Current expected framerate.
647 unsigned int current_framerate_;
649 // Byte size of the encoded stream (for bitrate calculation) since last
650 // time we checked bitrate.
651 size_t encoded_stream_size_since_last_check_;
653 // If true, verify performance at the end of the test.
654 bool test_perf_;
656 scoped_ptr<StreamValidator> validator_;
658 // The time when the encoding started.
659 base::TimeTicks encode_start_time_;
661 // The time when the last encoded frame is ready.
662 base::TimeTicks last_frame_ready_time_;
664 // All methods of this class should be run on the same thread.
665 base::ThreadChecker thread_checker_;
667 // Requested bitrate in bits per second.
668 unsigned int requested_bitrate_;
670 // Requested initial framerate.
671 unsigned int requested_framerate_;
673 // Bitrate to switch to in the middle of the stream.
674 unsigned int requested_subsequent_bitrate_;
676 // Framerate to switch to in the middle of the stream.
677 unsigned int requested_subsequent_framerate_;
679 // The timer used to feed the encoder with the input frames.
680 scoped_ptr<base::RepeatingTimer<VEAClient>> input_timer_;
682 // Feed the encoder with the input buffers at the |requested_framerate_|. If
683 // false, feed as fast as possible. This is set by the command line switch
684 // "--run_at_fps".
685 bool run_at_fps_;
688 VEAClient::VEAClient(TestStream* test_stream,
689 ClientStateNotification<ClientState>* note,
690 bool save_to_file,
691 unsigned int keyframe_period,
692 bool force_bitrate,
693 bool test_perf,
694 bool mid_stream_bitrate_switch,
695 bool mid_stream_framerate_switch,
696 bool run_at_fps)
697 : state_(CS_CREATED),
698 test_stream_(test_stream),
699 note_(note),
700 next_input_id_(0),
701 next_output_buffer_id_(0),
702 pos_in_input_stream_(0),
703 num_required_input_buffers_(0),
704 output_buffer_size_(0),
705 num_frames_to_encode_(0),
706 num_encoded_frames_(0),
707 num_frames_since_last_check_(0),
708 seen_keyframe_in_this_buffer_(false),
709 save_to_file_(save_to_file),
710 keyframe_period_(keyframe_period),
711 num_keyframes_requested_(0),
712 next_keyframe_at_(kMaxFrameNum),
713 force_bitrate_(force_bitrate),
714 current_requested_bitrate_(0),
715 current_framerate_(0),
716 encoded_stream_size_since_last_check_(0),
717 test_perf_(test_perf),
718 requested_bitrate_(0),
719 requested_framerate_(0),
720 requested_subsequent_bitrate_(0),
721 requested_subsequent_framerate_(0),
722 run_at_fps_(run_at_fps) {
723 if (keyframe_period_)
724 CHECK_LT(kMaxKeyframeDelay, keyframe_period_);
726 // Fake encoder produces an invalid stream, so skip validating it.
727 if (!g_fake_encoder) {
728 validator_ = StreamValidator::Create(
729 test_stream_->requested_profile,
730 base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
731 CHECK(validator_);
734 if (save_to_file_) {
735 CHECK(!test_stream_->out_filename.empty());
736 base::FilePath out_filename(test_stream_->out_filename);
737 // This creates or truncates out_filename.
738 // Without it, AppendToFile() will not work.
739 EXPECT_EQ(0, base::WriteFile(out_filename, NULL, 0));
742 // Initialize the parameters of the test streams.
743 UpdateTestStreamData(mid_stream_bitrate_switch, mid_stream_framerate_switch);
745 thread_checker_.DetachFromThread();
748 VEAClient::~VEAClient() { CHECK(!has_encoder()); }
750 scoped_ptr<media::VideoEncodeAccelerator> VEAClient::CreateFakeVEA() {
751 scoped_ptr<media::VideoEncodeAccelerator> encoder;
752 if (g_fake_encoder) {
753 encoder.reset(
754 new media::FakeVideoEncodeAccelerator(
755 scoped_refptr<base::SingleThreadTaskRunner>(
756 base::MessageLoopProxy::current())));
758 return encoder.Pass();
761 scoped_ptr<media::VideoEncodeAccelerator> VEAClient::CreateV4L2VEA() {
762 scoped_ptr<media::VideoEncodeAccelerator> encoder;
763 #if defined(OS_CHROMEOS) && (defined(ARCH_CPU_ARMEL) || \
764 (defined(USE_OZONE) && defined(USE_V4L2_CODEC)))
765 scoped_refptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
766 if (device)
767 encoder.reset(new V4L2VideoEncodeAccelerator(device));
768 #endif
769 return encoder.Pass();
772 scoped_ptr<media::VideoEncodeAccelerator> VEAClient::CreateVaapiVEA() {
773 scoped_ptr<media::VideoEncodeAccelerator> encoder;
774 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
775 encoder.reset(new VaapiVideoEncodeAccelerator());
776 #endif
777 return encoder.Pass();
780 void VEAClient::CreateEncoder() {
781 DCHECK(thread_checker_.CalledOnValidThread());
782 CHECK(!has_encoder());
784 scoped_ptr<media::VideoEncodeAccelerator> encoders[] = {
785 CreateFakeVEA(),
786 CreateV4L2VEA(),
787 CreateVaapiVEA()
790 DVLOG(1) << "Profile: " << test_stream_->requested_profile
791 << ", initial bitrate: " << requested_bitrate_;
793 for (size_t i = 0; i < arraysize(encoders); ++i) {
794 if (!encoders[i])
795 continue;
796 encoder_ = encoders[i].Pass();
797 SetState(CS_ENCODER_SET);
798 if (encoder_->Initialize(kInputFormat,
799 test_stream_->visible_size,
800 test_stream_->requested_profile,
801 requested_bitrate_,
802 this)) {
803 SetStreamParameters(requested_bitrate_, requested_framerate_);
804 SetState(CS_INITIALIZED);
805 return;
808 encoder_.reset();
809 LOG(ERROR) << "VideoEncodeAccelerator::Initialize() failed";
810 SetState(CS_ERROR);
813 void VEAClient::DestroyEncoder() {
814 DCHECK(thread_checker_.CalledOnValidThread());
815 if (!has_encoder())
816 return;
817 encoder_.reset();
818 input_timer_.reset();
821 void VEAClient::UpdateTestStreamData(bool mid_stream_bitrate_switch,
822 bool mid_stream_framerate_switch) {
823 // Use defaults for bitrate/framerate if they are not provided.
824 if (test_stream_->requested_bitrate == 0)
825 requested_bitrate_ = kDefaultBitrate;
826 else
827 requested_bitrate_ = test_stream_->requested_bitrate;
829 if (test_stream_->requested_framerate == 0)
830 requested_framerate_ = kDefaultFramerate;
831 else
832 requested_framerate_ = test_stream_->requested_framerate;
834 // If bitrate/framerate switch is requested, use the subsequent values if
835 // provided, or, if not, calculate them from their initial values using
836 // the default ratios.
837 // Otherwise, if a switch is not requested, keep the initial values.
838 if (mid_stream_bitrate_switch) {
839 if (test_stream_->requested_subsequent_bitrate == 0)
840 requested_subsequent_bitrate_ =
841 requested_bitrate_ * kDefaultSubsequentBitrateRatio;
842 else
843 requested_subsequent_bitrate_ =
844 test_stream_->requested_subsequent_bitrate;
845 } else {
846 requested_subsequent_bitrate_ = requested_bitrate_;
848 if (requested_subsequent_bitrate_ == 0)
849 requested_subsequent_bitrate_ = 1;
851 if (mid_stream_framerate_switch) {
852 if (test_stream_->requested_subsequent_framerate == 0)
853 requested_subsequent_framerate_ =
854 requested_framerate_ * kDefaultSubsequentFramerateRatio;
855 else
856 requested_subsequent_framerate_ =
857 test_stream_->requested_subsequent_framerate;
858 } else {
859 requested_subsequent_framerate_ = requested_framerate_;
861 if (requested_subsequent_framerate_ == 0)
862 requested_subsequent_framerate_ = 1;
865 double VEAClient::frames_per_second() {
866 base::TimeDelta duration = last_frame_ready_time_ - encode_start_time_;
867 return num_encoded_frames_ / duration.InSecondsF();
870 void VEAClient::RequireBitstreamBuffers(unsigned int input_count,
871 const gfx::Size& input_coded_size,
872 size_t output_size) {
873 DCHECK(thread_checker_.CalledOnValidThread());
874 ASSERT_EQ(state_, CS_INITIALIZED);
875 SetState(CS_ENCODING);
877 CreateAlignedInputStreamFile(input_coded_size, test_stream_);
879 num_frames_to_encode_ = test_stream_->num_frames;
880 if (g_num_frames_to_encode > 0)
881 num_frames_to_encode_ = g_num_frames_to_encode;
883 // We may need to loop over the stream more than once if more frames than
884 // provided is required for bitrate tests.
885 if (force_bitrate_ && num_frames_to_encode_ < kMinFramesForBitrateTests) {
886 DVLOG(1) << "Stream too short for bitrate test ("
887 << test_stream_->num_frames << " frames), will loop it to reach "
888 << kMinFramesForBitrateTests << " frames";
889 num_frames_to_encode_ = kMinFramesForBitrateTests;
891 if (save_to_file_ && IsVP8(test_stream_->requested_profile))
892 WriteIvfFileHeader();
894 input_coded_size_ = input_coded_size;
895 num_required_input_buffers_ = input_count;
896 ASSERT_GT(num_required_input_buffers_, 0UL);
898 output_buffer_size_ = output_size;
899 ASSERT_GT(output_buffer_size_, 0UL);
901 for (unsigned int i = 0; i < kNumOutputBuffers; ++i) {
902 base::SharedMemory* shm = new base::SharedMemory();
903 CHECK(shm->CreateAndMapAnonymous(output_buffer_size_));
904 output_shms_.push_back(shm);
905 FeedEncoderWithOutput(shm);
908 encode_start_time_ = base::TimeTicks::Now();
909 if (run_at_fps_) {
910 input_timer_.reset(new base::RepeatingTimer<VEAClient>());
911 input_timer_->Start(
912 FROM_HERE, base::TimeDelta::FromSeconds(1) / current_framerate_,
913 base::Bind(&VEAClient::OnInputTimer, base::Unretained(this)));
914 } else {
915 while (inputs_at_client_.size() <
916 num_required_input_buffers_ + kNumExtraInputFrames)
917 FeedEncoderWithOneInput();
921 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id,
922 size_t payload_size,
923 bool key_frame) {
924 DCHECK(thread_checker_.CalledOnValidThread());
925 ASSERT_LE(payload_size, output_buffer_size_);
927 IdToSHM::iterator it = output_buffers_at_client_.find(bitstream_buffer_id);
928 ASSERT_NE(it, output_buffers_at_client_.end());
929 base::SharedMemory* shm = it->second;
930 output_buffers_at_client_.erase(it);
932 if (state_ == CS_FINISHED)
933 return;
935 encoded_stream_size_since_last_check_ += payload_size;
937 const uint8* stream_ptr = static_cast<const uint8*>(shm->memory());
938 if (payload_size > 0) {
939 if (validator_) {
940 validator_->ProcessStreamBuffer(stream_ptr, payload_size);
941 } else {
942 HandleEncodedFrame(key_frame);
945 if (save_to_file_) {
946 if (IsVP8(test_stream_->requested_profile))
947 WriteIvfFrameHeader(num_encoded_frames_ - 1, payload_size);
949 EXPECT_TRUE(base::AppendToFile(
950 base::FilePath::FromUTF8Unsafe(test_stream_->out_filename),
951 static_cast<char*>(shm->memory()),
952 base::checked_cast<int>(payload_size)));
956 EXPECT_EQ(key_frame, seen_keyframe_in_this_buffer_);
957 seen_keyframe_in_this_buffer_ = false;
959 FeedEncoderWithOutput(shm);
962 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error) {
963 DCHECK(thread_checker_.CalledOnValidThread());
964 SetState(CS_ERROR);
967 void VEAClient::SetState(ClientState new_state) {
968 DVLOG(4) << "Changing state " << state_ << "->" << new_state;
969 note_->Notify(new_state);
970 state_ = new_state;
973 void VEAClient::SetStreamParameters(unsigned int bitrate,
974 unsigned int framerate) {
975 current_requested_bitrate_ = bitrate;
976 current_framerate_ = framerate;
977 CHECK_GT(current_requested_bitrate_, 0UL);
978 CHECK_GT(current_framerate_, 0UL);
979 encoder_->RequestEncodingParametersChange(current_requested_bitrate_,
980 current_framerate_);
981 DVLOG(1) << "Switched parameters to " << current_requested_bitrate_
982 << " bps @ " << current_framerate_ << " FPS";
985 void VEAClient::InputNoLongerNeededCallback(int32 input_id) {
986 std::set<int32>::iterator it = inputs_at_client_.find(input_id);
987 ASSERT_NE(it, inputs_at_client_.end());
988 inputs_at_client_.erase(it);
989 if (!run_at_fps_)
990 FeedEncoderWithOneInput();
993 scoped_refptr<media::VideoFrame> VEAClient::PrepareInputFrame(off_t position) {
994 CHECK_LE(position + test_stream_->aligned_buffer_size,
995 test_stream_->mapped_aligned_in_file.length());
997 uint8* frame_data_y = const_cast<uint8*>(
998 test_stream_->mapped_aligned_in_file.data() + position);
999 uint8* frame_data_u = frame_data_y + test_stream_->aligned_plane_size[0];
1000 uint8* frame_data_v = frame_data_u + test_stream_->aligned_plane_size[1];
1002 CHECK_GT(current_framerate_, 0U);
1003 scoped_refptr<media::VideoFrame> frame =
1004 media::VideoFrame::WrapExternalYuvData(
1005 kInputFormat,
1006 input_coded_size_,
1007 gfx::Rect(test_stream_->visible_size),
1008 test_stream_->visible_size,
1009 input_coded_size_.width(),
1010 input_coded_size_.width() / 2,
1011 input_coded_size_.width() / 2,
1012 frame_data_y,
1013 frame_data_u,
1014 frame_data_v,
1015 base::TimeDelta().FromMilliseconds(
1016 next_input_id_ * base::Time::kMillisecondsPerSecond /
1017 current_framerate_),
1018 media::BindToCurrentLoop(
1019 base::Bind(&VEAClient::InputNoLongerNeededCallback,
1020 base::Unretained(this),
1021 next_input_id_)));
1023 CHECK(inputs_at_client_.insert(next_input_id_).second);
1024 ++next_input_id_;
1026 return frame;
1029 void VEAClient::OnInputTimer() {
1030 if (!has_encoder() || state_ != CS_ENCODING)
1031 input_timer_.reset();
1032 else if (inputs_at_client_.size() <
1033 num_required_input_buffers_ + kNumExtraInputFrames)
1034 FeedEncoderWithOneInput();
1035 else
1036 DVLOG(1) << "Dropping input frame";
1039 void VEAClient::FeedEncoderWithOneInput() {
1040 if (!has_encoder() || state_ != CS_ENCODING)
1041 return;
1043 size_t bytes_left =
1044 test_stream_->mapped_aligned_in_file.length() - pos_in_input_stream_;
1045 if (bytes_left < test_stream_->aligned_buffer_size) {
1046 DCHECK_EQ(bytes_left, 0UL);
1047 // Rewind if at the end of stream and we are still encoding.
1048 // This is to flush the encoder with additional frames from the beginning
1049 // of the stream, or if the stream is shorter that the number of frames
1050 // we require for bitrate tests.
1051 pos_in_input_stream_ = 0;
1054 bool force_keyframe = false;
1055 if (keyframe_period_ && next_input_id_ % keyframe_period_ == 0) {
1056 force_keyframe = true;
1057 ++num_keyframes_requested_;
1060 scoped_refptr<media::VideoFrame> video_frame =
1061 PrepareInputFrame(pos_in_input_stream_);
1062 pos_in_input_stream_ += test_stream_->aligned_buffer_size;
1064 encoder_->Encode(video_frame, force_keyframe);
1067 void VEAClient::FeedEncoderWithOutput(base::SharedMemory* shm) {
1068 if (!has_encoder())
1069 return;
1071 if (state_ != CS_ENCODING)
1072 return;
1074 base::SharedMemoryHandle dup_handle;
1075 CHECK(shm->ShareToProcess(base::GetCurrentProcessHandle(), &dup_handle));
1077 media::BitstreamBuffer bitstream_buffer(
1078 next_output_buffer_id_++, dup_handle, output_buffer_size_);
1079 CHECK(output_buffers_at_client_.insert(std::make_pair(bitstream_buffer.id(),
1080 shm)).second);
1081 encoder_->UseOutputBitstreamBuffer(bitstream_buffer);
1084 bool VEAClient::HandleEncodedFrame(bool keyframe) {
1085 // This would be a bug in the test, which should not ignore false
1086 // return value from this method.
1087 CHECK_LE(num_encoded_frames_, num_frames_to_encode_);
1089 ++num_encoded_frames_;
1090 ++num_frames_since_last_check_;
1092 last_frame_ready_time_ = base::TimeTicks::Now();
1094 // Because the keyframe behavior requirements are loose, we give
1095 // the encoder more freedom here. It could either deliver a keyframe
1096 // immediately after we requested it, which could be for a frame number
1097 // before the one we requested it for (if the keyframe request
1098 // is asynchronous, i.e. not bound to any concrete frame, and because
1099 // the pipeline can be deeper than one frame), at that frame, or after.
1100 // So the only constraints we put here is that we get a keyframe not
1101 // earlier than we requested one (in time), and not later than
1102 // kMaxKeyframeDelay frames after the frame, for which we requested
1103 // it, comes back encoded.
1104 EXPECT_LE(num_encoded_frames_, next_keyframe_at_ + kMaxKeyframeDelay);
1106 if (keyframe) {
1107 if (num_keyframes_requested_ > 0)
1108 --num_keyframes_requested_;
1109 next_keyframe_at_ += keyframe_period_;
1110 seen_keyframe_in_this_buffer_ = true;
1113 if (num_encoded_frames_ == num_frames_to_encode_ / 2) {
1114 VerifyStreamProperties();
1115 if (requested_subsequent_bitrate_ != current_requested_bitrate_ ||
1116 requested_subsequent_framerate_ != current_framerate_) {
1117 SetStreamParameters(requested_subsequent_bitrate_,
1118 requested_subsequent_framerate_);
1119 if (run_at_fps_ && input_timer_)
1120 input_timer_->Start(
1121 FROM_HERE, base::TimeDelta::FromSeconds(1) / current_framerate_,
1122 base::Bind(&VEAClient::OnInputTimer, base::Unretained(this)));
1124 } else if (num_encoded_frames_ == num_frames_to_encode_) {
1125 VerifyPerf();
1126 VerifyStreamProperties();
1127 SetState(CS_FINISHED);
1128 return false;
1131 return true;
1134 void VEAClient::VerifyPerf() {
1135 double measured_fps = frames_per_second();
1136 LOG(INFO) << "Measured encoder FPS: " << measured_fps;
1137 if (test_perf_)
1138 EXPECT_GE(measured_fps, kMinPerfFPS);
1141 void VEAClient::VerifyStreamProperties() {
1142 CHECK_GT(num_frames_since_last_check_, 0UL);
1143 CHECK_GT(encoded_stream_size_since_last_check_, 0UL);
1144 unsigned int bitrate = encoded_stream_size_since_last_check_ * 8 *
1145 current_framerate_ / num_frames_since_last_check_;
1146 DVLOG(1) << "Current chunk's bitrate: " << bitrate
1147 << " (expected: " << current_requested_bitrate_
1148 << " @ " << current_framerate_ << " FPS,"
1149 << " num frames in chunk: " << num_frames_since_last_check_;
1151 num_frames_since_last_check_ = 0;
1152 encoded_stream_size_since_last_check_ = 0;
1154 if (force_bitrate_) {
1155 EXPECT_NEAR(bitrate,
1156 current_requested_bitrate_,
1157 kBitrateTolerance * current_requested_bitrate_);
1160 // All requested keyframes should've been provided. Allow the last requested
1161 // frame to remain undelivered if we haven't reached the maximum frame number
1162 // by which it should have arrived.
1163 if (num_encoded_frames_ < next_keyframe_at_ + kMaxKeyframeDelay)
1164 EXPECT_LE(num_keyframes_requested_, 1UL);
1165 else
1166 EXPECT_EQ(num_keyframes_requested_, 0UL);
1169 void VEAClient::WriteIvfFileHeader() {
1170 IvfFileHeader header;
1172 memset(&header, 0, sizeof(header));
1173 header.signature[0] = 'D';
1174 header.signature[1] = 'K';
1175 header.signature[2] = 'I';
1176 header.signature[3] = 'F';
1177 header.version = 0;
1178 header.header_size = base::ByteSwapToLE16(sizeof(header));
1179 header.fourcc = base::ByteSwapToLE32(0x30385056); // VP80
1180 header.width = base::ByteSwapToLE16(
1181 base::checked_cast<uint16_t>(test_stream_->visible_size.width()));
1182 header.height = base::ByteSwapToLE16(
1183 base::checked_cast<uint16_t>(test_stream_->visible_size.height()));
1184 header.framerate = base::ByteSwapToLE32(requested_framerate_);
1185 header.timescale = base::ByteSwapToLE32(1);
1186 header.num_frames = base::ByteSwapToLE32(num_frames_to_encode_);
1188 EXPECT_TRUE(base::AppendToFile(
1189 base::FilePath::FromUTF8Unsafe(test_stream_->out_filename),
1190 reinterpret_cast<char*>(&header), sizeof(header)));
1193 void VEAClient::WriteIvfFrameHeader(int frame_index, size_t frame_size) {
1194 IvfFrameHeader header;
1196 memset(&header, 0, sizeof(header));
1197 header.frame_size = base::ByteSwapToLE32(frame_size);
1198 header.timestamp = base::ByteSwapToLE64(frame_index);
1199 EXPECT_TRUE(base::AppendToFile(
1200 base::FilePath::FromUTF8Unsafe(test_stream_->out_filename),
1201 reinterpret_cast<char*>(&header), sizeof(header)));
1204 // Setup test stream data and delete temporary aligned files at the beginning
1205 // and end of unittest. We only need to setup once for all test cases.
1206 class VideoEncodeAcceleratorTestEnvironment : public ::testing::Environment {
1207 public:
1208 VideoEncodeAcceleratorTestEnvironment(
1209 scoped_ptr<base::FilePath::StringType> data,
1210 bool run_at_fps) {
1211 test_stream_data_ = data.Pass();
1212 run_at_fps_ = run_at_fps;
1215 virtual void SetUp() {
1216 ParseAndReadTestStreamData(*test_stream_data_, &test_streams_);
1219 virtual void TearDown() {
1220 for (size_t i = 0; i < test_streams_.size(); i++) {
1221 base::DeleteFile(test_streams_[i]->aligned_in_file, false);
1225 ScopedVector<TestStream> test_streams_;
1226 bool run_at_fps_;
1228 private:
1229 scoped_ptr<base::FilePath::StringType> test_stream_data_;
1232 // Test parameters:
1233 // - Number of concurrent encoders. The value takes effect when there is only
1234 // one input stream; otherwise, one encoder per input stream will be
1235 // instantiated.
1236 // - If true, save output to file (provided an output filename was supplied).
1237 // - Force a keyframe every n frames.
1238 // - Force bitrate; the actual required value is provided as a property
1239 // of the input stream, because it depends on stream type/resolution/etc.
1240 // - If true, measure performance.
1241 // - If true, switch bitrate mid-stream.
1242 // - If true, switch framerate mid-stream.
1243 class VideoEncodeAcceleratorTest
1244 : public ::testing::TestWithParam<
1245 Tuple<int, bool, int, bool, bool, bool, bool>> {};
1247 TEST_P(VideoEncodeAcceleratorTest, TestSimpleEncode) {
1248 size_t num_concurrent_encoders = get<0>(GetParam());
1249 const bool save_to_file = get<1>(GetParam());
1250 const unsigned int keyframe_period = get<2>(GetParam());
1251 const bool force_bitrate = get<3>(GetParam());
1252 const bool test_perf = get<4>(GetParam());
1253 const bool mid_stream_bitrate_switch = get<5>(GetParam());
1254 const bool mid_stream_framerate_switch = get<6>(GetParam());
1256 ScopedVector<ClientStateNotification<ClientState> > notes;
1257 ScopedVector<VEAClient> clients;
1258 base::Thread encoder_thread("EncoderThread");
1259 ASSERT_TRUE(encoder_thread.Start());
1261 if (g_env->test_streams_.size() > 1)
1262 num_concurrent_encoders = g_env->test_streams_.size();
1264 // Create all encoders.
1265 for (size_t i = 0; i < num_concurrent_encoders; i++) {
1266 size_t test_stream_index = i % g_env->test_streams_.size();
1267 // Disregard save_to_file if we didn't get an output filename.
1268 bool encoder_save_to_file =
1269 (save_to_file &&
1270 !g_env->test_streams_[test_stream_index]->out_filename.empty());
1272 notes.push_back(new ClientStateNotification<ClientState>());
1273 clients.push_back(
1274 new VEAClient(g_env->test_streams_[test_stream_index], notes.back(),
1275 encoder_save_to_file, keyframe_period, force_bitrate,
1276 test_perf, mid_stream_bitrate_switch,
1277 mid_stream_framerate_switch, g_env->run_at_fps_));
1279 encoder_thread.message_loop()->PostTask(
1280 FROM_HERE,
1281 base::Bind(&VEAClient::CreateEncoder,
1282 base::Unretained(clients.back())));
1285 // All encoders must pass through states in this order.
1286 enum ClientState state_transitions[] = {CS_ENCODER_SET, CS_INITIALIZED,
1287 CS_ENCODING, CS_FINISHED};
1289 // Wait for all encoders to go through all states and finish.
1290 // Do this by waiting for all encoders to advance to state n before checking
1291 // state n+1, to verify that they are able to operate concurrently.
1292 // It also simulates the real-world usage better, as the main thread, on which
1293 // encoders are created/destroyed, is a single GPU Process ChildThread.
1294 // Moreover, we can't have proper multithreading on X11, so this could cause
1295 // hard to debug issues there, if there were multiple "ChildThreads".
1296 for (size_t state_no = 0; state_no < arraysize(state_transitions); ++state_no)
1297 for (size_t i = 0; i < num_concurrent_encoders; i++)
1298 ASSERT_EQ(notes[i]->Wait(), state_transitions[state_no]);
1300 for (size_t i = 0; i < num_concurrent_encoders; ++i) {
1301 encoder_thread.message_loop()->PostTask(
1302 FROM_HERE,
1303 base::Bind(&VEAClient::DestroyEncoder, base::Unretained(clients[i])));
1306 // This ensures all tasks have finished.
1307 encoder_thread.Stop();
1310 INSTANTIATE_TEST_CASE_P(
1311 SimpleEncode,
1312 VideoEncodeAcceleratorTest,
1313 ::testing::Values(MakeTuple(1, true, 0, false, false, false, false)));
1315 INSTANTIATE_TEST_CASE_P(
1316 EncoderPerf,
1317 VideoEncodeAcceleratorTest,
1318 ::testing::Values(MakeTuple(1, false, 0, false, true, false, false)));
1320 INSTANTIATE_TEST_CASE_P(
1321 ForceKeyframes,
1322 VideoEncodeAcceleratorTest,
1323 ::testing::Values(MakeTuple(1, false, 10, false, false, false, false)));
1325 INSTANTIATE_TEST_CASE_P(
1326 ForceBitrate,
1327 VideoEncodeAcceleratorTest,
1328 ::testing::Values(MakeTuple(1, false, 0, true, false, false, false)));
1330 INSTANTIATE_TEST_CASE_P(
1331 MidStreamParamSwitchBitrate,
1332 VideoEncodeAcceleratorTest,
1333 ::testing::Values(MakeTuple(1, false, 0, true, false, true, false)));
1335 INSTANTIATE_TEST_CASE_P(
1336 MidStreamParamSwitchFPS,
1337 VideoEncodeAcceleratorTest,
1338 ::testing::Values(MakeTuple(1, false, 0, true, false, false, true)));
1340 INSTANTIATE_TEST_CASE_P(
1341 MultipleEncoders,
1342 VideoEncodeAcceleratorTest,
1343 ::testing::Values(MakeTuple(3, false, 0, false, false, false, false),
1344 MakeTuple(3, false, 0, true, false, false, true),
1345 MakeTuple(3, false, 0, true, false, true, false)));
1347 // TODO(posciak): more tests:
1348 // - async FeedEncoderWithOutput
1349 // - out-of-order return of outputs to encoder
1350 // - multiple encoders + decoders
1351 // - mid-stream encoder_->Destroy()
1353 } // namespace
1354 } // namespace content
1356 int main(int argc, char** argv) {
1357 testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args.
1358 base::CommandLine::Init(argc, argv);
1360 base::ShadowingAtExitManager at_exit_manager;
1361 base::MessageLoop main_loop;
1363 #if defined(USE_OZONE)
1364 ui::OzonePlatform::InitializeForUI();
1365 ui::OzonePlatform::InitializeForGPU();
1366 #endif
1368 scoped_ptr<base::FilePath::StringType> test_stream_data(
1369 new base::FilePath::StringType(
1370 media::GetTestDataFilePath(content::g_default_in_filename).value() +
1371 content::g_default_in_parameters));
1373 // Needed to enable DVLOG through --vmodule.
1374 logging::LoggingSettings settings;
1375 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
1376 CHECK(logging::InitLogging(settings));
1378 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
1379 DCHECK(cmd_line);
1381 bool run_at_fps = false;
1382 base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
1383 for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
1384 it != switches.end();
1385 ++it) {
1386 if (it->first == "test_stream_data") {
1387 test_stream_data->assign(it->second.c_str());
1388 continue;
1390 if (it->first == "num_frames_to_encode") {
1391 std::string input(it->second.begin(), it->second.end());
1392 CHECK(base::StringToInt(input, &content::g_num_frames_to_encode));
1393 continue;
1395 if (it->first == "fake_encoder") {
1396 content::g_fake_encoder = true;
1397 continue;
1399 if (it->first == "run_at_fps") {
1400 run_at_fps = true;
1401 continue;
1403 if (it->first == "v" || it->first == "vmodule")
1404 continue;
1405 if (it->first == "ozone-platform" || it->first == "ozone-use-surfaceless")
1406 continue;
1407 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
1410 content::g_env =
1411 reinterpret_cast<content::VideoEncodeAcceleratorTestEnvironment*>(
1412 testing::AddGlobalTestEnvironment(
1413 new content::VideoEncodeAcceleratorTestEnvironment(
1414 test_stream_data.Pass(), run_at_fps)));
1416 return RUN_ALL_TESTS();