diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 07:17:00 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 07:17:00 +0000 |
commit | 38c32b0dd2382b8addd6a071311c74d23dfe139f (patch) | |
tree | df864a77b4c1603a7fdb34391a18b323901b011d /media | |
parent | 898f29e4bab315619e953f82a9e554bac88870d3 (diff) | |
download | chromium_src-38c32b0dd2382b8addd6a071311c74d23dfe139f.zip chromium_src-38c32b0dd2382b8addd6a071311c74d23dfe139f.tar.gz chromium_src-38c32b0dd2382b8addd6a071311c74d23dfe139f.tar.bz2 |
Implement CdmFileIO in CdmAdapter.
R=ddorwin@chromium.org
TBR=ddorwin@chromium.org
BUG=324134
TEST=Tests will follow in another CL. All tests cases pass with that test.
Review URL: https://codereview.chromium.org/103853005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241176 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/cdm/ppapi/cdm_adapter.cc | 6 | ||||
-rw-r--r-- | media/cdm/ppapi/cdm_adapter.h | 1 | ||||
-rw-r--r-- | media/cdm/ppapi/cdm_file_io_impl.cc | 384 | ||||
-rw-r--r-- | media/cdm/ppapi/cdm_file_io_impl.h | 110 | ||||
-rw-r--r-- | media/media_cdm.gypi | 2 |
5 files changed, 503 insertions, 0 deletions
diff --git a/media/cdm/ppapi/cdm_adapter.cc b/media/cdm/ppapi/cdm_adapter.cc index d928903..d99926c 100644 --- a/media/cdm/ppapi/cdm_adapter.cc +++ b/media/cdm/ppapi/cdm_adapter.cc @@ -4,6 +4,7 @@ #include "media/cdm/ppapi/cdm_adapter.h" +#include "media/cdm/ppapi/cdm_file_io_impl.h" #include "media/cdm/ppapi/cdm_helpers.h" #include "media/cdm/ppapi/cdm_logging.h" #include "media/cdm/ppapi/supported_cdm_versions.h" @@ -921,6 +922,11 @@ void CdmAdapter::OnDeferredInitializationDone(cdm::StreamType stream_type, } } +// The CDM owns the returned object and must call FileIO::Close() to release it. +cdm::FileIO* CdmAdapter::CreateFileIO(cdm::FileIOClient* client) { + return new CdmFileIOImpl(client, pp_instance()); +} + #if defined(OS_CHROMEOS) void CdmAdapter::SendPlatformChallengeDone(int32_t result) { challenge_in_progress_ = false; diff --git a/media/cdm/ppapi/cdm_adapter.h b/media/cdm/ppapi/cdm_adapter.h index d256913..920e703 100644 --- a/media/cdm/ppapi/cdm_adapter.h +++ b/media/cdm/ppapi/cdm_adapter.h @@ -117,6 +117,7 @@ class CdmAdapter : public pp::Instance, virtual void OnSessionError(uint32_t session_id, cdm::MediaKeyError error_code, uint32_t system_code) OVERRIDE; + virtual cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) OVERRIDE; private: typedef linked_ptr<DecryptedBlockImpl> LinkedDecryptedBlock; 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 0000000..6a61b5f6 --- /dev/null +++ b/media/cdm/ppapi/cdm_file_io_impl.cc @@ -0,0 +1,384 @@ +// 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 "media/cdm/ppapi/cdm_logging.h" +#include "ppapi/c/pp_errors.h" + +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) + +#if !defined(NDEBUG) +// 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(); +} +#endif // !defined(NDEBUG) + +// 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_ == READING_FILE || state_ == WRITING_FILE) { + CDM_DLOG() << "Read() called during pending read/write."; + OnError(READ_WHILE_IN_USE); + return; + } + + if (state_ != FILE_OPENED) { + CDM_DLOG() << "Read() called in an invalid state."; + OnError(READ_ERROR); + 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_ == READING_FILE || state_ == WRITING_FILE) { + CDM_DLOG() << "Write() called during pending read/write."; + OnError(WRITE_WHILE_IN_USE); + return; + } + + if (state_ != FILE_OPENED) { + CDM_DLOG() << "Write() called in an invalid state."; + OnError(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; + + // Always SetLength() in case |data_size| is less than the file size. + SetLength(data_size); +} + +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) { + // For *_WHILE_IN_USE errors, do not reset these values. Otherwise, the + // existing read/write operation will fail. + if (error_type == READ_ERROR || error_type == WRITE_ERROR) { + 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_WHILE_IN_USE: + client_->OnOpenComplete(cdm::FileIOClient::kInUse); + break; + case READ_WHILE_IN_USE: + client_->OnReadComplete(cdm::FileIOClient::kInUse, NULL, 0); + break; + case WRITE_WHILE_IN_USE: + client_->OnWriteComplete(cdm::FileIOClient::kInUse); + break; + } +} + +} // namespace media diff --git a/media/cdm/ppapi/cdm_file_io_impl.h b/media/cdm/ppapi/cdm_file_io_impl.h new file mode 100644 index 0000000..07cde06 --- /dev/null +++ b/media/cdm/ppapi/cdm_file_io_impl.h @@ -0,0 +1,110 @@ +// 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. + +#ifndef MEDIA_CDM_PPAPI_CDM_FILE_IO_IMPL_H_ +#define MEDIA_CDM_PPAPI_CDM_FILE_IO_IMPL_H_ + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "media/cdm/ppapi/api/content_decryption_module.h" +#include "ppapi/c/ppb_file_io.h" +#include "ppapi/cpp/file_io.h" +#include "ppapi/cpp/file_ref.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/private/isolated_file_system_private.h" +#include "ppapi/utility/completion_callback_factory.h" + +namespace media { + +// Due to PPAPI limitations, all functions must be called on the main thread. +class CdmFileIOImpl : public cdm::FileIO { + public: + CdmFileIOImpl(cdm::FileIOClient* client, PP_Instance pp_instance); + virtual ~CdmFileIOImpl(); + + // cdm::FileIO implementation. + virtual void Open(const char* file_name, uint32_t file_name_size) OVERRIDE; + virtual void Read() OVERRIDE; + virtual void Write(const uint8_t* data, uint32_t data_size) OVERRIDE; + virtual void Close() OVERRIDE; + + private: + enum State { + FILE_UNOPENED, + OPENING_FILE_SYSTEM, + OPENING_FILE, + FILE_OPENED, + READING_FILE, + WRITING_FILE, + FILE_CLOSED + }; + + enum ErrorType { + OPEN_WHILE_IN_USE, + READ_WHILE_IN_USE, + WRITE_WHILE_IN_USE, + OPEN_ERROR, + READ_ERROR, + WRITE_ERROR + }; + + void OpenFileSystem(); + void OnFileSystemOpened(int32_t result, pp::FileSystem file_system); + void OpenFile(); + void OnFileOpened(int32_t result); + void ReadFile(); + void OnFileRead(int32_t bytes_read); + void SetLength(uint32_t length); + void OnLengthSet(int32_t result); + void WriteFile(); + void OnFileWritten(int32_t bytes_written); + + void CloseFile(); + + // Calls client_->OnXxxxComplete with kError asynchronously. In some cases we + // could actually call them synchronously, but since these errors shouldn't + // happen in normal cases, we are not optimizing such cases. + void OnError(ErrorType error_type); + // Callback to notify client of error asynchronously. + void NotifyClientOfError(int32_t result, ErrorType error_type); + + State state_; + + // Non-owning pointer. + cdm::FileIOClient* const client_; + + const pp::InstanceHandle pp_instance_handle_; + + std::string file_name_; + pp::IsolatedFileSystemPrivate isolated_file_system_; + pp::FileSystem file_system_; + pp::FileIO file_io_; + pp::FileRef file_ref_; + + pp::CompletionCallbackFactory<CdmFileIOImpl> callback_factory_; + + // A temporary buffer to hold (partial) data to write or the data that has + // been read. The size of |io_buffer_| is always "bytes to write" or "bytes to + // read". Use "char" instead of "unit8_t" because PPB_FileIO uses char* for + // binary data read and write. + std::vector<char> io_buffer_; + + // Offset into the file for reading/writing data. When writing data to the + // file, this is also the offset to the |io_buffer_|. + size_t io_offset_; + + // Buffer to hold all read data requested. This buffer is passed to |client_| + // when read completes. + std::vector<char> cumulative_read_buffer_; + + DISALLOW_COPY_AND_ASSIGN(CdmFileIOImpl); +}; + +} // namespace media + +#endif // MEDIA_CDM_PPAPI_CDM_FILE_IO_IMPL_H_ diff --git a/media/media_cdm.gypi b/media/media_cdm.gypi index d495e34..7c11765 100644 --- a/media/media_cdm.gypi +++ b/media/media_cdm.gypi @@ -101,6 +101,8 @@ 'cdm/ppapi/api/content_decryption_module.h', 'cdm/ppapi/cdm_adapter.cc', 'cdm/ppapi/cdm_adapter.h', + 'cdm/ppapi/cdm_file_io_impl.cc', + 'cdm/ppapi/cdm_file_io_impl.h', 'cdm/ppapi/cdm_helpers.cc', 'cdm/ppapi/cdm_helpers.h', 'cdm/ppapi/cdm_logging.cc', |