Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(419)

Side by Side Diff: content/browser/renderer_host/media/audio_input_renderer_host.cc

Issue 1272223003: Implement writing mic audio input data to file for debugging purposes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "content/browser/renderer_host/media/audio_input_renderer_host.h" 5 #include "content/browser/renderer_host/media/audio_input_renderer_host.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/files/file.h"
9 #include "base/memory/ref_counted.h"
8 #include "base/memory/shared_memory.h" 10 #include "base/memory/shared_memory.h"
9 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
10 #include "base/numerics/safe_math.h" 12 #include "base/numerics/safe_math.h"
11 #include "base/process/process.h" 13 #include "base/process/process.h"
14 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h" 15 #include "base/strings/stringprintf.h"
13 #include "content/browser/media/capture/web_contents_audio_input_stream.h" 16 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
14 #include "content/browser/media/capture/web_contents_capture_util.h" 17 #include "content/browser/media/capture/web_contents_capture_util.h"
15 #include "content/browser/media/media_internals.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"
16 #include "content/browser/renderer_host/media/audio_input_device_manager.h" 21 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
17 #include "content/browser/renderer_host/media/audio_input_sync_writer.h" 22 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h" 23 #include "content/browser/renderer_host/media/media_stream_manager.h"
24 #include "content/public/browser/render_process_host.h"
19 #include "media/audio/audio_manager_base.h" 25 #include "media/audio/audio_manager_base.h"
20 #include "media/base/audio_bus.h" 26 #include "media/base/audio_bus.h"
21 27
28 namespace content {
29
22 namespace { 30 namespace {
23 31
32 const char kDebugRecordingFileNameAddition[] = "source_input";
33 const char kDebugRecordingFileNameExtension[] = "pcm";
34
24 void LogMessage(int stream_id, const std::string& msg, bool add_prefix) { 35 void LogMessage(int stream_id, const std::string& msg, bool add_prefix) {
25 std::ostringstream oss; 36 std::ostringstream oss;
26 oss << "[stream_id=" << stream_id << "] "; 37 oss << "[stream_id=" << stream_id << "] ";
27 if (add_prefix) 38 if (add_prefix)
28 oss << "AIRH::"; 39 oss << "AIRH::";
29 oss << msg; 40 oss << msg;
30 content::MediaStreamManager::SendMessageToNativeLog(oss.str()); 41 content::MediaStreamManager::SendMessageToNativeLog(oss.str());
31 DVLOG(1) << oss.str(); 42 DVLOG(1) << oss.str();
32 } 43 }
33 44
45 base::File CreateDebugRecordingFile(base::FilePath file_path) {
46 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
47 base::File recording_file(
48 file_path,
tommi (sloooow) - chröme 2015/08/07 12:09:51 not sure about the indent here... 4 spaces from th
Henrik Grunell 2015/08/17 15:05:17 Indeed. Creative indenting. :) Fixed.
49 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
50 if (!recording_file.IsValid()) {
51 VLOG(1) << "Could not open debug recording file, error=" <<
tommi (sloooow) - chröme 2015/08/07 12:09:51 nit: could use [P]LOG_IF(ERROR, !recording_file.Is
Henrik Grunell 2015/08/17 15:05:16 Done.
52 recording_file.error_details();
53 }
54 return recording_file.Pass();
55 }
56
57 void CloseFile(base::File* file) {
tommi (sloooow) - chröme 2015/08/07 12:09:51 just wondering - could ownership be passed via mov
Henrik Grunell 2015/08/17 15:05:17 Done.
58 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
59 // |file| must be closed and destroyed on FILE thread.
60 delete file;
61 }
62
63 void DeleteInputDebugWriterOnFileThread(AudioInputDebugWriter* writer) {
tommi (sloooow) - chröme 2015/08/07 12:09:51 same here... wondering if we could use scoped_ptr<
Henrik Grunell 2015/08/17 15:05:17 Done.
64 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
65 delete writer;
66 }
67
34 } // namespace 68 } // namespace
35 69
36 namespace content {
37
38 struct AudioInputRendererHost::AudioEntry { 70 struct AudioInputRendererHost::AudioEntry {
39 AudioEntry(); 71 AudioEntry();
40 ~AudioEntry(); 72 ~AudioEntry();
41 73
42 // The AudioInputController that manages the audio input stream. 74 // The AudioInputController that manages the audio input stream.
43 scoped_refptr<media::AudioInputController> controller; 75 scoped_refptr<media::AudioInputController> controller;
44 76
45 // The audio input stream ID in the render view. 77 // The audio input stream ID in the render view.
46 int stream_id; 78 int stream_id;
47 79
48 // Shared memory for transmission of the audio data. It has 80 // Shared memory for transmission of the audio data. It has
49 // |shared_memory_segment_count| equal lengthed segments. 81 // |shared_memory_segment_count| equal lengthed segments.
50 base::SharedMemory shared_memory; 82 base::SharedMemory shared_memory;
51 int shared_memory_segment_count; 83 int shared_memory_segment_count;
52 84
53 // The synchronous writer to be used by the controller. We have the 85 // The synchronous writer to be used by the controller. We have the
54 // ownership of the writer. 86 // ownership of the writer.
55 scoped_ptr<media::AudioInputController::SyncWriter> writer; 87 scoped_ptr<media::AudioInputController::SyncWriter> writer;
56 88
89 // Keep a raw pointer since it must be deleted on the file thread. Must be
90 // posted for deletion and nulled before the AudioEntry is deleted.
91 AudioInputDebugWriter* input_debug_writer;
tommi (sloooow) - chröme 2015/08/07 12:09:51 I think you could still use a scoped_ptr even thou
Henrik Grunell 2015/08/17 15:05:17 I think that would signal that it's OK if it's del
tommi (sloooow) - chröme 2015/08/19 11:32:21 By that logic, it sounds like not deleting it shou
Henrik Grunell 2015/08/19 19:57:15 Hm, well, not sure I agree. :) But maybe "scoped_p
92
57 // Set to true after we called Close() for the controller. 93 // Set to true after we called Close() for the controller.
58 bool pending_close; 94 bool pending_close;
59 95
60 // If this entry's layout has a keyboard mic channel. 96 // If this entry's layout has a keyboard mic channel.
61 bool has_keyboard_mic_; 97 bool has_keyboard_mic;
62 }; 98 };
63 99
64 AudioInputRendererHost::AudioEntry::AudioEntry() 100 AudioInputRendererHost::AudioEntry::AudioEntry()
65 : stream_id(0), 101 : stream_id(0),
66 shared_memory_segment_count(0), 102 shared_memory_segment_count(0),
103 input_debug_writer(nullptr),
67 pending_close(false), 104 pending_close(false),
68 has_keyboard_mic_(false) { 105 has_keyboard_mic(false) {
69 } 106 }
70 107
71 AudioInputRendererHost::AudioEntry::~AudioEntry() {} 108 AudioInputRendererHost::AudioEntry::~AudioEntry() {
109 DCHECK(!input_debug_writer);
110 }
72 111
73 AudioInputRendererHost::AudioInputRendererHost( 112 AudioInputRendererHost::AudioInputRendererHost(
74 int render_process_id, 113 int render_process_id,
114 RenderProcessHost* render_process_host,
75 media::AudioManager* audio_manager, 115 media::AudioManager* audio_manager,
76 MediaStreamManager* media_stream_manager, 116 MediaStreamManager* media_stream_manager,
77 AudioMirroringManager* audio_mirroring_manager, 117 AudioMirroringManager* audio_mirroring_manager,
78 media::UserInputMonitor* user_input_monitor) 118 media::UserInputMonitor* user_input_monitor)
79 : BrowserMessageFilter(AudioMsgStart), 119 : BrowserMessageFilter(AudioMsgStart),
80 render_process_id_(render_process_id), 120 render_process_id_(render_process_id),
121 render_process_host_(render_process_host),
81 audio_manager_(audio_manager), 122 audio_manager_(audio_manager),
82 media_stream_manager_(media_stream_manager), 123 media_stream_manager_(media_stream_manager),
83 audio_mirroring_manager_(audio_mirroring_manager), 124 audio_mirroring_manager_(audio_mirroring_manager),
84 user_input_monitor_(user_input_monitor), 125 user_input_monitor_(user_input_monitor),
85 audio_log_(MediaInternals::GetInstance()->CreateAudioLog( 126 audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
86 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)) {} 127 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)),
128 weak_factory_(this) {}
87 129
88 AudioInputRendererHost::~AudioInputRendererHost() { 130 AudioInputRendererHost::~AudioInputRendererHost() {
89 DCHECK_CURRENTLY_ON(BrowserThread::IO); 131 DCHECK_CURRENTLY_ON(BrowserThread::IO);
90 DCHECK(audio_entries_.empty()); 132 DCHECK(audio_entries_.empty());
91 } 133 }
92 134
135 void AudioInputRendererHost::EnableDebugRecording(const base::FilePath& file) {
136 DCHECK_CURRENTLY_ON(BrowserThread::IO);
137 base::FilePath file_with_extensions =
138 GetDebugRecordingFilePathWithExtensions(file);
139 for (AudioEntryMap::iterator i = audio_entries_.begin();
tommi (sloooow) - chröme 2015/08/07 12:09:51 for (const auto& entry : audio_entries_) Enabled
Henrik Grunell 2015/08/17 15:05:17 Done.
140 i != audio_entries_.end(); ++i) {
141 EnabledDebugRecordingForId(file_with_extensions, i->first);
142 }
143 }
144
145 void AudioInputRendererHost::DisableDebugRecording() {
146 for (AudioEntryMap::iterator it = audio_entries_.begin();
tommi (sloooow) - chröme 2015/08/07 12:09:51 could use a range based loop here as well
Henrik Grunell 2015/08/17 15:05:17 Done.
147 it != audio_entries_.end(); ++it) {
148 it->second->controller->DisableDebugRecording(
149 base::Bind(&AudioInputRendererHost::DeleteDebugWriter,
150 this,
151 it->first));
152 }
153 }
154
93 void AudioInputRendererHost::OnChannelClosing() { 155 void AudioInputRendererHost::OnChannelClosing() {
94 // Since the IPC sender is gone, close all requested audio streams. 156 // Since the IPC sender is gone, close all requested audio streams.
95 DeleteEntries(); 157 DeleteEntries();
96 } 158 }
97 159
98 void AudioInputRendererHost::OnDestruct() const { 160 void AudioInputRendererHost::OnDestruct() const {
99 BrowserThread::DeleteOnIOThread::Destruct(this); 161 BrowserThread::DeleteOnIOThread::Destruct(this);
100 } 162 }
101 163
102 void AudioInputRendererHost::OnCreated( 164 void AudioInputRendererHost::OnCreated(
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 464
403 if (!entry->controller.get()) { 465 if (!entry->controller.get()) {
404 SendErrorMessage(stream_id, STREAM_CREATE_ERROR); 466 SendErrorMessage(stream_id, STREAM_CREATE_ERROR);
405 MaybeUnregisterKeyboardMicStream(config); 467 MaybeUnregisterKeyboardMicStream(config);
406 return; 468 return;
407 } 469 }
408 470
409 #if defined(OS_CHROMEOS) 471 #if defined(OS_CHROMEOS)
410 if (config.params.channel_layout() == 472 if (config.params.channel_layout() ==
411 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { 473 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
412 entry->has_keyboard_mic_ = true; 474 entry->has_keyboard_mic = true;
413 } 475 }
414 #endif 476 #endif
415 477
416 MediaStreamManager::SendMessageToNativeLog(oss.str()); 478 MediaStreamManager::SendMessageToNativeLog(oss.str());
417 DVLOG(1) << oss.str(); 479 DVLOG(1) << oss.str();
418 480
419 // Since the controller was created successfully, create an entry and add it 481 // Since the controller was created successfully, create an entry and add it
420 // to the map. 482 // to the map.
421 entry->stream_id = stream_id; 483 entry->stream_id = stream_id;
422 audio_entries_.insert(std::make_pair(stream_id, entry.release())); 484 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
423 audio_log_->OnCreated(stream_id, audio_params, device_id); 485 audio_log_->OnCreated(stream_id, audio_params, device_id);
424 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry( 486 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
425 stream_id, render_process_id_, render_frame_id, audio_log_.get()); 487 stream_id, render_process_id_, render_frame_id, audio_log_.get());
488
489 if (WebRTCInternals::GetInstance()->audio_debug_recordings_enabled()) {
490 AudioInputRendererHost::EnabledDebugRecordingForId(
491 GetDebugRecordingFilePathWithExtensions(
492 WebRTCInternals::GetInstance()->audio_debug_recordings_file_path()),
493 stream_id);
494 }
426 } 495 }
427 496
428 void AudioInputRendererHost::OnRecordStream(int stream_id) { 497 void AudioInputRendererHost::OnRecordStream(int stream_id) {
429 DCHECK_CURRENTLY_ON(BrowserThread::IO); 498 DCHECK_CURRENTLY_ON(BrowserThread::IO);
430 LogMessage(stream_id, "OnRecordStream", true); 499 LogMessage(stream_id, "OnRecordStream", true);
431 500
432 AudioEntry* entry = LookupById(stream_id); 501 AudioEntry* entry = LookupById(stream_id);
433 if (!entry) { 502 if (!entry) {
434 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY); 503 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
435 return; 504 return;
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 entry->pending_close = true; 560 entry->pending_close = true;
492 audio_log_->OnClosed(entry->stream_id); 561 audio_log_->OnClosed(entry->stream_id);
493 } 562 }
494 } 563 }
495 564
496 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) { 565 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
497 DCHECK_CURRENTLY_ON(BrowserThread::IO); 566 DCHECK_CURRENTLY_ON(BrowserThread::IO);
498 LogMessage(entry->stream_id, "DeleteEntry: stream is now closed", true); 567 LogMessage(entry->stream_id, "DeleteEntry: stream is now closed", true);
499 568
500 #if defined(OS_CHROMEOS) 569 #if defined(OS_CHROMEOS)
501 if (entry->has_keyboard_mic_) { 570 if (entry->has_keyboard_mic) {
502 media_stream_manager_->audio_input_device_manager() 571 media_stream_manager_->audio_input_device_manager()
503 ->UnregisterKeyboardMicStream(); 572 ->UnregisterKeyboardMicStream();
504 } 573 }
505 #endif 574 #endif
506 575
576 if (entry->input_debug_writer) {
577 BrowserThread::PostTask(
578 BrowserThread::FILE,
579 FROM_HERE,
580 base::Bind(
581 &DeleteInputDebugWriterOnFileThread,
582 entry->input_debug_writer));
tommi (sloooow) - chröme 2015/08/07 12:09:51 would be nice to do input_debug_writer.Pass() :)
Henrik Grunell 2015/08/17 15:05:17 Why not? Made it move-only. :) EDIT: Argh, can't
583 entry->input_debug_writer = nullptr;
584 }
585
507 // Delete the entry when this method goes out of scope. 586 // Delete the entry when this method goes out of scope.
508 scoped_ptr<AudioEntry> entry_deleter(entry); 587 scoped_ptr<AudioEntry> entry_deleter(entry);
509 588
510 // Erase the entry from the map. 589 // Erase the entry from the map.
511 audio_entries_.erase(entry->stream_id); 590 audio_entries_.erase(entry->stream_id);
512 } 591 }
513 592
514 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry, 593 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry,
515 ErrorCode error_code) { 594 ErrorCode error_code) {
516 DCHECK_CURRENTLY_ON(BrowserThread::IO); 595 DCHECK_CURRENTLY_ON(BrowserThread::IO);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 const AudioInputHostMsg_CreateStream_Config& config) { 628 const AudioInputHostMsg_CreateStream_Config& config) {
550 #if defined(OS_CHROMEOS) 629 #if defined(OS_CHROMEOS)
551 if (config.params.channel_layout() == 630 if (config.params.channel_layout() ==
552 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { 631 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
553 media_stream_manager_->audio_input_device_manager() 632 media_stream_manager_->audio_input_device_manager()
554 ->UnregisterKeyboardMicStream(); 633 ->UnregisterKeyboardMicStream();
555 } 634 }
556 #endif 635 #endif
557 } 636 }
558 637
638 #if defined(OS_WIN)
639 #define IntToStringType base::IntToString16
640 #else
641 #define IntToStringType base::IntToString
642 #endif
643
644 base::FilePath AudioInputRendererHost::GetDebugRecordingFilePathWithExtensions(
645 const base::FilePath& file) {
646 return file.AddExtension(kDebugRecordingFileNameAddition).AddExtension(
647 IntToStringType(base::GetProcId(render_process_host_->GetHandle())));
648 }
649
650 void AudioInputRendererHost::EnabledDebugRecordingForId(
651 const base::FilePath& file,
652 int stream_id) {
653 DCHECK_CURRENTLY_ON(BrowserThread::IO);
654 BrowserThread::PostTaskAndReplyWithResult(
655 BrowserThread::FILE,
656 FROM_HERE,
657 base::Bind(
658 &CreateDebugRecordingFile,
659 file.AddExtension(IntToStringType(stream_id))
660 .AddExtension(
661 FILE_PATH_LITERAL(kDebugRecordingFileNameExtension))),
662 base::Bind(
663 &AudioInputRendererHost::DoEnableDebugRecording,
664 weak_factory_.GetWeakPtr(),
665 stream_id));
666 }
667
668 #undef IntToStringType
669
670 void AudioInputRendererHost::DoEnableDebugRecording(
671 int stream_id,
672 base::File file) {
673 DCHECK_CURRENTLY_ON(BrowserThread::IO);
674 if (!file.IsValid())
675 return;
tommi (sloooow) - chröme 2015/08/07 12:09:51 should we dcheck here or is this an expected case?
Henrik Grunell 2015/08/17 15:05:17 It could happen if the user selects a folder witho
676 AudioEntry* entry = LookupById(stream_id);
677 if (!entry) {
678 BrowserThread::PostTask(
679 BrowserThread::FILE,
680 FROM_HERE,
681 base::Bind(
682 &CloseFile,
683 base::Unretained(new base::File(file.Pass()))));
tommi (sloooow) - chröme 2015/08/07 12:09:51 is there no way we could just do file.Pass() here?
Henrik Grunell 2015/08/17 15:05:17 Done.
684 return;
685 }
686 entry->input_debug_writer = new AudioInputDebugWriter(file.Pass());
687 entry->controller->EnableDebugRecording(entry->input_debug_writer);
688 }
689
690 void AudioInputRendererHost::DeleteDebugWriter(int stream_id) {
691 DCHECK_CURRENTLY_ON(BrowserThread::IO);
692 AudioEntry* entry = LookupById(stream_id);
693 if (!entry) {
694 // This happens if DisableDebugRecording is called right after
695 // DeleteEntries.
696 return;
697 }
698
699 if (entry->input_debug_writer) {
700 BrowserThread::PostTask(
701 BrowserThread::FILE,
702 FROM_HERE,
703 base::Bind(
704 &DeleteInputDebugWriterOnFileThread,
705 entry->input_debug_writer));
706 entry->input_debug_writer = nullptr;
707 }
708 }
709
559 } // namespace content 710 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698