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

Unified 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: review comments addressed 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 side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/media/audio_input_debug_writer.cc
diff --git a/content/browser/renderer_host/media/audio_input_debug_writer.cc b/content/browser/renderer_host/media/audio_input_debug_writer.cc
index 7866c651a34288ab83a26094cdf3d334cf2ed549..3ddc01e60778c77fc70ad1dbfbba7504b8c81783 100644
--- a/content/browser/renderer_host/media/audio_input_debug_writer.cc
+++ b/content/browser/renderer_host/media/audio_input_debug_writer.cc
@@ -7,6 +7,7 @@
#include <array>
#include <utility>
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/sys_byteorder.h"
#include "content/public/browser/browser_thread.h"
#include "media/base/audio_bus.h"
@@ -127,36 +128,82 @@ void WriteWavHeader(WavHeaderBuffer* buf,
} // namespace
-AudioInputDebugWriter::AudioInputDebugWriter(
- base::File file,
- const media::AudioParameters& params)
- : file_(std::move(file)),
- samples_(0),
- params_(params),
- interleaved_data_size_(0),
- weak_factory_(this) {
- DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8);
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&AudioInputDebugWriter::WriteHeader,
- weak_factory_.GetWeakPtr()));
+class AudioInputDebugWriter::AudioFileWriter {
+ public:
+ static std::unique_ptr<AudioFileWriter> Create(
+ const base::FilePath& file_name,
+ const media::AudioParameters& params);
+
+ // Must be called on FILE thread or on FILE message loop destruction.
+ ~AudioFileWriter();
o1ka 2016/10/11 12:11:24 The question I have not found an answer for so far
+
+ // Write data from |data| to file. Called on the FILE thread.
+ void Write(const media::AudioBus* data);
+
+ // Must be called on FILE thread.
Henrik Grunell 2016/10/11 13:07:24 Nit: Does this class live only on the FILE thread?
o1ka 2016/10/11 14:04:36 Done.
+ void Close();
+
+ private:
+ AudioFileWriter(const media::AudioParameters& params);
+
+ // Write wave header to file. Called on the FILE thread twice: on construction
+ // of AudioFileWriter size of the wave data is unknown, so the header is
+ // written with zero sizes; then on destruction it is re-written with the
+ // actual size info accumulated throughout the object lifetime.
+ void WriteHeader();
+
+ void CreateRecordingFile(const base::FilePath& file_name);
+
+ // The file to write to.
+ base::File file_;
+
+ // Number of written samples.
+ uint64_t samples_;
+
+ // Input audio parameters required to build wave header.
+ const media::AudioParameters params_;
+
+ // Intermediate buffer to be written to file. Interleaved 16 bit audio data.
+ std::unique_ptr<int16_t[]> interleaved_data_;
+ int interleaved_data_size_;
+};
+
+// static
+std::unique_ptr<AudioInputDebugWriter::AudioFileWriter>
+AudioInputDebugWriter::AudioFileWriter::Create(
+ const base::FilePath& file_name,
+ const media::AudioParameters& params) {
+ std::unique_ptr<AudioInputDebugWriter::AudioFileWriter> file_writer(
+ new AudioFileWriter(params));
+
+ // base::Unretained is safe, because destructor is called on FILE thread or on
+ // FILE message loop destruction.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&AudioFileWriter::CreateRecordingFile,
+ base::Unretained(file_writer.get()), file_name));
+ return file_writer;
}
-AudioInputDebugWriter::~AudioInputDebugWriter() {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- WriteHeader();
+AudioInputDebugWriter::AudioFileWriter::AudioFileWriter(
+ const media::AudioParameters& params)
+ : samples_(0), params_(params), interleaved_data_size_(0) {
+ DCHECK_EQ(params.bits_per_sample(), kBytesPerSample * 8);
}
-void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) {
- BrowserThread::PostTask(
- BrowserThread::FILE,
- FROM_HERE,
- base::Bind(&AudioInputDebugWriter::DoWrite,
- weak_factory_.GetWeakPtr(),
- base::Passed(&data)));
+AudioInputDebugWriter::AudioFileWriter::~AudioFileWriter() {
Henrik Grunell 2016/10/11 13:07:24 Nit: DCHECK correct thread?
o1ka 2016/10/11 14:04:37 Seem my question above :) But I changed the code a
Henrik Grunell 2016/10/13 07:26:11 Great!
+ // In case a scheduled Close() call has not been executed because message loop
+ // is destroyed.
+ if (file_.IsValid())
+ WriteHeader();
}
-void AudioInputDebugWriter::DoWrite(std::unique_ptr<media::AudioBus> data) {
+void AudioInputDebugWriter::AudioFileWriter::Write(
+ const media::AudioBus* data) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ if (!file_.IsValid())
+ return;
+
// Convert to 16 bit audio and write to file.
int data_size = data->frames() * data->channels();
if (!interleaved_data_ || interleaved_data_size_ < data_size) {
@@ -178,13 +225,17 @@ void AudioInputDebugWriter::DoWrite(std::unique_ptr<media::AudioBus> data) {
data_size * sizeof(interleaved_data_[0]));
}
-// This method is called twice: on construction of AudioInputDebugWriter size of
-// the data is unknown, so the header is written with zero sizes; then on
-// destruction it is re-written with the actual size info accumulated throughout
-// its lifetime.
-void AudioInputDebugWriter::WriteHeader() {
+void AudioInputDebugWriter::AudioFileWriter::Close() {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ if (file_.IsValid())
+ WriteHeader();
+ file_ = base::File();
+}
+void AudioInputDebugWriter::AudioFileWriter::WriteHeader() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ if (!file_.IsValid())
+ return;
WavHeaderBuffer buf;
WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_);
file_.Write(0, &buf[0], kWavHeaderSize);
@@ -194,4 +245,81 @@ void AudioInputDebugWriter::WriteHeader() {
file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize);
}
+void AudioInputDebugWriter::AudioFileWriter::CreateRecordingFile(
+ const base::FilePath& file_name) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ DCHECK(!file_.IsValid());
+
+ file_ = base::File(file_name,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+
+ if (file_.IsValid()) {
+ WriteHeader();
+ return;
+ }
+
+ // Note that we do not inform AudioInputDebugWriter that the file creation
+ // fails, so it will continue to post data to be recorded, which won't
+ // be written to the file. This also won't be reflect in WillWrite(). It's
Guido Urdaneta 2016/10/11 12:39:25 nit: reflect -> reflected
o1ka 2016/10/11 14:04:37 Done.
+ // fine, because this situation is rare, and all the posting is expected to
+ // happen in case of success anyways. This allow us to save on thread hops for
Guido Urdaneta 2016/10/11 12:39:25 nit: allow -> allows
o1ka 2016/10/11 14:04:36 Done.
+ // error reporting and to avoid dealing with lifetime issues. It also means
+ // file_.IsValid() should always be checked before issuing writes to it.
+ PLOG(ERROR) << "Could not open debug recording file, error="
+ << file_.error_details();
+}
+
+AudioInputDebugWriter::AudioInputDebugWriter(
+ const media::AudioParameters& params)
+ : params_(params) {
+ client_sequence_checker_.DetachFromSequence();
+}
+
+AudioInputDebugWriter::~AudioInputDebugWriter() {
+ Stop();
+}
+
+void AudioInputDebugWriter::Start(const base::FilePath& file_name) {
+ DCHECK(client_sequence_checker_.CalledOnValidSequence());
+ DCHECK(!file_writer_);
+ file_writer_ = AudioFileWriter::Create(file_name, params_);
+}
+
+void AudioInputDebugWriter::Stop() {
+ DCHECK(client_sequence_checker_.CalledOnValidSequence());
+ // Callback takes ownership of |file_writer_|, so it will be deleted after
+ // Close() is executed or when FILE thread message loop is destroyed. Posting
+ // non-nestable just like in SequencedTaskRunner::DeleteSoon().
o1ka 2016/10/11 12:11:24 I still do not quite understand this "nestability"
Henrik Grunell 2016/10/11 13:07:24 Afaict, if you want to guarantee it's run after an
o1ka 2016/10/11 14:04:36 Ok, looks like non-netsable post is needed if I wa
+ if (file_writer_) {
+ BrowserThread::PostNonNestableTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&AudioFileWriter::Close,
+ base::Owned(file_writer_.release())));
+ }
+
+ client_sequence_checker_.DetachFromSequence();
+}
+
+void AudioInputDebugWriter::Write(std::unique_ptr<media::AudioBus> data) {
+ DCHECK(client_sequence_checker_.CalledOnValidSequence());
+ if (!file_writer_)
+ return;
+
+ // base::Unretained for |file_writer_| is safe, see the destructor.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ // Callback takes ownership of |data|:
+ base::Bind(&AudioFileWriter::Write, base::Unretained(file_writer_.get()),
+ base::Owned(data.release())));
+}
+
+bool AudioInputDebugWriter::WillWrite() {
+ // Note that if this is called from any place other than
+ // |client_sequence_checker_|
o1ka 2016/10/11 12:11:24 will fix (git cl format did not do a perfect job h
Henrik Grunell 2016/10/11 13:07:24 git cl format doesn't format comments afaik.
o1ka 2016/10/11 14:04:36 it wraps lines.
+ // then there is a data race here, but it's fine, because Write() will check
+ // for |file_writer_|. So, we are not very precies here, but it's fine: we can
Guido Urdaneta 2016/10/11 12:39:25 nit: precies -> precise
o1ka 2016/10/11 14:04:36 Done.
+ // afford missing some data or scheduling some no-op writes.
+ return !!file_writer_;
+}
+
} // namspace content

Powered by Google App Engine
This is Rietveld 408576698