| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <windows.h> | 5 #include <windows.h> |
| 6 #include <mmsystem.h> | 6 #include <mmsystem.h> |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include "base/environment.h" | 10 #include "base/environment.h" |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 Close(); | 243 Close(); |
| 244 stream_ = new_stream; | 244 stream_ = new_stream; |
| 245 } | 245 } |
| 246 | 246 |
| 247 private: | 247 private: |
| 248 AudioInputStream* stream_; | 248 AudioInputStream* stream_; |
| 249 | 249 |
| 250 DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream); | 250 DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream); |
| 251 }; | 251 }; |
| 252 | 252 |
| 253 class WinAudioInputTest : public ::testing::Test { | |
| 254 public: | |
| 255 WinAudioInputTest() { | |
| 256 audio_manager_ = | |
| 257 AudioManager::CreateForTesting(message_loop_.task_runner()); | |
| 258 message_loop_.RunUntilIdle(); | |
| 259 } | |
| 260 ~WinAudioInputTest() override { | |
| 261 audio_manager_.reset(); | |
| 262 message_loop_.RunUntilIdle(); | |
| 263 } | |
| 264 | |
| 265 protected: | |
| 266 base::MessageLoop message_loop_; | |
| 267 ScopedAudioManagerPtr audio_manager_; | |
| 268 }; | |
| 269 | |
| 270 // Verify that we can retrieve the current hardware/mixing sample rate | 253 // Verify that we can retrieve the current hardware/mixing sample rate |
| 271 // for all available input devices. | 254 // for all available input devices. |
| 272 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { | 255 TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { |
| 273 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 256 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 257 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 274 | 258 |
| 275 // Retrieve a list of all available input devices. | 259 // Retrieve a list of all available input devices. |
| 276 media::AudioDeviceNames device_names; | 260 media::AudioDeviceNames device_names; |
| 277 audio_manager_->GetAudioInputDeviceNames(&device_names); | 261 audio_manager->GetAudioInputDeviceNames(&device_names); |
| 278 | 262 |
| 279 // Scan all available input devices and repeat the same test for all of them. | 263 // Scan all available input devices and repeat the same test for all of them. |
| 280 for (media::AudioDeviceNames::const_iterator it = device_names.begin(); | 264 for (media::AudioDeviceNames::const_iterator it = device_names.begin(); |
| 281 it != device_names.end(); ++it) { | 265 it != device_names.end(); ++it) { |
| 282 // Retrieve the hardware sample rate given a specified audio input device. | 266 // Retrieve the hardware sample rate given a specified audio input device. |
| 283 AudioParameters params; | 267 AudioParameters params; |
| 284 ASSERT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( | 268 ASSERT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( |
| 285 it->unique_id, false, ¶ms))); | 269 it->unique_id, false, ¶ms))); |
| 286 EXPECT_GE(params.sample_rate(), 0); | 270 EXPECT_GE(params.sample_rate(), 0); |
| 287 } | 271 } |
| 288 } | 272 } |
| 289 | 273 |
| 290 // Test Create(), Close() calling sequence. | 274 // Test Create(), Close() calling sequence. |
| 291 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { | 275 TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { |
| 292 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 276 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 277 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 293 ScopedAudioInputStream ais( | 278 ScopedAudioInputStream ais( |
| 294 CreateDefaultAudioInputStream(audio_manager_.get())); | 279 CreateDefaultAudioInputStream(audio_manager.get())); |
| 295 ais.Close(); | 280 ais.Close(); |
| 296 } | 281 } |
| 297 | 282 |
| 298 // Test Open(), Close() calling sequence. | 283 // Test Open(), Close() calling sequence. |
| 299 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { | 284 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { |
| 300 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 285 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 286 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 301 ScopedAudioInputStream ais( | 287 ScopedAudioInputStream ais( |
| 302 CreateDefaultAudioInputStream(audio_manager_.get())); | 288 CreateDefaultAudioInputStream(audio_manager.get())); |
| 303 EXPECT_TRUE(ais->Open()); | 289 EXPECT_TRUE(ais->Open()); |
| 304 ais.Close(); | 290 ais.Close(); |
| 305 } | 291 } |
| 306 | 292 |
| 307 // Test Open(), Start(), Close() calling sequence. | 293 // Test Open(), Start(), Close() calling sequence. |
| 308 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { | 294 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { |
| 309 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 295 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 296 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 310 ScopedAudioInputStream ais( | 297 ScopedAudioInputStream ais( |
| 311 CreateDefaultAudioInputStream(audio_manager_.get())); | 298 CreateDefaultAudioInputStream(audio_manager.get())); |
| 312 EXPECT_TRUE(ais->Open()); | 299 EXPECT_TRUE(ais->Open()); |
| 313 MockAudioInputCallback sink; | 300 MockAudioInputCallback sink; |
| 314 ais->Start(&sink); | 301 ais->Start(&sink); |
| 315 ais.Close(); | 302 ais.Close(); |
| 316 } | 303 } |
| 317 | 304 |
| 318 // Test Open(), Start(), Stop(), Close() calling sequence. | 305 // Test Open(), Start(), Stop(), Close() calling sequence. |
| 319 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { | 306 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { |
| 320 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 307 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 308 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 321 ScopedAudioInputStream ais( | 309 ScopedAudioInputStream ais( |
| 322 CreateDefaultAudioInputStream(audio_manager_.get())); | 310 CreateDefaultAudioInputStream(audio_manager.get())); |
| 323 EXPECT_TRUE(ais->Open()); | 311 EXPECT_TRUE(ais->Open()); |
| 324 MockAudioInputCallback sink; | 312 MockAudioInputCallback sink; |
| 325 ais->Start(&sink); | 313 ais->Start(&sink); |
| 326 ais->Stop(); | 314 ais->Stop(); |
| 327 ais.Close(); | 315 ais.Close(); |
| 328 } | 316 } |
| 329 | 317 |
| 330 // Test some additional calling sequences. | 318 // Test some additional calling sequences. |
| 331 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { | 319 TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { |
| 332 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 320 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 321 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 333 ScopedAudioInputStream ais( | 322 ScopedAudioInputStream ais( |
| 334 CreateDefaultAudioInputStream(audio_manager_.get())); | 323 CreateDefaultAudioInputStream(audio_manager.get())); |
| 335 WASAPIAudioInputStream* wais = | 324 WASAPIAudioInputStream* wais = |
| 336 static_cast<WASAPIAudioInputStream*>(ais.get()); | 325 static_cast<WASAPIAudioInputStream*>(ais.get()); |
| 337 | 326 |
| 338 // Open(), Open() should fail the second time. | 327 // Open(), Open() should fail the second time. |
| 339 EXPECT_TRUE(ais->Open()); | 328 EXPECT_TRUE(ais->Open()); |
| 340 EXPECT_FALSE(ais->Open()); | 329 EXPECT_FALSE(ais->Open()); |
| 341 | 330 |
| 342 MockAudioInputCallback sink; | 331 MockAudioInputCallback sink; |
| 343 | 332 |
| 344 // Start(), Start() is a valid calling sequence (second call does nothing). | 333 // Start(), Start() is a valid calling sequence (second call does nothing). |
| 345 ais->Start(&sink); | 334 ais->Start(&sink); |
| 346 EXPECT_TRUE(wais->started()); | 335 EXPECT_TRUE(wais->started()); |
| 347 ais->Start(&sink); | 336 ais->Start(&sink); |
| 348 EXPECT_TRUE(wais->started()); | 337 EXPECT_TRUE(wais->started()); |
| 349 | 338 |
| 350 // Stop(), Stop() is a valid calling sequence (second call does nothing). | 339 // Stop(), Stop() is a valid calling sequence (second call does nothing). |
| 351 ais->Stop(); | 340 ais->Stop(); |
| 352 EXPECT_FALSE(wais->started()); | 341 EXPECT_FALSE(wais->started()); |
| 353 ais->Stop(); | 342 ais->Stop(); |
| 354 EXPECT_FALSE(wais->started()); | 343 EXPECT_FALSE(wais->started()); |
| 355 ais.Close(); | 344 ais.Close(); |
| 356 } | 345 } |
| 357 | 346 |
| 358 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { | 347 TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { |
| 359 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 348 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 349 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 360 | 350 |
| 361 int count = 0; | 351 int count = 0; |
| 352 base::MessageLoopForUI loop; |
| 362 | 353 |
| 363 // 10 ms packet size. | 354 // 10 ms packet size. |
| 364 | 355 |
| 365 // Create default WASAPI input stream which records in stereo using | 356 // Create default WASAPI input stream which records in stereo using |
| 366 // the shared mixing rate. The default buffer size is 10ms. | 357 // the shared mixing rate. The default buffer size is 10ms. |
| 367 AudioInputStreamWrapper aisw(audio_manager_.get()); | 358 AudioInputStreamWrapper aisw(audio_manager.get()); |
| 368 ScopedAudioInputStream ais(aisw.Create()); | 359 ScopedAudioInputStream ais(aisw.Create()); |
| 369 EXPECT_TRUE(ais->Open()); | 360 EXPECT_TRUE(ais->Open()); |
| 370 | 361 |
| 371 MockAudioInputCallback sink; | 362 MockAudioInputCallback sink; |
| 372 | 363 |
| 373 // Derive the expected size in bytes of each recorded packet. | 364 // Derive the expected size in bytes of each recorded packet. |
| 374 uint32_t bytes_per_packet = | 365 uint32_t bytes_per_packet = |
| 375 aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8); | 366 aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8); |
| 376 | 367 |
| 377 // We use 10ms packets and will run the test until ten packets are received. | 368 // We use 10ms packets and will run the test until ten packets are received. |
| 378 // All should contain valid packets of the same size and a valid delay | 369 // All should contain valid packets of the same size and a valid delay |
| 379 // estimate. | 370 // estimate. |
| 380 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) | 371 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) |
| 381 .Times(AtLeast(10)) | 372 .Times(AtLeast(10)) |
| 382 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &message_loop_)); | 373 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); |
| 383 ais->Start(&sink); | 374 ais->Start(&sink); |
| 384 message_loop_.Run(); | 375 loop.Run(); |
| 385 ais->Stop(); | 376 ais->Stop(); |
| 386 | 377 |
| 387 // Store current packet size (to be used in the subsequent tests). | 378 // Store current packet size (to be used in the subsequent tests). |
| 388 int frames_per_buffer_10ms = aisw.frames_per_buffer(); | 379 int frames_per_buffer_10ms = aisw.frames_per_buffer(); |
| 389 | 380 |
| 390 ais.Close(); | 381 ais.Close(); |
| 391 | 382 |
| 392 // 20 ms packet size. | 383 // 20 ms packet size. |
| 393 | 384 |
| 394 count = 0; | 385 count = 0; |
| 395 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms)); | 386 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms)); |
| 396 EXPECT_TRUE(ais->Open()); | 387 EXPECT_TRUE(ais->Open()); |
| 397 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * | 388 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * |
| 398 (aisw.bits_per_sample() / 8); | 389 (aisw.bits_per_sample() / 8); |
| 399 | 390 |
| 400 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) | 391 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) |
| 401 .Times(AtLeast(10)) | 392 .Times(AtLeast(10)) |
| 402 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &message_loop_)); | 393 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); |
| 403 ais->Start(&sink); | 394 ais->Start(&sink); |
| 404 message_loop_.Run(); | 395 loop.Run(); |
| 405 ais->Stop(); | 396 ais->Stop(); |
| 406 ais.Close(); | 397 ais.Close(); |
| 407 | 398 |
| 408 // 5 ms packet size. | 399 // 5 ms packet size. |
| 409 | 400 |
| 410 count = 0; | 401 count = 0; |
| 411 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2)); | 402 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2)); |
| 412 EXPECT_TRUE(ais->Open()); | 403 EXPECT_TRUE(ais->Open()); |
| 413 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * | 404 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * |
| 414 (aisw.bits_per_sample() / 8); | 405 (aisw.bits_per_sample() / 8); |
| 415 | 406 |
| 416 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) | 407 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) |
| 417 .Times(AtLeast(10)) | 408 .Times(AtLeast(10)) |
| 418 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &message_loop_)); | 409 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); |
| 419 ais->Start(&sink); | 410 ais->Start(&sink); |
| 420 message_loop_.Run(); | 411 loop.Run(); |
| 421 ais->Stop(); | 412 ais->Stop(); |
| 422 ais.Close(); | 413 ais.Close(); |
| 423 } | 414 } |
| 424 | 415 |
| 425 // Test that we can capture a stream in loopback. | 416 // Test that we can capture a stream in loopback. |
| 426 TEST_F(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { | 417 TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { |
| 427 ABORT_AUDIO_TEST_IF_NOT(audio_manager_->HasAudioOutputDevices() && | 418 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 419 ABORT_AUDIO_TEST_IF_NOT(audio_manager->HasAudioOutputDevices() && |
| 428 CoreAudioUtil::IsSupported()); | 420 CoreAudioUtil::IsSupported()); |
| 429 | 421 |
| 430 AudioParameters params = audio_manager_->GetInputStreamParameters( | 422 AudioParameters params = audio_manager->GetInputStreamParameters( |
| 431 AudioManagerBase::kLoopbackInputDeviceId); | 423 AudioManagerBase::kLoopbackInputDeviceId); |
| 432 EXPECT_EQ(params.effects(), 0); | 424 EXPECT_EQ(params.effects(), 0); |
| 433 | 425 |
| 434 AudioParameters output_params = | 426 AudioParameters output_params = |
| 435 audio_manager_->GetOutputStreamParameters(std::string()); | 427 audio_manager->GetOutputStreamParameters(std::string()); |
| 436 EXPECT_EQ(params.sample_rate(), output_params.sample_rate()); | 428 EXPECT_EQ(params.sample_rate(), output_params.sample_rate()); |
| 437 EXPECT_EQ(params.channel_layout(), output_params.channel_layout()); | 429 EXPECT_EQ(params.channel_layout(), output_params.channel_layout()); |
| 438 | 430 |
| 439 ScopedAudioInputStream stream(audio_manager_->MakeAudioInputStream( | 431 ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream( |
| 440 params, AudioManagerBase::kLoopbackInputDeviceId)); | 432 params, AudioManagerBase::kLoopbackInputDeviceId)); |
| 441 ASSERT_TRUE(stream->Open()); | 433 ASSERT_TRUE(stream->Open()); |
| 442 FakeAudioInputCallback sink; | 434 FakeAudioInputCallback sink; |
| 443 stream->Start(&sink); | 435 stream->Start(&sink); |
| 444 ASSERT_FALSE(sink.error()); | 436 ASSERT_FALSE(sink.error()); |
| 445 | 437 |
| 446 sink.WaitForData(); | 438 sink.WaitForData(); |
| 447 stream.Close(); | 439 stream.Close(); |
| 448 | 440 |
| 449 EXPECT_GT(sink.num_received_audio_frames(), 0); | 441 EXPECT_GT(sink.num_received_audio_frames(), 0); |
| 450 EXPECT_FALSE(sink.error()); | 442 EXPECT_FALSE(sink.error()); |
| 451 } | 443 } |
| 452 | 444 |
| 453 // This test is intended for manual tests and should only be enabled | 445 // This test is intended for manual tests and should only be enabled |
| 454 // when it is required to store the captured data on a local file. | 446 // when it is required to store the captured data on a local file. |
| 455 // By default, GTest will print out YOU HAVE 1 DISABLED TEST. | 447 // By default, GTest will print out YOU HAVE 1 DISABLED TEST. |
| 456 // To include disabled tests in test execution, just invoke the test program | 448 // To include disabled tests in test execution, just invoke the test program |
| 457 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS | 449 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS |
| 458 // environment variable to a value greater than 0. | 450 // environment variable to a value greater than 0. |
| 459 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { | 451 TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { |
| 460 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 452 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); |
| 453 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); |
| 461 | 454 |
| 462 // Name of the output PCM file containing captured data. The output file | 455 // Name of the output PCM file containing captured data. The output file |
| 463 // will be stored in the directory containing 'media_unittests.exe'. | 456 // will be stored in the directory containing 'media_unittests.exe'. |
| 464 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. | 457 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. |
| 465 const char* file_name = "out_stereo_10sec.pcm"; | 458 const char* file_name = "out_stereo_10sec.pcm"; |
| 466 | 459 |
| 467 AudioInputStreamWrapper aisw(audio_manager_.get()); | 460 AudioInputStreamWrapper aisw(audio_manager.get()); |
| 468 ScopedAudioInputStream ais(aisw.Create()); | 461 ScopedAudioInputStream ais(aisw.Create()); |
| 469 EXPECT_TRUE(ais->Open()); | 462 EXPECT_TRUE(ais->Open()); |
| 470 | 463 |
| 471 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; | 464 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; |
| 472 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); | 465 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); |
| 473 VLOG(0) << ">> Speak into the default microphone while recording."; | 466 VLOG(0) << ">> Speak into the default microphone while recording."; |
| 474 ais->Start(&file_sink); | 467 ais->Start(&file_sink); |
| 475 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); | 468 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); |
| 476 ais->Stop(); | 469 ais->Stop(); |
| 477 VLOG(0) << ">> Recording has stopped."; | 470 VLOG(0) << ">> Recording has stopped."; |
| 478 ais.Close(); | 471 ais.Close(); |
| 479 } | 472 } |
| 480 | 473 |
| 481 } // namespace media | 474 } // namespace media |
| OLD | NEW |