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.
9 #include "base/at_exit.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/files/memory_mapped_file.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/process/process_handle.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_split.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/sys_byteorder.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_checker.h"
23 #include "base/time/time.h"
24 #include "base/timer/timer.h"
25 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
26 #include "media/base/bind_to_current_loop.h"
27 #include "media/base/bitstream_buffer.h"
28 #include "media/base/test_data_util.h"
29 #include "media/filters/h264_parser.h"
30 #include "media/video/fake_video_encode_accelerator.h"
31 #include "media/video/video_encode_accelerator.h"
32 #include "testing/gtest/include/gtest/gtest.h"
34 #if defined(OS_CHROMEOS)
35 #if defined(ARCH_CPU_ARMEL) || (defined(USE_OZONE) && defined(USE_V4L2_CODEC))
36 #include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
38 #if defined(ARCH_CPU_X86_FAMILY)
39 #include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
40 #include "content/common/gpu/media/vaapi_wrapper.h"
41 #endif // defined(ARCH_CPU_X86_FAMILY)
43 #error The VideoEncodeAcceleratorUnittest is not supported on this platform.
46 using media::VideoEncodeAccelerator
;
51 const media::VideoPixelFormat kInputFormat
= media::PIXEL_FORMAT_I420
;
53 // Arbitrarily chosen to add some depth to the pipeline.
54 const unsigned int kNumOutputBuffers
= 4;
55 const unsigned int kNumExtraInputFrames
= 4;
56 // Maximum delay between requesting a keyframe and receiving one, in frames.
57 // Arbitrarily chosen as a reasonable requirement.
58 const unsigned int kMaxKeyframeDelay
= 4;
59 // Default initial bitrate.
60 const uint32 kDefaultBitrate
= 2000000;
61 // Default ratio of requested_subsequent_bitrate to initial_bitrate
62 // (see test parameters below) if one is not provided.
63 const double kDefaultSubsequentBitrateRatio
= 2.0;
64 // Default initial framerate.
65 const uint32 kDefaultFramerate
= 30;
66 // Default ratio of requested_subsequent_framerate to initial_framerate
67 // (see test parameters below) if one is not provided.
68 const double kDefaultSubsequentFramerateRatio
= 0.1;
69 // Tolerance factor for how encoded bitrate can differ from requested bitrate.
70 const double kBitrateTolerance
= 0.1;
71 // Minimum required FPS throughput for the basic performance test.
72 const uint32 kMinPerfFPS
= 30;
73 // Minimum (arbitrary) number of frames required to enforce bitrate requirements
74 // over. Streams shorter than this may be too short to realistically require
75 // an encoder to be able to converge to the requested bitrate over.
76 // The input stream will be looped as many times as needed in bitrate tests
77 // to reach at least this number of frames before calculating final bitrate.
78 const unsigned int kMinFramesForBitrateTests
= 300;
79 // The percentiles to measure for encode latency.
80 const unsigned int kLoggedLatencyPercentiles
[] = {50, 75, 95};
82 // The syntax of multiple test streams is:
83 // test-stream1;test-stream2;test-stream3
84 // The syntax of each test stream is:
85 // "in_filename:width:height:out_filename:requested_bitrate:requested_framerate
86 // :requested_subsequent_bitrate:requested_subsequent_framerate"
87 // - |in_filename| must be an I420 (YUV planar) raw stream
88 // (see http://www.fourcc.org/yuv.php#IYUV).
89 // - |width| and |height| are in pixels.
90 // - |profile| to encode into (values of media::VideoCodecProfile).
91 // - |out_filename| filename to save the encoded stream to (optional). The
92 // format for H264 is Annex-B byte stream. The format for VP8 is IVF. Output
93 // stream is saved for the simple encode test only. H264 raw stream and IVF
94 // can be used as input of VDA unittest. H264 raw stream can be played by
95 // "mplayer -fps 25 out.h264" and IVF can be played by mplayer directly.
96 // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
97 // Further parameters are optional (need to provide preceding positional
98 // parameters if a specific subsequent parameter is required):
99 // - |requested_bitrate| requested bitrate in bits per second.
100 // - |requested_framerate| requested initial framerate.
101 // - |requested_subsequent_bitrate| bitrate to switch to in the middle of the
103 // - |requested_subsequent_framerate| framerate to switch to in the middle
105 // Bitrate is only forced for tests that test bitrate.
106 const char* g_default_in_filename
= "bear_320x192_40frames.yuv";
107 const char* g_default_in_parameters
= ":320:192:1:out.h264:200000";
109 // Enabled by including a --fake_encoder flag to the command line invoking the
111 bool g_fake_encoder
= false;
113 // Environment to store test stream data for all test cases.
114 class VideoEncodeAcceleratorTestEnvironment
;
115 VideoEncodeAcceleratorTestEnvironment
* g_env
;
117 struct IvfFileHeader
{
118 char signature
[4]; // signature: 'DKIF'
119 uint16_t version
; // version (should be 0)
120 uint16_t header_size
; // size of header in bytes
121 uint32_t fourcc
; // codec FourCC (e.g., 'VP80')
122 uint16_t width
; // width in pixels
123 uint16_t height
; // height in pixels
124 uint32_t framerate
; // frame rate per seconds
125 uint32_t timescale
; // time scale. For example, if framerate is 30 and
126 // timescale is 2, the unit of IvfFrameHeader.timestamp
128 uint32_t num_frames
; // number of frames in file
129 uint32_t unused
; // unused
130 } __attribute__((packed
));
132 struct IvfFrameHeader
{
133 uint32_t frame_size
; // Size of frame in bytes (not including the header)
134 uint64_t timestamp
; // 64-bit presentation timestamp
135 } __attribute__((packed
));
137 // The number of frames to be encoded. This variable is set by the switch
138 // "--num_frames_to_encode". Ignored if 0.
139 int g_num_frames_to_encode
= 0;
144 aligned_buffer_size(0),
145 requested_bitrate(0),
146 requested_framerate(0),
147 requested_subsequent_bitrate(0),
148 requested_subsequent_framerate(0) {}
151 gfx::Size visible_size
;
152 gfx::Size coded_size
;
153 unsigned int num_frames
;
155 // Original unaligned input file name provided as an argument to the test.
156 // And the file must be an I420 (YUV planar) raw stream.
157 std::string in_filename
;
159 // A temporary file used to prepare aligned input buffers of |in_filename|.
160 // The file makes sure starting address of YUV planes are 64 byte-aligned.
161 base::FilePath aligned_in_file
;
163 // The memory mapping of |aligned_in_file|
164 base::MemoryMappedFile mapped_aligned_in_file
;
166 // Byte size of a frame of |aligned_in_file|.
167 size_t aligned_buffer_size
;
169 // Byte size for each aligned plane of a frame
170 std::vector
<size_t> aligned_plane_size
;
172 std::string out_filename
;
173 media::VideoCodecProfile requested_profile
;
174 unsigned int requested_bitrate
;
175 unsigned int requested_framerate
;
176 unsigned int requested_subsequent_bitrate
;
177 unsigned int requested_subsequent_framerate
;
180 inline static size_t Align64Bytes(size_t value
) {
181 return (value
+ 63) & ~63;
184 // Write |data| of |size| bytes at |offset| bytes into |file|.
185 static bool WriteFile(base::File
* file
,
189 size_t written_bytes
= 0;
190 while (written_bytes
< size
) {
191 int bytes
= file
->Write(offset
+ written_bytes
,
192 reinterpret_cast<const char*>(data
+ written_bytes
),
193 size
- written_bytes
);
196 written_bytes
+= bytes
;
201 // Return the |percentile| from a sorted vector.
202 static base::TimeDelta
Percentile(
203 const std::vector
<base::TimeDelta
>& sorted_values
,
204 unsigned int percentile
) {
205 size_t size
= sorted_values
.size();
207 CHECK_LE(percentile
, 100UL);
208 // Use Nearest Rank method in http://en.wikipedia.org/wiki/Percentile.
210 std::max(static_cast<int>(ceil(0.01f
* percentile
* size
)) - 1, 0);
211 return sorted_values
[index
];
214 static bool IsH264(media::VideoCodecProfile profile
) {
215 return profile
>= media::H264PROFILE_MIN
&& profile
<= media::H264PROFILE_MAX
;
218 static bool IsVP8(media::VideoCodecProfile profile
) {
219 return profile
>= media::VP8PROFILE_MIN
&& profile
<= media::VP8PROFILE_MAX
;
222 // ARM performs CPU cache management with CPU cache line granularity. We thus
223 // need to ensure our buffers are CPU cache line-aligned (64 byte-aligned).
224 // Otherwise newer kernels will refuse to accept them, and on older kernels
225 // we'll be treating ourselves to random corruption.
226 // Since we are just mapping and passing chunks of the input file directly to
227 // the VEA as input frames to avoid copying large chunks of raw data on each
228 // frame and thus affecting performance measurements, we have to prepare a
229 // temporary file with all planes aligned to 64-byte boundaries beforehand.
230 static void CreateAlignedInputStreamFile(const gfx::Size
& coded_size
,
231 TestStream
* test_stream
) {
232 // Test case may have many encoders and memory should be prepared once.
233 if (test_stream
->coded_size
== coded_size
&&
234 test_stream
->mapped_aligned_in_file
.IsValid())
237 // All encoders in multiple encoder test reuse the same test_stream, make
238 // sure they requested the same coded_size
239 ASSERT_TRUE(!test_stream
->mapped_aligned_in_file
.IsValid() ||
240 coded_size
== test_stream
->coded_size
);
241 test_stream
->coded_size
= coded_size
;
243 size_t num_planes
= media::VideoFrame::NumPlanes(kInputFormat
);
244 std::vector
<size_t> padding_sizes(num_planes
);
245 std::vector
<size_t> coded_bpl(num_planes
);
246 std::vector
<size_t> visible_bpl(num_planes
);
247 std::vector
<size_t> visible_plane_rows(num_planes
);
249 // Calculate padding in bytes to be added after each plane required to keep
250 // starting addresses of all planes at a 64 byte boudnary. This padding will
251 // be added after each plane when copying to the temporary file.
252 // At the same time we also need to take into account coded_size requested by
253 // the VEA; each row of visible_bpl bytes in the original file needs to be
254 // copied into a row of coded_bpl bytes in the aligned file.
255 for (size_t i
= 0; i
< num_planes
; i
++) {
257 media::VideoFrame::PlaneSize(kInputFormat
, i
, coded_size
).GetArea();
258 test_stream
->aligned_plane_size
.push_back(Align64Bytes(size
));
259 test_stream
->aligned_buffer_size
+= test_stream
->aligned_plane_size
.back();
262 media::VideoFrame::RowBytes(i
, kInputFormat
, coded_size
.width());
263 visible_bpl
[i
] = media::VideoFrame::RowBytes(
264 i
, kInputFormat
, test_stream
->visible_size
.width());
265 visible_plane_rows
[i
] = media::VideoFrame::Rows(
266 i
, kInputFormat
, test_stream
->visible_size
.height());
267 const size_t padding_rows
=
268 media::VideoFrame::Rows(i
, kInputFormat
, coded_size
.height()) -
269 visible_plane_rows
[i
];
270 padding_sizes
[i
] = padding_rows
* coded_bpl
[i
] + Align64Bytes(size
) - size
;
273 base::MemoryMappedFile src_file
;
274 CHECK(src_file
.Initialize(base::FilePath(test_stream
->in_filename
)));
275 CHECK(base::CreateTemporaryFile(&test_stream
->aligned_in_file
));
277 size_t visible_buffer_size
= media::VideoFrame::AllocationSize(
278 kInputFormat
, test_stream
->visible_size
);
279 CHECK_EQ(src_file
.length() % visible_buffer_size
, 0U)
280 << "Stream byte size is not a product of calculated frame byte size";
282 test_stream
->num_frames
= src_file
.length() / visible_buffer_size
;
283 uint32 flags
= base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
|
284 base::File::FLAG_READ
;
286 // Create a temporary file with coded_size length.
287 base::File
dest_file(test_stream
->aligned_in_file
, flags
);
288 CHECK_GT(test_stream
->aligned_buffer_size
, 0UL);
289 dest_file
.SetLength(test_stream
->aligned_buffer_size
*
290 test_stream
->num_frames
);
292 const uint8
* src
= src_file
.data();
293 off_t dest_offset
= 0;
294 for (size_t frame
= 0; frame
< test_stream
->num_frames
; frame
++) {
295 for (size_t i
= 0; i
< num_planes
; i
++) {
296 // Assert that each plane of frame starts at 64 byte boundary.
297 ASSERT_EQ(dest_offset
& 63, 0)
298 << "Planes of frame should be mapped at a 64 byte boundary";
299 for (size_t j
= 0; j
< visible_plane_rows
[i
]; j
++) {
300 CHECK(WriteFile(&dest_file
, dest_offset
, src
, visible_bpl
[i
]));
301 src
+= visible_bpl
[i
];
302 dest_offset
+= coded_bpl
[i
];
304 dest_offset
+= padding_sizes
[i
];
307 CHECK(test_stream
->mapped_aligned_in_file
.Initialize(dest_file
.Pass()));
308 // Assert that memory mapped of file starts at 64 byte boundary. So each
309 // plane of frames also start at 64 byte boundary.
311 reinterpret_cast<off_t
>(test_stream
->mapped_aligned_in_file
.data()) & 63,
313 << "File should be mapped at a 64 byte boundary";
315 CHECK_EQ(test_stream
->mapped_aligned_in_file
.length() %
316 test_stream
->aligned_buffer_size
,
318 << "Stream byte size is not a product of calculated frame byte size";
319 CHECK_GT(test_stream
->num_frames
, 0UL);
322 // Parse |data| into its constituent parts, set the various output fields
323 // accordingly, read in video stream, and store them to |test_streams|.
324 static void ParseAndReadTestStreamData(const base::FilePath::StringType
& data
,
325 ScopedVector
<TestStream
>* test_streams
) {
326 // Split the string to individual test stream data.
327 std::vector
<base::FilePath::StringType
> test_streams_data
;
328 base::SplitString(data
, ';', &test_streams_data
);
329 CHECK_GE(test_streams_data
.size(), 1U) << data
;
331 // Parse each test stream data and read the input file.
332 for (size_t index
= 0; index
< test_streams_data
.size(); ++index
) {
333 std::vector
<base::FilePath::StringType
> fields
;
334 base::SplitString(test_streams_data
[index
], ':', &fields
);
335 CHECK_GE(fields
.size(), 4U) << data
;
336 CHECK_LE(fields
.size(), 9U) << data
;
337 TestStream
* test_stream
= new TestStream();
339 test_stream
->in_filename
= fields
[0];
341 CHECK(base::StringToInt(fields
[1], &width
));
342 CHECK(base::StringToInt(fields
[2], &height
));
343 test_stream
->visible_size
= gfx::Size(width
, height
);
344 CHECK(!test_stream
->visible_size
.IsEmpty());
346 CHECK(base::StringToInt(fields
[3], &profile
));
347 CHECK_GT(profile
, media::VIDEO_CODEC_PROFILE_UNKNOWN
);
348 CHECK_LE(profile
, media::VIDEO_CODEC_PROFILE_MAX
);
349 test_stream
->requested_profile
=
350 static_cast<media::VideoCodecProfile
>(profile
);
352 if (fields
.size() >= 5 && !fields
[4].empty())
353 test_stream
->out_filename
= fields
[4];
355 if (fields
.size() >= 6 && !fields
[5].empty())
356 CHECK(base::StringToUint(fields
[5], &test_stream
->requested_bitrate
));
358 if (fields
.size() >= 7 && !fields
[6].empty())
359 CHECK(base::StringToUint(fields
[6], &test_stream
->requested_framerate
));
361 if (fields
.size() >= 8 && !fields
[7].empty()) {
362 CHECK(base::StringToUint(fields
[7],
363 &test_stream
->requested_subsequent_bitrate
));
366 if (fields
.size() >= 9 && !fields
[8].empty()) {
367 CHECK(base::StringToUint(fields
[8],
368 &test_stream
->requested_subsequent_framerate
));
370 test_streams
->push_back(test_stream
);
374 // Basic test environment shared across multiple test cases. We only need to
375 // setup it once for all test cases.
377 // - maintain test stream data and other test settings.
378 // - clean up temporary aligned files.
379 // - output log to file.
380 class VideoEncodeAcceleratorTestEnvironment
: public ::testing::Environment
{
382 VideoEncodeAcceleratorTestEnvironment(
383 scoped_ptr
<base::FilePath::StringType
> data
,
384 const base::FilePath
& log_path
,
386 bool needs_encode_latency
)
387 : test_stream_data_(data
.Pass()),
389 run_at_fps_(run_at_fps
),
390 needs_encode_latency_(needs_encode_latency
) {}
392 virtual void SetUp() {
393 if (!log_path_
.empty()) {
394 log_file_
.reset(new base::File(
395 log_path_
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
396 CHECK(log_file_
->IsValid());
398 ParseAndReadTestStreamData(*test_stream_data_
, &test_streams_
);
401 virtual void TearDown() {
402 for (size_t i
= 0; i
< test_streams_
.size(); i
++) {
403 base::DeleteFile(test_streams_
[i
]->aligned_in_file
, false);
408 // Log one entry of machine-readable data to file and LOG(INFO).
409 // The log has one data entry per line in the format of "<key>: <value>".
410 // Note that Chrome OS video_VEAPerf autotest parses the output key and value
411 // pairs. Be sure to keep the autotest in sync.
412 void LogToFile(const std::string
& key
, const std::string
& value
) {
413 std::string s
= base::StringPrintf("%s: %s\n", key
.c_str(), value
.c_str());
416 log_file_
->WriteAtCurrentPos(s
.data(), s
.length());
420 // Feed the encoder with the input buffers at the requested framerate. If
421 // false, feed as fast as possible. This is set by the command line switch
423 bool run_at_fps() const { return run_at_fps_
; }
425 // Whether to measure encode latency. This is set by the command line switch
426 // "--measure_latency".
427 bool needs_encode_latency() const { return needs_encode_latency_
; }
429 ScopedVector
<TestStream
> test_streams_
;
432 scoped_ptr
<base::FilePath::StringType
> test_stream_data_
;
433 base::FilePath log_path_
;
434 scoped_ptr
<base::File
> log_file_
;
436 bool needs_encode_latency_
;
448 // Performs basic, codec-specific sanity checks on the stream buffers passed
449 // to ProcessStreamBuffer(): whether we've seen keyframes before non-keyframes,
450 // correct sequences of H.264 NALUs (SPS before PPS and before slices), etc.
451 // Calls given FrameFoundCallback when a complete frame is found while
453 class StreamValidator
{
455 // To be called when a complete frame is found while processing a stream
456 // buffer, passing true if the frame is a keyframe. Returns false if we
457 // are not interested in more frames and further processing should be aborted.
458 typedef base::Callback
<bool(bool)> FrameFoundCallback
;
460 virtual ~StreamValidator() {}
462 // Provide a StreamValidator instance for the given |profile|.
463 static scoped_ptr
<StreamValidator
> Create(media::VideoCodecProfile profile
,
464 const FrameFoundCallback
& frame_cb
);
466 // Process and verify contents of a bitstream buffer.
467 virtual void ProcessStreamBuffer(const uint8
* stream
, size_t size
) = 0;
470 explicit StreamValidator(const FrameFoundCallback
& frame_cb
)
471 : frame_cb_(frame_cb
) {}
473 FrameFoundCallback frame_cb_
;
476 class H264Validator
: public StreamValidator
{
478 explicit H264Validator(const FrameFoundCallback
& frame_cb
)
479 : StreamValidator(frame_cb
),
484 void ProcessStreamBuffer(const uint8
* stream
, size_t size
) override
;
487 // Set to true when encoder provides us with the corresponding NALU type.
492 media::H264Parser h264_parser_
;
495 void H264Validator::ProcessStreamBuffer(const uint8
* stream
, size_t size
) {
496 h264_parser_
.SetStream(stream
, size
);
499 media::H264NALU nalu
;
500 media::H264Parser::Result result
;
502 result
= h264_parser_
.AdvanceToNextNALU(&nalu
);
503 if (result
== media::H264Parser::kEOStream
)
506 ASSERT_EQ(media::H264Parser::kOk
, result
);
508 bool keyframe
= false;
510 switch (nalu
.nal_unit_type
) {
511 case media::H264NALU::kIDRSlice
:
512 ASSERT_TRUE(seen_sps_
);
513 ASSERT_TRUE(seen_pps_
);
517 case media::H264NALU::kNonIDRSlice
: {
518 ASSERT_TRUE(seen_idr_
);
519 if (!frame_cb_
.Run(keyframe
))
524 case media::H264NALU::kSPS
: {
526 ASSERT_EQ(media::H264Parser::kOk
, h264_parser_
.ParseSPS(&sps_id
));
531 case media::H264NALU::kPPS
: {
532 ASSERT_TRUE(seen_sps_
);
534 ASSERT_EQ(media::H264Parser::kOk
, h264_parser_
.ParsePPS(&pps_id
));
545 class VP8Validator
: public StreamValidator
{
547 explicit VP8Validator(const FrameFoundCallback
& frame_cb
)
548 : StreamValidator(frame_cb
),
549 seen_keyframe_(false) {}
551 void ProcessStreamBuffer(const uint8
* stream
, size_t size
) override
;
554 // Have we already got a keyframe in the stream?
558 void VP8Validator::ProcessStreamBuffer(const uint8
* stream
, size_t size
) {
559 bool keyframe
= !(stream
[0] & 0x01);
561 seen_keyframe_
= true;
563 EXPECT_TRUE(seen_keyframe_
);
565 frame_cb_
.Run(keyframe
);
566 // TODO(posciak): We could be getting more frames in the buffer, but there is
567 // no simple way to detect this. We'd need to parse the frames and go through
568 // partition numbers/sizes. For now assume one frame per buffer.
572 scoped_ptr
<StreamValidator
> StreamValidator::Create(
573 media::VideoCodecProfile profile
,
574 const FrameFoundCallback
& frame_cb
) {
575 scoped_ptr
<StreamValidator
> validator
;
577 if (IsH264(profile
)) {
578 validator
.reset(new H264Validator(frame_cb
));
579 } else if (IsVP8(profile
)) {
580 validator
.reset(new VP8Validator(frame_cb
));
582 LOG(FATAL
) << "Unsupported profile: " << profile
;
585 return validator
.Pass();
588 class VEAClient
: public VideoEncodeAccelerator::Client
{
590 VEAClient(TestStream
* test_stream
,
591 ClientStateNotification
<ClientState
>* note
,
593 unsigned int keyframe_period
,
596 bool mid_stream_bitrate_switch
,
597 bool mid_stream_framerate_switch
);
598 ~VEAClient() override
;
599 void CreateEncoder();
600 void DestroyEncoder();
602 // VideoDecodeAccelerator::Client implementation.
603 void RequireBitstreamBuffers(unsigned int input_count
,
604 const gfx::Size
& input_coded_size
,
605 size_t output_buffer_size
) override
;
606 void BitstreamBufferReady(int32 bitstream_buffer_id
,
608 bool key_frame
) override
;
609 void NotifyError(VideoEncodeAccelerator::Error error
) override
;
612 bool has_encoder() { return encoder_
.get(); }
614 // Return the number of encoded frames per second.
615 double frames_per_second();
617 scoped_ptr
<media::VideoEncodeAccelerator
> CreateFakeVEA();
618 scoped_ptr
<media::VideoEncodeAccelerator
> CreateV4L2VEA();
619 scoped_ptr
<media::VideoEncodeAccelerator
> CreateVaapiVEA();
621 void SetState(ClientState new_state
);
623 // Set current stream parameters to given |bitrate| at |framerate|.
624 void SetStreamParameters(unsigned int bitrate
, unsigned int framerate
);
626 // Called when encoder is done with a VideoFrame.
627 void InputNoLongerNeededCallback(int32 input_id
);
629 // Feed the encoder with one input frame.
630 void FeedEncoderWithOneInput();
632 // Provide the encoder with a new output buffer.
633 void FeedEncoderWithOutput(base::SharedMemory
* shm
);
635 // Called on finding a complete frame (with |keyframe| set to true for
636 // keyframes) in the stream, to perform codec-independent, per-frame checks
637 // and accounting. Returns false once we have collected all frames we needed.
638 bool HandleEncodedFrame(bool keyframe
);
640 // Verify the minimum FPS requirement.
643 // Verify that stream bitrate has been close to current_requested_bitrate_,
644 // assuming current_framerate_ since the last time VerifyStreamProperties()
645 // was called. Fail the test if |force_bitrate_| is true and the bitrate
646 // is not within kBitrateTolerance.
647 void VerifyStreamProperties();
649 // Log the performance data.
652 // Write IVF file header to test_stream_->out_filename.
653 void WriteIvfFileHeader();
655 // Write an IVF frame header to test_stream_->out_filename.
656 void WriteIvfFrameHeader(int frame_index
, size_t frame_size
);
658 // Prepare and return a frame wrapping the data at |position| bytes in the
659 // input stream, ready to be sent to encoder.
660 // The input frame id is returned in |input_id|.
661 scoped_refptr
<media::VideoFrame
> PrepareInputFrame(off_t position
,
664 // Update the parameters according to |mid_stream_bitrate_switch| and
665 // |mid_stream_framerate_switch|.
666 void UpdateTestStreamData(bool mid_stream_bitrate_switch
,
667 bool mid_stream_framerate_switch
);
669 // Callback function of the |input_timer_|.
673 scoped_ptr
<VideoEncodeAccelerator
> encoder_
;
675 TestStream
* test_stream_
;
677 // Used to notify another thread about the state. VEAClient does not own this.
678 ClientStateNotification
<ClientState
>* note_
;
680 // Ids assigned to VideoFrames.
681 std::set
<int32
> inputs_at_client_
;
682 int32 next_input_id_
;
684 // Encode start time of all encoded frames. The position in the vector is the
686 std::vector
<base::TimeTicks
> encode_start_time_
;
687 // The encode latencies of all encoded frames. We define encode latency as the
688 // time delay from input of each VideoFrame (VEA::Encode()) to output of the
689 // corresponding BitstreamBuffer (VEA::Client::BitstreamBufferReady()).
690 std::vector
<base::TimeDelta
> encode_latencies_
;
692 // Ids for output BitstreamBuffers.
693 typedef std::map
<int32
, base::SharedMemory
*> IdToSHM
;
694 ScopedVector
<base::SharedMemory
> output_shms_
;
695 IdToSHM output_buffers_at_client_
;
696 int32 next_output_buffer_id_
;
698 // Current offset into input stream.
699 off_t pos_in_input_stream_
;
700 gfx::Size input_coded_size_
;
701 // Requested by encoder.
702 unsigned int num_required_input_buffers_
;
703 size_t output_buffer_size_
;
705 // Number of frames to encode. This may differ from the number of frames in
706 // stream if we need more frames for bitrate tests.
707 unsigned int num_frames_to_encode_
;
709 // Number of encoded frames we've got from the encoder thus far.
710 unsigned int num_encoded_frames_
;
712 // Frames since last bitrate verification.
713 unsigned int num_frames_since_last_check_
;
715 // True if received a keyframe while processing current bitstream buffer.
716 bool seen_keyframe_in_this_buffer_
;
718 // True if we are to save the encoded stream to a file.
721 // Request a keyframe every keyframe_period_ frames.
722 const unsigned int keyframe_period_
;
724 // Number of keyframes requested by now.
725 unsigned int num_keyframes_requested_
;
727 // Next keyframe expected before next_keyframe_at_ + kMaxKeyframeDelay.
728 unsigned int next_keyframe_at_
;
730 // True if we are asking encoder for a particular bitrate.
733 // Current requested bitrate.
734 unsigned int current_requested_bitrate_
;
736 // Current expected framerate.
737 unsigned int current_framerate_
;
739 // Byte size of the encoded stream (for bitrate calculation) since last
740 // time we checked bitrate.
741 size_t encoded_stream_size_since_last_check_
;
743 // If true, verify performance at the end of the test.
746 scoped_ptr
<StreamValidator
> validator_
;
748 // The time when the first frame is submitted for encode.
749 base::TimeTicks first_frame_start_time_
;
751 // The time when the last encoded frame is ready.
752 base::TimeTicks last_frame_ready_time_
;
754 // All methods of this class should be run on the same thread.
755 base::ThreadChecker thread_checker_
;
757 // Requested bitrate in bits per second.
758 unsigned int requested_bitrate_
;
760 // Requested initial framerate.
761 unsigned int requested_framerate_
;
763 // Bitrate to switch to in the middle of the stream.
764 unsigned int requested_subsequent_bitrate_
;
766 // Framerate to switch to in the middle of the stream.
767 unsigned int requested_subsequent_framerate_
;
769 // The timer used to feed the encoder with the input frames.
770 scoped_ptr
<base::RepeatingTimer
<VEAClient
>> input_timer_
;
773 VEAClient::VEAClient(TestStream
* test_stream
,
774 ClientStateNotification
<ClientState
>* note
,
776 unsigned int keyframe_period
,
779 bool mid_stream_bitrate_switch
,
780 bool mid_stream_framerate_switch
)
781 : state_(CS_CREATED
),
782 test_stream_(test_stream
),
785 next_output_buffer_id_(0),
786 pos_in_input_stream_(0),
787 num_required_input_buffers_(0),
788 output_buffer_size_(0),
789 num_frames_to_encode_(0),
790 num_encoded_frames_(0),
791 num_frames_since_last_check_(0),
792 seen_keyframe_in_this_buffer_(false),
793 save_to_file_(save_to_file
),
794 keyframe_period_(keyframe_period
),
795 num_keyframes_requested_(0),
796 next_keyframe_at_(0),
797 force_bitrate_(force_bitrate
),
798 current_requested_bitrate_(0),
799 current_framerate_(0),
800 encoded_stream_size_since_last_check_(0),
801 test_perf_(test_perf
),
802 requested_bitrate_(0),
803 requested_framerate_(0),
804 requested_subsequent_bitrate_(0),
805 requested_subsequent_framerate_(0) {
806 if (keyframe_period_
)
807 CHECK_LT(kMaxKeyframeDelay
, keyframe_period_
);
809 // Fake encoder produces an invalid stream, so skip validating it.
810 if (!g_fake_encoder
) {
811 validator_
= StreamValidator::Create(
812 test_stream_
->requested_profile
,
813 base::Bind(&VEAClient::HandleEncodedFrame
, base::Unretained(this)));
818 CHECK(!test_stream_
->out_filename
.empty());
819 base::FilePath
out_filename(test_stream_
->out_filename
);
820 // This creates or truncates out_filename.
821 // Without it, AppendToFile() will not work.
822 EXPECT_EQ(0, base::WriteFile(out_filename
, NULL
, 0));
825 // Initialize the parameters of the test streams.
826 UpdateTestStreamData(mid_stream_bitrate_switch
, mid_stream_framerate_switch
);
828 thread_checker_
.DetachFromThread();
831 VEAClient::~VEAClient() { CHECK(!has_encoder()); }
833 scoped_ptr
<media::VideoEncodeAccelerator
> VEAClient::CreateFakeVEA() {
834 scoped_ptr
<media::VideoEncodeAccelerator
> encoder
;
835 if (g_fake_encoder
) {
836 encoder
.reset(new media::FakeVideoEncodeAccelerator(
837 scoped_refptr
<base::SingleThreadTaskRunner
>(
838 base::ThreadTaskRunnerHandle::Get())));
840 return encoder
.Pass();
843 scoped_ptr
<media::VideoEncodeAccelerator
> VEAClient::CreateV4L2VEA() {
844 scoped_ptr
<media::VideoEncodeAccelerator
> encoder
;
845 #if defined(OS_CHROMEOS) && (defined(ARCH_CPU_ARMEL) || \
846 (defined(USE_OZONE) && defined(USE_V4L2_CODEC)))
847 scoped_refptr
<V4L2Device
> device
= V4L2Device::Create(V4L2Device::kEncoder
);
849 encoder
.reset(new V4L2VideoEncodeAccelerator(device
));
851 return encoder
.Pass();
854 scoped_ptr
<media::VideoEncodeAccelerator
> VEAClient::CreateVaapiVEA() {
855 scoped_ptr
<media::VideoEncodeAccelerator
> encoder
;
856 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
857 encoder
.reset(new VaapiVideoEncodeAccelerator());
859 return encoder
.Pass();
862 void VEAClient::CreateEncoder() {
863 DCHECK(thread_checker_
.CalledOnValidThread());
864 CHECK(!has_encoder());
866 scoped_ptr
<media::VideoEncodeAccelerator
> encoders
[] = {
872 DVLOG(1) << "Profile: " << test_stream_
->requested_profile
873 << ", initial bitrate: " << requested_bitrate_
;
875 for (size_t i
= 0; i
< arraysize(encoders
); ++i
) {
878 encoder_
= encoders
[i
].Pass();
879 SetState(CS_ENCODER_SET
);
880 if (encoder_
->Initialize(kInputFormat
,
881 test_stream_
->visible_size
,
882 test_stream_
->requested_profile
,
885 SetStreamParameters(requested_bitrate_
, requested_framerate_
);
886 SetState(CS_INITIALIZED
);
891 LOG(ERROR
) << "VideoEncodeAccelerator::Initialize() failed";
895 void VEAClient::DestroyEncoder() {
896 DCHECK(thread_checker_
.CalledOnValidThread());
900 input_timer_
.reset();
903 void VEAClient::UpdateTestStreamData(bool mid_stream_bitrate_switch
,
904 bool mid_stream_framerate_switch
) {
905 // Use defaults for bitrate/framerate if they are not provided.
906 if (test_stream_
->requested_bitrate
== 0)
907 requested_bitrate_
= kDefaultBitrate
;
909 requested_bitrate_
= test_stream_
->requested_bitrate
;
911 if (test_stream_
->requested_framerate
== 0)
912 requested_framerate_
= kDefaultFramerate
;
914 requested_framerate_
= test_stream_
->requested_framerate
;
916 // If bitrate/framerate switch is requested, use the subsequent values if
917 // provided, or, if not, calculate them from their initial values using
918 // the default ratios.
919 // Otherwise, if a switch is not requested, keep the initial values.
920 if (mid_stream_bitrate_switch
) {
921 if (test_stream_
->requested_subsequent_bitrate
== 0)
922 requested_subsequent_bitrate_
=
923 requested_bitrate_
* kDefaultSubsequentBitrateRatio
;
925 requested_subsequent_bitrate_
=
926 test_stream_
->requested_subsequent_bitrate
;
928 requested_subsequent_bitrate_
= requested_bitrate_
;
930 if (requested_subsequent_bitrate_
== 0)
931 requested_subsequent_bitrate_
= 1;
933 if (mid_stream_framerate_switch
) {
934 if (test_stream_
->requested_subsequent_framerate
== 0)
935 requested_subsequent_framerate_
=
936 requested_framerate_
* kDefaultSubsequentFramerateRatio
;
938 requested_subsequent_framerate_
=
939 test_stream_
->requested_subsequent_framerate
;
941 requested_subsequent_framerate_
= requested_framerate_
;
943 if (requested_subsequent_framerate_
== 0)
944 requested_subsequent_framerate_
= 1;
947 double VEAClient::frames_per_second() {
948 CHECK_NE(num_encoded_frames_
, 0UL);
949 base::TimeDelta duration
= last_frame_ready_time_
- first_frame_start_time_
;
950 return num_encoded_frames_
/ duration
.InSecondsF();
953 void VEAClient::RequireBitstreamBuffers(unsigned int input_count
,
954 const gfx::Size
& input_coded_size
,
955 size_t output_size
) {
956 DCHECK(thread_checker_
.CalledOnValidThread());
957 ASSERT_EQ(state_
, CS_INITIALIZED
);
958 SetState(CS_ENCODING
);
960 CreateAlignedInputStreamFile(input_coded_size
, test_stream_
);
962 num_frames_to_encode_
= test_stream_
->num_frames
;
963 if (g_num_frames_to_encode
> 0)
964 num_frames_to_encode_
= g_num_frames_to_encode
;
966 // We may need to loop over the stream more than once if more frames than
967 // provided is required for bitrate tests.
968 if (force_bitrate_
&& num_frames_to_encode_
< kMinFramesForBitrateTests
) {
969 DVLOG(1) << "Stream too short for bitrate test ("
970 << test_stream_
->num_frames
<< " frames), will loop it to reach "
971 << kMinFramesForBitrateTests
<< " frames";
972 num_frames_to_encode_
= kMinFramesForBitrateTests
;
974 if (save_to_file_
&& IsVP8(test_stream_
->requested_profile
))
975 WriteIvfFileHeader();
977 input_coded_size_
= input_coded_size
;
978 num_required_input_buffers_
= input_count
;
979 ASSERT_GT(num_required_input_buffers_
, 0UL);
981 output_buffer_size_
= output_size
;
982 ASSERT_GT(output_buffer_size_
, 0UL);
984 for (unsigned int i
= 0; i
< kNumOutputBuffers
; ++i
) {
985 base::SharedMemory
* shm
= new base::SharedMemory();
986 CHECK(shm
->CreateAndMapAnonymous(output_buffer_size_
));
987 output_shms_
.push_back(shm
);
988 FeedEncoderWithOutput(shm
);
991 if (g_env
->run_at_fps()) {
992 input_timer_
.reset(new base::RepeatingTimer
<VEAClient
>());
994 FROM_HERE
, base::TimeDelta::FromSeconds(1) / current_framerate_
,
995 base::Bind(&VEAClient::OnInputTimer
, base::Unretained(this)));
997 while (inputs_at_client_
.size() <
998 num_required_input_buffers_
+ kNumExtraInputFrames
)
999 FeedEncoderWithOneInput();
1003 void VEAClient::BitstreamBufferReady(int32 bitstream_buffer_id
,
1004 size_t payload_size
,
1006 DCHECK(thread_checker_
.CalledOnValidThread());
1007 ASSERT_LE(payload_size
, output_buffer_size_
);
1009 IdToSHM::iterator it
= output_buffers_at_client_
.find(bitstream_buffer_id
);
1010 ASSERT_NE(it
, output_buffers_at_client_
.end());
1011 base::SharedMemory
* shm
= it
->second
;
1012 output_buffers_at_client_
.erase(it
);
1014 if (state_
== CS_FINISHED
)
1017 encoded_stream_size_since_last_check_
+= payload_size
;
1019 const uint8
* stream_ptr
= static_cast<const uint8
*>(shm
->memory());
1020 if (payload_size
> 0) {
1022 validator_
->ProcessStreamBuffer(stream_ptr
, payload_size
);
1024 HandleEncodedFrame(key_frame
);
1027 if (save_to_file_
) {
1028 if (IsVP8(test_stream_
->requested_profile
))
1029 WriteIvfFrameHeader(num_encoded_frames_
- 1, payload_size
);
1031 EXPECT_TRUE(base::AppendToFile(
1032 base::FilePath::FromUTF8Unsafe(test_stream_
->out_filename
),
1033 static_cast<char*>(shm
->memory()),
1034 base::checked_cast
<int>(payload_size
)));
1038 EXPECT_EQ(key_frame
, seen_keyframe_in_this_buffer_
);
1039 seen_keyframe_in_this_buffer_
= false;
1041 FeedEncoderWithOutput(shm
);
1044 void VEAClient::NotifyError(VideoEncodeAccelerator::Error error
) {
1045 DCHECK(thread_checker_
.CalledOnValidThread());
1049 void VEAClient::SetState(ClientState new_state
) {
1050 DVLOG(4) << "Changing state " << state_
<< "->" << new_state
;
1051 note_
->Notify(new_state
);
1055 void VEAClient::SetStreamParameters(unsigned int bitrate
,
1056 unsigned int framerate
) {
1057 current_requested_bitrate_
= bitrate
;
1058 current_framerate_
= framerate
;
1059 CHECK_GT(current_requested_bitrate_
, 0UL);
1060 CHECK_GT(current_framerate_
, 0UL);
1061 encoder_
->RequestEncodingParametersChange(current_requested_bitrate_
,
1062 current_framerate_
);
1063 DVLOG(1) << "Switched parameters to " << current_requested_bitrate_
1064 << " bps @ " << current_framerate_
<< " FPS";
1067 void VEAClient::InputNoLongerNeededCallback(int32 input_id
) {
1068 std::set
<int32
>::iterator it
= inputs_at_client_
.find(input_id
);
1069 ASSERT_NE(it
, inputs_at_client_
.end());
1070 inputs_at_client_
.erase(it
);
1071 if (!g_env
->run_at_fps())
1072 FeedEncoderWithOneInput();
1075 scoped_refptr
<media::VideoFrame
> VEAClient::PrepareInputFrame(off_t position
,
1077 CHECK_LE(position
+ test_stream_
->aligned_buffer_size
,
1078 test_stream_
->mapped_aligned_in_file
.length());
1080 uint8
* frame_data_y
= const_cast<uint8
*>(
1081 test_stream_
->mapped_aligned_in_file
.data() + position
);
1082 uint8
* frame_data_u
= frame_data_y
+ test_stream_
->aligned_plane_size
[0];
1083 uint8
* frame_data_v
= frame_data_u
+ test_stream_
->aligned_plane_size
[1];
1085 CHECK_GT(current_framerate_
, 0U);
1086 scoped_refptr
<media::VideoFrame
> frame
=
1087 media::VideoFrame::WrapExternalYuvData(
1090 gfx::Rect(test_stream_
->visible_size
),
1091 test_stream_
->visible_size
,
1092 input_coded_size_
.width(),
1093 input_coded_size_
.width() / 2,
1094 input_coded_size_
.width() / 2,
1098 base::TimeDelta().FromMilliseconds(
1099 next_input_id_
* base::Time::kMillisecondsPerSecond
/
1100 current_framerate_
));
1101 frame
->AddDestructionObserver(
1102 media::BindToCurrentLoop(
1103 base::Bind(&VEAClient::InputNoLongerNeededCallback
,
1104 base::Unretained(this),
1107 CHECK(inputs_at_client_
.insert(next_input_id_
).second
);
1109 *input_id
= next_input_id_
++;
1113 void VEAClient::OnInputTimer() {
1114 if (!has_encoder() || state_
!= CS_ENCODING
)
1115 input_timer_
.reset();
1116 else if (inputs_at_client_
.size() <
1117 num_required_input_buffers_
+ kNumExtraInputFrames
)
1118 FeedEncoderWithOneInput();
1120 DVLOG(1) << "Dropping input frame";
1123 void VEAClient::FeedEncoderWithOneInput() {
1124 if (!has_encoder() || state_
!= CS_ENCODING
)
1128 test_stream_
->mapped_aligned_in_file
.length() - pos_in_input_stream_
;
1129 if (bytes_left
< test_stream_
->aligned_buffer_size
) {
1130 DCHECK_EQ(bytes_left
, 0UL);
1131 // Rewind if at the end of stream and we are still encoding.
1132 // This is to flush the encoder with additional frames from the beginning
1133 // of the stream, or if the stream is shorter that the number of frames
1134 // we require for bitrate tests.
1135 pos_in_input_stream_
= 0;
1139 scoped_refptr
<media::VideoFrame
> video_frame
=
1140 PrepareInputFrame(pos_in_input_stream_
, &input_id
);
1141 pos_in_input_stream_
+= test_stream_
->aligned_buffer_size
;
1143 bool force_keyframe
= false;
1144 if (keyframe_period_
&& input_id
% keyframe_period_
== 0) {
1145 force_keyframe
= true;
1146 ++num_keyframes_requested_
;
1149 if (input_id
== 0) {
1150 first_frame_start_time_
= base::TimeTicks::Now();
1153 if (g_env
->needs_encode_latency()) {
1154 CHECK_EQ(input_id
, static_cast<int32
>(encode_start_time_
.size()));
1155 encode_start_time_
.push_back(base::TimeTicks::Now());
1157 encoder_
->Encode(video_frame
, force_keyframe
);
1160 void VEAClient::FeedEncoderWithOutput(base::SharedMemory
* shm
) {
1164 if (state_
!= CS_ENCODING
)
1167 base::SharedMemoryHandle dup_handle
;
1168 CHECK(shm
->ShareToProcess(base::GetCurrentProcessHandle(), &dup_handle
));
1170 media::BitstreamBuffer
bitstream_buffer(
1171 next_output_buffer_id_
++, dup_handle
, output_buffer_size_
);
1172 CHECK(output_buffers_at_client_
.insert(std::make_pair(bitstream_buffer
.id(),
1174 encoder_
->UseOutputBitstreamBuffer(bitstream_buffer
);
1177 bool VEAClient::HandleEncodedFrame(bool keyframe
) {
1178 // This would be a bug in the test, which should not ignore false
1179 // return value from this method.
1180 CHECK_LE(num_encoded_frames_
, num_frames_to_encode_
);
1182 last_frame_ready_time_
= base::TimeTicks::Now();
1184 if (g_env
->needs_encode_latency()) {
1185 CHECK_LT(num_encoded_frames_
, encode_start_time_
.size());
1186 base::TimeTicks start_time
= encode_start_time_
[num_encoded_frames_
];
1187 CHECK(!start_time
.is_null());
1188 encode_latencies_
.push_back(last_frame_ready_time_
- start_time
);
1191 ++num_encoded_frames_
;
1192 ++num_frames_since_last_check_
;
1194 // Because the keyframe behavior requirements are loose, we give
1195 // the encoder more freedom here. It could either deliver a keyframe
1196 // immediately after we requested it, which could be for a frame number
1197 // before the one we requested it for (if the keyframe request
1198 // is asynchronous, i.e. not bound to any concrete frame, and because
1199 // the pipeline can be deeper than one frame), at that frame, or after.
1200 // So the only constraints we put here is that we get a keyframe not
1201 // earlier than we requested one (in time), and not later than
1202 // kMaxKeyframeDelay frames after the frame, for which we requested
1203 // it, comes back encoded.
1205 if (num_keyframes_requested_
> 0) {
1206 --num_keyframes_requested_
;
1207 next_keyframe_at_
+= keyframe_period_
;
1209 seen_keyframe_in_this_buffer_
= true;
1212 if (num_keyframes_requested_
> 0)
1213 EXPECT_LE(num_encoded_frames_
, next_keyframe_at_
+ kMaxKeyframeDelay
);
1215 if (num_encoded_frames_
== num_frames_to_encode_
/ 2) {
1216 VerifyStreamProperties();
1217 if (requested_subsequent_bitrate_
!= current_requested_bitrate_
||
1218 requested_subsequent_framerate_
!= current_framerate_
) {
1219 SetStreamParameters(requested_subsequent_bitrate_
,
1220 requested_subsequent_framerate_
);
1221 if (g_env
->run_at_fps() && input_timer_
)
1222 input_timer_
->Start(
1223 FROM_HERE
, base::TimeDelta::FromSeconds(1) / current_framerate_
,
1224 base::Bind(&VEAClient::OnInputTimer
, base::Unretained(this)));
1226 } else if (num_encoded_frames_
== num_frames_to_encode_
) {
1229 VerifyStreamProperties();
1230 SetState(CS_FINISHED
);
1237 void VEAClient::LogPerf() {
1238 g_env
->LogToFile("Measured encoder FPS",
1239 base::StringPrintf("%.3f", frames_per_second()));
1241 // Log encode latencies.
1242 if (g_env
->needs_encode_latency()) {
1243 std::sort(encode_latencies_
.begin(), encode_latencies_
.end());
1244 for (const auto& percentile
: kLoggedLatencyPercentiles
) {
1245 base::TimeDelta latency
= Percentile(encode_latencies_
, percentile
);
1247 base::StringPrintf("Encode latency for the %dth percentile",
1249 base::StringPrintf("%" PRId64
" us", latency
.InMicroseconds()));
1254 void VEAClient::VerifyMinFPS() {
1256 EXPECT_GE(frames_per_second(), kMinPerfFPS
);
1259 void VEAClient::VerifyStreamProperties() {
1260 CHECK_GT(num_frames_since_last_check_
, 0UL);
1261 CHECK_GT(encoded_stream_size_since_last_check_
, 0UL);
1262 unsigned int bitrate
= encoded_stream_size_since_last_check_
* 8 *
1263 current_framerate_
/ num_frames_since_last_check_
;
1264 DVLOG(1) << "Current chunk's bitrate: " << bitrate
1265 << " (expected: " << current_requested_bitrate_
1266 << " @ " << current_framerate_
<< " FPS,"
1267 << " num frames in chunk: " << num_frames_since_last_check_
;
1269 num_frames_since_last_check_
= 0;
1270 encoded_stream_size_since_last_check_
= 0;
1272 if (force_bitrate_
) {
1273 EXPECT_NEAR(bitrate
,
1274 current_requested_bitrate_
,
1275 kBitrateTolerance
* current_requested_bitrate_
);
1278 // All requested keyframes should've been provided. Allow the last requested
1279 // frame to remain undelivered if we haven't reached the maximum frame number
1280 // by which it should have arrived.
1281 if (num_encoded_frames_
< next_keyframe_at_
+ kMaxKeyframeDelay
)
1282 EXPECT_LE(num_keyframes_requested_
, 1UL);
1284 EXPECT_EQ(num_keyframes_requested_
, 0UL);
1287 void VEAClient::WriteIvfFileHeader() {
1288 IvfFileHeader header
;
1290 memset(&header
, 0, sizeof(header
));
1291 header
.signature
[0] = 'D';
1292 header
.signature
[1] = 'K';
1293 header
.signature
[2] = 'I';
1294 header
.signature
[3] = 'F';
1296 header
.header_size
= base::ByteSwapToLE16(sizeof(header
));
1297 header
.fourcc
= base::ByteSwapToLE32(0x30385056); // VP80
1298 header
.width
= base::ByteSwapToLE16(
1299 base::checked_cast
<uint16_t>(test_stream_
->visible_size
.width()));
1300 header
.height
= base::ByteSwapToLE16(
1301 base::checked_cast
<uint16_t>(test_stream_
->visible_size
.height()));
1302 header
.framerate
= base::ByteSwapToLE32(requested_framerate_
);
1303 header
.timescale
= base::ByteSwapToLE32(1);
1304 header
.num_frames
= base::ByteSwapToLE32(num_frames_to_encode_
);
1306 EXPECT_TRUE(base::AppendToFile(
1307 base::FilePath::FromUTF8Unsafe(test_stream_
->out_filename
),
1308 reinterpret_cast<char*>(&header
), sizeof(header
)));
1311 void VEAClient::WriteIvfFrameHeader(int frame_index
, size_t frame_size
) {
1312 IvfFrameHeader header
;
1314 memset(&header
, 0, sizeof(header
));
1315 header
.frame_size
= base::ByteSwapToLE32(frame_size
);
1316 header
.timestamp
= base::ByteSwapToLE64(frame_index
);
1317 EXPECT_TRUE(base::AppendToFile(
1318 base::FilePath::FromUTF8Unsafe(test_stream_
->out_filename
),
1319 reinterpret_cast<char*>(&header
), sizeof(header
)));
1323 // - Number of concurrent encoders. The value takes effect when there is only
1324 // one input stream; otherwise, one encoder per input stream will be
1326 // - If true, save output to file (provided an output filename was supplied).
1327 // - Force a keyframe every n frames.
1328 // - Force bitrate; the actual required value is provided as a property
1329 // of the input stream, because it depends on stream type/resolution/etc.
1330 // - If true, measure performance.
1331 // - If true, switch bitrate mid-stream.
1332 // - If true, switch framerate mid-stream.
1333 class VideoEncodeAcceleratorTest
1334 : public ::testing::TestWithParam
<
1335 base::Tuple
<int, bool, int, bool, bool, bool, bool>> {};
1337 TEST_P(VideoEncodeAcceleratorTest
, TestSimpleEncode
) {
1338 size_t num_concurrent_encoders
= base::get
<0>(GetParam());
1339 const bool save_to_file
= base::get
<1>(GetParam());
1340 const unsigned int keyframe_period
= base::get
<2>(GetParam());
1341 const bool force_bitrate
= base::get
<3>(GetParam());
1342 const bool test_perf
= base::get
<4>(GetParam());
1343 const bool mid_stream_bitrate_switch
= base::get
<5>(GetParam());
1344 const bool mid_stream_framerate_switch
= base::get
<6>(GetParam());
1346 ScopedVector
<ClientStateNotification
<ClientState
> > notes
;
1347 ScopedVector
<VEAClient
> clients
;
1348 base::Thread
encoder_thread("EncoderThread");
1349 ASSERT_TRUE(encoder_thread
.Start());
1351 if (g_env
->test_streams_
.size() > 1)
1352 num_concurrent_encoders
= g_env
->test_streams_
.size();
1354 // Create all encoders.
1355 for (size_t i
= 0; i
< num_concurrent_encoders
; i
++) {
1356 size_t test_stream_index
= i
% g_env
->test_streams_
.size();
1357 // Disregard save_to_file if we didn't get an output filename.
1358 bool encoder_save_to_file
=
1360 !g_env
->test_streams_
[test_stream_index
]->out_filename
.empty());
1362 notes
.push_back(new ClientStateNotification
<ClientState
>());
1363 clients
.push_back(new VEAClient(
1364 g_env
->test_streams_
[test_stream_index
], notes
.back(),
1365 encoder_save_to_file
, keyframe_period
, force_bitrate
, test_perf
,
1366 mid_stream_bitrate_switch
, mid_stream_framerate_switch
));
1368 encoder_thread
.message_loop()->PostTask(
1370 base::Bind(&VEAClient::CreateEncoder
,
1371 base::Unretained(clients
.back())));
1374 // All encoders must pass through states in this order.
1375 enum ClientState state_transitions
[] = {CS_ENCODER_SET
, CS_INITIALIZED
,
1376 CS_ENCODING
, CS_FINISHED
};
1378 // Wait for all encoders to go through all states and finish.
1379 // Do this by waiting for all encoders to advance to state n before checking
1380 // state n+1, to verify that they are able to operate concurrently.
1381 // It also simulates the real-world usage better, as the main thread, on which
1382 // encoders are created/destroyed, is a single GPU Process ChildThread.
1383 // Moreover, we can't have proper multithreading on X11, so this could cause
1384 // hard to debug issues there, if there were multiple "ChildThreads".
1385 for (size_t state_no
= 0; state_no
< arraysize(state_transitions
); ++state_no
)
1386 for (size_t i
= 0; i
< num_concurrent_encoders
; i
++)
1387 ASSERT_EQ(notes
[i
]->Wait(), state_transitions
[state_no
]);
1389 for (size_t i
= 0; i
< num_concurrent_encoders
; ++i
) {
1390 encoder_thread
.message_loop()->PostTask(
1392 base::Bind(&VEAClient::DestroyEncoder
, base::Unretained(clients
[i
])));
1395 // This ensures all tasks have finished.
1396 encoder_thread
.Stop();
1399 INSTANTIATE_TEST_CASE_P(
1401 VideoEncodeAcceleratorTest
,
1402 ::testing::Values(base::MakeTuple(1, true, 0, false, false, false, false)));
1404 INSTANTIATE_TEST_CASE_P(
1406 VideoEncodeAcceleratorTest
,
1407 ::testing::Values(base::MakeTuple(1, false, 0, false, true, false, false)));
1409 INSTANTIATE_TEST_CASE_P(
1411 VideoEncodeAcceleratorTest
,
1412 ::testing::Values(base::MakeTuple(1, false, 10, false, false, false,
1415 INSTANTIATE_TEST_CASE_P(
1417 VideoEncodeAcceleratorTest
,
1418 ::testing::Values(base::MakeTuple(1, false, 0, true, false, false, false)));
1420 INSTANTIATE_TEST_CASE_P(
1421 MidStreamParamSwitchBitrate
,
1422 VideoEncodeAcceleratorTest
,
1423 ::testing::Values(base::MakeTuple(1, false, 0, true, false, true, false)));
1425 INSTANTIATE_TEST_CASE_P(
1426 MidStreamParamSwitchFPS
,
1427 VideoEncodeAcceleratorTest
,
1428 ::testing::Values(base::MakeTuple(1, false, 0, true, false, false, true)));
1430 INSTANTIATE_TEST_CASE_P(
1432 VideoEncodeAcceleratorTest
,
1433 ::testing::Values(base::MakeTuple(3, false, 0, false, false, false, false),
1434 base::MakeTuple(3, false, 0, true, false, false, true),
1435 base::MakeTuple(3, false, 0, true, false, true, false)));
1437 // TODO(posciak): more tests:
1438 // - async FeedEncoderWithOutput
1439 // - out-of-order return of outputs to encoder
1440 // - multiple encoders + decoders
1441 // - mid-stream encoder_->Destroy()
1444 } // namespace content
1446 int main(int argc
, char** argv
) {
1447 testing::InitGoogleTest(&argc
, argv
); // Removes gtest-specific args.
1448 base::CommandLine::Init(argc
, argv
);
1450 base::ShadowingAtExitManager at_exit_manager
;
1451 base::MessageLoop main_loop
;
1453 scoped_ptr
<base::FilePath::StringType
> test_stream_data(
1454 new base::FilePath::StringType(
1455 media::GetTestDataFilePath(content::g_default_in_filename
).value() +
1456 content::g_default_in_parameters
));
1458 // Needed to enable DVLOG through --vmodule.
1459 logging::LoggingSettings settings
;
1460 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
1461 CHECK(logging::InitLogging(settings
));
1463 const base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
1466 bool run_at_fps
= false;
1467 bool needs_encode_latency
= false;
1468 base::FilePath log_path
;
1470 base::CommandLine::SwitchMap switches
= cmd_line
->GetSwitches();
1471 for (base::CommandLine::SwitchMap::const_iterator it
= switches
.begin();
1472 it
!= switches
.end();
1474 if (it
->first
== "test_stream_data") {
1475 test_stream_data
->assign(it
->second
.c_str());
1478 // Output machine-readable logs with fixed formats to a file.
1479 if (it
->first
== "output_log") {
1480 log_path
= base::FilePath(
1481 base::FilePath::StringType(it
->second
.begin(), it
->second
.end()));
1484 if (it
->first
== "num_frames_to_encode") {
1485 std::string
input(it
->second
.begin(), it
->second
.end());
1486 CHECK(base::StringToInt(input
, &content::g_num_frames_to_encode
));
1489 if (it
->first
== "measure_latency") {
1490 needs_encode_latency
= true;
1493 if (it
->first
== "fake_encoder") {
1494 content::g_fake_encoder
= true;
1497 if (it
->first
== "run_at_fps") {
1501 if (it
->first
== "v" || it
->first
== "vmodule")
1503 if (it
->first
== "ozone-platform" || it
->first
== "ozone-use-surfaceless")
1505 LOG(FATAL
) << "Unexpected switch: " << it
->first
<< ":" << it
->second
;
1508 if (needs_encode_latency
&& !run_at_fps
) {
1509 // Encode latency can only be measured with --run_at_fps. Otherwise, we get
1510 // skewed results since it may queue too many frames at once with the same
1511 // encode start time.
1512 LOG(FATAL
) << "--measure_latency requires --run_at_fps enabled to work.";
1515 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
1516 content::VaapiWrapper::PreSandboxInitialization();
1520 reinterpret_cast<content::VideoEncodeAcceleratorTestEnvironment
*>(
1521 testing::AddGlobalTestEnvironment(
1522 new content::VideoEncodeAcceleratorTestEnvironment(
1523 test_stream_data
.Pass(), log_path
, run_at_fps
,
1524 needs_encode_latency
)));
1526 return RUN_ALL_TESTS();