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
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/media/audio_input_renderer_host.h"
8 #include "base/files/file.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/metrics/histogram.h"
12 #include "base/numerics/safe_math.h"
13 #include "base/process/process.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
17 #include "content/browser/media/capture/web_contents_capture_util.h"
18 #include "content/browser/media/media_internals.h"
19 #include "content/browser/media/webrtc_internals.h"
20 #include "content/browser/renderer_host/media/audio_input_debug_writer.h"
21 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
22 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
23 #include "content/browser/renderer_host/media/media_stream_manager.h"
24 #include "media/audio/audio_manager_base.h"
25 #include "media/base/audio_bus.h"
32 const base::FilePath::CharType kDebugRecordingFileNameAddition
[] =
33 FILE_PATH_LITERAL("source_input");
34 const base::FilePath::CharType kDebugRecordingFileNameExtension
[] =
35 FILE_PATH_LITERAL("pcm");
38 void LogMessage(int stream_id
, const std::string
& msg
, bool add_prefix
) {
39 std::ostringstream oss
;
40 oss
<< "[stream_id=" << stream_id
<< "] ";
44 content::MediaStreamManager::SendMessageToNativeLog(oss
.str());
45 DVLOG(1) << oss
.str();
49 base::File
CreateDebugRecordingFile(base::FilePath file_path
) {
50 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
51 base::File
recording_file(
52 file_path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
);
53 PLOG_IF(ERROR
, !recording_file
.IsValid())
54 << "Could not open debug recording file, error="
55 << recording_file
.error_details();
56 return recording_file
.Pass();
59 void CloseFile(base::File file
) {
60 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
61 // |file| must be closed and destroyed on FILE thread.
64 void DeleteInputDebugWriterOnFileThread(
65 scoped_ptr
<AudioInputDebugWriter
> writer
) {
66 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
67 // |writer| must be closed and destroyed on FILE thread.
69 #endif // ENABLE_WEBRTC
73 struct AudioInputRendererHost::AudioEntry
{
77 // The AudioInputController that manages the audio input stream.
78 scoped_refptr
<media::AudioInputController
> controller
;
80 // The audio input stream ID in the render view.
83 // Shared memory for transmission of the audio data. It has
84 // |shared_memory_segment_count| equal lengthed segments.
85 base::SharedMemory shared_memory
;
86 int shared_memory_segment_count
;
88 // The synchronous writer to be used by the controller. We have the
89 // ownership of the writer.
90 scoped_ptr
<media::AudioInputController::SyncWriter
> writer
;
92 // Must be deleted on the file thread. Must be posted for deletion and nulled
93 // before the AudioEntry is deleted.
94 scoped_ptr
<AudioInputDebugWriter
> input_debug_writer
;
96 // Set to true after we called Close() for the controller.
99 // If this entry's layout has a keyboard mic channel.
100 bool has_keyboard_mic
;
103 AudioInputRendererHost::AudioEntry::AudioEntry()
105 shared_memory_segment_count(0),
106 pending_close(false),
107 has_keyboard_mic(false) {
110 AudioInputRendererHost::AudioEntry::~AudioEntry() {
111 DCHECK(!input_debug_writer
.get());
114 AudioInputRendererHost::AudioInputRendererHost(
115 int render_process_id
,
117 media::AudioManager
* audio_manager
,
118 MediaStreamManager
* media_stream_manager
,
119 AudioMirroringManager
* audio_mirroring_manager
,
120 media::UserInputMonitor
* user_input_monitor
)
121 : BrowserMessageFilter(AudioMsgStart
),
122 render_process_id_(render_process_id
),
123 renderer_pid_(renderer_pid
),
124 audio_manager_(audio_manager
),
125 media_stream_manager_(media_stream_manager
),
126 audio_mirroring_manager_(audio_mirroring_manager
),
127 user_input_monitor_(user_input_monitor
),
128 audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
129 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER
)),
130 weak_factory_(this) {}
132 AudioInputRendererHost::~AudioInputRendererHost() {
133 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
134 DCHECK(audio_entries_
.empty());
138 void AudioInputRendererHost::EnableDebugRecording(const base::FilePath
& file
) {
139 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
140 base::FilePath file_with_extensions
=
141 GetDebugRecordingFilePathWithExtensions(file
);
142 for (const auto& entry
: audio_entries_
)
143 EnableDebugRecordingForId(file_with_extensions
, entry
.first
);
146 void AudioInputRendererHost::DisableDebugRecording() {
147 for (const auto& entry
: audio_entries_
) {
148 entry
.second
->controller
->DisableDebugRecording(
149 base::Bind(&AudioInputRendererHost::DeleteDebugWriter
,
154 #endif // ENABLE_WEBRTC
156 void AudioInputRendererHost::OnChannelClosing() {
157 // Since the IPC sender is gone, close all requested audio streams.
161 void AudioInputRendererHost::OnDestruct() const {
162 BrowserThread::DeleteOnIOThread::Destruct(this);
165 void AudioInputRendererHost::OnCreated(
166 media::AudioInputController
* controller
) {
167 BrowserThread::PostTask(
171 &AudioInputRendererHost::DoCompleteCreation
,
173 make_scoped_refptr(controller
)));
176 void AudioInputRendererHost::OnRecording(
177 media::AudioInputController
* controller
) {
178 BrowserThread::PostTask(
182 &AudioInputRendererHost::DoSendRecordingMessage
,
184 make_scoped_refptr(controller
)));
187 void AudioInputRendererHost::OnError(media::AudioInputController
* controller
,
188 media::AudioInputController::ErrorCode error_code
) {
189 BrowserThread::PostTask(
193 &AudioInputRendererHost::DoHandleError
,
195 make_scoped_refptr(controller
),
199 void AudioInputRendererHost::OnData(media::AudioInputController
* controller
,
200 const media::AudioBus
* data
) {
201 NOTREACHED() << "Only low-latency mode is supported.";
204 void AudioInputRendererHost::OnLog(media::AudioInputController
* controller
,
205 const std::string
& message
) {
206 BrowserThread::PostTask(BrowserThread::IO
,
208 base::Bind(&AudioInputRendererHost::DoLog
,
210 make_scoped_refptr(controller
),
214 void AudioInputRendererHost::set_renderer_pid(int32 renderer_pid
) {
215 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
216 renderer_pid_
= renderer_pid
;
219 void AudioInputRendererHost::DoCompleteCreation(
220 media::AudioInputController
* controller
) {
221 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
223 AudioEntry
* entry
= LookupByController(controller
);
225 NOTREACHED() << "AudioInputController is invalid.";
230 NOTREACHED() << "Renderer process handle is invalid.";
231 DeleteEntryOnError(entry
, INVALID_PEER_HANDLE
);
235 if (!entry
->controller
->SharedMemoryAndSyncSocketMode()) {
236 NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
237 DeleteEntryOnError(entry
, INVALID_LATENCY_MODE
);
241 // Once the audio stream is created then complete the creation process by
242 // mapping shared memory and sharing with the renderer process.
243 base::SharedMemoryHandle foreign_memory_handle
;
244 if (!entry
->shared_memory
.ShareToProcess(PeerHandle(),
245 &foreign_memory_handle
)) {
246 // If we failed to map and share the shared memory then close the audio
247 // stream and send an error message.
248 DeleteEntryOnError(entry
, MEMORY_SHARING_FAILED
);
252 AudioInputSyncWriter
* writer
=
253 static_cast<AudioInputSyncWriter
*>(entry
->writer
.get());
255 base::SyncSocket::TransitDescriptor socket_transit_descriptor
;
257 // If we failed to prepare the sync socket for the renderer then we fail
258 // the construction of audio input stream.
259 if (!writer
->PrepareForeignSocket(PeerHandle(), &socket_transit_descriptor
)) {
260 DeleteEntryOnError(entry
, SYNC_SOCKET_ERROR
);
264 LogMessage(entry
->stream_id
,
265 "DoCompleteCreation: IPC channel and stream are now open",
268 Send(new AudioInputMsg_NotifyStreamCreated(
269 entry
->stream_id
, foreign_memory_handle
, socket_transit_descriptor
,
270 entry
->shared_memory
.requested_size(),
271 entry
->shared_memory_segment_count
));
274 void AudioInputRendererHost::DoSendRecordingMessage(
275 media::AudioInputController
* controller
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
277 // TODO(henrika): See crbug.com/115262 for details on why this method
278 // should be implemented.
279 AudioEntry
* entry
= LookupByController(controller
);
281 NOTREACHED() << "AudioInputController is invalid.";
285 entry
->stream_id
, "DoSendRecordingMessage: stream is now started", true);
288 void AudioInputRendererHost::DoHandleError(
289 media::AudioInputController
* controller
,
290 media::AudioInputController::ErrorCode error_code
) {
291 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
292 AudioEntry
* entry
= LookupByController(controller
);
294 NOTREACHED() << "AudioInputController is invalid.";
298 // This is a fix for crbug.com/357501. The error can be triggered when closing
299 // the lid on Macs, which causes more problems than it fixes.
300 // Also, in crbug.com/357569, the goal is to remove usage of the error since
301 // it was added to solve a crash on Windows that no longer can be reproduced.
302 if (error_code
== media::AudioInputController::NO_DATA_ERROR
) {
303 // TODO(henrika): it might be possible to do something other than just
304 // logging when we detect many NO_DATA_ERROR calls for a stream.
305 LogMessage(entry
->stream_id
, "AIC::DoCheckForNoData: NO_DATA_ERROR", false);
309 std::ostringstream oss
;
310 oss
<< "AIC reports error_code=" << error_code
;
311 LogMessage(entry
->stream_id
, oss
.str(), false);
313 audio_log_
->OnError(entry
->stream_id
);
314 DeleteEntryOnError(entry
, AUDIO_INPUT_CONTROLLER_ERROR
);
317 void AudioInputRendererHost::DoLog(media::AudioInputController
* controller
,
318 const std::string
& message
) {
319 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
320 AudioEntry
* entry
= LookupByController(controller
);
322 NOTREACHED() << "AudioInputController is invalid.";
326 // Add stream ID and current audio level reported by AIC to native log.
327 LogMessage(entry
->stream_id
, message
, false);
330 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message
& message
) {
332 IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost
, message
)
333 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream
, OnCreateStream
)
334 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream
, OnRecordStream
)
335 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream
, OnCloseStream
)
336 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume
, OnSetVolume
)
337 IPC_MESSAGE_UNHANDLED(handled
= false)
338 IPC_END_MESSAGE_MAP()
343 void AudioInputRendererHost::OnCreateStream(
347 const AudioInputHostMsg_CreateStream_Config
& config
) {
348 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
350 #if defined(OS_CHROMEOS)
351 if (config
.params
.channel_layout() ==
352 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
353 media_stream_manager_
->audio_input_device_manager()
354 ->RegisterKeyboardMicStream(
355 base::Bind(&AudioInputRendererHost::DoCreateStream
, this, stream_id
,
356 render_frame_id
, session_id
, config
));
358 DoCreateStream(stream_id
, render_frame_id
, session_id
, config
);
361 DoCreateStream(stream_id
, render_frame_id
, session_id
, config
);
365 void AudioInputRendererHost::DoCreateStream(
369 const AudioInputHostMsg_CreateStream_Config
& config
) {
370 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
372 std::ostringstream oss
;
373 oss
<< "[stream_id=" << stream_id
<< "] "
374 << "AIRH::OnCreateStream(render_frame_id=" << render_frame_id
375 << ", session_id=" << session_id
<< ")";
376 DCHECK_GT(render_frame_id
, 0);
378 // media::AudioParameters is validated in the deserializer.
379 if (LookupById(stream_id
) != NULL
) {
380 SendErrorMessage(stream_id
, STREAM_ALREADY_EXISTS
);
381 MaybeUnregisterKeyboardMicStream(config
);
385 media::AudioParameters
audio_params(config
.params
);
386 if (media_stream_manager_
->audio_input_device_manager()
387 ->ShouldUseFakeDevice())
388 audio_params
.set_format(media::AudioParameters::AUDIO_FAKE
);
390 // Check if we have the permission to open the device and which device to use.
391 std::string device_name
;
392 std::string device_id
= media::AudioManagerBase::kDefaultDeviceId
;
393 if (audio_params
.format() != media::AudioParameters::AUDIO_FAKE
) {
394 const StreamDeviceInfo
* info
= media_stream_manager_
->
395 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id
);
397 SendErrorMessage(stream_id
, PERMISSION_DENIED
);
398 DLOG(WARNING
) << "No permission has been granted to input stream with "
399 << "session_id=" << session_id
;
400 MaybeUnregisterKeyboardMicStream(config
);
404 device_id
= info
->device
.id
;
405 device_name
= info
->device
.name
;
406 oss
<< ": device_name=" << device_name
;
409 // Create a new AudioEntry structure.
410 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
412 const uint32 segment_size
=
413 (sizeof(media::AudioInputBufferParameters
) +
414 media::AudioBus::CalculateMemorySize(audio_params
));
415 entry
->shared_memory_segment_count
= config
.shared_memory_count
;
417 // Create the shared memory and share it with the renderer process
418 // using a new SyncWriter object.
419 base::CheckedNumeric
<uint32
> size
= segment_size
;
420 size
*= entry
->shared_memory_segment_count
;
421 if (!size
.IsValid() ||
422 !entry
->shared_memory
.CreateAndMapAnonymous(size
.ValueOrDie())) {
423 // If creation of shared memory failed then send an error message.
424 SendErrorMessage(stream_id
, SHARED_MEMORY_CREATE_FAILED
);
425 MaybeUnregisterKeyboardMicStream(config
);
429 scoped_ptr
<AudioInputSyncWriter
> writer(new AudioInputSyncWriter(
430 entry
->shared_memory
.memory(), entry
->shared_memory
.requested_size(),
431 entry
->shared_memory_segment_count
, audio_params
));
433 if (!writer
->Init()) {
434 SendErrorMessage(stream_id
, SYNC_WRITER_INIT_FAILED
);
435 MaybeUnregisterKeyboardMicStream(config
);
439 // If we have successfully created the SyncWriter then assign it to the
440 // entry and construct an AudioInputController.
441 entry
->writer
.reset(writer
.release());
442 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id
)) {
443 entry
->controller
= media::AudioInputController::CreateForStream(
444 audio_manager_
->GetTaskRunner(),
446 WebContentsAudioInputStream::Create(
449 audio_manager_
->GetWorkerTaskRunner(),
450 audio_mirroring_manager_
),
452 user_input_monitor_
);
454 DCHECK_EQ(config
.params
.format(),
455 media::AudioParameters::AUDIO_PCM_LOW_LATENCY
);
456 entry
->controller
= media::AudioInputController::CreateLowLatency(
463 config
.automatic_gain_control
);
464 oss
<< ", AGC=" << config
.automatic_gain_control
;
467 if (!entry
->controller
.get()) {
468 SendErrorMessage(stream_id
, STREAM_CREATE_ERROR
);
469 MaybeUnregisterKeyboardMicStream(config
);
473 #if defined(OS_CHROMEOS)
474 if (config
.params
.channel_layout() ==
475 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
476 entry
->has_keyboard_mic
= true;
480 MediaStreamManager::SendMessageToNativeLog(oss
.str());
481 DVLOG(1) << oss
.str();
483 // Since the controller was created successfully, create an entry and add it
485 entry
->stream_id
= stream_id
;
486 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
487 audio_log_
->OnCreated(stream_id
, audio_params
, device_id
);
488 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
489 stream_id
, render_process_id_
, render_frame_id
, audio_log_
.get());
491 #if defined(ENABLE_WEBRTC)
492 BrowserThread::PostTask(
496 &AudioInputRendererHost::MaybeEnableDebugRecordingForId
,
502 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
503 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
504 LogMessage(stream_id
, "OnRecordStream", true);
506 AudioEntry
* entry
= LookupById(stream_id
);
508 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
512 entry
->controller
->Record();
513 audio_log_
->OnStarted(stream_id
);
516 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
517 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
518 LogMessage(stream_id
, "OnCloseStream", true);
520 AudioEntry
* entry
= LookupById(stream_id
);
523 CloseAndDeleteStream(entry
);
526 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
527 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
529 AudioEntry
* entry
= LookupById(stream_id
);
531 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
535 entry
->controller
->SetVolume(volume
);
536 audio_log_
->OnSetVolume(stream_id
, volume
);
539 void AudioInputRendererHost::SendErrorMessage(
540 int stream_id
, ErrorCode error_code
) {
541 std::string err_msg
=
542 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code
);
543 LogMessage(stream_id
, err_msg
, true);
545 Send(new AudioInputMsg_NotifyStreamStateChanged(
546 stream_id
, media::AUDIO_INPUT_IPC_DELEGATE_STATE_ERROR
));
549 void AudioInputRendererHost::DeleteEntries() {
550 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
552 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
553 i
!= audio_entries_
.end(); ++i
) {
554 CloseAndDeleteStream(i
->second
);
558 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
559 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
561 if (!entry
->pending_close
) {
562 LogMessage(entry
->stream_id
, "CloseAndDeleteStream", true);
563 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
565 entry
->pending_close
= true;
566 audio_log_
->OnClosed(entry
->stream_id
);
570 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
571 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
572 LogMessage(entry
->stream_id
, "DeleteEntry: stream is now closed", true);
574 #if defined(OS_CHROMEOS)
575 if (entry
->has_keyboard_mic
) {
576 media_stream_manager_
->audio_input_device_manager()
577 ->UnregisterKeyboardMicStream();
581 #if defined(ENABLE_WEBRTC)
582 if (entry
->input_debug_writer
.get()) {
583 BrowserThread::PostTask(
586 base::Bind(&DeleteInputDebugWriterOnFileThread
,
587 base::Passed(entry
->input_debug_writer
.Pass())));
591 // Delete the entry when this method goes out of scope.
592 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
594 // Erase the entry from the map.
595 audio_entries_
.erase(entry
->stream_id
);
598 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
,
599 ErrorCode error_code
) {
600 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
602 // Sends the error message first before we close the stream because
603 // |entry| is destroyed in DeleteEntry().
604 SendErrorMessage(entry
->stream_id
, error_code
);
605 CloseAndDeleteStream(entry
);
608 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
610 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
612 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
613 if (i
!= audio_entries_
.end())
618 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
619 media::AudioInputController
* controller
) {
620 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
622 // Iterate the map of entries.
623 // TODO(hclam): Implement a faster look up method.
624 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
625 i
!= audio_entries_
.end(); ++i
) {
626 if (controller
== i
->second
->controller
.get())
632 void AudioInputRendererHost::MaybeUnregisterKeyboardMicStream(
633 const AudioInputHostMsg_CreateStream_Config
& config
) {
634 #if defined(OS_CHROMEOS)
635 if (config
.params
.channel_layout() ==
636 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
637 media_stream_manager_
->audio_input_device_manager()
638 ->UnregisterKeyboardMicStream();
643 #if defined(ENABLE_WEBRTC)
644 void AudioInputRendererHost::MaybeEnableDebugRecordingForId(int stream_id
) {
645 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
646 if (WebRTCInternals::GetInstance()->IsAudioDebugRecordingsEnabled()) {
647 BrowserThread::PostTask(
651 &AudioInputRendererHost::EnableDebugRecordingForId
,
653 GetDebugRecordingFilePathWithExtensions(
654 WebRTCInternals::GetInstance()->
655 GetAudioDebugRecordingsFilePath()),
661 #define IntToStringType base::IntToString16
663 #define IntToStringType base::IntToString
666 base::FilePath
AudioInputRendererHost::GetDebugRecordingFilePathWithExtensions(
667 const base::FilePath
& file
) {
668 return file
.AddExtension(IntToStringType(renderer_pid_
))
669 .AddExtension(kDebugRecordingFileNameAddition
);
672 void AudioInputRendererHost::EnableDebugRecordingForId(
673 const base::FilePath
& file
,
675 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
676 BrowserThread::PostTaskAndReplyWithResult(
680 &CreateDebugRecordingFile
,
681 file
.AddExtension(IntToStringType(stream_id
))
682 .AddExtension(kDebugRecordingFileNameExtension
)),
684 &AudioInputRendererHost::DoEnableDebugRecording
,
685 weak_factory_
.GetWeakPtr(),
689 #undef IntToStringType
691 void AudioInputRendererHost::DoEnableDebugRecording(
694 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
697 AudioEntry
* entry
= LookupById(stream_id
);
699 BrowserThread::PostTask(
704 Passed(file
.Pass())));
707 entry
->input_debug_writer
.reset(new AudioInputDebugWriter(file
.Pass()));
708 entry
->controller
->EnableDebugRecording(entry
->input_debug_writer
.get());
711 void AudioInputRendererHost::DeleteDebugWriter(int stream_id
) {
712 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
713 AudioEntry
* entry
= LookupById(stream_id
);
715 // This happens if DisableDebugRecording is called right after
720 if (entry
->input_debug_writer
.get()) {
721 BrowserThread::PostTask(
724 base::Bind(&DeleteInputDebugWriterOnFileThread
,
725 base::Passed(entry
->input_debug_writer
.Pass())));
728 #endif // defined(ENABLE_WEBRTC)
730 } // namespace content