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