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