Chromium Code Reviews| Index: media/cdm/ppapi/cdm_file_io_impl.cc |
| diff --git a/media/cdm/ppapi/cdm_file_io_impl.cc b/media/cdm/ppapi/cdm_file_io_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..def52206edcbbfe5c31f7d71c6fdae73962c121b |
| --- /dev/null |
| +++ b/media/cdm/ppapi/cdm_file_io_impl.cc |
| @@ -0,0 +1,371 @@ |
| +// Copyright 2013 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. |
| + |
| +#include "media/cdm/ppapi/cdm_file_io_impl.h" |
| + |
| +#include <algorithm> |
| +#include <sstream> |
| + |
| +#include "ppapi/c/pp_errors.h" |
| + |
| +// TODO(xhwang): Rebase and remove this. |
| +#include "base/logging.h" |
| +#define CDM_DLOG() DVLOG(3) |
| + |
| +namespace media { |
| + |
| +const int kReadSize = 4 * 1024; // Arbitrary choice. |
| + |
| +// Call func_call and check the result. If the result is not |
| +// PP_OK_COMPLETIONPENDING, print out logs, call OnError() and return. |
| +#define CHECK_PP_OK_COMPLETIONPENDING(func_call, error_type) \ |
| + do { \ |
| + int32_t result = func_call; \ |
| + PP_DCHECK(result != PP_OK); \ |
| + if (result != PP_OK_COMPLETIONPENDING) { \ |
| + CDM_DLOG() << #func_call << " failed with result: " << result; \ |
| + OnError(error_type); \ |
| + return; \ |
| + } \ |
| + } while (0) |
| + |
| +// PPAPI calls should only be made on the main thread. In this file, main thread |
| +// checking is only performed in public APIs and the completion callbacks. This |
| +// ensures all functions are running on the main thread since internal methods |
| +// are called either by the public APIs or by the completion callbacks. |
| +static bool IsMainThread() { |
| + return pp::Module::Get()->core()->IsMainThread(); |
| +} |
| + |
| +// Posts a task to run |cb| on the main thread. The task is posted even if the |
| +// current thread is the main thread. |
| +static void PostOnMain(pp::CompletionCallback cb) { |
| + pp::Module::Get()->core()->CallOnMainThread(0, cb, PP_OK); |
| +} |
| + |
| +CdmFileIOImpl::CdmFileIOImpl(cdm::FileIOClient* client, PP_Instance pp_instance) |
| + : state_(FILE_UNOPENED), |
| + client_(client), |
| + pp_instance_handle_(pp_instance), |
| + callback_factory_(this), |
| + io_offset_(0) { |
| + PP_DCHECK(IsMainThread()); |
| + PP_DCHECK(pp_instance); // 0 indicates a "NULL handle". |
| +} |
| + |
| +CdmFileIOImpl::~CdmFileIOImpl() { |
| + if (state_ != FILE_CLOSED) |
| + CloseFile(); |
| +} |
| + |
| +// Call sequence: Open() -> OpenFileSystem() -> OpenFile() -> FILE_OPENED. |
| +void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) { |
| + CDM_DLOG() << __FUNCTION__; |
| + PP_DCHECK(IsMainThread()); |
| + |
| + if (state_ != FILE_UNOPENED) { |
| + CDM_DLOG() << "Open() called in an invalid state."; |
| + OnError(OPEN_ERROR); |
| + return; |
| + } |
| + |
| + state_ = OPENING_FILE_SYSTEM; |
| + |
| + // File name should not contain any path separators. |
| + std::string file_name_str(file_name, file_name_size); |
| + if (file_name_str.find('/') != std::string::npos || |
| + file_name_str.find('\\') != std::string::npos) { |
| + CDM_DLOG() << "Invalid file name."; |
| + OnError(OPEN_ERROR); |
| + return; |
| + } |
| + |
| + // pp::FileRef only accepts path that begins with a '/' character. |
| + file_name_ = '/' + file_name_str; |
| + OpenFileSystem(); |
| +} |
| + |
| +// Call sequence: |
| +// finished |
| +// Read() -> ReadFile() -> OnFileRead() ----------> Done. |
| +// ^ | |
| +// | not finished | |
| +// |--------------| |
| +void CdmFileIOImpl::Read() { |
| + CDM_DLOG() << __FUNCTION__; |
| + PP_DCHECK(IsMainThread()); |
| + |
| + if (state_ != FILE_OPENED) { |
| + CDM_DLOG() << "Read() called in an invalid state."; |
| + bool in_use = state_ == READING_FILE || state_ == WRITING_FILE; |
|
ddorwin
2013/12/14 20:44:04
You share the logging this way, but it might be si
xhwang
2013/12/16 23:04:29
Done.
|
| + OnError(in_use ? READ_IN_USE : READ_ERROR); |
|
ddorwin
2013/12/14 20:44:04
READ_WHILE_IN_USE?
xhwang
2013/12/16 23:04:29
Done.
|
| + return; |
| + } |
| + |
| + PP_DCHECK(io_buffer_.empty()); |
| + PP_DCHECK(cumulative_read_buffer_.empty()); |
| + |
| + io_buffer_.resize(kReadSize); |
| + io_offset_ = 0; |
| + |
| + state_ = READING_FILE; |
| + ReadFile(); |
| +} |
| + |
| +// Call sequence: |
| +// finished |
| +// Write() -> WriteFile() -> OnFileWritten() ----------> Done. |
| +// ^ | |
| +// | | not finished |
| +// |------------------| |
| +void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) { |
| + CDM_DLOG() << __FUNCTION__; |
| + PP_DCHECK(IsMainThread()); |
| + |
| + if (state_ != FILE_OPENED) { |
| + CDM_DLOG() << "Write() called in an invalid state."; |
| + bool in_use = state_ == READING_FILE || state_ == WRITING_FILE; |
| + OnError(in_use ? WRITE_IN_USE : WRITE_ERROR); |
| + return; |
| + } |
| + |
| + PP_DCHECK(io_offset_ == 0); |
| + PP_DCHECK(io_buffer_.empty()); |
| + if (data_size > 0) |
| + io_buffer_.assign(data, data + data_size); |
| + else |
| + PP_DCHECK(!data); |
| + |
| + state_ = WRITING_FILE; |
| + SetLength(data_size); |
|
ddorwin
2013/12/14 20:44:04
You might explain why you need to do this.
xhwang
2013/12/16 23:04:29
Done.
|
| +} |
| + |
| +void CdmFileIOImpl::Close() { |
| + CDM_DLOG() << __FUNCTION__; |
| + PP_DCHECK(IsMainThread()); |
| + if (state_ != FILE_CLOSED) |
| + CloseFile(); |
| + delete this; |
| +} |
| + |
| +void CdmFileIOImpl::OpenFileSystem() { |
| + PP_DCHECK(state_ == OPENING_FILE_SYSTEM); |
| + |
| + pp::CompletionCallbackWithOutput<pp::FileSystem> cb = |
| + callback_factory_.NewCallbackWithOutput( |
| + &CdmFileIOImpl::OnFileSystemOpened); |
| + isolated_file_system_ = pp::IsolatedFileSystemPrivate( |
| + pp_instance_handle_, PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); |
| + |
| + CHECK_PP_OK_COMPLETIONPENDING(isolated_file_system_.Open(cb), OPEN_ERROR); |
| +} |
| + |
| +void CdmFileIOImpl::OnFileSystemOpened(int32_t result, |
| + pp::FileSystem file_system) { |
| + PP_DCHECK(IsMainThread()); |
| + PP_DCHECK(state_ == OPENING_FILE_SYSTEM); |
| + |
| + if (result != PP_OK) { |
| + CDM_DLOG() << "File system open failed asynchronously."; |
| + OnError(OPEN_ERROR); |
| + return; |
| + } |
| + |
| + file_system_ = file_system; |
| + state_ = OPENING_FILE; |
| + OpenFile(); |
| +} |
| + |
| +void CdmFileIOImpl::OpenFile() { |
| + PP_DCHECK(state_ == OPENING_FILE); |
| + |
| + file_io_ = pp::FileIO(pp_instance_handle_); |
| + file_ref_ = pp::FileRef(file_system_, file_name_.c_str()); |
| + int32_t file_open_flag = PP_FILEOPENFLAG_READ | |
| + PP_FILEOPENFLAG_WRITE | |
| + PP_FILEOPENFLAG_CREATE; |
| + pp::CompletionCallback cb = |
| + callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpened); |
| + CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb), |
| + OPEN_ERROR); |
| +} |
| + |
| +void CdmFileIOImpl::OnFileOpened(int32_t result) { |
| + PP_DCHECK(IsMainThread()); |
| + PP_DCHECK(state_ == OPENING_FILE); |
| + |
| + if (result != PP_OK) { |
| + CDM_DLOG() << "File open failed."; |
| + OnError(OPEN_ERROR); |
| + return; |
| + } |
| + |
| + state_ = FILE_OPENED; |
| + client_->OnOpenComplete(cdm::FileIOClient::kSuccess); |
| +} |
| + |
| +void CdmFileIOImpl::ReadFile() { |
| + PP_DCHECK(state_ == READING_FILE); |
| + PP_DCHECK(!io_buffer_.empty()); |
| + |
| + pp::CompletionCallback cb = |
| + callback_factory_.NewCallback(&CdmFileIOImpl::OnFileRead); |
| + CHECK_PP_OK_COMPLETIONPENDING( |
| + file_io_.Read(io_offset_, &io_buffer_[0], io_buffer_.size(), cb), |
| + READ_ERROR); |
| +} |
| + |
| +void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { |
| + CDM_DLOG() << __FUNCTION__ << ": " << bytes_read; |
| + PP_DCHECK(IsMainThread()); |
| + PP_DCHECK(state_ == READING_FILE); |
| + |
| + // 0 |bytes_read| indicates end-of-file reached. |
| + if (bytes_read < PP_OK) { |
| + CDM_DLOG() << "Read file failed."; |
| + OnError(READ_ERROR); |
| + return; |
| + } |
| + |
| + PP_DCHECK(static_cast<size_t>(bytes_read) <= io_buffer_.size()); |
| + // Append |bytes_read| in |io_buffer_| to |cumulative_read_buffer_|. |
| + cumulative_read_buffer_.insert(cumulative_read_buffer_.end(), |
| + io_buffer_.begin(), |
| + io_buffer_.begin() + bytes_read); |
| + io_offset_ += bytes_read; |
| + |
| + // Not received end-of-file yet. |
| + if (bytes_read > 0) { |
| + ReadFile(); |
| + return; |
| + } |
| + |
| + // We hit end-of-file. Return read data to the client. |
| + io_buffer_.clear(); |
| + io_offset_ = 0; |
| + // Clear |cumulative_read_buffer_| in case OnReadComplete() calls Read() or |
| + // Write(). |
| + std::vector<char> local_buffer; |
| + std::swap(cumulative_read_buffer_, local_buffer); |
| + |
| + state_ = FILE_OPENED; |
| + const uint8_t* data = local_buffer.empty() ? |
| + NULL : reinterpret_cast<const uint8_t*>(&local_buffer[0]); |
| + client_->OnReadComplete( |
| + cdm::FileIOClient::kSuccess, data, local_buffer.size()); |
| +} |
| + |
| +void CdmFileIOImpl::SetLength(uint32_t length) { |
| + PP_DCHECK(state_ == WRITING_FILE); |
| + |
| + pp::CompletionCallback cb = |
| + callback_factory_.NewCallback(&CdmFileIOImpl::OnLengthSet); |
| + CHECK_PP_OK_COMPLETIONPENDING(file_io_.SetLength(length, cb), WRITE_ERROR); |
| +} |
| + |
| +void CdmFileIOImpl::OnLengthSet(int32_t result) { |
| + CDM_DLOG() << __FUNCTION__ << ": " << result; |
| + PP_DCHECK(IsMainThread()); |
| + PP_DCHECK(state_ == WRITING_FILE); |
| + |
| + if (result != PP_OK) { |
| + CDM_DLOG() << "File SetLength failed."; |
| + OnError(WRITE_ERROR); |
| + return; |
| + } |
| + |
| + if (io_buffer_.empty()) { |
| + state_ = FILE_OPENED; |
| + client_->OnWriteComplete(cdm::FileIOClient::kSuccess); |
| + return; |
| + } |
| + |
| + WriteFile(); |
| +} |
| + |
| +void CdmFileIOImpl::WriteFile() { |
| + PP_DCHECK(state_ == WRITING_FILE); |
| + PP_DCHECK(io_offset_ < io_buffer_.size()); |
| + |
| + pp::CompletionCallback cb = |
| + callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten); |
| + CHECK_PP_OK_COMPLETIONPENDING(file_io_.Write(io_offset_, |
| + &io_buffer_[io_offset_], |
| + io_buffer_.size() - io_offset_, |
| + cb), |
| + WRITE_ERROR); |
| +} |
| + |
| +void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { |
| + CDM_DLOG() << __FUNCTION__ << ": " << bytes_written; |
| + PP_DCHECK(IsMainThread()); |
| + PP_DCHECK(state_ == WRITING_FILE); |
| + |
| + if (bytes_written <= PP_OK) { |
| + CDM_DLOG() << "Write file failed."; |
| + OnError(READ_ERROR); |
| + return; |
| + } |
| + |
| + io_offset_ += bytes_written; |
| + PP_DCHECK(io_offset_ <= io_buffer_.size()); |
| + |
| + if (io_offset_ < io_buffer_.size()) { |
| + WriteFile(); |
| + return; |
| + } |
| + |
| + io_buffer_.clear(); |
| + io_offset_ = 0; |
| + state_ = FILE_OPENED; |
| + client_->OnWriteComplete(cdm::FileIOClient::kSuccess); |
| +} |
| + |
| +void CdmFileIOImpl::CloseFile() { |
| + PP_DCHECK(IsMainThread()); |
| + |
| + state_ = FILE_CLOSED; |
| + |
| + file_io_.Close(); |
| + io_buffer_.clear(); |
| + io_offset_ = 0; |
| + cumulative_read_buffer_.clear(); |
| +} |
| + |
| +void CdmFileIOImpl::OnError(ErrorType error_type) { |
| + if (error_type == READ_ERROR || error_type == WRITE_ERROR) { |
|
ddorwin
2013/12/14 20:44:04
Is there any harm in always resetting these values
xhwang
2013/12/16 23:04:29
Yes, for *_WHILE_IN_USE, we don't want to interrup
|
| + io_buffer_.clear(); |
| + io_offset_ = 0; |
| + cumulative_read_buffer_.clear(); |
| + } |
| + PostOnMain(callback_factory_.NewCallback(&CdmFileIOImpl::NotifyClientOfError, |
| + error_type)); |
| +} |
| + |
| +void CdmFileIOImpl::NotifyClientOfError(int32_t result, |
| + ErrorType error_type) { |
| + PP_DCHECK(result == PP_OK); |
| + switch (error_type) { |
| + case OPEN_ERROR: |
| + client_->OnOpenComplete(cdm::FileIOClient::kError); |
| + break; |
| + case READ_ERROR: |
| + client_->OnReadComplete(cdm::FileIOClient::kError, NULL, 0); |
| + break; |
| + case WRITE_ERROR: |
| + client_->OnWriteComplete(cdm::FileIOClient::kError); |
| + break; |
| + case OPEN_IN_USE: |
| + client_->OnOpenComplete(cdm::FileIOClient::kInUse); |
| + break; |
| + case READ_IN_USE: |
| + client_->OnReadComplete(cdm::FileIOClient::kInUse, NULL, 0); |
| + break; |
| + case WRITE_IN_USE: |
| + client_->OnWriteComplete(cdm::FileIOClient::kInUse); |
| + break; |
| + } |
| +} |
| + |
| +} // namespace media |