| 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 "content/browser/renderer_host/media/audio_input_debug_writer.h" | 5 #include "content/browser/renderer_host/media/audio_debug_file_writer.h" |
| 6 |
| 6 #include <stdint.h> | 7 #include <stdint.h> |
| 7 #include <array> | 8 #include <array> |
| 8 #include <utility> | 9 #include <utility> |
| 10 |
| 9 #include "base/logging.h" | 11 #include "base/logging.h" |
| 10 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 11 #include "base/sys_byteorder.h" | 13 #include "base/sys_byteorder.h" |
| 12 #include "content/public/browser/browser_thread.h" | 14 #include "content/public/browser/browser_thread.h" |
| 13 #include "media/base/audio_bus.h" | 15 #include "media/base/audio_bus.h" |
| 14 | 16 |
| 15 namespace content { | 17 namespace content { |
| 16 | 18 |
| 17 namespace { | 19 namespace { |
| 18 | 20 |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 writer.WriteLE16(kBytesPerSample * 8); | 126 writer.WriteLE16(kBytesPerSample * 8); |
| 125 writer.Write(kData); | 127 writer.Write(kData); |
| 126 writer.WriteLE32(bytes_in_payload); | 128 writer.WriteLE32(bytes_in_payload); |
| 127 } | 129 } |
| 128 | 130 |
| 129 } // namespace | 131 } // namespace |
| 130 | 132 |
| 131 // Manages the debug recording file and writes to it. Can be created on any | 133 // Manages the debug recording file and writes to it. Can be created on any |
| 132 // thread. All the operations must be executed on FILE thread. Must be destroyed | 134 // thread. All the operations must be executed on FILE thread. Must be destroyed |
| 133 // on FILE thread. | 135 // on FILE thread. |
| 134 class AudioInputDebugWriter::AudioFileWriter { | 136 class AudioDebugFileWriter::AudioFileWriter { |
| 135 public: | 137 public: |
| 136 static AudioFileWriterUniquePtr Create(const base::FilePath& file_name, | 138 static AudioFileWriterUniquePtr Create(const base::FilePath& file_name, |
| 137 const media::AudioParameters& params); | 139 const media::AudioParameters& params); |
| 138 | 140 |
| 139 ~AudioFileWriter(); | 141 ~AudioFileWriter(); |
| 140 | 142 |
| 141 // Write data from |data| to file. | 143 // Write data from |data| to file. |
| 142 void Write(const media::AudioBus* data); | 144 void Write(const media::AudioBus* data); |
| 143 | 145 |
| 144 private: | 146 private: |
| (...skipping 15 matching lines...) Expand all Loading... |
| 160 | 162 |
| 161 // Input audio parameters required to build wave header. | 163 // Input audio parameters required to build wave header. |
| 162 const media::AudioParameters params_; | 164 const media::AudioParameters params_; |
| 163 | 165 |
| 164 // Intermediate buffer to be written to file. Interleaved 16 bit audio data. | 166 // Intermediate buffer to be written to file. Interleaved 16 bit audio data. |
| 165 std::unique_ptr<int16_t[]> interleaved_data_; | 167 std::unique_ptr<int16_t[]> interleaved_data_; |
| 166 int interleaved_data_size_; | 168 int interleaved_data_size_; |
| 167 }; | 169 }; |
| 168 | 170 |
| 169 // static | 171 // static |
| 170 AudioInputDebugWriter::AudioFileWriterUniquePtr | 172 AudioDebugFileWriter::AudioFileWriterUniquePtr |
| 171 AudioInputDebugWriter::AudioFileWriter::Create( | 173 AudioDebugFileWriter::AudioFileWriter::Create( |
| 172 const base::FilePath& file_name, | 174 const base::FilePath& file_name, |
| 173 const media::AudioParameters& params) { | 175 const media::AudioParameters& params) { |
| 174 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params)); | 176 AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params)); |
| 175 | 177 |
| 176 // base::Unretained is safe, because destructor is called on FILE thread or on | 178 // base::Unretained is safe, because destructor is called on FILE thread or on |
| 177 // FILE message loop destruction. | 179 // FILE message loop destruction. |
| 178 BrowserThread::PostTask( | 180 BrowserThread::PostTask( |
| 179 BrowserThread::FILE, FROM_HERE, | 181 BrowserThread::FILE, FROM_HERE, |
| 180 base::Bind(&AudioFileWriter::CreateRecordingFile, | 182 base::Bind(&AudioFileWriter::CreateRecordingFile, |
| 181 base::Unretained(file_writer.get()), file_name)); | 183 base::Unretained(file_writer.get()), file_name)); |
| 182 return file_writer; | 184 return file_writer; |
| 183 } | 185 } |
| 184 | 186 |
| 185 AudioInputDebugWriter::AudioFileWriter::AudioFileWriter( | 187 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( |
| 186 const media::AudioParameters& params) | 188 const media::AudioParameters& params) |
| 187 : samples_(0), params_(params), interleaved_data_size_(0) { | 189 : samples_(0), params_(params), interleaved_data_size_(0) { |
| 188 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); | 190 DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); |
| 189 } | 191 } |
| 190 | 192 |
| 191 AudioInputDebugWriter::AudioFileWriter::~AudioFileWriter() { | 193 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { |
| 192 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 194 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 193 if (file_.IsValid()) | 195 if (file_.IsValid()) |
| 194 WriteHeader(); | 196 WriteHeader(); |
| 195 } | 197 } |
| 196 | 198 |
| 197 void AudioInputDebugWriter::AudioFileWriter::Write( | 199 void AudioDebugFileWriter::AudioFileWriter::Write( |
| 198 const media::AudioBus* data) { | 200 const media::AudioBus* data) { |
| 199 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 201 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 200 if (!file_.IsValid()) | 202 if (!file_.IsValid()) |
| 201 return; | 203 return; |
| 202 | 204 |
| 203 // Convert to 16 bit audio and write to file. | 205 // Convert to 16 bit audio and write to file. |
| 204 int data_size = data->frames() * data->channels(); | 206 int data_size = data->frames() * data->channels(); |
| 205 if (!interleaved_data_ || interleaved_data_size_ < data_size) { | 207 if (!interleaved_data_ || interleaved_data_size_ < data_size) { |
| 206 interleaved_data_.reset(new int16_t[data_size]); | 208 interleaved_data_.reset(new int16_t[data_size]); |
| 207 interleaved_data_size_ = data_size; | 209 interleaved_data_size_ = data_size; |
| 208 } | 210 } |
| 209 samples_ += data_size; | 211 samples_ += data_size; |
| 210 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), | 212 data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), |
| 211 interleaved_data_.get()); | 213 interleaved_data_.get()); |
| 212 | 214 |
| 213 #ifndef ARCH_CPU_LITTLE_ENDIAN | 215 #ifndef ARCH_CPU_LITTLE_ENDIAN |
| 214 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), | 216 static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), |
| 215 "Only 2 bytes per channel is supported."); | 217 "Only 2 bytes per channel is supported."); |
| 216 for (int i = 0; i < data_size; ++i) | 218 for (int i = 0; i < data_size; ++i) |
| 217 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); | 219 interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); |
| 218 #endif | 220 #endif |
| 219 | 221 |
| 220 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), | 222 file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), |
| 221 data_size * sizeof(interleaved_data_[0])); | 223 data_size * sizeof(interleaved_data_[0])); |
| 222 } | 224 } |
| 223 | 225 |
| 224 void AudioInputDebugWriter::AudioFileWriter::WriteHeader() { | 226 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { |
| 225 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 227 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 226 if (!file_.IsValid()) | 228 if (!file_.IsValid()) |
| 227 return; | 229 return; |
| 228 WavHeaderBuffer buf; | 230 WavHeaderBuffer buf; |
| 229 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); | 231 WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); |
| 230 file_.Write(0, &buf[0], kWavHeaderSize); | 232 file_.Write(0, &buf[0], kWavHeaderSize); |
| 231 | 233 |
| 232 // Write() does not move the cursor if file is not in APPEND mode; Seek() so | 234 // Write() does not move the cursor if file is not in APPEND mode; Seek() so |
| 233 // that the header is not overwritten by the following writes. | 235 // that the header is not overwritten by the following writes. |
| 234 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); | 236 file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); |
| 235 } | 237 } |
| 236 | 238 |
| 237 void AudioInputDebugWriter::AudioFileWriter::CreateRecordingFile( | 239 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( |
| 238 const base::FilePath& file_name) { | 240 const base::FilePath& file_name) { |
| 239 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 241 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 240 DCHECK(!file_.IsValid()); | 242 DCHECK(!file_.IsValid()); |
| 241 | 243 |
| 242 file_ = base::File(file_name, | 244 file_ = base::File(file_name, |
| 243 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | 245 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| 244 | 246 |
| 245 if (file_.IsValid()) { | 247 if (file_.IsValid()) { |
| 246 WriteHeader(); | 248 WriteHeader(); |
| 247 return; | 249 return; |
| 248 } | 250 } |
| 249 | 251 |
| 250 // Note that we do not inform AudioInputDebugWriter that the file creation | 252 // Note that we do not inform AudioDebugFileWriter that the file creation |
| 251 // fails, so it will continue to post data to be recorded, which won't | 253 // 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 | 254 // 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 | 255 // 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 | 256 // 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 | 257 // 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. | 258 // means file_.IsValid() should always be checked before issuing writes to it. |
| 257 PLOG(ERROR) << "Could not open debug recording file, error=" | 259 PLOG(ERROR) << "Could not open debug recording file, error=" |
| 258 << file_.error_details(); | 260 << file_.error_details(); |
| 259 } | 261 } |
| 260 | 262 |
| 261 AudioInputDebugWriter::AudioInputDebugWriter( | 263 AudioDebugFileWriter::AudioDebugFileWriter( |
| 262 const media::AudioParameters& params) | 264 const media::AudioParameters& params) |
| 263 : params_(params) { | 265 : params_(params) { |
| 264 client_sequence_checker_.DetachFromSequence(); | 266 client_sequence_checker_.DetachFromSequence(); |
| 265 } | 267 } |
| 266 | 268 |
| 267 AudioInputDebugWriter::~AudioInputDebugWriter() { | 269 AudioDebugFileWriter::~AudioDebugFileWriter() { |
| 268 // |file_writer_| will be deleted on FILE thread. | 270 // |file_writer_| will be deleted on FILE thread. |
| 269 } | 271 } |
| 270 | 272 |
| 271 void AudioInputDebugWriter::Start(const base::FilePath& file_name) { | 273 void AudioDebugFileWriter::Start(const base::FilePath& file_name) { |
| 272 DCHECK(client_sequence_checker_.CalledOnValidSequence()); | 274 DCHECK(client_sequence_checker_.CalledOnValidSequence()); |
| 273 DCHECK(!file_writer_); | 275 DCHECK(!file_writer_); |
| 274 file_writer_ = AudioFileWriter::Create(file_name, params_); | 276 file_writer_ = AudioFileWriter::Create(file_name, params_); |
| 275 } | 277 } |
| 276 | 278 |
| 277 void AudioInputDebugWriter::Stop() { | 279 void AudioDebugFileWriter::Stop() { |
| 278 DCHECK(client_sequence_checker_.CalledOnValidSequence()); | 280 DCHECK(client_sequence_checker_.CalledOnValidSequence()); |
| 279 // |file_writer_| is deleted on FILE thread. | 281 // |file_writer_| is deleted on FILE thread. |
| 280 file_writer_.reset(); | 282 file_writer_.reset(); |
| 281 client_sequence_checker_.DetachFromSequence(); | 283 client_sequence_checker_.DetachFromSequence(); |
| 282 } | 284 } |
| 283 | 285 |
| 284 void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) { | 286 void AudioDebugFileWriter::Write(std::unique_ptr<media::AudioBus> data) { |
| 285 DCHECK(client_sequence_checker_.CalledOnValidSequence()); | 287 DCHECK(client_sequence_checker_.CalledOnValidSequence()); |
| 286 if (!file_writer_) | 288 if (!file_writer_) |
| 287 return; | 289 return; |
| 288 | 290 |
| 289 // base::Unretained for |file_writer_| is safe, see the destructor. | 291 // base::Unretained for |file_writer_| is safe, see the destructor. |
| 290 BrowserThread::PostTask( | 292 BrowserThread::PostTask( |
| 291 BrowserThread::FILE, FROM_HERE, | 293 BrowserThread::FILE, FROM_HERE, |
| 292 // Callback takes ownership of |data|: | 294 // Callback takes ownership of |data|: |
| 293 base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()), | 295 base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()), |
| 294 base::Owned(data.release()))); | 296 base::Owned(data.release()))); |
| 295 } | 297 } |
| 296 | 298 |
| 297 bool AudioInputDebugWriter::WillWrite() { | 299 bool AudioDebugFileWriter::WillWrite() { |
| 298 // Note that if this is called from any place other than | 300 // 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, | 301 // |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 | 302 // 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 | 303 // here, but it's fine: we can afford missing some data or scheduling some |
| 302 // no-op writes. | 304 // no-op writes. |
| 303 return !!file_writer_; | 305 return !!file_writer_; |
| 304 } | 306 } |
| 305 | 307 |
| 306 } // namspace content | 308 } // namspace content |
| OLD | NEW |