| 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_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/logging.h" |   12 #include "base/logging.h" | 
|   12 #include "base/memory/ptr_util.h" |   13 #include "base/memory/ptr_util.h" | 
|   13 #include "base/sys_byteorder.h" |   14 #include "base/sys_byteorder.h" | 
|   14 #include "content/public/browser/browser_thread.h" |  | 
|   15 #include "media/base/audio_bus.h" |   15 #include "media/base/audio_bus.h" | 
|   16  |   16  | 
|   17 namespace content { |   17 namespace media { | 
|   18  |   18  | 
|   19 namespace { |   19 namespace { | 
|   20  |   20  | 
|   21 // Windows WAVE format header |   21 // Windows WAVE format header | 
|   22 // Byte order: Little-endian |   22 // Byte order: Little-endian | 
|   23 // Offset Length  Content |   23 // Offset Length  Content | 
|   24 //  0      4     "RIFF" |   24 //  0      4     "RIFF" | 
|   25 //  4      4     <file length - 8> |   25 //  4      4     <file length - 8> | 
|   26 //  8      4     "WAVE" |   26 //  8      4     "WAVE" | 
|   27 // 12      4     "fmt " |   27 // 12      4     "fmt " | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
|   56  public: |   56  public: | 
|   57   CharBufferWriter(char* buf, int max_size) |   57   CharBufferWriter(char* buf, int max_size) | 
|   58       : buf_(buf), max_size_(max_size), size_(0) {} |   58       : buf_(buf), max_size_(max_size), size_(0) {} | 
|   59  |   59  | 
|   60   void Write(const char* data, int data_size) { |   60   void Write(const char* data, int data_size) { | 
|   61     CHECK_LE(size_ + data_size, max_size_); |   61     CHECK_LE(size_ + data_size, max_size_); | 
|   62     memcpy(&buf_[size_], data, data_size); |   62     memcpy(&buf_[size_], data, data_size); | 
|   63     size_ += data_size; |   63     size_ += data_size; | 
|   64   } |   64   } | 
|   65  |   65  | 
|   66   void Write(const char(&data)[4]) { Write(static_cast<const char*>(data), 4); } |   66   void Write(const char (&data)[4]) { | 
 |   67     Write(static_cast<const char*>(data), 4); | 
 |   68   } | 
|   67  |   69  | 
|   68   void WriteLE16(uint16_t data) { |   70   void WriteLE16(uint16_t data) { | 
|   69     uint16_t val = base::ByteSwapToLE16(data); |   71     uint16_t val = base::ByteSwapToLE16(data); | 
|   70     Write(reinterpret_cast<const char*>(&val), sizeof(val)); |   72     Write(reinterpret_cast<const char*>(&val), sizeof(val)); | 
|   71   } |   73   } | 
|   72  |   74  | 
|   73   void WriteLE32(uint32_t data) { |   75   void WriteLE32(uint32_t data) { | 
|   74     uint32_t val = base::ByteSwapToLE32(data); |   76     uint32_t val = base::ByteSwapToLE32(data); | 
|   75     Write(reinterpret_cast<const char*>(&val), sizeof(val)); |   77     Write(reinterpret_cast<const char*>(&val), sizeof(val)); | 
|   76   } |   78   } | 
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  124   writer.WriteLE32(byte_rate); |  126   writer.WriteLE32(byte_rate); | 
|  125   writer.WriteLE16(block_align); |  127   writer.WriteLE16(block_align); | 
|  126   writer.WriteLE16(kBytesPerSample * 8); |  128   writer.WriteLE16(kBytesPerSample * 8); | 
|  127   writer.Write(kData); |  129   writer.Write(kData); | 
|  128   writer.WriteLE32(bytes_in_payload); |  130   writer.WriteLE32(bytes_in_payload); | 
|  129 } |  131 } | 
|  130  |  132  | 
|  131 }  // namespace |  133 }  // namespace | 
|  132  |  134  | 
|  133 // Manages the debug recording file and writes to it. Can be created on any |  135 // Manages the debug recording file and writes to it. Can be created on any | 
|  134 // thread. All the operations must be executed on FILE thread. Must be destroyed |  136 // thread. All the operations must be executed on |task_runner_|. Must be | 
|  135 // on FILE thread. |  137 // destroyed on |task_runner_|. | 
|  136 class AudioDebugFileWriter::AudioFileWriter { |  138 class AudioDebugFileWriter::AudioFileWriter { | 
|  137  public: |  139  public: | 
|  138   static AudioFileWriterUniquePtr Create(const base::FilePath& file_name, |  140   static AudioFileWriterUniquePtr Create( | 
|  139                                          const media::AudioParameters& params); |  141       const base::FilePath& file_name, | 
 |  142       const AudioParameters& params, | 
 |  143       scoped_refptr<base::SingleThreadTaskRunner> task_runner); | 
|  140  |  144  | 
|  141   ~AudioFileWriter(); |  145   ~AudioFileWriter(); | 
|  142  |  146  | 
|  143   // Write data from |data| to file. |  147   // Write data from |data| to file. | 
|  144   void Write(const media::AudioBus* data); |  148   void Write(const AudioBus* data); | 
 |  149  | 
 |  150   scoped_refptr<base::SingleThreadTaskRunner> task_runner() { | 
 |  151     return task_runner_; | 
 |  152   } | 
|  145  |  153  | 
|  146  private: |  154  private: | 
|  147   AudioFileWriter(const media::AudioParameters& params); |  155   AudioFileWriter(const AudioParameters& params, | 
 |  156                   scoped_refptr<base::SingleThreadTaskRunner> task_runner); | 
|  148  |  157  | 
|  149   // Write wave header to file. Called on the FILE thread twice: on construction |  158   // Write wave header to file. Called on the |task_runner_| twice: on | 
 |  159   // construction | 
|  150   // of AudioFileWriter size of the wave data is unknown, so the header is |  160   // 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 |  161   // written with zero sizes; then on destruction it is re-written with the | 
|  152   // actual size info accumulated throughout the object lifetime. |  162   // actual size info accumulated throughout the object lifetime. | 
|  153   void WriteHeader(); |  163   void WriteHeader(); | 
|  154  |  164  | 
|  155   void CreateRecordingFile(const base::FilePath& file_name); |  165   void CreateRecordingFile(const base::FilePath& file_name); | 
|  156  |  166  | 
|  157   // The file to write to. |  167   // The file to write to. | 
|  158   base::File file_; |  168   base::File file_; | 
|  159  |  169  | 
|  160   // Number of written samples. |  170   // Number of written samples. | 
|  161   uint64_t samples_; |  171   uint64_t samples_; | 
|  162  |  172  | 
|  163   // Input audio parameters required to build wave header. |  173   // Input audio parameters required to build wave header. | 
|  164   const media::AudioParameters params_; |  174   const AudioParameters params_; | 
|  165  |  175  | 
|  166   // Intermediate buffer to be written to file. Interleaved 16 bit audio data. |  176   // Intermediate buffer to be written to file. Interleaved 16 bit audio data. | 
|  167   std::unique_ptr<int16_t[]> interleaved_data_; |  177   std::unique_ptr<int16_t[]> interleaved_data_; | 
|  168   int interleaved_data_size_; |  178   int interleaved_data_size_; | 
 |  179  | 
 |  180   // The task runner this class operates on. | 
 |  181   scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 
|  169 }; |  182 }; | 
|  170  |  183  | 
 |  184 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter() {} | 
 |  185  | 
 |  186 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter( | 
 |  187     const OnThreadDeleter& other) | 
 |  188     : task_runner_(other.task_runner_) {} | 
 |  189  | 
 |  190 AudioDebugFileWriter::OnThreadDeleter::OnThreadDeleter( | 
 |  191     scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 
 |  192     : task_runner_(task_runner) {} | 
 |  193  | 
 |  194 AudioDebugFileWriter::OnThreadDeleter::~OnThreadDeleter() {} | 
 |  195  | 
 |  196 // AudioFileWriter deleter. Inspired by | 
 |  197 // content::BrowserThread::DeleteOnFileThread. | 
 |  198 void AudioDebugFileWriter::OnThreadDeleter::operator()( | 
 |  199     AudioDebugFileWriter::AudioFileWriter* ptr) const { | 
 |  200   if (!task_runner_->DeleteSoon(FROM_HERE, ptr)) { | 
 |  201 #if defined(UNIT_TEST) | 
 |  202     // Only logged under unit testing because leaks at shutdown | 
 |  203     // are acceptable under normal circumstances. | 
 |  204     LOG(ERROR) << "DeleteSoon failed for AudioDebugFileWriter::AudioFileWriter"; | 
 |  205 #endif | 
 |  206   } | 
 |  207 } | 
 |  208  | 
|  171 // static |  209 // static | 
|  172 AudioDebugFileWriter::AudioFileWriterUniquePtr |  210 AudioDebugFileWriter::AudioFileWriterUniquePtr | 
|  173 AudioDebugFileWriter::AudioFileWriter::Create( |  211 AudioDebugFileWriter::AudioFileWriter::Create( | 
|  174     const base::FilePath& file_name, |  212     const base::FilePath& file_name, | 
|  175     const media::AudioParameters& params) { |  213     const AudioParameters& params, | 
|  176   AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params)); |  214     scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | 
 |  215   AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params, task_runner), | 
 |  216                                        OnThreadDeleter(task_runner)); | 
|  177  |  217  | 
|  178   // base::Unretained is safe, because destructor is called on FILE thread or on |  218   // base::Unretained is safe, because destructor is called on | 
|  179   // FILE message loop destruction. |  219   // |task_runner|. | 
|  180   BrowserThread::PostTask( |  220   task_runner->PostTask( | 
|  181       BrowserThread::FILE, FROM_HERE, |  221       FROM_HERE, | 
|  182       base::Bind(&AudioFileWriter::CreateRecordingFile, |  222       base::Bind(&AudioFileWriter::CreateRecordingFile, | 
|  183                  base::Unretained(file_writer.get()), file_name)); |  223                  base::Unretained(file_writer.get()), file_name)); | 
|  184   return file_writer; |  224   return file_writer; | 
|  185 } |  225 } | 
|  186  |  226  | 
|  187 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( |  227 AudioDebugFileWriter::AudioFileWriter::AudioFileWriter( | 
|  188     const media::AudioParameters& params) |  228     const AudioParameters& params, | 
|  189     : samples_(0), params_(params), interleaved_data_size_(0) { |  229     scoped_refptr<base::SingleThreadTaskRunner> task_runner) | 
 |  230     : samples_(0), | 
 |  231       params_(params), | 
 |  232       interleaved_data_size_(0), | 
 |  233       task_runner_(std::move(task_runner)) { | 
|  190   DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); |  234   DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8); | 
|  191 } |  235 } | 
|  192  |  236  | 
|  193 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { |  237 AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() { | 
|  194   DCHECK_CURRENTLY_ON(BrowserThread::FILE); |  238   DCHECK(task_runner_->BelongsToCurrentThread()); | 
|  195   if (file_.IsValid()) |  239   if (file_.IsValid()) | 
|  196     WriteHeader(); |  240     WriteHeader(); | 
|  197 } |  241 } | 
|  198  |  242  | 
|  199 void AudioDebugFileWriter::AudioFileWriter::Write( |  243 void AudioDebugFileWriter::AudioFileWriter::Write(const AudioBus* data) { | 
|  200     const media::AudioBus* data) { |  244   DCHECK(task_runner_->BelongsToCurrentThread()); | 
|  201   DCHECK_CURRENTLY_ON(BrowserThread::FILE); |  | 
|  202   if (!file_.IsValid()) |  245   if (!file_.IsValid()) | 
|  203     return; |  246     return; | 
|  204  |  247  | 
|  205   // Convert to 16 bit audio and write to file. |  248   // Convert to 16 bit audio and write to file. | 
|  206   int data_size = data->frames() * data->channels(); |  249   int data_size = data->frames() * data->channels(); | 
|  207   if (!interleaved_data_ || interleaved_data_size_ < data_size) { |  250   if (!interleaved_data_ || interleaved_data_size_ < data_size) { | 
|  208     interleaved_data_.reset(new int16_t[data_size]); |  251     interleaved_data_.reset(new int16_t[data_size]); | 
|  209     interleaved_data_size_ = data_size; |  252     interleaved_data_size_ = data_size; | 
|  210   } |  253   } | 
|  211   samples_ += data_size; |  254   samples_ += data_size; | 
|  212   data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), |  255   data->ToInterleaved(data->frames(), sizeof(interleaved_data_[0]), | 
|  213                       interleaved_data_.get()); |  256                       interleaved_data_.get()); | 
|  214  |  257  | 
|  215 #ifndef ARCH_CPU_LITTLE_ENDIAN |  258 #ifndef ARCH_CPU_LITTLE_ENDIAN | 
|  216   static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), |  259   static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t), | 
|  217                 "Only 2 bytes per channel is supported."); |  260                 "Only 2 bytes per channel is supported."); | 
|  218   for (int i = 0; i < data_size; ++i) |  261   for (int i = 0; i < data_size; ++i) | 
|  219     interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); |  262     interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]); | 
|  220 #endif |  263 #endif | 
|  221  |  264  | 
|  222   file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), |  265   file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()), | 
|  223                           data_size * sizeof(interleaved_data_[0])); |  266                           data_size * sizeof(interleaved_data_[0])); | 
|  224 } |  267 } | 
|  225  |  268  | 
|  226 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { |  269 void AudioDebugFileWriter::AudioFileWriter::WriteHeader() { | 
|  227   DCHECK_CURRENTLY_ON(BrowserThread::FILE); |  270   DCHECK(task_runner_->BelongsToCurrentThread()); | 
|  228   if (!file_.IsValid()) |  271   if (!file_.IsValid()) | 
|  229     return; |  272     return; | 
|  230   WavHeaderBuffer buf; |  273   WavHeaderBuffer buf; | 
|  231   WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); |  274   WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_); | 
|  232   file_.Write(0, &buf[0], kWavHeaderSize); |  275   file_.Write(0, &buf[0], kWavHeaderSize); | 
|  233  |  276  | 
|  234   // Write() does not move the cursor if file is not in APPEND mode; Seek() so |  277   // Write() does not move the cursor if file is not in APPEND mode; Seek() so | 
|  235   // that the header is not overwritten by the following writes. |  278   // that the header is not overwritten by the following writes. | 
|  236   file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); |  279   file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize); | 
|  237 } |  280 } | 
|  238  |  281  | 
|  239 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( |  282 void AudioDebugFileWriter::AudioFileWriter::CreateRecordingFile( | 
|  240     const base::FilePath& file_name) { |  283     const base::FilePath& file_name) { | 
|  241   DCHECK_CURRENTLY_ON(BrowserThread::FILE); |  284   DCHECK(task_runner_->BelongsToCurrentThread()); | 
|  242   DCHECK(!file_.IsValid()); |  285   DCHECK(!file_.IsValid()); | 
|  243  |  286  | 
|  244   file_ = base::File(file_name, |  287   file_ = base::File(file_name, | 
|  245                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |  288                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | 
|  246  |  289  | 
|  247   if (file_.IsValid()) { |  290   if (file_.IsValid()) { | 
|  248     WriteHeader(); |  291     WriteHeader(); | 
|  249     return; |  292     return; | 
|  250   } |  293   } | 
|  251  |  294  | 
|  252   // Note that we do not inform AudioDebugFileWriter that the file creation |  295   // Note that we do not inform AudioDebugFileWriter that the file creation | 
|  253   // fails, so it will continue to post data to be recorded, which won't |  296   // fails, so it will continue to post data to be recorded, which won't | 
|  254   // be written to the file. This also won't be reflected in WillWrite(). It's |  297   // be written to the file. This also won't be reflected in WillWrite(). It's | 
|  255   // fine, because this situation is rare, and all the posting is expected to |  298   // fine, because this situation is rare, and all the posting is expected to | 
|  256   // happen in case of success anyways. This allows us to save on thread hops |  299   // happen in case of success anyways. This allows us to save on thread hops | 
|  257   // for error reporting and to avoid dealing with lifetime issues. It also |  300   // for error reporting and to avoid dealing with lifetime issues. It also | 
|  258   // means file_.IsValid() should always be checked before issuing writes to it. |  301   // means file_.IsValid() should always be checked before issuing writes to it. | 
|  259   PLOG(ERROR) << "Could not open debug recording file, error=" |  302   PLOG(ERROR) << "Could not open debug recording file, error=" | 
|  260               << file_.error_details(); |  303               << file_.error_details(); | 
|  261 } |  304 } | 
|  262  |  305  | 
|  263 AudioDebugFileWriter::AudioDebugFileWriter( |  306 AudioDebugFileWriter::AudioDebugFileWriter( | 
|  264     const media::AudioParameters& params) |  307     const AudioParameters& params, | 
|  265     : params_(params) { |  308     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) | 
 |  309     : params_(params), file_task_runner_(std::move(file_task_runner)) { | 
|  266   client_sequence_checker_.DetachFromSequence(); |  310   client_sequence_checker_.DetachFromSequence(); | 
|  267 } |  311 } | 
|  268  |  312  | 
|  269 AudioDebugFileWriter::~AudioDebugFileWriter() { |  313 AudioDebugFileWriter::~AudioDebugFileWriter() { | 
|  270   // |file_writer_| will be deleted on FILE thread. |  314   // |file_writer_| will be deleted on |task_runner_|. | 
|  271 } |  315 } | 
|  272  |  316  | 
|  273 void AudioDebugFileWriter::Start(const base::FilePath& file_name) { |  317 void AudioDebugFileWriter::Start(const base::FilePath& file_name) { | 
|  274   DCHECK(client_sequence_checker_.CalledOnValidSequence()); |  318   DCHECK(client_sequence_checker_.CalledOnValidSequence()); | 
|  275   DCHECK(!file_writer_); |  319   DCHECK(!file_writer_); | 
|  276   file_writer_ = AudioFileWriter::Create(file_name, params_); |  320   file_writer_ = AudioFileWriter::Create(file_name, params_, file_task_runner_); | 
|  277 } |  321 } | 
|  278  |  322  | 
|  279 void AudioDebugFileWriter::Stop() { |  323 void AudioDebugFileWriter::Stop() { | 
|  280   DCHECK(client_sequence_checker_.CalledOnValidSequence()); |  324   DCHECK(client_sequence_checker_.CalledOnValidSequence()); | 
|  281   // |file_writer_| is deleted on FILE thread. |  325   // |file_writer_| is deleted on FILE thread. | 
|  282   file_writer_.reset(); |  326   file_writer_.reset(); | 
|  283   client_sequence_checker_.DetachFromSequence(); |  327   client_sequence_checker_.DetachFromSequence(); | 
|  284 } |  328 } | 
|  285  |  329  | 
|  286 void AudioDebugFileWriter::Write(std::unique_ptr<media::AudioBus> data) { |  330 void AudioDebugFileWriter::Write(std::unique_ptr<AudioBus> data) { | 
|  287   DCHECK(client_sequence_checker_.CalledOnValidSequence()); |  331   DCHECK(client_sequence_checker_.CalledOnValidSequence()); | 
|  288   if (!file_writer_) |  332   if (!file_writer_) | 
|  289     return; |  333     return; | 
|  290  |  334  | 
|  291   // base::Unretained for |file_writer_| is safe, see the destructor. |  335   // base::Unretained for |file_writer_| is safe, see the destructor. | 
|  292   BrowserThread::PostTask( |  336   file_task_runner_->PostTask( | 
|  293       BrowserThread::FILE, FROM_HERE, |  337       FROM_HERE, | 
|  294       // Callback takes ownership of |data|: |  338       // Callback takes ownership of |data|: | 
|  295       base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()), |  339       base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()), | 
|  296                  base::Owned(data.release()))); |  340                  base::Owned(data.release()))); | 
|  297 } |  341 } | 
|  298  |  342  | 
|  299 bool AudioDebugFileWriter::WillWrite() { |  343 bool AudioDebugFileWriter::WillWrite() { | 
|  300   // Note that if this is called from any place other than |  344   // Note that if this is called from any place other than | 
|  301   // |client_sequence_checker_| then there is a data race here, but it's fine, |  345   // |client_sequence_checker_| then there is a data race here, but it's fine, | 
|  302   // because Write() will check for |file_writer_|. So, we are not very precise |  346   // because Write() will check for |file_writer_|. So, we are not very precise | 
|  303   // here, but it's fine: we can afford missing some data or scheduling some |  347   // here, but it's fine: we can afford missing some data or scheduling some | 
|  304   // no-op writes. |  348   // no-op writes. | 
|  305   return !!file_writer_; |  349   return !!file_writer_; | 
|  306 } |  350 } | 
|  307  |  351  | 
|  308 }  // namspace content |  352 }  // namspace media | 
| OLD | NEW |