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

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: cleaning up file writer deletion on FILE thread 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 // Manages the debug recording file and writes to it. Can be created on any
131 base::File file, 132 // thread. All the operations must be executed on FILE thread. Must be destroyed
132 const media::AudioParameters& params) 133 // on FILE thread.
133 : file_(std::move(file)), 134 class AudioInputDebugWriter::AudioFileWriter {
134 samples_(0), 135 public:
135 params_(params), 136 static AudioFileWriterUniquePtr Create(const base::FilePath& file_name,
136 interleaved_data_size_(0), 137 const media::AudioParameters& params);
137 weak_factory_(this) { 138
138 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); 139 ~AudioFileWriter();
139 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 140
140 base::Bind(&AudioInputDebugWriter::WriteHeader, 141 // Write data from |data| to file.
141 weak_factory_.GetWeakPtr())); 142 void Write(const media::AudioBus* data);
143
144 private:
145 AudioFileWriter(const media::AudioParameters& params);
146
147 // Write wave header to file. Called on the FILE thread twice: on construction
148 // of AudioFileWriter size of the wave data is unknown, so the header is
149 // written with zero sizes; then on destruction it is re-written with the
150 // actual size info accumulated throughout the object lifetime.
151 void WriteHeader();
152
153 void CreateRecordingFile(const base::FilePath& file_name);
154
155 // The file to write to.
156 base::File file_;
157
158 // Number of written samples.
159 uint64_t samples_;
160
161 // Input audio parameters required to build wave header.
162 const media::AudioParameters params_;
163
164 // Intermediate buffer to be written to file. Interleaved 16 bit audio data.
165 std::unique_ptr<int16_t[]> interleaved_data_;
166 int interleaved_data_size_;
167 };
168
169 // static
170 AudioInputDebugWriter::AudioFileWriterUniquePtr
171 AudioInputDebugWriter::AudioFileWriter::Create(
172 const base::FilePath& file_name,
173 const media::AudioParameters& params) {
174 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params));
175
176 // base::Unretained is safe, because destructor is called on FILE thread or on
177 // FILE message loop destruction.
178 BrowserThread::PostTask(
179 BrowserThread::FILE, FROM_HERE,
180 base::Bind(&AudioFileWriter::CreateRecordingFile,
181 base::Unretained(file_writer.get()), file_name));
182 return file_writer;
142 } 183 }
143 184
144 AudioInputDebugWriter::~AudioInputDebugWriter() { 185 AudioInputDebugWriter::AudioFileWriter::AudioFileWriter(
145 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 186 const media::AudioParameters& params)
146 WriteHeader(); 187 : samples_(0), params_(params), interleaved_data_size_(0) {
188 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8);
147 } 189 }
148 190
149 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) { 191 AudioInputDebugWriter::AudioFileWriter::~AudioFileWriter() {
150 BrowserThread::PostTask( 192 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
151 BrowserThread::FILE, 193 if (file_.IsValid())
152 FROM_HERE, 194 WriteHeader();
153 base::Bind(&AudioInputDebugWriter::DoWrite,
154 weak_factory_.GetWeakPtr(),
155 base::Passed(&data)));
156 } 195 }
157 196
158 void AudioInputDebugWriter::DoWrite(std::unique_ptr<media::AudioBus> data) { 197 void AudioInputDebugWriter::AudioFileWriter::Write(
198 const media::AudioBus* data) {
159 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 199 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
200 if (!file_.IsValid())
201 return;
202
160 // Convert to 16 bit audio and write to file. 203 // Convert to 16 bit audio and write to file.
161 int data_size = data->frames() * data->channels(); 204 int data_size = data->frames() * data->channels();
162 if (!interleaved_data_ || interleaved_data_size_ < data_size) { 205 if (!interleaved_data_ || interleaved_data_size_ < data_size) {
163 interleaved_data_.reset(new int16_t[data_size]); 206 interleaved_data_.reset(new int16_t[data_size]);
164 interleaved_data_size_ = data_size; 207 interleaved_data_size_ = data_size;
165 } 208 }
166 samples_ += data_size; 209 samples_ += data_size;
167 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), 210 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]),
168 interleaved_data_.get()); 211 interleaved_data_.get());
169 212
170 #ifndef ARCH_CPU_LITTLE_ENDIAN 213 #ifndef ARCH_CPU_LITTLE_ENDIAN
171 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), 214 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t),
172 "Only 2 bytes per channel is supported."); 215 "Only 2 bytes per channel is supported.");
173 for (int i = 0; i < data_size; ++i) 216 for (int i = 0; i < data_size; ++i)
174 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); 217 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]);
175 #endif 218 #endif
176 219
177 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), 220 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()),
178 data_size * sizeof(interleaved_data_[0])); 221 data_size * sizeof(interleaved_data_[0]));
179 } 222 }
180 223
181 // This method is called twice: on construction of AudioInputDebugWriter size of 224 void AudioInputDebugWriter::AudioFileWriter::WriteHeader() {
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); 225 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
187 226 if (!file_.IsValid())
227 return;
188 WavHeaderBuffer buf; 228 WavHeaderBuffer buf;
189 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); 229 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_);
190 file_.Write(0, &buf[0], kWavHeaderSize); 230 file_.Write(0, &buf[0], kWavHeaderSize);
191 231
192 // Write() does not move the cursor if file is not in APPEND mode; Seek() so 232 // 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. 233 // that the header is not overwritten by the following writes.
194 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); 234 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize);
195 } 235 }
196 236
237 void AudioInputDebugWriter::AudioFileWriter::CreateRecordingFile(
238 const base::FilePath& file_name) {
239 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
240 DCHECK(!file_.IsValid());
241
242 file_ = base::File(file_name,
243 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
244
245 if (file_.IsValid()) {
246 WriteHeader();
247 return;
248 }
249
250 // Note that we do not inform AudioInputDebugWriter that the file creation
251 // fails, so it will continue to post data to be recorded, which won't
252 // be written to the file. This also won't be reflected in WillWrite(). It's
253 // fine, because this situation is rare, and all the posting is expected to
254 // happen in case of success anyways. This allows us to save on thread hops
255 // for error reporting and to avoid dealing with lifetime issues. It also
256 // means file_.IsValid() should always be checked before issuing writes to it.
257 PLOG(ERROR) << "Could not open debug recording file, error="
258 << file_.error_details();
259 }
260
261 AudioInputDebugWriter::AudioInputDebugWriter(
262 const media::AudioParameters& params)
263 : params_(params) {
264 client_sequence_checker_.DetachFromSequence();
265 }
266
267 AudioInputDebugWriter::~AudioInputDebugWriter() {
268 // |file_writer_| will be deleted on FILE thread.
Guido Urdaneta 2016/10/13 08:30:28 Why not put a DCHECK here to verify that?
o1ka 2016/10/13 09:13:34 Not sure what do you mean. DCHECK is in AudioFileW
Guido Urdaneta 2016/10/13 12:06:31 Acknowledged. Maybe remove this comment.
269 }
270
271 void AudioInputDebugWriter::Start(const base::FilePath& file_name) {
272 DCHECK(client_sequence_checker_.CalledOnValidSequence());
273 DCHECK(!file_writer_);
274 file_writer_ = AudioFileWriter::Create(file_name, params_);
275 }
276
277 void AudioInputDebugWriter::Stop() {
278 DCHECK(client_sequence_checker_.CalledOnValidSequence());
279 // |file_writer_| is deleted on FILE thread.
280 file_writer_.reset();
281 client_sequence_checker_.DetachFromSequence();
282 }
283
284 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) {
285 DCHECK(client_sequence_checker_.CalledOnValidSequence());
286 if (!file_writer_)
287 return;
288
289 // base::Unretained for |file_writer_| is safe, see the destructor.
290 BrowserThread::PostTask(
291 BrowserThread::FILE, FROM_HERE,
292 // Callback takes ownership of |data|:
293 base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()),
294 base::Owned(data.release())));
295 }
296
297 bool AudioInputDebugWriter::WillWrite() {
298 // Note that if this is called from any place other than
299 // |client_sequence_checker_| then there is a data race here, but it's fine,
300 // because Write() will check for |file_writer_|. So, we are not very precise
301 // here, but it's fine: we can afford missing some data or scheduling some
302 // no-op writes.
303 return !!file_writer_;
304 }
305
197 } // namspace content 306 } // namspace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698