blob: f7cd1e4615e118a51dad3735f688a423aaf19118 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_CAPTURE_MODE_VIDEO_FILE_HANDLER_H_
#define ASH_CAPTURE_MODE_VIDEO_FILE_HANDLER_H_
#include <string>
#include <vector>
#include "ash/ash_export.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/thread_annotations.h"
#include "base/threading/sequence_bound.h"
namespace ash {
// Defines an object that takes care of creating and saving video contents to a
// file with a given |path_|. Video contents are received from the Recording
// Service as a sequence of byte chunks on the UI thread, however creating the
// file and appending those chunks to it must be done on a blocking thread,
// whose |blocking_task_runner| is given to |Create()|.
//
// Depending on the audio/video contents and their encoding, and the amount of
// muxed chunks buffering done in the recording service itself, there can be
// many chunks received every second. Therefore, it would be very expensive to
// write to the file system every time we receive a chunk, so this object also
// takes care of caching those chunks by std::move()'ing them into
// |buffered_chunks_| until the total size of the cached chunks |current_size_|
// is nearing the maximum allowed |max_buffered_bytes_|, at which point, the
// buffered chunks will be appended to the file, and |buffered_chunks_| will be
// cleared.
//
// A maximum |max_buffered_bytes_| is enforced on the total number of bytes of
// all the cached chunks in |buffered_chunks_| to keep things deterministic, and
// avoid video recording consuming too much memory and eventually OOM'ing the
// system.
//
// An instance of this object can only be accessed on the blocking pool
// sequenced task runner given to |Create()|, which will construct a
// |base::SequenceBound| wrapper around an instance that guarantees all
// operations, including construction and destruction happening in sequence on
// the provided blooking pool. This asserts that destruction of this object
// happens only after all chunks have been flushed to the file.
class ASH_EXPORT VideoFileHandler {
public:
VideoFileHandler(const VideoFileHandler&) = delete;
VideoFileHandler& operator=(const VideoFileHandler&) = delete;
// Creates a |base::SequenceBound| wrapping an instance of VideoFileHandler
// that enforces all operations this object provides can only be done
// asynchronously on the given |blocking_task_runner|. This should be called
// on the UI thread. |max_buffered_bytes| should be chosen to be big enough to
// allow caching of many video chunks before we need to write to disk.
static base::SequenceBound<VideoFileHandler> Create(
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
const base::FilePath& path,
size_t max_buffered_bytes);
// Must be done only once before all other operations to open/create the file
// at |path_| via |file_| in preparation for future writes. The file remains
// open until this object is destroyed. Returns true on success, and false
// otherwise. |success_| is also updated with this result before returning.
// Callers must check that result before doing further operations since if
// this fails, all future IO will fail too.
bool Initialize();
// Returns whether all IO operations done so far were successful.
bool GetSuccessStatus() const;
// Depending of the number of bytes currently cached, the given |chunk| is
// either cached or appended to the file at |path_| along with any previously
// cached chunks.
// |success_| will be updated and returned as the result.
bool AppendChunk(std::string chunk);
// Flushes the currently cached chunks in |buffered_chunks_| by appending them
// to |file_| and clearing the buffer so further chunks can be cached.
// |success_| will be updated and returned as the result.
bool FlushBufferedChunks();
private:
friend class base::SequenceBound<VideoFileHandler>;
VideoFileHandler(const base::FilePath& path, size_t max_buffered_bytes);
~VideoFileHandler();
// Appends the given |chunk| to |file_| at its current position, and updates
// |success_| with the result.
void AppendToFile(const std::string& chunk);
// The video file path.
const base::FilePath path_;
// This is the maximum allowed number of bytes that the total size of all the
// chunks cached in |buffered_chunks_| cannot exceed. This is to make sure
// there's an upper bound to the memory consumed by video recording to avoid
// OOM'in the system.
const size_t max_buffered_bytes_;
// The opened file that exists at |path_|. All I/O operations on the file will
// be done through this object, which must first be initialized by calling
// |Initialize()|.
base::File file_ GUARDED_BY_CONTEXT(sequence_checker_);
// The buffer used to cache the video chunks before they're written to disk.
// Accessed only on the blocking thread.
std::vector<std::string> buffered_chunks_
GUARDED_BY_CONTEXT(sequence_checker_);
// The current size in bytes of all the cached chunks in |buffered_chunks_|.
// Accessed only on the blocking thread.
size_t current_size_ GUARDED_BY_CONTEXT(sequence_checker_) = 0;
// The status of all IO operations done so far. It remains as false, until the
// file is successfully opened in |Initialize()|. After that it will be
// repeatedly updated by each write operation.
bool success_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
// Checker to guarantee certain operations are run on the
// |blocking_task_runner| given to |Create()|.
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace ash
#endif // ASH_CAPTURE_MODE_VIDEO_FILE_HANDLER_H_