Chromium Code Reviews| 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); | 69 if (GetParam().has_video) |
| 70 registry_.AddVideoTrack(kTestVideoTrackId); | |
| 71 if (GetParam().has_audio) | |
| 72 registry_.AddAudioTrack(kTestAudioTrackId); | |
| 55 } | 73 } |
| 56 | 74 |
| 57 ~MediaRecorderHandlerTest() { | 75 ~MediaRecorderHandlerTest() { |
| 58 registry_.reset(); | 76 registry_.reset(); |
| 59 blink::WebHeap::collectAllGarbageForTesting(); | 77 blink::WebHeap::collectAllGarbageForTesting(); |
| 60 } | 78 } |
| 61 | 79 |
| 62 MOCK_METHOD3(writeData, void(const char*, size_t, bool)); | 80 MOCK_METHOD3(writeData, void(const char*, size_t, bool)); |
| 63 MOCK_METHOD1(failOutOfMemory, void(const WebString& message)); | 81 MOCK_METHOD1(failOutOfMemory, void(const WebString& message)); |
| 64 MOCK_METHOD1(failIllegalStreamModification, void(const WebString& message)); | 82 MOCK_METHOD1(failIllegalStreamModification, void(const WebString& message)); |
| 65 MOCK_METHOD1(failOtherRecordingError, void(const WebString& message)); | 83 MOCK_METHOD1(failOtherRecordingError, void(const WebString& message)); |
| 66 | 84 |
| 67 bool recording() const { return media_recorder_handler_->recording_; } | 85 bool recording() const { return media_recorder_handler_->recording_; } |
| 68 bool hasVideoRecorders() const { | 86 bool hasVideoRecorders() const { |
| 69 return !media_recorder_handler_->video_recorders_.empty(); | 87 return !media_recorder_handler_->video_recorders_.empty(); |
| 70 } | 88 } |
| 89 bool hasAudioRecorders() const { | |
| 90 return !media_recorder_handler_->audio_recorders_.empty(); | |
| 91 } | |
| 71 | 92 |
| 72 void OnVideoFrameForTesting(const scoped_refptr<media::VideoFrame>& frame) { | 93 void OnVideoFrameForTesting(const scoped_refptr<media::VideoFrame>& frame) { |
| 73 media_recorder_handler_->OnVideoFrameForTesting(frame, | 94 media_recorder_handler_->OnVideoFrameForTesting(frame, |
| 74 base::TimeTicks::Now()); | 95 base::TimeTicks::Now()); |
| 75 } | 96 } |
| 97 void OnAudioBusForTesting(const media::AudioBus& audio_bus) { | |
| 98 media_recorder_handler_->OnAudioBusForTesting(audio_bus, | |
| 99 base::TimeTicks::Now()); | |
| 100 } | |
| 101 void SetAudioFormatForTesting(const media::AudioParameters& params) { | |
| 102 media_recorder_handler_->SetAudioFormatForTesting(params); | |
| 103 } | |
| 104 | |
| 105 scoped_ptr<media::AudioBus> NextAudioBus() { | |
| 106 scoped_ptr<media::AudioBus> bus(media::AudioBus::Create( | |
| 107 kTestAudioChannels, | |
| 108 kTestAudioSampleRate * kTestAudioBufferDurationMS / 1000)); | |
| 109 audio_source_.OnMoreData(bus.get(), 0); | |
| 110 return bus.Pass(); | |
| 111 } | |
| 76 | 112 |
| 77 // A ChildProcess and a MessageLoopForUI are both needed to fool the Tracks | 113 // 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. | 114 // and Sources in |registry_| into believing they are on the right threads. |
| 79 const base::MessageLoopForUI message_loop_; | 115 const base::MessageLoopForUI message_loop_; |
| 80 const ChildProcess child_process_; | 116 const ChildProcess child_process_; |
| 81 MockMediaStreamRegistry registry_; | 117 MockMediaStreamRegistry registry_; |
| 82 | 118 |
| 83 // The Class under test. Needs to be scoped_ptr to force its destruction. | 119 // The Class under test. Needs to be scoped_ptr to force its destruction. |
| 84 scoped_ptr<MediaRecorderHandler> media_recorder_handler_; | 120 scoped_ptr<MediaRecorderHandler> media_recorder_handler_; |
| 85 | 121 |
| 122 // For generating test AudioBuses | |
| 123 media::SineWaveAudioSource audio_source_; | |
| 124 | |
| 86 private: | 125 private: |
| 87 DISALLOW_COPY_AND_ASSIGN(MediaRecorderHandlerTest); | 126 DISALLOW_COPY_AND_ASSIGN(MediaRecorderHandlerTest); |
| 88 }; | 127 }; |
| 89 | 128 |
| 90 // Checks that canSupportMimeType() works as expected. | 129 // Checks that canSupportMimeType() works as expected. |
| 91 // TODO(mcasas): revisit this when canSupportMimeType() is fully implemented. | 130 // TODO(mcasas): revisit this when canSupportMimeType() is fully implemented. |
| 92 TEST_F(MediaRecorderHandlerTest, CanSupportMimeType) { | 131 TEST_P(MediaRecorderHandlerTest, CanSupportMimeType) { |
|
mcasas
2015/11/19 00:49:52
Hmm this should not be parameterised, right?
Keep
ajose
2015/11/19 22:17:19
Done.
| |
| 93 const WebString good_mime_type_vp8(base::UTF8ToUTF16("video/vp8")); | 132 const WebString good_mime_type_vp8(base::UTF8ToUTF16("video/vp8")); |
| 94 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp8)); | 133 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp8)); |
| 95 | 134 |
| 96 const WebString good_mime_type_vp9(base::UTF8ToUTF16("video/vp9")); | 135 const WebString good_mime_type_vp9(base::UTF8ToUTF16("video/vp9")); |
| 97 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp9)); | 136 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(good_mime_type_vp9)); |
| 98 | 137 |
| 138 const WebString audio_mime_type(base::UTF8ToUTF16("audio/opus")); | |
| 139 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(audio_mime_type)); | |
| 140 | |
| 99 const WebString bad_mime_type(base::UTF8ToUTF16("video/unsupportedcodec")); | 141 const WebString bad_mime_type(base::UTF8ToUTF16("video/unsupportedcodec")); |
| 100 EXPECT_FALSE(media_recorder_handler_->canSupportMimeType(bad_mime_type)); | 142 EXPECT_FALSE(media_recorder_handler_->canSupportMimeType(bad_mime_type)); |
| 101 | 143 |
| 102 const WebString empty_mime_type(base::UTF8ToUTF16("")); | 144 const WebString empty_mime_type(base::UTF8ToUTF16("")); |
| 103 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(empty_mime_type)); | 145 EXPECT_TRUE(media_recorder_handler_->canSupportMimeType(empty_mime_type)); |
| 104 } | 146 } |
|
mcasas
2015/11/19 00:49:52
Guess this test should at least try an OK combinat
ajose
2015/11/19 22:17:19
Done.
| |
| 105 | 147 |
| 106 // Checks that the initialization-destruction sequence works fine. | 148 // Checks that the initialization-destruction sequence works fine. |
| 107 TEST_P(MediaRecorderHandlerTest, InitializeStartStop) { | 149 TEST_P(MediaRecorderHandlerTest, InitializeStartStop) { |
| 108 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); | 150 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); |
| 109 EXPECT_TRUE(media_recorder_handler_->initialize(this, | 151 EXPECT_TRUE(media_recorder_handler_->initialize(this, |
| 110 registry_.test_stream(), | 152 registry_.test_stream(), |
| 111 mime_type)); | 153 mime_type)); |
| 112 EXPECT_FALSE(recording()); | 154 EXPECT_FALSE(recording()); |
| 113 EXPECT_FALSE(hasVideoRecorders()); | 155 EXPECT_FALSE(hasVideoRecorders()); |
| 156 EXPECT_FALSE(hasAudioRecorders()); | |
| 114 | 157 |
| 115 EXPECT_TRUE(media_recorder_handler_->start()); | 158 EXPECT_TRUE(media_recorder_handler_->start()); |
| 116 EXPECT_TRUE(recording()); | 159 EXPECT_TRUE(recording()); |
| 117 EXPECT_TRUE(hasVideoRecorders()); | 160 |
| 161 if (GetParam().has_video) | |
| 162 EXPECT_TRUE(hasVideoRecorders()); | |
| 163 else | |
| 164 EXPECT_FALSE(hasVideoRecorders()); | |
|
mcasas
2015/11/19 00:49:52
EXPECT_TRUE(hasVideoRecorders() || !GetParam().has
ajose
2015/11/19 22:17:19
Done.
| |
| 165 | |
| 166 if (GetParam().has_audio) | |
| 167 EXPECT_TRUE(hasAudioRecorders()); | |
| 168 else | |
| 169 EXPECT_FALSE(hasAudioRecorders()); | |
| 118 | 170 |
| 119 media_recorder_handler_->stop(); | 171 media_recorder_handler_->stop(); |
| 120 EXPECT_FALSE(recording()); | 172 EXPECT_FALSE(recording()); |
| 121 EXPECT_FALSE(hasVideoRecorders()); | 173 EXPECT_FALSE(hasVideoRecorders()); |
| 174 EXPECT_FALSE(hasAudioRecorders()); | |
| 122 | 175 |
| 123 // Expect a last call on destruction. | 176 // Expect a last call on destruction. |
| 124 EXPECT_CALL(*this, writeData(_, _, true)).Times(1); | 177 EXPECT_CALL(*this, writeData(_, _, true)).Times(1); |
| 125 media_recorder_handler_.reset(); | 178 media_recorder_handler_.reset(); |
| 126 } | 179 } |
| 127 | 180 |
| 128 // Sends 2 frames and expect them as WebM contained encoded data in writeData(). | 181 // Sends 2 frames and expect them as WebM contained encoded data in writeData(). |
| 129 TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) { | 182 TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) { |
| 183 // Video-only test. | |
| 184 if (GetParam().has_audio) { | |
|
mcasas
2015/11/19 00:49:52
no {}
ajose
2015/11/19 22:17:19
Done.
| |
| 185 return; | |
| 186 } | |
| 187 | |
| 130 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); | 188 const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type)); |
| 131 EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(), | 189 EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(), |
| 132 mime_type)); | 190 mime_type)); |
| 133 EXPECT_TRUE(media_recorder_handler_->start()); | 191 EXPECT_TRUE(media_recorder_handler_->start()); |
| 134 | 192 |
| 135 InSequence s; | 193 InSequence s; |
| 136 const scoped_refptr<media::VideoFrame> video_frame = | 194 const scoped_refptr<media::VideoFrame> video_frame = |
| 137 media::VideoFrame::CreateBlackFrame(gfx::Size(160, 80)); | 195 media::VideoFrame::CreateBlackFrame(gfx::Size(160, 80)); |
| 138 | 196 |
| 139 { | 197 { |
| 140 base::RunLoop run_loop; | 198 base::RunLoop run_loop; |
| 141 base::Closure quit_closure = run_loop.QuitClosure(); | 199 base::Closure quit_closure = run_loop.QuitClosure(); |
| 142 // writeData() is pinged a number of times as the WebM header is written; | 200 // 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. | 201 // the last time it is called it has the encoded data. |
| 144 const size_t encoded_data_size = GetParam().first_encoded_frame_size; | 202 const size_t encoded_data_size = GetParam().first_encoded_video_frame_size; |
| 145 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) | 203 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) |
| 146 .Times(AtLeast(1)); | 204 .Times(AtLeast(1)); |
| 147 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) | 205 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) |
| 148 .Times(1) | 206 .Times(1) |
| 149 .WillOnce(RunClosure(quit_closure)); | 207 .WillOnce(RunClosure(quit_closure)); |
| 150 | 208 |
| 151 OnVideoFrameForTesting(video_frame); | 209 OnVideoFrameForTesting(video_frame); |
| 152 run_loop.Run(); | 210 run_loop.Run(); |
| 153 } | 211 } |
| 154 | 212 |
| 155 { | 213 { |
| 156 base::RunLoop run_loop; | 214 base::RunLoop run_loop; |
| 157 base::Closure quit_closure = run_loop.QuitClosure(); | 215 base::Closure quit_closure = run_loop.QuitClosure(); |
| 158 // The second time around writeData() is called a number of times to write | 216 // 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. | 217 // the WebM frame header, and then is pinged with the encoded data. |
| 160 const size_t encoded_data_size = GetParam().second_encoded_frame_size; | 218 const size_t encoded_data_size = GetParam().second_encoded_video_frame_size; |
| 161 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) | 219 EXPECT_CALL(*this, writeData(_, Lt(encoded_data_size), _)) |
| 162 .Times(AtLeast(1)); | 220 .Times(AtLeast(1)); |
| 163 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) | 221 EXPECT_CALL(*this, writeData(_, encoded_data_size, _)) |
| 164 .Times(1) | 222 .Times(1) |
| 165 .WillOnce(RunClosure(quit_closure)); | 223 .WillOnce(RunClosure(quit_closure)); |
| 166 | 224 |
| 167 OnVideoFrameForTesting(video_frame); | 225 OnVideoFrameForTesting(video_frame); |
| 168 run_loop.Run(); | 226 run_loop.Run(); |
| 169 } | 227 } |
| 170 | 228 |
| 171 media_recorder_handler_->stop(); | 229 media_recorder_handler_->stop(); |
| 172 | 230 |
| 173 // Expect a last call on destruction, with size 0 and |lastInSlice| true. | 231 // Expect a last call on destruction, with size 0 and |lastInSlice| true. |
| 174 EXPECT_CALL(*this, writeData(nullptr, 0, true)).Times(1); | 232 EXPECT_CALL(*this, writeData(nullptr, 0, true)).Times(1); |
| 175 media_recorder_handler_.reset(); | 233 media_recorder_handler_.reset(); |
| 176 } | 234 } |
| 177 | 235 |
| 178 INSTANTIATE_TEST_CASE_P(, | 236 INSTANTIATE_TEST_CASE_P(, |
| 179 MediaRecorderHandlerTest, | 237 MediaRecorderHandlerTest, |
| 180 ValuesIn(kMediaRecorderTestParams)); | 238 ValuesIn(kMediaRecorderTestParams)); |
| 181 | 239 |
| 240 // Sends 2 frames and expect them as WebM contained encoded data in writeData(). | |
| 241 TEST_P(MediaRecorderHandlerTest, EncodeAudioFrames) { | |
| 242 // Audio-only test. | |
| 243 if (GetParam().has_video) { | |
| 244 return; | |
| 245 } | |
|
mcasas
2015/11/19 00:49:52
no {}
ajose
2015/11/19 22:17:19
Done.
| |
| 246 | |
| 247 const WebString mime_type(base::UTF8ToUTF16("audio/opus")); | |
| 248 EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(), | |
| 249 mime_type)); | |
| 250 EXPECT_TRUE(media_recorder_handler_->start()); | |
| 251 | |
| 252 InSequence s; | |
| 253 const scoped_ptr<media::AudioBus> audio_bus1 = NextAudioBus(); | |
| 254 const scoped_ptr<media::AudioBus> audio_bus2 = NextAudioBus(); | |
| 255 | |
| 256 media::AudioParameters params( | |
| 257 media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_STEREO, | |
| 258 kTestAudioSampleRate, kTestAudioBitsPerSample, | |
| 259 kTestAudioSampleRate * kTestAudioBufferDurationMS / 1000); | |
| 260 SetAudioFormatForTesting(params); | |
| 261 | |
| 262 { | |
| 263 base::RunLoop run_loop; | |
| 264 base::Closure quit_closure = run_loop.QuitClosure(); | |
| 265 // writeData() is pinged a number of times as the WebM header is written; | |
| 266 // the last time it is called it has the encoded data. | |
| 267 const size_t kEncodedDataSize = GetParam().first_encoded_audio_frame_size; | |
| 268 EXPECT_CALL(*this, writeData(_, Lt(kEncodedDataSize), _)).Times(AtLeast(1)); | |
| 269 EXPECT_CALL(*this, writeData(_, kEncodedDataSize, _)) | |
| 270 .Times(1) | |
| 271 .WillOnce(RunClosure(quit_closure)); | |
| 272 | |
| 273 OnAudioBusForTesting(*audio_bus1); | |
| 274 run_loop.Run(); | |
| 275 } | |
| 276 | |
| 277 { | |
| 278 base::RunLoop run_loop; | |
| 279 base::Closure quit_closure = run_loop.QuitClosure(); | |
| 280 // The second time around writeData() is called a number of times to write | |
| 281 // the WebM frame header, and then is pinged with the encoded data. | |
| 282 const size_t kSecondEncodedDataSize = | |
| 283 GetParam().second_encoded_audio_frame_size; | |
| 284 EXPECT_CALL(*this, writeData(_, Lt(kSecondEncodedDataSize), _)) | |
| 285 .Times(AtLeast(1)); | |
| 286 EXPECT_CALL(*this, writeData(_, kSecondEncodedDataSize, _)) | |
| 287 .Times(1) | |
| 288 .WillOnce(RunClosure(quit_closure)); | |
| 289 | |
| 290 OnAudioBusForTesting(*audio_bus2); | |
| 291 run_loop.Run(); | |
| 292 } | |
| 293 | |
| 294 media_recorder_handler_->stop(); | |
| 295 | |
| 296 // Expect a last call on destruction, with size 0 and |lastInSlice| true. | |
| 297 EXPECT_CALL(*this, writeData(nullptr, 0, true)).Times(1); | |
| 298 media_recorder_handler_.reset(); | |
| 299 } | |
| 300 | |
| 182 } // namespace content | 301 } // namespace content |
| OLD | NEW |