Chromium Code Reviews| 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 const OnSequenceDeleter& other) |
| 190 : task_runner_(other.task_runner_) {} | 185 : task_runner_(other.task_runner_) {} |
| 191 | 186 |
| 192 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter( | 187 AudioDebugFileWriter::OnSequenceDeleter::OnSequenceDeleter( |
| 193 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 188 scoped_refptr<base::SequencedTaskRunner> task_runner) |
| 194 : task_runner_(task_runner) {} | 189 : task_runner_(task_runner) {} |
|
gab
2017/05/18 20:38:17
std::move(task_runner)
Sébastien Marchand
2017/05/18 21:24:25
Done.
| |
| 195 | 190 |
| 196 AudioDebugFileWriter::OnThreadDeleter::~OnThreadDeleter() {} | 191 AudioDebugFileWriter::OnSequenceDeleter::~OnSequenceDeleter() {} |
| 197 | 192 |
| 198 // AudioFileWriter deleter. Inspired by | 193 // AudioFileWriter deleter. |
| 199 // content::BrowserThread::DeleteOnFileThread. | 194 void AudioDebugFileWriter::OnSequenceDeleter::operator()( |
| 200 void AudioDebugFileWriter::OnThreadDeleter::operator()( | |
| 201 AudioDebugFileWriter::AudioFileWriter* ptr) const { | 195 AudioDebugFileWriter::AudioFileWriter* ptr) const { |
| 202 if (!task_runner_->DeleteSoon(FROM_HERE, ptr)) { | 196 if (!task_runner_->DeleteSoon(FROM_HERE, ptr)) { |
| 203 #if defined(UNIT_TEST) | 197 #if defined(UNIT_TEST) |
| 204 // Only logged under unit testing because leaks at shutdown | 198 // Only logged under unit testing because leaks at shutdown |
| 205 // are acceptable under normal circumstances. | 199 // are acceptable under normal circumstances. |
| 206 LOG(ERROR) << "DeleteSoon failed for AudioDebugFileWriter::AudioFileWriter"; | 200 LOG(ERROR) << "DeleteSoon failed for AudioDebugFileWriter::AudioFileWriter"; |
| 207 #endif | 201 #endif |
| 208 } | 202 } |
| 209 } | 203 } |
| 210 | 204 |
| 211 // static | 205 // static |
| 212 AudioDebugFileWriter::AudioFileWriterUniquePtr | 206 AudioDebugFileWriter::AudioFileWriterUniquePtr |
| 213 AudioDebugFileWriter::AudioFileWriter::Create( | 207 AudioDebugFileWriter::AudioFileWriter::Create( |
| 214 const base::FilePath& file_name, | 208 const base::FilePath& file_name, |
| 215 const AudioParameters& params, | 209 const AudioParameters& params, |
| 216 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | 210 scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| 217 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params, task_runner), | 211 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params), |
| 218 OnThreadDeleter(task_runner)); | 212 OnSequenceDeleter(task_runner)); |
| 219 | 213 |
| 220 // base::Unretained is safe, because destructor is called on | 214 // base::Unretained is safe, because destructor is called on |
| 221 // |task_runner|. | 215 // |task_runner|. |
| 222 task_runner->PostTask( | 216 task_runner->PostTask( |
| 223 FROM_HERE, | 217 FROM_HERE, |
| 224 base::Bind(&AudioFileWriter::CreateRecordingFile, | 218 base::Bind(&AudioFileWriter::CreateRecordingFile, |
| 225 base::Unretained(file_writer.get()), file_name)); | 219 base::Unretained(file_writer.get()), file_name)); |
| 226 return file_writer; | 220 return file_writer; |
| 227 } | 221 } |
| 228 | 222 |
| 229 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( | 223 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( |
| 230 const AudioParameters& params, | 224 const AudioParameters& params) |
| 231 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 225 : samples_(0), params_(params), interleaved_data_size_(0) { |
| 232 : samples_(0), | 226 DETACH_FROM_SEQUENCE(sequence_checker_); |
| 233 params_(params), | 227 } |
| 234 interleaved_data_size_(0), | |
| 235 task_runner_(std::move(task_runner)) {} | |
| 236 | 228 |
| 237 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { | 229 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { |
| 238 DCHECK(task_runner_->BelongsToCurrentThread()); | 230 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 239 if (file_.IsValid()) | 231 if (file_.IsValid()) |
| 240 WriteHeader(); | 232 WriteHeader(); |
| 241 } | 233 } |
| 242 | 234 |
| 243 void AudioDebugFileWriter::AudioFileWriter::Write(const AudioBus* data) { | 235 void AudioDebugFileWriter::AudioFileWriter::Write(const AudioBus* data) { |
| 244 DCHECK(task_runner_->BelongsToCurrentThread()); | 236 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 245 DCHECK_EQ(params_.channels(), data->channels()); | 237 DCHECK_EQ(params_.channels(), data->channels()); |
| 246 if (!file_.IsValid()) | 238 if (!file_.IsValid()) |
| 247 return; | 239 return; |
| 248 | 240 |
| 249 // Convert to 16 bit audio and write to file. | 241 // Convert to 16 bit audio and write to file. |
| 250 int data_size = data->frames() * data->channels(); | 242 int data_size = data->frames() * data->channels(); |
| 251 if (!interleaved_data_ || interleaved_data_size_ < data_size) { | 243 if (!interleaved_data_ || interleaved_data_size_ < data_size) { |
| 252 interleaved_data_.reset(new int16_t[data_size]); | 244 interleaved_data_.reset(new int16_t[data_size]); |
| 253 interleaved_data_size_ = data_size; | 245 interleaved_data_size_ = data_size; |
| 254 } | 246 } |
| 255 samples_ += data_size; | 247 samples_ += data_size; |
| 256 data->ToInterleaved<media::SignedInt16SampleTypeTraits>( | 248 data->ToInterleaved<media::SignedInt16SampleTypeTraits>( |
| 257 data->frames(), interleaved_data_.get()); | 249 data->frames(), interleaved_data_.get()); |
| 258 | 250 |
| 259 #ifndef ARCH_CPU_LITTLE_ENDIAN | 251 #ifndef ARCH_CPU_LITTLE_ENDIAN |
| 260 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), | 252 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), |
| 261 "Only 2 bytes per channel is supported."); | 253 "Only 2 bytes per channel is supported."); |
| 262 for (int i = 0; i < data_size; ++i) | 254 for (int i = 0; i < data_size; ++i) |
| 263 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); | 255 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); |
| 264 #endif | 256 #endif |
| 265 | 257 |
| 266 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), | 258 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), |
| 267 data_size * sizeof(interleaved_data_[0])); | 259 data_size * sizeof(interleaved_data_[0])); |
| 268 } | 260 } |
| 269 | 261 |
| 270 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { | 262 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { |
| 271 DCHECK(task_runner_->BelongsToCurrentThread()); | 263 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 272 if (!file_.IsValid()) | 264 if (!file_.IsValid()) |
| 273 return; | 265 return; |
| 274 WavHeaderBuffer buf; | 266 WavHeaderBuffer buf; |
| 275 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); | 267 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); |
| 276 file_.Write(0, &buf[0], kWavHeaderSize); | 268 file_.Write(0, &buf[0], kWavHeaderSize); |
| 277 | 269 |
| 278 // Write() does not move the cursor if file is not in APPEND mode; Seek() so | 270 // 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. | 271 // that the header is not overwritten by the following writes. |
| 280 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); | 272 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); |
| 281 } | 273 } |
| 282 | 274 |
| 283 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( | 275 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( |
| 284 const base::FilePath& file_name) { | 276 const base::FilePath& file_name) { |
| 285 DCHECK(task_runner_->BelongsToCurrentThread()); | 277 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 278 base::ThreadRestrictions::AssertIOAllowed(); | |
| 286 DCHECK(!file_.IsValid()); | 279 DCHECK(!file_.IsValid()); |
| 287 | 280 |
| 288 file_ = base::File(file_name, | 281 file_ = base::File(file_name, |
| 289 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | 282 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| 290 | 283 |
| 291 if (file_.IsValid()) { | 284 if (file_.IsValid()) { |
| 292 WriteHeader(); | 285 WriteHeader(); |
| 293 return; | 286 return; |
| 294 } | 287 } |
| 295 | 288 |
| (...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 | 341 // here, but it's fine: we can afford missing some data or scheduling some |
| 349 // no-op writes. | 342 // no-op writes. |
| 350 return !!file_writer_; | 343 return !!file_writer_; |
| 351 } | 344 } |
| 352 | 345 |
| 353 const base::FilePath::CharType* AudioDebugFileWriter::GetFileNameExtension() { | 346 const base::FilePath::CharType* AudioDebugFileWriter::GetFileNameExtension() { |
| 354 return FILE_PATH_LITERAL("wav"); | 347 return FILE_PATH_LITERAL("wav"); |
| 355 } | 348 } |
| 356 | 349 |
| 357 } // namspace media | 350 } // namspace media |
| OLD | NEW |