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
, entry
->shared_memory_segment_count
, audio_params
));
432 if (!writer
->Init()) {
433 SendErrorMessage(stream_id
, SYNC_WRITER_INIT_FAILED
);
434 MaybeUnregisterKeyboardMicStream(config
);
438 // If we have successfully created the SyncWriter then assign it to the
439 // entry and construct an AudioInputController.
440 entry
->writer
.reset(writer
.release());
441 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id
)) {
442 entry
->controller
= media::AudioInputController::CreateForStream(
443 audio_manager_
->GetTaskRunner(),
445 WebContentsAudioInputStream::Create(
448 audio_manager_
->GetWorkerTaskRunner(),
449 audio_mirroring_manager_
),
451 user_input_monitor_
);
453 DCHECK_EQ(config
.params
.format(),
454 media::AudioParameters::AUDIO_PCM_LOW_LATENCY
);
455 entry
->controller
= media::AudioInputController::CreateLowLatency(
462 config
.automatic_gain_control
);
463 oss
<< ", AGC=" << config
.automatic_gain_control
;
466 if (!entry
->controller
.get()) {
467 SendErrorMessage(stream_id
, STREAM_CREATE_ERROR
);
468 MaybeUnregisterKeyboardMicStream(config
);
472 #if defined(OS_CHROMEOS)
473 if (config
.params
.channel_layout() ==
474 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
475 entry
->has_keyboard_mic
= true;
479 MediaStreamManager::SendMessageToNativeLog(oss
.str());
480 DVLOG(1) << oss
.str();
482 // Since the controller was created successfully, create an entry and add it
484 entry
->stream_id
= stream_id
;
485 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
486 audio_log_
->OnCreated(stream_id
, audio_params
, device_id
);
487 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
488 stream_id
, render_process_id_
, render_frame_id
, audio_log_
.get());
490 #if defined(ENABLE_WEBRTC)
491 BrowserThread::PostTask(
495 &AudioInputRendererHost::MaybeEnableDebugRecordingForId
,
501 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
502 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
503 LogMessage(stream_id
, "OnRecordStream", true);
505 AudioEntry
* entry
= LookupById(stream_id
);
507 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
511 entry
->controller
->Record();
512 audio_log_
->OnStarted(stream_id
);
515 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
516 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
517 LogMessage(stream_id
, "OnCloseStream", true);
519 AudioEntry
* entry
= LookupById(stream_id
);
522 CloseAndDeleteStream(entry
);
525 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
526 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
528 AudioEntry
* entry
= LookupById(stream_id
);
530 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
534 entry
->controller
->SetVolume(volume
);
535 audio_log_
->OnSetVolume(stream_id
, volume
);
538 void AudioInputRendererHost::SendErrorMessage(
539 int stream_id
, ErrorCode error_code
) {
540 std::string err_msg
=
541 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code
);
542 LogMessage(stream_id
, err_msg
, true);
544 Send(new AudioInputMsg_NotifyStreamStateChanged(
545 stream_id
, media::AUDIO_INPUT_IPC_DELEGATE_STATE_ERROR
));
548 void AudioInputRendererHost::DeleteEntries() {
549 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
551 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
552 i
!= audio_entries_
.end(); ++i
) {
553 CloseAndDeleteStream(i
->second
);
557 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
558 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
560 if (!entry
->pending_close
) {
561 LogMessage(entry
->stream_id
, "CloseAndDeleteStream", true);
562 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
564 entry
->pending_close
= true;
565 audio_log_
->OnClosed(entry
->stream_id
);
569 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
570 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
571 LogMessage(entry
->stream_id
, "DeleteEntry: stream is now closed", true);
573 #if defined(OS_CHROMEOS)
574 if (entry
->has_keyboard_mic
) {
575 media_stream_manager_
->audio_input_device_manager()
576 ->UnregisterKeyboardMicStream();
580 #if defined(ENABLE_WEBRTC)
581 if (entry
->input_debug_writer
.get()) {
582 BrowserThread::PostTask(
585 base::Bind(&DeleteInputDebugWriterOnFileThread
,
586 base::Passed(entry
->input_debug_writer
.Pass())));
590 // Delete the entry when this method goes out of scope.
591 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
593 // Erase the entry from the map.
594 audio_entries_
.erase(entry
->stream_id
);
597 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
,
598 ErrorCode error_code
) {
599 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
601 // Sends the error message first before we close the stream because
602 // |entry| is destroyed in DeleteEntry().
603 SendErrorMessage(entry
->stream_id
, error_code
);
604 CloseAndDeleteStream(entry
);
607 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
609 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
611 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
612 if (i
!= audio_entries_
.end())
617 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
618 media::AudioInputController
* controller
) {
619 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
621 // Iterate the map of entries.
622 // TODO(hclam): Implement a faster look up method.
623 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
624 i
!= audio_entries_
.end(); ++i
) {
625 if (controller
== i
->second
->controller
.get())
631 void AudioInputRendererHost::MaybeUnregisterKeyboardMicStream(
632 const AudioInputHostMsg_CreateStream_Config
& config
) {
633 #if defined(OS_CHROMEOS)
634 if (config
.params
.channel_layout() ==
635 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
636 media_stream_manager_
->audio_input_device_manager()
637 ->UnregisterKeyboardMicStream();
642 #if defined(ENABLE_WEBRTC)
643 void AudioInputRendererHost::MaybeEnableDebugRecordingForId(int stream_id
) {
644 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
645 if (WebRTCInternals::GetInstance()->IsAudioDebugRecordingsEnabled()) {
646 BrowserThread::PostTask(
650 &AudioInputRendererHost::EnableDebugRecordingForId
,
652 GetDebugRecordingFilePathWithExtensions(
653 WebRTCInternals::GetInstance()->
654 GetAudioDebugRecordingsFilePath()),
660 #define IntToStringType base::IntToString16
662 #define IntToStringType base::IntToString
665 base::FilePath
AudioInputRendererHost::GetDebugRecordingFilePathWithExtensions(
666 const base::FilePath
& file
) {
667 return file
.AddExtension(IntToStringType(renderer_pid_
))
668 .AddExtension(kDebugRecordingFileNameAddition
);
671 void AudioInputRendererHost::EnableDebugRecordingForId(
672 const base::FilePath
& file
,
674 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
675 BrowserThread::PostTaskAndReplyWithResult(
679 &CreateDebugRecordingFile
,
680 file
.AddExtension(IntToStringType(stream_id
))
681 .AddExtension(kDebugRecordingFileNameExtension
)),
683 &AudioInputRendererHost::DoEnableDebugRecording
,
684 weak_factory_
.GetWeakPtr(),
688 #undef IntToStringType
690 void AudioInputRendererHost::DoEnableDebugRecording(
693 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
696 AudioEntry
* entry
= LookupById(stream_id
);
698 BrowserThread::PostTask(
703 Passed(file
.Pass())));
706 entry
->input_debug_writer
.reset(new AudioInputDebugWriter(file
.Pass()));
707 entry
->controller
->EnableDebugRecording(entry
->input_debug_writer
.get());
710 void AudioInputRendererHost::DeleteDebugWriter(int stream_id
) {
711 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
712 AudioEntry
* entry
= LookupById(stream_id
);
714 // This happens if DisableDebugRecording is called right after
719 if (entry
->input_debug_writer
.get()) {
720 BrowserThread::PostTask(
723 base::Bind(&DeleteInputDebugWriterOnFileThread
,
724 base::Passed(entry
->input_debug_writer
.Pass())));
727 #endif // defined(ENABLE_WEBRTC)
729 } // namespace content