OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/run_loop.h" | 5 #include "base/run_loop.h" |
6 #include "base/strings/utf_string_conversions.h" | 6 #include "base/strings/utf_string_conversions.h" |
7 #include "content/child/child_process.h" | 7 #include "content/child/child_process.h" |
8 #include "content/renderer/media/media_recorder_handler.h" | 8 #include "content/renderer/media/media_recorder_handler.h" |
9 #include "content/renderer/media/mock_media_stream_registry.h" | 9 #include "content/renderer/media/mock_media_stream_registry.h" |
| 10 #include "media/audio/simple_sources.h" |
| 11 #include "media/base/audio_bus.h" |
10 #include "media/base/video_frame.h" | 12 #include "media/base/video_frame.h" |
11 #include "testing/gmock/include/gmock/gmock.h" | 13 #include "testing/gmock/include/gmock/gmock.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
13 #include "third_party/WebKit/public/platform/WebMediaRecorderHandlerClient.h" | 15 #include "third_party/WebKit/public/platform/WebMediaRecorderHandlerClient.h" |
14 #include "third_party/WebKit/public/platform/WebString.h" | 16 #include "third_party/WebKit/public/platform/WebString.h" |
15 #include "third_party/WebKit/public/web/WebHeap.h" | 17 #include "third_party/WebKit/public/web/WebHeap.h" |
16 | 18 |
17 using ::testing::_; | 19 using ::testing::_; |
18 using ::testing::AtLeast; | 20 using ::testing::AtLeast; |
19 using ::testing::InSequence; | 21 using ::testing::InSequence; |
20 using ::testing::Lt; | 22 using ::testing::Lt; |
21 using ::testing::Mock; | 23 using ::testing::Mock; |
22 using ::testing::TestWithParam; | 24 using ::testing::TestWithParam; |
23 using ::testing::ValuesIn; | 25 using ::testing::ValuesIn; |
24 | 26 |
25 using blink::WebString; | 27 using blink::WebString; |
26 | 28 |
27 namespace content { | 29 namespace content { |
28 | 30 |
29 ACTION_P(RunClosure, closure) { | 31 ACTION_P(RunClosure, closure) { |
30 closure.Run(); | 32 closure.Run(); |
31 } | 33 } |
32 | 34 |
33 static const std::string kTestStreamUrl = "stream_url"; | 35 static const std::string kTestStreamUrl = "stream_url"; |
34 static const std::string kTestVideoTrackId = "video_track_id"; | 36 static const std::string kTestVideoTrackId = "video_track_id"; |
| 37 static const std::string kTestAudioTrackId = "audio_track_id"; |
| 38 static const int kTestAudioChannels = 2; |
| 39 static const int kTestAudioBitsPerSample = 16; |
| 40 static const int kTestAudioSampleRate = 48000; |
| 41 static const int kTestAudioBufferDurationMS = 60; |
35 | 42 |
36 struct MediaRecorderTestParams { | 43 struct MediaRecorderTestParams { |
| 44 const bool has_video; |
| 45 const bool has_audio; |
37 const char* const mime_type; | 46 const char* const mime_type; |
38 const size_t first_encoded_frame_size; | 47 const size_t first_encoded_video_frame_size; |
39 const size_t second_encoded_frame_size; | 48 const size_t second_encoded_video_frame_size; |
| 49 const size_t first_encoded_audio_frame_size; |
| 50 const size_t second_encoded_audio_frame_size; |
40 }; | 51 }; |
41 | 52 |
42 static const MediaRecorderTestParams kMediaRecorderTestParams[] = { | 53 static const MediaRecorderTestParams kMediaRecorderTestParams[] = { |
43 {"video/vp8", 52, 32}, | 54 {true, false, "video/vp8", 52, 32, 0, 0}, |
44 {"video/vp9", 33, 18}}; | 55 {true, false, "video/vp9", 33, 18, 0, 0}, |
| 56 {false, true, "video/vp8", 0, 0, 990, 706}}; |
45 | 57 |
46 class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams>, | 58 class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams>, |
47 public blink::WebMediaRecorderHandlerClient { | 59 public blink::WebMediaRecorderHandlerClient { |
48 public: | 60 public: |
49 MediaRecorderHandlerTest() | 61 MediaRecorderHandlerTest() |
50 : media_recorder_handler_(new MediaRecorderHandler()) { | 62 : media_recorder_handler_(new MediaRecorderHandler()), |
| 63 audio_source_(kTestAudioChannels, |
| 64 440 /* freq */, |
| 65 kTestAudioSampleRate) { |
51 EXPECT_FALSE(media_recorder_handler_->recording_); | 66 EXPECT_FALSE(media_recorder_handler_->recording_); |
52 | 67 |
53 registry_.Init(kTestStreamUrl); | 68 registry_.Init(kTestStreamUrl); |
54 registry_.AddVideoTrack(kTestVideoTrackId); | |
55 } | 69 } |
56 | 70 |
57 ~MediaRecorderHandlerTest() { | 71 ~MediaRecorderHandlerTest() { |
58 registry_.reset(); | 72 registry_.reset(); |
59 blink::WebHeap::collectAllGarbageForTesting(); | 73 blink::WebHeap::collectAllGarbageForTesting(); |
60 } | 74 } |
61 | 75 |
62 MOCK_METHOD3(writeData, void(const char*, size_t, bool)); | 76 MOCK_METHOD3(writeData, void(const char*, size_t, bool)); |
63 MOCK_METHOD1(failOutOfMemory, void(const WebString& message)); | 77 MOCK_METHOD1(failOutOfMemory, void(const WebString& message)); |
64 MOCK_METHOD1(failIllegalStreamModification, void(const WebString& message)); | 78 MOCK_METHOD1(failIllegalStreamModification, void(const WebString& message)); |
65 MOCK_METHOD1(failOtherRecordingError, void(const WebString& message)); | 79 MOCK_METHOD1(failOtherRecordingError, void(const WebString& message)); |
66 | 80 |
67 bool recording() const { return media_recorder_handler_->recording_; } | 81 bool recording() const { return media_recorder_handler_->recording_; } |
68 bool hasVideoRecorders() const { | 82 bool hasVideoRecorders() const { |
69 return !media_recorder_handler_->video_recorders_.empty(); | 83 return !media_recorder_handler_->video_recorders_.empty(); |
70 } | 84 } |
| 85 bool hasAudioRecorders() const { |
| 86 return !media_recorder_handler_->audio_recorders_.empty(); |
| 87 } |
71 | 88 |
72 void OnVideoFrameForTesting(const scoped_refptr<media::VideoFrame>& frame) { | 89 void OnVideoFrameForTesting(const scoped_refptr<media::VideoFrame>& frame) { |
73 media_recorder_handler_->OnVideoFrameForTesting(frame, | 90 media_recorder_handler_->OnVideoFrameForTesting(frame, |
74 base::TimeTicks::Now()); | 91 base::TimeTicks::Now()); |
75 } | 92 } |
| 93 void OnAudioBusForTesting(const media::AudioBus& audio_bus) { |
| 94 media_recorder_handler_->OnAudioBusForTesting(audio_bus, |
| 95 base::TimeTicks::Now()); |
| 96 } |
| 97 void SetAudioFormatForTesting(const media::AudioParameters& params) { |
| 98 media_recorder_handler_->SetAudioFormatForTesting(params); |
| 99 } |
| 100 |
| 101 void AddTracks() { |
| 102 // Avoid issues with non-parameterized tests by calling this outside of ctr. |
| 103 if (GetParam().has_video) |
| 104 registry_.AddVideoTrack(kTestVideoTrackId); |
| 105 if (GetParam().has_audio) |
| 106 registry_.AddAudioTrack(kTestAudioTrackId); |
| 107 } |
| 108 |
| 109 scoped_ptr<media::AudioBus> NextAudioBus() { |
| 110 scoped_ptr<media::AudioBus> bus(media::AudioBus::Create( |
| 111 kTestAudioChannels, |
| 112 kTestAudioSampleRate * kTestAudioBufferDurationMS / 1000)); |
| 113 audio_source_.OnMoreData(bus.get(), 0); |
| 114 return bus.Pass(); |
| 115 } |
76 | 116 |
77 // A ChildProcess and a MessageLoopForUI are both needed to fool the Tracks | 117 // A ChildProcess and a MessageLoopForUI are both needed to fool the Tracks |
78 // and Sources in |registry_| into believing they are on the right threads. | 118 // and Sources in |registry_| into believing they are on the right threads. |
79 const base::MessageLoopForUI message_loop_; | 119 const base::MessageLoopForUI message_loop_; |
80 const ChildProcess child_process_; | 120 const ChildProcess child_process_; |
81 MockMediaStreamRegistry registry_; | 121 MockMediaStreamRegistry registry_; |
82 | 122 |
83 // The Class under test. Needs to be scoped_ptr to force its destruction. | 123 // The Class under test. Needs to be scoped_ptr to force its destruction. |
84 scoped_ptr<MediaRecorderHandler> media_recorder_handler_; | 124 scoped_ptr<MediaRecorderHandler> media_recorder_handler_; |
85 | 125 |
| 126 // For generating test AudioBuses |
| 127 media::SineWaveAudioSource audio_source_; |
| 128 |
86 private: | 129 private: |
87 DISALLOW_COPY_AND_ASSIGN(MediaRecorderHandlerTest); | 130 DISALLOW_COPY_AND_ASSIGN(MediaRecorderHandlerTest); |
88 }; | 131 }; |
89 | 132 |
90 // Checks that canSupportMimeType() works as expected. | 133 // Checks that canSupportMimeType() works as expected. |
91 // TODO(mcasas): revisit this when canSupportMimeType() is fully implemented. | 134 // TODO(mcasas): revisit this when canSupportMimeType() is fully implemented. |
92 TEST_F(MediaRecorderHandlerTest, CanSupportMimeType) { | 135 TEST_F(MediaRecorderHandlerTest, CanSupportMimeType) { |
93 const WebString good_mime_type_vp8(base::UTF8ToUTF16("video/vp8")); | 136 const WebString good_mime_type_vp8(base::UTF8ToUTF16("video/vp8")); |
94 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp8)); | 137 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp8)); |
95 | 138 |
96 const WebString good_mime_type_vp9(base::UTF8ToUTF16("video/vp9")); | 139 const WebString good_mime_type_vp9(base::UTF8ToUTF16("video/vp9")); |
97 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp9)); | 140 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp9)); |
98 | 141 |
| 142 const WebString audio_mime_type(base::UTF8ToUTF16("audio/opus")); |
| 143 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(audio_mime_type)); |
| 144 |
| 145 const WebString combo_mime_type(base::UTF8ToUTF16("video/vp8, audio/opus")); |
| 146 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(combo_mime_type)); |
| 147 |
| 148 const WebString bad_combo(base::UTF8ToUTF16("video/vp8, audio/unsupported")); |
| 149 EXPECT_FALSE(media_recorder_handler_->canSupportMimeType(bad_combo)); |
| 150 |
99 const WebString bad_mime_type(base::UTF8ToUTF16("video/unsupportedcodec")); | 151 const WebString bad_mime_type(base::UTF8ToUTF16("video/unsupportedcodec")); |
100 EXPECT_FALSE(media_recorder_handler_->canSupportMimeType(bad_mime_type)); | 152 EXPECT_FALSE(media_recorder_handler_->canSupportMimeType(bad_mime_type)); |
101 | 153 |
102 const WebString empty_mime_type(base::UTF8ToUTF16("")); | 154 const WebString empty_mime_type(base::UTF8ToUTF16("")); |
103 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(empty_mime_type)); | 155 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(empty_mime_type)); |
104 } | 156 } |
105 | 157 |
106 // Checks that the initialization-destruction sequence works fine. | 158 // Checks that the initialization-destruction sequence works fine. |
107 TEST_P(MediaRecorderHandlerTest, InitializeStartStop) { | 159 TEST_P(MediaRecorderHandlerTest, InitializeStartStop) { |
| 160 AddTracks(); |
108 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); | 161 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); |
109 EXPECT_TRUE(media_recorder_handler_->initialize(this, | 162 EXPECT_TRUE(media_recorder_handler_->initialize(this, |
110 registry_.test_stream(), | 163 registry_.test_stream(), |
111 mime_type)); | 164 mime_type)); |
112 EXPECT_FALSE(recording()); | 165 EXPECT_FALSE(recording()); |
113 EXPECT_FALSE(hasVideoRecorders()); | 166 EXPECT_FALSE(hasVideoRecorders()); |
| 167 EXPECT_FALSE(hasAudioRecorders()); |
114 | 168 |
115 EXPECT_TRUE(media_recorder_handler_->start()); | 169 EXPECT_TRUE(media_recorder_handler_->start()); |
116 EXPECT_TRUE(recording()); | 170 EXPECT_TRUE(recording()); |
117 EXPECT_TRUE(hasVideoRecorders()); | 171 |
| 172 EXPECT_TRUE(hasVideoRecorders() || !GetParam().has_video); |
| 173 EXPECT_TRUE(hasAudioRecorders() || !GetParam().has_audio); |
118 | 174 |
119 media_recorder_handler_->stop(); | 175 media_recorder_handler_->stop(); |
120 EXPECT_FALSE(recording()); | 176 EXPECT_FALSE(recording()); |
121 EXPECT_FALSE(hasVideoRecorders()); | 177 EXPECT_FALSE(hasVideoRecorders()); |
| 178 EXPECT_FALSE(hasAudioRecorders()); |
122 | 179 |
123 // Expect a last call on destruction. | 180 // Expect a last call on destruction. |
124 EXPECT_CALL(*this, writeData(_, _, true)).Times(1); | 181 EXPECT_CALL(*this, writeData(_, _, true)).Times(1); |
125 media_recorder_handler_.reset(); | 182 media_recorder_handler_.reset(); |
126 } | 183 } |
127 | 184 |
128 // Sends 2 frames and expect them as WebM contained encoded data in writeData(). | 185 // Sends 2 frames and expect them as WebM contained encoded data in writeData(). |
129 TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) { | 186 TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) { |
| 187 // Video-only test. |
| 188 if (GetParam().has_audio) |
| 189 return; |
| 190 |
| 191 AddTracks(); |
| 192 |
130 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); | 193 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); |
131 EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(), | 194 EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(), |
132 mime_type)); | 195 mime_type)); |
133 EXPECT_TRUE(media_recorder_handler_->start()); | 196 EXPECT_TRUE(media_recorder_handler_->start()); |
134 | 197 |
135 InSequence s; | 198 InSequence s; |
136 const scoped_refptr<media::VideoFrame> video_frame = | 199 const scoped_refptr<media::VideoFrame> video_frame = |
137 media::VideoFrame::CreateBlackFrame(gfx::Size(160, 80)); | 200 media::VideoFrame::CreateBlackFrame(gfx::Size(160, 80)); |
138 | 201 |
139 { | 202 { |
140 base::RunLoop run_loop; | 203 base::RunLoop run_loop; |
141 base::Closure quit_closure = run_loop.QuitClosure(); | 204 base::Closure quit_closure = run_loop.QuitClosure(); |
142 // writeData() is pinged a number of times as the WebM header is written; | 205 // writeData() is pinged a number of times as the WebM header is written; |
143 // the last time it is called it has the encoded data. | 206 // the last time it is called it has the encoded data. |
144 const size_t encoded_data_size = GetParam().first_encoded_frame_size; | 207 const size_t encoded_data_size = GetParam().first_encoded_video_frame_size; |
145 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) | 208 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) |
146 .Times(AtLeast(1)); | 209 .Times(AtLeast(1)); |
147 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) | 210 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) |
148 .Times(1) | 211 .Times(1) |
149 .WillOnce(RunClosure(quit_closure)); | 212 .WillOnce(RunClosure(quit_closure)); |
150 | 213 |
151 OnVideoFrameForTesting(video_frame); | 214 OnVideoFrameForTesting(video_frame); |
152 run_loop.Run(); | 215 run_loop.Run(); |
153 } | 216 } |
154 | 217 |
155 { | 218 { |
156 base::RunLoop run_loop; | 219 base::RunLoop run_loop; |
157 base::Closure quit_closure = run_loop.QuitClosure(); | 220 base::Closure quit_closure = run_loop.QuitClosure(); |
158 // The second time around writeData() is called a number of times to write | 221 // The second time around writeData() is called a number of times to write |
159 // the WebM frame header, and then is pinged with the encoded data. | 222 // the WebM frame header, and then is pinged with the encoded data. |
160 const size_t encoded_data_size = GetParam().second_encoded_frame_size; | 223 const size_t encoded_data_size = GetParam().second_encoded_video_frame_size; |
161 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) | 224 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) |
162 .Times(AtLeast(1)); | 225 .Times(AtLeast(1)); |
163 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) | 226 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) |
164 .Times(1) | 227 .Times(1) |
165 .WillOnce(RunClosure(quit_closure)); | 228 .WillOnce(RunClosure(quit_closure)); |
166 | 229 |
167 OnVideoFrameForTesting(video_frame); | 230 OnVideoFrameForTesting(video_frame); |
168 run_loop.Run(); | 231 run_loop.Run(); |
169 } | 232 } |
170 | 233 |
171 media_recorder_handler_->stop(); | 234 media_recorder_handler_->stop(); |
172 | 235 |
173 // Expect a last call on destruction, with size 0 and |lastInSlice| true. | 236 // Expect a last call on destruction, with size 0 and |lastInSlice| true. |
174 EXPECT_CALL(*this, writeData(nullptr, 0, true)).Times(1); | 237 EXPECT_CALL(*this, writeData(nullptr, 0, true)).Times(1); |
175 media_recorder_handler_.reset(); | 238 media_recorder_handler_.reset(); |
176 } | 239 } |
177 | 240 |
178 INSTANTIATE_TEST_CASE_P(, | 241 INSTANTIATE_TEST_CASE_P(, |
179 MediaRecorderHandlerTest, | 242 MediaRecorderHandlerTest, |
180 ValuesIn(kMediaRecorderTestParams)); | 243 ValuesIn(kMediaRecorderTestParams)); |
181 | 244 |
| 245 // Sends 2 frames and expect them as WebM contained encoded data in writeData(). |
| 246 TEST_P(MediaRecorderHandlerTest, EncodeAudioFrames) { |
| 247 // Audio-only test. |
| 248 if (GetParam().has_video) |
| 249 return; |
| 250 |
| 251 AddTracks(); |
| 252 |
| 253 const WebString mime_type(base::UTF8ToUTF16("audio/opus")); |
| 254 EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(), |
| 255 mime_type)); |
| 256 EXPECT_TRUE(media_recorder_handler_->start()); |
| 257 |
| 258 InSequence s; |
| 259 const scoped_ptr<media::AudioBus> audio_bus1 = NextAudioBus(); |
| 260 const scoped_ptr<media::AudioBus> audio_bus2 = NextAudioBus(); |
| 261 |
| 262 media::AudioParameters params( |
| 263 media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_STEREO, |
| 264 kTestAudioSampleRate, kTestAudioBitsPerSample, |
| 265 kTestAudioSampleRate * kTestAudioBufferDurationMS / 1000); |
| 266 SetAudioFormatForTesting(params); |
| 267 |
| 268 { |
| 269 base::RunLoop run_loop; |
| 270 base::Closure quit_closure = run_loop.QuitClosure(); |
| 271 // writeData() is pinged a number of times as the WebM header is written; |
| 272 // the last time it is called it has the encoded data. |
| 273 const size_t kEncodedDataSize = GetParam().first_encoded_audio_frame_size; |
| 274 EXPECT_CALL(*this, writeData(_, Lt(kEncodedDataSize), _)).Times(AtLeast(1)); |
| 275 EXPECT_CALL(*this, writeData(_, kEncodedDataSize, _)) |
| 276 .Times(1) |
| 277 .WillOnce(RunClosure(quit_closure)); |
| 278 |
| 279 OnAudioBusForTesting(*audio_bus1); |
| 280 run_loop.Run(); |
| 281 } |
| 282 |
| 283 { |
| 284 base::RunLoop run_loop; |
| 285 base::Closure quit_closure = run_loop.QuitClosure(); |
| 286 // The second time around writeData() is called a number of times to write |
| 287 // the WebM frame header, and then is pinged with the encoded data. |
| 288 const size_t kSecondEncodedDataSize = |
| 289 GetParam().second_encoded_audio_frame_size; |
| 290 EXPECT_CALL(*this, writeData(_, Lt(kSecondEncodedDataSize), _)) |
| 291 .Times(AtLeast(1)); |
| 292 EXPECT_CALL(*this, writeData(_, kSecondEncodedDataSize, _)) |
| 293 .Times(1) |
| 294 .WillOnce(RunClosure(quit_closure)); |
| 295 |
| 296 OnAudioBusForTesting(*audio_bus2); |
| 297 run_loop.Run(); |
| 298 } |
| 299 |
| 300 media_recorder_handler_->stop(); |
| 301 |
| 302 // Expect a last call on destruction, with size 0 and |lastInSlice| true. |
| 303 EXPECT_CALL(*this, writeData(nullptr, 0, true)).Times(1); |
| 304 media_recorder_handler_.reset(); |
| 305 } |
| 306 |
182 } // namespace content | 307 } // namespace content |
OLD | NEW |