blob: 66003bbc9073c9e7822e0bffbb9f862c1c472a05 [file] [log] [blame]
// Copyright (c) 2015 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "encoder/file_writer.h"
#include <condition_variable>
#include <cstdio>
#include <ctime>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include "glog/logging.h"
#include "encoder/time_util.h"
namespace webmlive {
bool FileWriter::Init(bool dash_mode, const std::string& directory) {
if (!dash_mode) {
file_name_ = LocalDateString() + LocalTimeString() + ".webm";
}
directory_ = directory;
return true;
}
bool FileWriter::Run() {
using std::bind;
using std::shared_ptr;
using std::thread;
using std::nothrow;
thread_ = shared_ptr<thread>(
new (nothrow) thread(bind(&FileWriter::WriterThread, // NOLINT
this)));
if (!thread_) {
LOG(ERROR) << "Out of memory.";
return false;
}
return true;
}
bool FileWriter::Stop() {
// Tell WriterThread() to stop.
mutex_.lock();
stop_ = true;
mutex_.unlock();
// Wake and wait for WriterThread() to exit.
wake_condition_.notify_one();
thread_->join();
return true;
}
// Copies data into |buffer_q_| and returns true.
bool FileWriter::WriteData(DataSinkInterface::SharedDataSinkBuffer buffer) {
// TODO(tomfinegan): Copying data is not necessary; SharedDataBufferQueue or
// something should be provided by data_sink.h. Abusing BufferQueue
// temporarily since it just works and incoming buffers aren't that large.
if (!buffer_q_.EnqueueBuffer(buffer->id,
&buffer->data[0], buffer->data.size())) {
LOG(ERROR) << "Write buffer enqueue failed.";
return false;
}
// Wake WriterThread().
LOG(INFO) << "waking WriterThread with " << buffer->data.size() << " bytes";
wake_condition_.notify_one();
return true;
}
// Try to obtain lock on |mutex_|, and return the value of |stop_| if lock is
// obtained. Returns false if unable to obtain the lock.
bool FileWriter::StopRequested() {
bool stop_requested = false;
std::unique_lock<std::mutex> lock(mutex_, std::try_to_lock);
if (lock.owns_lock()) {
stop_requested = stop_;
}
return stop_requested;
}
// Idle the upload thread while awaiting user data.
void FileWriter::WaitForUserData() {
std::unique_lock<std::mutex> lock(mutex_);
wake_condition_.wait(lock); // Unlock |mutex_| and idle the thread while we
// wait for the next chunk of user data.
}
// Writes |data| contents to file and returns true upon success.
bool FileWriter::WriteFile(const BufferQueue::Buffer& data) const {
std::string file_name;
if (dash_mode_) {
file_name = directory_ + data.id;
} else {
file_name = directory_ + file_name_;
}
FILE* file = fopen(file_name.c_str(), "ab");
if (!file) {
LOG(ERROR) << "Unable to open output file.";
return false;
}
const size_t bytes_written =
fwrite(reinterpret_cast<const void*>(&data.data[0]),
1, data.data.size(), file);
fclose(file);
return (bytes_written == data.data.size());
}
// Runs until StopRequested() returns true.
void FileWriter::WriterThread() {
while (!StopRequested() || buffer_q_.GetNumBuffers() > 0) {
const BufferQueue::Buffer* buffer = buffer_q_.DequeueBuffer();
if (!buffer) {
// Wait for a buffer.
WaitForUserData();
continue;
}
if (!WriteFile(*buffer)) {
LOG(ERROR) << "Write failed for id: " << buffer->id;
}
}
}
} // namespace webmlive