OLD | NEW |
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 "media/audio/audio_debug_file_writer.h" | 5 #include "media/audio/audio_debug_file_writer.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <array> | 8 #include <array> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
14 #include "base/sys_byteorder.h" | 14 #include "base/sys_byteorder.h" |
| 15 #include "base/threading/thread_restrictions.h" |
15 #include "media/base/audio_bus.h" | 16 #include "media/base/audio_bus.h" |
16 #include "media/base/audio_sample_types.h" | 17 #include "media/base/audio_sample_types.h" |
17 | 18 |
18 namespace media { | 19 namespace media { |
19 | 20 |
20 namespace { | 21 namespace { |
21 | 22 |
22 // Windows WAVE format header | 23 // Windows WAVE format header |
23 // Byte order: Little-endian | 24 // Byte order: Little-endian |
24 // Offset Length Content | 25 // Offset Length Content |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 writer.WriteLE32(byte_rate); | 128 writer.WriteLE32(byte_rate); |
128 writer.WriteLE16(block_align); | 129 writer.WriteLE16(block_align); |
129 writer.WriteLE16(kBytesPerSample * 8); | 130 writer.WriteLE16(kBytesPerSample * 8); |
130 writer.Write(kData); | 131 writer.Write(kData); |
131 writer.WriteLE32(bytes_in_payload); | 132 writer.WriteLE32(bytes_in_payload); |
132 } | 133 } |
133 | 134 |
134 } // namespace | 135 } // namespace |
135 | 136 |
136 // Manages the debug recording file and writes to it. Can be created on any | 137 // Manages the debug recording file and writes to it. Can be created on any |
137 // thread. All the operations must be executed on |task_runner_|. Must be | 138 // thread. All the operations must be executed on a thread that has IO |
138 // destroyed on |task_runner_|. | 139 // permissions. |
139 class AudioDebugFileWriter::AudioFileWriter { | 140 class AudioDebugFileWriter::AudioFileWriter { |
140 public: | 141 public: |
141 static AudioFileWriterUniquePtr Create( | 142 static AudioFileWriterUniquePtr Create( |
142 const base::FilePath& file_name, | 143 const base::FilePath& file_name, |
143 const AudioParameters& params, | 144 const AudioParameters& params, |
144 scoped_refptr<base::SingleThreadTaskRunner> task_runner); | 145 scoped_refptr<base::SequencedTaskRunner> task_runner); |
145 | 146 |
146 ~AudioFileWriter(); | 147 ~AudioFileWriter(); |
147 | 148 |
148 // Write data from |data| to file. | 149 // Write data from |data| to file. |
149 void Write(const AudioBus* data); | 150 void Write(const AudioBus* data); |
150 | 151 |
151 scoped_refptr<base::SingleThreadTaskRunner> task_runner() { | |
152 return task_runner_; | |
153 } | |
154 | |
155 private: | 152 private: |
156 AudioFileWriter(const AudioParameters& params, | 153 AudioFileWriter(const AudioParameters& params); |
157 scoped_refptr<base::SingleThreadTaskRunner> task_runner); | |
158 | 154 |
159 // Write wave header to file. Called on the |task_runner_| twice: on | 155 // Write wave header to file. Called on the |task_runner_| twice: on |
160 // construction | 156 // construction |
161 // of AudioFileWriter size of the wave data is unknown, so the header is | 157 // of AudioFileWriter size of the wave data is unknown, so the header is |
162 // written with zero sizes; then on destruction it is re-written with the | 158 // written with zero sizes; then on destruction it is re-written with the |
163 // actual size info accumulated throughout the object lifetime. | 159 // actual size info accumulated throughout the object lifetime. |
164 void WriteHeader(); | 160 void WriteHeader(); |
165 | 161 |
166 void CreateRecordingFile(const base::FilePath& file_name); | 162 void CreateRecordingFile(const base::FilePath& file_name); |
167 | 163 |
168 // The file to write to. | 164 // The file to write to. |
169 base::File file_; | 165 base::File file_; |
170 | 166 |
171 // Number of written samples. | 167 // Number of written samples. |
172 uint64_t samples_; | 168 uint64_t samples_; |
173 | 169 |
174 // Audio parameters required to build wave header. Number of channels and | 170 // Audio parameters required to build wave header. Number of channels and |
175 // sample rate are used. | 171 // sample rate are used. |
176 const AudioParameters params_; | 172 const AudioParameters params_; |
177 | 173 |
178 // Intermediate buffer to be written to file. Interleaved 16 bit audio data. | 174 // Intermediate buffer to be written to file. Interleaved 16 bit audio data. |
179 std::unique_ptr<int16_t[]> interleaved_data_; | 175 std::unique_ptr<int16_t[]> interleaved_data_; |
180 int interleaved_data_size_; | 176 int interleaved_data_size_; |
181 | 177 |
182 // The task runner this class operates on. | 178 SEQUENCE_CHECKER(sequence_checker_); |
183 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
184 }; | 179 }; |
185 | 180 |
186 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter() {} | 181 AudioDebugFileWriter::OnSequenceDeleter::OnSequenceDeleter() {} |
187 | 182 |
188 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter( | 183 AudioDebugFileWriter::OnSequenceDeleter::OnSequenceDeleter( |
189 const OnThreadDeleter& other) | 184 AudioDebugFileWriter::OnSequenceDeleter&& other) = default; |
190 : task_runner_(other.task_runner_) {} | |
191 | 185 |
192 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter( | 186 AudioDebugFileWriter::OnSequenceDeleter& |
193 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 187 AudioDebugFileWriter::OnSequenceDeleter::operator=( |
194 : task_runner_(task_runner) {} | 188 AudioDebugFileWriter::OnSequenceDeleter&&) = default; |
195 | 189 |
196 AudioDebugFileWriter::OnThreadDeleter::~OnThreadDeleter() {} | 190 AudioDebugFileWriter::OnSequenceDeleter::OnSequenceDeleter( |
| 191 scoped_refptr<base::SequencedTaskRunner> task_runner) |
| 192 : task_runner_(std::move(task_runner)) {} |
197 | 193 |
198 // AudioFileWriter deleter. Inspired by | 194 AudioDebugFileWriter::OnSequenceDeleter::~OnSequenceDeleter() {} |
199 // content::BrowserThread::DeleteOnFileThread. | 195 |
200 void AudioDebugFileWriter::OnThreadDeleter::operator()( | 196 // AudioFileWriter deleter. |
| 197 void AudioDebugFileWriter::OnSequenceDeleter::operator()( |
201 AudioDebugFileWriter::AudioFileWriter* ptr) const { | 198 AudioDebugFileWriter::AudioFileWriter* ptr) const { |
202 if (!task_runner_->DeleteSoon(FROM_HERE, ptr)) { | 199 if (!task_runner_->DeleteSoon(FROM_HERE, ptr)) { |
203 #if defined(UNIT_TEST) | 200 #if defined(UNIT_TEST) |
204 // Only logged under unit testing because leaks at shutdown | 201 // Only logged under unit testing because leaks at shutdown |
205 // are acceptable under normal circumstances. | 202 // are acceptable under normal circumstances. |
206 LOG(ERROR) << "DeleteSoon failed for AudioDebugFileWriter::AudioFileWriter"; | 203 LOG(ERROR) << "DeleteSoon failed for AudioDebugFileWriter::AudioFileWriter"; |
207 #endif | 204 #endif |
208 } | 205 } |
209 } | 206 } |
210 | 207 |
211 // static | 208 // static |
212 AudioDebugFileWriter::AudioFileWriterUniquePtr | 209 AudioDebugFileWriter::AudioFileWriterUniquePtr |
213 AudioDebugFileWriter::AudioFileWriter::Create( | 210 AudioDebugFileWriter::AudioFileWriter::Create( |
214 const base::FilePath& file_name, | 211 const base::FilePath& file_name, |
215 const AudioParameters& params, | 212 const AudioParameters& params, |
216 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | 213 scoped_refptr<base::SequencedTaskRunner> task_runner) { |
217 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params, task_runner), | 214 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params), |
218 OnThreadDeleter(task_runner)); | 215 OnSequenceDeleter(task_runner)); |
219 | 216 |
220 // base::Unretained is safe, because destructor is called on | 217 // base::Unretained is safe, because destructor is called on |
221 // |task_runner|. | 218 // |task_runner|. |
222 task_runner->PostTask( | 219 task_runner->PostTask( |
223 FROM_HERE, | 220 FROM_HERE, |
224 base::Bind(&AudioFileWriter::CreateRecordingFile, | 221 base::Bind(&AudioFileWriter::CreateRecordingFile, |
225 base::Unretained(file_writer.get()), file_name)); | 222 base::Unretained(file_writer.get()), file_name)); |
226 return file_writer; | 223 return file_writer; |
227 } | 224 } |
228 | 225 |
229 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( | 226 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( |
230 const AudioParameters& params, | 227 const AudioParameters& params) |
231 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 228 : samples_(0), params_(params), interleaved_data_size_(0) { |
232 : samples_(0), | 229 DETACH_FROM_SEQUENCE(sequence_checker_); |
233 params_(params), | 230 } |
234 interleaved_data_size_(0), | |
235 task_runner_(std::move(task_runner)) {} | |
236 | 231 |
237 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { | 232 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { |
238 DCHECK(task_runner_->BelongsToCurrentThread()); | 233 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
239 if (file_.IsValid()) | 234 if (file_.IsValid()) |
240 WriteHeader(); | 235 WriteHeader(); |
241 } | 236 } |
242 | 237 |
243 void AudioDebugFileWriter::AudioFileWriter::Write(const AudioBus* data) { | 238 void AudioDebugFileWriter::AudioFileWriter::Write(const AudioBus* data) { |
244 DCHECK(task_runner_->BelongsToCurrentThread()); | 239 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
245 DCHECK_EQ(params_.channels(), data->channels()); | 240 DCHECK_EQ(params_.channels(), data->channels()); |
246 if (!file_.IsValid()) | 241 if (!file_.IsValid()) |
247 return; | 242 return; |
248 | 243 |
249 // Convert to 16 bit audio and write to file. | 244 // Convert to 16 bit audio and write to file. |
250 int data_size = data->frames() * data->channels(); | 245 int data_size = data->frames() * data->channels(); |
251 if (!interleaved_data_ || interleaved_data_size_ < data_size) { | 246 if (!interleaved_data_ || interleaved_data_size_ < data_size) { |
252 interleaved_data_.reset(new int16_t[data_size]); | 247 interleaved_data_.reset(new int16_t[data_size]); |
253 interleaved_data_size_ = data_size; | 248 interleaved_data_size_ = data_size; |
254 } | 249 } |
255 samples_ += data_size; | 250 samples_ += data_size; |
256 data->ToInterleaved<media::SignedInt16SampleTypeTraits>( | 251 data->ToInterleaved<media::SignedInt16SampleTypeTraits>( |
257 data->frames(), interleaved_data_.get()); | 252 data->frames(), interleaved_data_.get()); |
258 | 253 |
259 #ifndef ARCH_CPU_LITTLE_ENDIAN | 254 #ifndef ARCH_CPU_LITTLE_ENDIAN |
260 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), | 255 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), |
261 "Only 2 bytes per channel is supported."); | 256 "Only 2 bytes per channel is supported."); |
262 for (int i = 0; i < data_size; ++i) | 257 for (int i = 0; i < data_size; ++i) |
263 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); | 258 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); |
264 #endif | 259 #endif |
265 | 260 |
266 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), | 261 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), |
267 data_size * sizeof(interleaved_data_[0])); | 262 data_size * sizeof(interleaved_data_[0])); |
268 } | 263 } |
269 | 264 |
270 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { | 265 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { |
271 DCHECK(task_runner_->BelongsToCurrentThread()); | 266 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
272 if (!file_.IsValid()) | 267 if (!file_.IsValid()) |
273 return; | 268 return; |
274 WavHeaderBuffer buf; | 269 WavHeaderBuffer buf; |
275 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); | 270 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); |
276 file_.Write(0, &buf[0], kWavHeaderSize); | 271 file_.Write(0, &buf[0], kWavHeaderSize); |
277 | 272 |
278 // Write() does not move the cursor if file is not in APPEND mode; Seek() so | 273 // Write() does not move the cursor if file is not in APPEND mode; Seek() so |
279 // that the header is not overwritten by the following writes. | 274 // that the header is not overwritten by the following writes. |
280 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); | 275 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); |
281 } | 276 } |
282 | 277 |
283 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( | 278 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( |
284 const base::FilePath& file_name) { | 279 const base::FilePath& file_name) { |
285 DCHECK(task_runner_->BelongsToCurrentThread()); | 280 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 281 base::ThreadRestrictions::AssertIOAllowed(); |
286 DCHECK(!file_.IsValid()); | 282 DCHECK(!file_.IsValid()); |
287 | 283 |
288 file_ = base::File(file_name, | 284 file_ = base::File(file_name, |
289 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | 285 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
290 | 286 |
291 if (file_.IsValid()) { | 287 if (file_.IsValid()) { |
292 WriteHeader(); | 288 WriteHeader(); |
293 return; | 289 return; |
294 } | 290 } |
295 | 291 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 // here, but it's fine: we can afford missing some data or scheduling some | 344 // here, but it's fine: we can afford missing some data or scheduling some |
349 // no-op writes. | 345 // no-op writes. |
350 return !!file_writer_; | 346 return !!file_writer_; |
351 } | 347 } |
352 | 348 |
353 const base::FilePath::CharType* AudioDebugFileWriter::GetFileNameExtension() { | 349 const base::FilePath::CharType* AudioDebugFileWriter::GetFileNameExtension() { |
354 return FILE_PATH_LITERAL("wav"); | 350 return FILE_PATH_LITERAL("wav"); |
355 } | 351 } |
356 | 352 |
357 } // namspace media | 353 } // namspace media |
OLD | NEW |