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

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: review comments addressed 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:
132 const media::AudioParameters& params) 133 static std::unique_ptr<AudioFileWriter> Create(
133 : file_(std::move(file)), 134 const base::FilePath& file_name,
134 samples_(0), 135 const media::AudioParameters& params);
135 params_(params), 136
136 interleaved_data_size_(0), 137 // Must be called on FILE thread or on FILE message loop destruction.
137 weak_factory_(this) { 138 ~AudioFileWriter();
o1ka 2016/10/11 12:11:24 The question I have not found an answer for so far
138 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); 139
139 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 140 // Write data from |data| to file. Called on the FILE thread.
140 base::Bind(&AudioInputDebugWriter::WriteHeader, 141 void Write(const media::AudioBus* data);
141 weak_factory_.GetWeakPtr())); 142
143 // Must be called on FILE thread.
Henrik Grunell 2016/10/11 13:07:24 Nit: Does this class live only on the FILE thread?
o1ka 2016/10/11 14:04:36 Done.
144 void Close();
145
146 private:
147 AudioFileWriter(const media::AudioParameters& params);
148
149 // Write wave header to file. Called on the FILE thread twice: on construction
150 // of AudioFileWriter size of the wave data is unknown, so the header is
151 // written with zero sizes; then on destruction it is re-written with the
152 // actual size info accumulated throughout the object lifetime.
153 void WriteHeader();
154
155 void CreateRecordingFile(const base::FilePath& file_name);
156
157 // The file to write to.
158 base::File file_;
159
160 // Number of written samples.
161 uint64_t samples_;
162
163 // Input audio parameters required to build wave header.
164 const media::AudioParameters params_;
165
166 // Intermediate buffer to be written to file. Interleaved 16 bit audio data.
167 std::unique_ptr<int16_t[]> interleaved_data_;
168 int interleaved_data_size_;
169 };
170
171 // static
172 std::unique_ptr<AudioInputDebugWriter::AudioFileWriter>
173 AudioInputDebugWriter::AudioFileWriter::Create(
174 const base::FilePath& file_name,
175 const media::AudioParameters& params) {
176 std::unique_ptr<AudioInputDebugWriter::AudioFileWriter> file_writer(
177 new AudioFileWriter(params));
178
179 // base::Unretained is safe, because destructor is called on FILE thread or on
180 // FILE message loop destruction.
181 BrowserThread::PostTask(
182 BrowserThread::FILE, FROM_HERE,
183 base::Bind(&AudioFileWriter::CreateRecordingFile,
184 base::Unretained(file_writer.get()), file_name));
185 return file_writer;
142 } 186 }
143 187
144 AudioInputDebugWriter::~AudioInputDebugWriter() { 188 AudioInputDebugWriter::AudioFileWriter::AudioFileWriter(
145 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 189 const media::AudioParameters& params)
146 WriteHeader(); 190 : samples_(0), params_(params), interleaved_data_size_(0) {
191 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8);
147 } 192 }
148 193
149 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) { 194 AudioInputDebugWriter::AudioFileWriter::~AudioFileWriter() {
Henrik Grunell 2016/10/11 13:07:24 Nit: DCHECK correct thread?
o1ka 2016/10/11 14:04:37 Seem my question above :) But I changed the code a
Henrik Grunell 2016/10/13 07:26:11 Great!
150 BrowserThread::PostTask( 195 // In case a scheduled Close() call has not been executed because message loop
151 BrowserThread::FILE, 196 // is destroyed.
152 FROM_HERE, 197 if (file_.IsValid())
153 base::Bind(&AudioInputDebugWriter::DoWrite, 198 WriteHeader();
154 weak_factory_.GetWeakPtr(),
155 base::Passed(&data)));
156 } 199 }
157 200
158 void AudioInputDebugWriter::DoWrite(std::unique_ptr<media::AudioBus> data) { 201 void AudioInputDebugWriter::AudioFileWriter::Write(
202 const media::AudioBus* data) {
159 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 203 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
204 if (!file_.IsValid())
205 return;
206
160 // Convert to 16 bit audio and write to file. 207 // Convert to 16 bit audio and write to file.
161 int data_size = data->frames() * data->channels(); 208 int data_size = data->frames() * data->channels();
162 if (!interleaved_data_ || interleaved_data_size_ < data_size) { 209 if (!interleaved_data_ || interleaved_data_size_ < data_size) {
163 interleaved_data_.reset(new int16_t[data_size]); 210 interleaved_data_.reset(new int16_t[data_size]);
164 interleaved_data_size_ = data_size; 211 interleaved_data_size_ = data_size;
165 } 212 }
166 samples_ += data_size; 213 samples_ += data_size;
167 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), 214 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]),
168 interleaved_data_.get()); 215 interleaved_data_.get());
169 216
170 #ifndef ARCH_CPU_LITTLE_ENDIAN 217 #ifndef ARCH_CPU_LITTLE_ENDIAN
171 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), 218 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t),
172 "Only 2 bytes per channel is supported."); 219 "Only 2 bytes per channel is supported.");
173 for (int i = 0; i < data_size; ++i) 220 for (int i = 0; i < data_size; ++i)
174 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); 221 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]);
175 #endif 222 #endif
176 223
177 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), 224 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()),
178 data_size * sizeof(interleaved_data_[0])); 225 data_size * sizeof(interleaved_data_[0]));
179 } 226 }
180 227
181 // This method is called twice: on construction of AudioInputDebugWriter size of 228 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); 229 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
230 if (file_.IsValid())
231 WriteHeader();
232 file_ = base::File();
233 }
187 234
235 void AudioInputDebugWriter::AudioFileWriter::WriteHeader() {
236 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
237 if (!file_.IsValid())
238 return;
188 WavHeaderBuffer buf; 239 WavHeaderBuffer buf;
189 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); 240 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_);
190 file_.Write(0, &buf[0], kWavHeaderSize); 241 file_.Write(0, &buf[0], kWavHeaderSize);
191 242
192 // Write() does not move the cursor if file is not in APPEND mode; Seek() so 243 // 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. 244 // that the header is not overwritten by the following writes.
194 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); 245 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize);
195 } 246 }
196 247
248 void AudioInputDebugWriter::AudioFileWriter::CreateRecordingFile(
249 const base::FilePath& file_name) {
250 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
251 DCHECK(!file_.IsValid());
252
253 file_ = base::File(file_name,
254 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
255
256 if (file_.IsValid()) {
257 WriteHeader();
258 return;
259 }
260
261 // Note that we do not inform AudioInputDebugWriter that the file creation
262 // fails, so it will continue to post data to be recorded, which won't
263 // be written to the file. This also won't be reflect in WillWrite(). It's
Guido Urdaneta 2016/10/11 12:39:25 nit: reflect -> reflected
o1ka 2016/10/11 14:04:37 Done.
264 // fine, because this situation is rare, and all the posting is expected to
265 // happen in case of success anyways. This allow us to save on thread hops for
Guido Urdaneta 2016/10/11 12:39:25 nit: allow -> allows
o1ka 2016/10/11 14:04:36 Done.
266 // error reporting and to avoid dealing with lifetime issues. It also means
267 // file_.IsValid() should always be checked before issuing writes to it.
268 PLOG(ERROR) << "Could not open debug recording file, error="
269 << file_.error_details();
270 }
271
272 AudioInputDebugWriter::AudioInputDebugWriter(
273 const media::AudioParameters& params)
274 : params_(params) {
275 client_sequence_checker_.DetachFromSequence();
276 }
277
278 AudioInputDebugWriter::~AudioInputDebugWriter() {
279 Stop();
280 }
281
282 void AudioInputDebugWriter::Start(const base::FilePath& file_name) {
283 DCHECK(client_sequence_checker_.CalledOnValidSequence());
284 DCHECK(!file_writer_);
285 file_writer_ = AudioFileWriter::Create(file_name, params_);
286 }
287
288 void AudioInputDebugWriter::Stop() {
289 DCHECK(client_sequence_checker_.CalledOnValidSequence());
290 // Callback takes ownership of |file_writer_|, so it will be deleted after
291 // Close() is executed or when FILE thread message loop is destroyed. Posting
292 // non-nestable just like in SequencedTaskRunner::DeleteSoon().
o1ka 2016/10/11 12:11:24 I still do not quite understand this "nestability"
Henrik Grunell 2016/10/11 13:07:24 Afaict, if you want to guarantee it's run after an
o1ka 2016/10/11 14:04:36 Ok, looks like non-netsable post is needed if I wa
293 if (file_writer_) {
294 BrowserThread::PostNonNestableTask(
295 BrowserThread::FILE, FROM_HERE,
296 base::Bind(&AudioFileWriter::Close,
297 base::Owned(file_writer_.release())));
298 }
299
300 client_sequence_checker_.DetachFromSequence();
301 }
302
303 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) {
304 DCHECK(client_sequence_checker_.CalledOnValidSequence());
305 if (!file_writer_)
306 return;
307
308 // base::Unretained for |file_writer_| is safe, see the destructor.
309 BrowserThread::PostTask(
310 BrowserThread::FILE, FROM_HERE,
311 // Callback takes ownership of |data|:
312 base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()),
313 base::Owned(data.release())));
314 }
315
316 bool AudioInputDebugWriter::WillWrite() {
317 // Note that if this is called from any place other than
318 // |client_sequence_checker_|
o1ka 2016/10/11 12:11:24 will fix (git cl format did not do a perfect job h
Henrik Grunell 2016/10/11 13:07:24 git cl format doesn't format comments afaik.
o1ka 2016/10/11 14:04:36 it wraps lines.
319 // then there is a data race here, but it's fine, because Write() will check
320 // for |file_writer_|. So, we are not very precies here, but it's fine: we can
Guido Urdaneta 2016/10/11 12:39:25 nit: precies -> precise
o1ka 2016/10/11 14:04:36 Done.
321 // afford missing some data or scheduling some no-op writes.
322 return !!file_writer_;
323 }
324
197 } // namspace content 325 } // namspace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698