1 // Copyright 2014 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.
8 #include "base/command_line.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/test/launcher/unit_test_launcher.h"
11 #include "base/test/simple_test_tick_clock.h"
12 #include "base/test/test_suite.h"
13 #include "media/base/decoder_buffer.h"
14 #include "media/base/media.h"
15 #include "media/base/media_switches.h"
16 #include "media/cast/sender/h264_vt_encoder.h"
17 #include "media/cast/sender/video_frame_factory.h"
18 #include "media/cast/test/utility/default_config.h"
19 #include "media/cast/test/utility/video_utility.h"
20 #include "media/ffmpeg/ffmpeg_common.h"
21 #include "media/filters/ffmpeg_glue.h"
22 #include "media/filters/ffmpeg_video_decoder.h"
23 #include "testing/gtest/include/gtest/gtest.h"
27 const int kVideoWidth
= 1280;
28 const int kVideoHeight
= 720;
30 class MediaTestSuite
: public base::TestSuite
{
32 MediaTestSuite(int argc
, char** argv
) : TestSuite(argc
, argv
) {}
33 ~MediaTestSuite() override
{}
36 void Initialize() override
;
39 void MediaTestSuite::Initialize() {
40 base::TestSuite::Initialize();
41 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
42 command_line
->AppendSwitch(switches::kEnableInbandTextTracks
);
43 media::InitializeMediaLibraryForTesting();
48 int main(int argc
, char** argv
) {
50 base::AtExitManager at_exit_manager
;
51 CHECK(VideoToolboxGlue::Get())
52 << "VideoToolbox is not available. Requires OS X 10.8 or iOS 8.0.";
54 MediaTestSuite
test_suite(argc
, argv
);
55 return base::LaunchUnitTests(
57 base::Bind(&MediaTestSuite::Run
, base::Unretained(&test_suite
)));
63 // See comment in end2end_unittest.cc for details on this value.
64 const double kVideoAcceptedPSNR
= 38.0;
66 void SavePipelineStatus(PipelineStatus
* out_status
, PipelineStatus in_status
) {
67 *out_status
= in_status
;
70 void SaveOperationalStatus(OperationalStatus
* out_status
,
71 OperationalStatus in_status
) {
72 *out_status
= in_status
;
75 class MetadataRecorder
: public base::RefCountedThreadSafe
<MetadataRecorder
> {
77 MetadataRecorder() : count_frames_delivered_(0) {}
79 int count_frames_delivered() const { return count_frames_delivered_
; }
81 void PushExpectation(uint32 expected_frame_id
,
82 uint32 expected_last_referenced_frame_id
,
83 uint32 expected_rtp_timestamp
,
84 const base::TimeTicks
& expected_reference_time
) {
85 expectations_
.push(Expectation
{expected_frame_id
,
86 expected_last_referenced_frame_id
,
87 expected_rtp_timestamp
,
88 expected_reference_time
});
91 void CompareFrameWithExpected(scoped_ptr
<EncodedFrame
> encoded_frame
) {
92 ASSERT_LT(0u, expectations_
.size());
93 auto e
= expectations_
.front();
95 if (e
.expected_frame_id
!= e
.expected_last_referenced_frame_id
) {
96 EXPECT_EQ(EncodedFrame::DEPENDENT
, encoded_frame
->dependency
);
98 EXPECT_EQ(EncodedFrame::KEY
, encoded_frame
->dependency
);
100 EXPECT_EQ(e
.expected_frame_id
, encoded_frame
->frame_id
);
101 EXPECT_EQ(e
.expected_last_referenced_frame_id
,
102 encoded_frame
->referenced_frame_id
)
103 << "frame id: " << e
.expected_frame_id
;
104 EXPECT_EQ(e
.expected_rtp_timestamp
, encoded_frame
->rtp_timestamp
);
105 EXPECT_EQ(e
.expected_reference_time
, encoded_frame
->reference_time
);
106 EXPECT_FALSE(encoded_frame
->data
.empty());
107 ++count_frames_delivered_
;
111 friend class base::RefCountedThreadSafe
<MetadataRecorder
>;
112 virtual ~MetadataRecorder() {}
114 int count_frames_delivered_
;
117 uint32 expected_frame_id
;
118 uint32 expected_last_referenced_frame_id
;
119 uint32 expected_rtp_timestamp
;
120 base::TimeTicks expected_reference_time
;
122 std::queue
<Expectation
> expectations_
;
124 DISALLOW_COPY_AND_ASSIGN(MetadataRecorder
);
127 class EndToEndFrameChecker
128 : public base::RefCountedThreadSafe
<EndToEndFrameChecker
> {
130 explicit EndToEndFrameChecker(const VideoDecoderConfig
& config
)
131 : decoder_(base::MessageLoop::current()->task_runner()),
132 count_frames_checked_(0) {
133 PipelineStatus pipeline_status
;
135 config
, false, base::Bind(&SavePipelineStatus
, &pipeline_status
),
136 base::Bind(&EndToEndFrameChecker::CompareFrameWithExpected
,
137 base::Unretained(this)));
138 base::MessageLoop::current()->RunUntilIdle();
139 EXPECT_EQ(PIPELINE_OK
, pipeline_status
);
142 void PushExpectation(const scoped_refptr
<VideoFrame
>& frame
) {
143 expectations_
.push(frame
);
146 void EncodeDone(scoped_ptr
<EncodedFrame
> encoded_frame
) {
147 auto buffer
= DecoderBuffer::CopyFrom(encoded_frame
->bytes(),
148 encoded_frame
->data
.size());
149 decoder_
.Decode(buffer
, base::Bind(&EndToEndFrameChecker::DecodeDone
,
150 base::Unretained(this)));
153 void CompareFrameWithExpected(const scoped_refptr
<VideoFrame
>& frame
) {
154 ASSERT_LT(0u, expectations_
.size());
155 auto& e
= expectations_
.front();
157 EXPECT_LE(kVideoAcceptedPSNR
, I420PSNR(e
, frame
));
158 ++count_frames_checked_
;
161 void DecodeDone(VideoDecoder::Status status
) {
162 EXPECT_EQ(VideoDecoder::kOk
, status
);
165 int count_frames_checked() const { return count_frames_checked_
; }
168 friend class base::RefCountedThreadSafe
<EndToEndFrameChecker
>;
169 virtual ~EndToEndFrameChecker() {}
171 FFmpegVideoDecoder decoder_
;
172 std::queue
<scoped_refptr
<VideoFrame
>> expectations_
;
173 int count_frames_checked_
;
175 DISALLOW_COPY_AND_ASSIGN(EndToEndFrameChecker
);
178 void CreateFrameAndMemsetPlane(VideoFrameFactory
* const video_frame_factory
) {
179 const scoped_refptr
<media::VideoFrame
> video_frame
=
180 video_frame_factory
->MaybeCreateFrame(
181 gfx::Size(kVideoWidth
, kVideoHeight
), base::TimeDelta());
182 ASSERT_TRUE(video_frame
.get());
183 auto cv_pixel_buffer
= video_frame
->cv_pixel_buffer();
184 ASSERT_TRUE(cv_pixel_buffer
);
185 CVPixelBufferLockBaseAddress(cv_pixel_buffer
, 0);
186 auto ptr
= CVPixelBufferGetBaseAddressOfPlane(cv_pixel_buffer
, 0);
188 memset(ptr
, 0xfe, CVPixelBufferGetBytesPerRowOfPlane(cv_pixel_buffer
, 0) *
189 CVPixelBufferGetHeightOfPlane(cv_pixel_buffer
, 0));
190 CVPixelBufferUnlockBaseAddress(cv_pixel_buffer
, 0);
193 class H264VideoToolboxEncoderTest
: public ::testing::Test
{
195 H264VideoToolboxEncoderTest()
196 : operational_status_(STATUS_UNINITIALIZED
) {
197 frame_
->set_timestamp(base::TimeDelta());
200 void SetUp() override
{
201 clock_
= new base::SimpleTestTickClock();
202 clock_
->Advance(base::TimeTicks::Now() - base::TimeTicks());
204 cast_environment_
= new CastEnvironment(
205 scoped_ptr
<base::TickClock
>(clock_
).Pass(),
206 message_loop_
.message_loop_proxy(), message_loop_
.message_loop_proxy(),
207 message_loop_
.message_loop_proxy());
208 encoder_
.reset(new H264VideoToolboxEncoder(
210 video_sender_config_
,
211 gfx::Size(kVideoWidth
, kVideoHeight
),
213 base::Bind(&SaveOperationalStatus
, &operational_status_
)));
214 message_loop_
.RunUntilIdle();
215 EXPECT_EQ(STATUS_INITIALIZED
, operational_status_
);
218 void TearDown() override
{
220 message_loop_
.RunUntilIdle();
223 void AdvanceClockAndVideoFrameTimestamp() {
224 clock_
->Advance(base::TimeDelta::FromMilliseconds(33));
225 frame_
->set_timestamp(frame_
->timestamp() +
226 base::TimeDelta::FromMilliseconds(33));
229 static void SetUpTestCase() {
230 // Reusable test data.
231 video_sender_config_
= GetDefaultVideoSenderConfig();
232 video_sender_config_
.codec
= CODEC_VIDEO_H264
;
233 const gfx::Size
size(kVideoWidth
, kVideoHeight
);
234 frame_
= media::VideoFrame::CreateFrame(
235 VideoFrame::I420
, size
, gfx::Rect(size
), size
, base::TimeDelta());
236 PopulateVideoFrame(frame_
.get(), 123);
239 static void TearDownTestCase() { frame_
= nullptr; }
241 static scoped_refptr
<media::VideoFrame
> frame_
;
242 static VideoSenderConfig video_sender_config_
;
244 base::SimpleTestTickClock
* clock_
; // Owned by CastEnvironment.
245 base::MessageLoop message_loop_
;
246 scoped_refptr
<CastEnvironment
> cast_environment_
;
247 scoped_ptr
<VideoEncoder
> encoder_
;
248 OperationalStatus operational_status_
;
251 DISALLOW_COPY_AND_ASSIGN(H264VideoToolboxEncoderTest
);
255 scoped_refptr
<media::VideoFrame
> H264VideoToolboxEncoderTest::frame_
;
256 VideoSenderConfig
H264VideoToolboxEncoderTest::video_sender_config_
;
258 TEST_F(H264VideoToolboxEncoderTest
, CheckFrameMetadataSequence
) {
259 scoped_refptr
<MetadataRecorder
> metadata_recorder(new MetadataRecorder());
260 VideoEncoder::FrameEncodedCallback cb
= base::Bind(
261 &MetadataRecorder::CompareFrameWithExpected
, metadata_recorder
.get());
263 metadata_recorder
->PushExpectation(
264 0, 0, TimeDeltaToRtpDelta(frame_
->timestamp(), kVideoFrequency
),
266 EXPECT_TRUE(encoder_
->EncodeVideoFrame(frame_
, clock_
->NowTicks(), cb
));
267 message_loop_
.RunUntilIdle();
269 for (uint32 frame_id
= 1; frame_id
< 10; ++frame_id
) {
270 AdvanceClockAndVideoFrameTimestamp();
271 metadata_recorder
->PushExpectation(
272 frame_id
, frame_id
- 1,
273 TimeDeltaToRtpDelta(frame_
->timestamp(), kVideoFrequency
),
275 EXPECT_TRUE(encoder_
->EncodeVideoFrame(frame_
, clock_
->NowTicks(), cb
));
279 message_loop_
.RunUntilIdle();
281 EXPECT_EQ(10, metadata_recorder
->count_frames_delivered());
284 #if defined(USE_PROPRIETARY_CODECS)
285 TEST_F(H264VideoToolboxEncoderTest
, CheckFramesAreDecodable
) {
286 VideoDecoderConfig
config(kCodecH264
, H264PROFILE_MAIN
, frame_
->format(),
287 frame_
->coded_size(), frame_
->visible_rect(),
288 frame_
->natural_size(), nullptr, 0, false);
289 scoped_refptr
<EndToEndFrameChecker
> checker(new EndToEndFrameChecker(config
));
291 VideoEncoder::FrameEncodedCallback cb
=
292 base::Bind(&EndToEndFrameChecker::EncodeDone
, checker
.get());
293 for (uint32 frame_id
= 0; frame_id
< 6; ++frame_id
) {
294 checker
->PushExpectation(frame_
);
295 EXPECT_TRUE(encoder_
->EncodeVideoFrame(frame_
, clock_
->NowTicks(), cb
));
296 AdvanceClockAndVideoFrameTimestamp();
300 message_loop_
.RunUntilIdle();
302 EXPECT_EQ(5, checker
->count_frames_checked());
306 TEST_F(H264VideoToolboxEncoderTest
, CheckVideoFrameFactory
) {
307 auto video_frame_factory
= encoder_
->CreateVideoFrameFactory();
308 ASSERT_TRUE(video_frame_factory
.get());
309 CreateFrameAndMemsetPlane(video_frame_factory
.get());
310 // TODO(jfroy): Need to test that the encoder can encode VideoFrames provided
311 // by the VideoFrameFactory.
313 message_loop_
.RunUntilIdle();
314 CreateFrameAndMemsetPlane(video_frame_factory
.get());