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

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

Issue 2390153006: Audio input debug recording refactoring to reduce thread hops and simplify object ownership (Closed)
Patch Set: Created 4 years, 2 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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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_debug_writer.h" 5 #include "content/browser/renderer_host/media/audio_input_debug_writer.h"
6 #include <stdint.h> 6 #include <stdint.h>
7 #include <array> 7 #include <array>
8 #include <utility> 8 #include <utility>
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h"
10 #include "base/sys_byteorder.h" 11 #include "base/sys_byteorder.h"
11 #include "content/public/browser/browser_thread.h" 12 #include "content/public/browser/browser_thread.h"
12 #include "media/base/audio_bus.h" 13 #include "media/base/audio_bus.h"
13 14
14 namespace content { 15 namespace content {
15 16
16 namespace { 17 namespace {
17 18
18 // Windows WAVE format header 19 // Windows WAVE format header
19 // Byte order: Little-endian 20 // Byte order: Little-endian
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 writer.WriteLE32(sample_rate); 121 writer.WriteLE32(sample_rate);
121 writer.WriteLE32(byte_rate); 122 writer.WriteLE32(byte_rate);
122 writer.WriteLE16(block_align); 123 writer.WriteLE16(block_align);
123 writer.WriteLE16(kBytesPerSample * 8); 124 writer.WriteLE16(kBytesPerSample * 8);
124 writer.Write(kData); 125 writer.Write(kData);
125 writer.WriteLE32(bytes_in_payload); 126 writer.WriteLE32(bytes_in_payload);
126 } 127 }
127 128
128 } // namespace 129 } // namespace
129 130
130 AudioInputDebugWriter::AudioInputDebugWriter( 131 class AudioInputDebugWriter::AudioFileWriter {
131 base::File file, 132 public:
133 AudioFileWriter(const base::FilePath& file_name,
134 const media::AudioParameters& params);
135
136 // Must be called on FILE thread.
137 ~AudioFileWriter();
138
139 // Write data from |data| to file. Called on the FILE thread.
140 void Write(const media::AudioBus* data);
141
142 // Must be called on FILE thread.
143 void Close();
144
145 private:
146 // Write wave header to file. Called on the FILE thread twice: on construction
147 // of AudioFileWriter size of the wave data is unknown, so the header is
148 // written with zero sizes; then on destruction it is re-written with the
149 // actual size info accumulated throughout the object lifetime.
150 void WriteHeader();
151
152 void CreateRecordingFile(const base::FilePath& file_name);
153
154 // The file to write to.
155 base::File file_;
156
157 // Number of written samples.
158 uint64_t samples_;
159
160 // Input audio parameters required to build wave header.
161 const media::AudioParameters params_;
162
163 // Intermediate buffer to be written to file. Interleaved 16 bit audio data.
164 std::unique_ptr<int16_t[]> interleaved_data_;
165 int interleaved_data_size_;
Guido Urdaneta 2016/10/10 10:09:24 nit: should this be size_t?
o1ka 2016/10/10 13:48:01 We get the value by multiplying two ints (AudioBus
Guido Urdaneta 2016/10/10 14:53:22 leave as int then
166 };
167
168 AudioInputDebugWriter::AudioFileWriter::AudioFileWriter(
169 const base::FilePath& file_name,
132 const media::AudioParameters& params) 170 const media::AudioParameters& params)
133 : file_(std::move(file)), 171 : samples_(0), params_(params), interleaved_data_size_(0) {
134 samples_(0),
135 params_(params),
136 interleaved_data_size_(0),
137 weak_factory_(this) {
138 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); 172 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8);
173 // base::Unretained is safe, because destructor is called on FILE thread.
Guido Urdaneta 2016/10/10 10:09:24 nit: It would be good to clarify that all other op
o1ka 2016/10/10 13:48:01 Well... Actually it will :) Fixing it.
139 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 174 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
140 base::Bind(&AudioInputDebugWriter::WriteHeader, 175 base::Bind(&AudioFileWriter::CreateRecordingFile,
141 weak_factory_.GetWeakPtr())); 176 base::Unretained(this), file_name));
142 } 177 }
143 178
144 AudioInputDebugWriter::~AudioInputDebugWriter() { 179 AudioInputDebugWriter::AudioFileWriter::~AudioFileWriter() {
145 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 180 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
146 WriteHeader();
147 } 181 }
148 182
149 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) { 183 void AudioInputDebugWriter::AudioFileWriter::Write(
150 BrowserThread::PostTask( 184 const media::AudioBus* data) {
151 BrowserThread::FILE, 185 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
152 FROM_HERE, 186 if (!file_.IsValid())
153 base::Bind(&AudioInputDebugWriter::DoWrite, 187 return;
154 weak_factory_.GetWeakPtr(),
155 base::Passed(&data)));
156 }
157 188
158 void AudioInputDebugWriter::DoWrite(std::unique_ptr<media::AudioBus> data) {
159 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
160 // Convert to 16 bit audio and write to file. 189 // Convert to 16 bit audio and write to file.
161 int data_size = data->frames() * data->channels(); 190 int data_size = data->frames() * data->channels();
162 if (!interleaved_data_ || interleaved_data_size_ < data_size) { 191 if (!interleaved_data_ || interleaved_data_size_ < data_size) {
163 interleaved_data_.reset(new int16_t[data_size]); 192 interleaved_data_.reset(new int16_t[data_size]);
164 interleaved_data_size_ = data_size; 193 interleaved_data_size_ = data_size;
165 } 194 }
166 samples_ += data_size; 195 samples_ += data_size;
167 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), 196 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]),
168 interleaved_data_.get()); 197 interleaved_data_.get());
169 198
170 #ifndef ARCH_CPU_LITTLE_ENDIAN 199 #ifndef ARCH_CPU_LITTLE_ENDIAN
171 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), 200 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t),
172 "Only 2 bytes per channel is supported."); 201 "Only 2 bytes per channel is supported.");
173 for (int i = 0; i < data_size; ++i) 202 for (int i = 0; i < data_size; ++i)
174 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); 203 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]);
175 #endif 204 #endif
176 205
177 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), 206 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()),
178 data_size * sizeof(interleaved_data_[0])); 207 data_size * sizeof(interleaved_data_[0]));
179 } 208 }
180 209
181 // This method is called twice: on construction of AudioInputDebugWriter size of 210 void AudioInputDebugWriter::AudioFileWriter::Close() {
182 // the data is unknown, so the header is written with zero sizes; then on
183 // destruction it is re-written with the actual size info accumulated throughout
184 // its lifetime.
185 void AudioInputDebugWriter::WriteHeader() {
186 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 211 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
212 if (file_.IsValid())
213 WriteHeader();
214 }
215
216 void AudioInputDebugWriter::AudioFileWriter::WriteHeader() {
217 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
218 DCHECK(file_.IsValid());
187 219
188 WavHeaderBuffer buf; 220 WavHeaderBuffer buf;
189 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); 221 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_);
190 file_.Write(0, &buf[0], kWavHeaderSize); 222 file_.Write(0, &buf[0], kWavHeaderSize);
191 223
192 // Write() does not move the cursor if file is not in APPEND mode; Seek() so 224 // Write() does not move the cursor if file is not in APPEND mode; Seek() so
193 // that the header is not overwritten by the following writes. 225 // that the header is not overwritten by the following writes.
194 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); 226 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize);
195 } 227 }
196 228
229 void AudioInputDebugWriter::AudioFileWriter::CreateRecordingFile(
230 const base::FilePath& file_name) {
231 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
232
233 file_ = base::File(file_name,
234 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
235
236 if (file_.IsValid()) {
237 WriteHeader();
238 return;
239 }
240
241 // Note that we do not inform AudioInputDebugWriter that the file creation
242 // fails, so it will continue to post data to be recorded, which won't
243 // be written to the file. This also won't be reflect in IsRecprding(). It's
Guido Urdaneta 2016/10/10 10:09:23 typo: IsRecprding -> IsRecording
o1ka 2016/10/10 13:48:01 Done.
244 // fine, because this situation is rare, and all the posting is expected to
245 // happen in case of success anyways. This allow us to save on thread hops for
246 // error reporting and to avoid dealing with lifetime issues.
247 PLOG(ERROR) << "Could not open debug recording file, error="
248 << file_.error_details();
249 }
250
251 AudioInputDebugWriter::AudioInputDebugWriter(
252 const media::AudioParameters& params)
253 : params_(params) {
254 client_sequence_checker_.DetachFromSequence();
255 }
256
257 AudioInputDebugWriter::~AudioInputDebugWriter() {
258 // Callback takes ownership of |file_writer_|, so it will be deleted after
Guido Urdaneta 2016/10/10 10:09:23 It would be good to make it clearer what "Callback
o1ka 2016/10/10 13:48:01 Done.
259 // Close() is executed or when FILE thread message loop is destroyed. Posting
Henrik Grunell 2016/10/10 10:56:59 This could mean that the header isn't updated at a
260 // non-nestable to make sure it is executed after all the writes are
o1ka 2016/10/10 08:44:58 Actually, I'm not quite sure I interpreted this co
Guido Urdaneta 2016/10/10 10:09:23 It's also not clear to me. Why would it be incorre
Henrik Grunell 2016/10/10 10:56:59 It should only be needed if the writes call any ta
o1ka 2016/10/10 13:48:01 I don't really understand what is behind this. But
261 // completed.
262 if (file_writer_) {
263 BrowserThread::PostNonNestableTask(
264 BrowserThread::FILE, FROM_HERE,
265 base::Bind(&AudioFileWriter::Close,
266 base::Owned(file_writer_.release())));
267 }
268 }
269
270 void AudioInputDebugWriter::Start(const base::FilePath& file_name) {
271 DCHECK(client_sequence_checker_.CalledOnValidSequence());
272 DCHECK(!file_writer_);
273 file_writer_.reset(new AudioFileWriter(file_name, params_));
274
275 base::AutoLock auto_lock(recording_lock_);
276 is_recording_ = true;
277 }
278
279 void AudioInputDebugWriter::Stop() {
280 DCHECK(client_sequence_checker_.CalledOnValidSequence());
281 {
282 base::AutoLock auto_lock(recording_lock_);
283 is_recording_ = false;
284 }
285
286 if (file_writer_) {
287 // Callback takes ownership of |file_writer_|, so it will be deleted after
288 // Close() is executed or when FILE thread message loop is destroyed.
289 // Posting
Guido Urdaneta 2016/10/10 10:09:24 fix comment flow (A whole line just for Posting).
o1ka 2016/10/11 12:11:24 Done.
290 // non-nestable to make sure it is executed after all the writes are
291 // completed.
292 BrowserThread::PostNonNestableTask(
293 BrowserThread::FILE, FROM_HERE,
294 base::Bind(&AudioFileWriter::Close,
295 base::Owned(file_writer_.release())));
296 }
297
298 client_sequence_checker_.DetachFromSequence();
299 }
300
301 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) {
302 DCHECK(client_sequence_checker_.CalledOnValidSequence());
303 DCHECK(file_writer_);
304
305 // base::Unretained for |file_writer_| is safe, see the destructor.
306 // Callback takes ownership of |data|.
307 BrowserThread::PostTask(
308 BrowserThread::FILE, FROM_HERE,
309 base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()),
310 base::Owned(data.release())));
311 }
312
313 bool AudioInputDebugWriter::IsRecording() {
314 base::AutoLock auto_lock(recording_lock_);
315 return is_recording_;
316 }
317
197 } // namspace content 318 } // namspace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698