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..66429499b5d52371ec24e93103271cc28fd25aa8 |
--- /dev/null |
+++ b/media/cdm/ppapi/cdm_file_io_impl.cc |
@@ -0,0 +1,317 @@ |
+// 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 "base/logging.h" |
+#include "ppapi/c/pp_errors.h" |
+ |
+namespace media { |
+ |
+const int kReadSize = 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; \ |
ddorwin
2013/12/04 05:27:08
PP_DCHECK(result != PP_OK)?
xhwang
2013/12/10 01:24:25
Done.
|
+ if (result != PP_OK_COMPLETIONPENDING) { \ |
+ std::ostringstream error_message; \ |
+ error_message << #func_call << " failed with result: " << result; \ |
ddorwin
2013/12/04 05:27:08
All these strings will get included in prod binari
xhwang
2013/12/10 01:24:25
Done.
|
+ OnError((error_type), (error_message).str()); \ |
+ 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 callback |
+// (HandleCompletedTask). This ensures all functions are running on the main |
+// thread since internal methods are called either by the public APIs or by the |
+// completion callback. |
+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::CdmFileIOClient* client, |
+ PP_Instance pp_instance) |
ddorwin
2013/12/04 05:27:08
fits?
xhwang
2013/12/10 01:24:25
Done.
|
+ : state_(FILE_CLOSED), |
ddorwin
2013/12/04 05:27:08
Kind of odd to start out in closed, esp. since the
xhwang
2013/12/10 01:24:25
Done. Added FILE_UNOPENED.
|
+ client_(client), |
+ pp_instance_handle_(pp_instance), |
+ callback_factory_(this), |
+ file_offset_(0) { |
+ 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) { |
+ DVLOG(3) << __FUNCTION__; |
+ PP_DCHECK(IsMainThread()); |
+ |
+ if (state_ != FILE_CLOSED) { |
+ OnError(OPEN_ERROR, "Open() called in an invalid state."); |
+ 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) { |
+ OnError(OPEN_ERROR, "Invalid file name."); |
+ return; |
+ } |
+ |
+ // pp::FileRef only accepts path that begins with a '/' character. |
+ file_name_ = '/'; |
+ file_name_ += file_name_str; |
ddorwin
2013/12/04 05:27:08
Can we merge with above line?
xhwang
2013/12/10 01:24:25
Done.
|
+ |
+ OpenFileSystem(); |
+} |
+ |
+// Call sequence: |
+// finished |
+// Read() -> ReadFile() -> OnFileRead() ----------> Done. |
+// ^ | |
+// | | not finished |
ddorwin
2013/12/04 05:27:08
This is a bit confusing. I guess because of the di
xhwang
2013/12/10 01:24:25
Done.
|
+// |--------------| |
+void CdmFileIOImpl::Read() { |
+ DVLOG(3) << __FUNCTION__; |
+ PP_DCHECK(IsMainThread()); |
+ |
+ if (state_ != FILE_OPENED) { |
+ OnError(READ_ERROR, "Read() called in an invalid state."); |
+ return; |
+ } |
+ |
+ PP_DCHECK(buffer_.empty()); |
+ PP_DCHECK(read_buffer_.empty()); |
+ |
+ buffer_.resize(kReadSize); |
+ file_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) { |
+ DVLOG(3) << __FUNCTION__; |
+ PP_DCHECK(IsMainThread()); |
+ |
+ if (state_ != FILE_OPENED) { |
+ OnError(WRITE_ERROR, "Write() called in an invalid state."); |
+ return; |
+ } |
+ |
+ PP_DCHECK(state_ == FILE_OPENED); |
ddorwin
2013/12/04 05:27:08
duplicate with 122?
xhwang
2013/12/10 01:24:25
Done.
|
+ // TODO(xhwang): Support NULL |data|. |
+ PP_DCHECK(data); |
+ |
+ buffer_.assign(data, data + data_size); |
+ file_offset_ = 0; |
+ |
+ state_ = WRITING_FILE; |
+ WriteFile(); |
+} |
+ |
+void CdmFileIOImpl::Close() { |
+ if (state_ != FILE_CLOSED) |
+ CloseFile(); |
+ delete this; |
+} |
+ |
+void CdmFileIOImpl::OpenFileSystem() { |
+ DVLOG(3) << __FUNCTION__; |
+ PP_DCHECK(state_ == OPENING_FILE_SYSTEM); |
+ |
+ pp::CompletionCallbackWithOutput<pp::FileSystem> cb = |
+ callback_factory_.NewCallbackWithOutput(&CdmFileIOImpl::OnFileSystemOpen); |
+ pp::IsolatedFileSystemPrivate isolated_file_system( |
+ pp_instance_handle_, PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); |
+ |
+ CHECK_PP_OK_COMPLETIONPENDING(isolated_file_system.Open(cb), OPEN_ERROR); |
ddorwin
2013/12/04 05:27:08
Is it okay that isolated_file_system will go out o
xhwang
2013/12/10 01:24:25
Done.
|
+} |
+ |
+void CdmFileIOImpl::OnFileSystemOpen(int32_t result, |
+ pp::FileSystem file_system) { |
ddorwin
2013/12/04 05:27:08
fits
xhwang
2013/12/10 01:24:25
doesn't fit now
|
+ DVLOG(3) << __FUNCTION__; |
ddorwin
2013/12/04 05:27:08
PP_DCHECK(IsMainThread());? Need to check callback
xhwang
2013/12/10 01:24:25
Done.
|
+ PP_DCHECK(state_ == OPENING_FILE_SYSTEM); |
+ if (result != PP_OK) { |
+ OnError(OPEN_ERROR, "File system open failed asynchronously."); |
+ return; |
+ } |
+ |
+ file_system_ = file_system; |
+ state_ = OPENING_FILE; |
+ OpenFile(); |
+} |
+ |
+void CdmFileIOImpl::OpenFile() { |
+ DVLOG(3) << __FUNCTION__; |
+ PP_DCHECK(state_ == OPENING_FILE); |
+ |
+ file_io_ = pp::FileIO(pp_instance_handle_); |
+ file_ref_ = pp::FileRef(file_system_, file_name_.c_str()); |
+ // TODO(xhwang): This is not safe when we have multiple instances trying to |
ddorwin
2013/12/11 21:16:16
What did you do that made this safe in the new pat
|
+ // access the same file. |
+ int32_t file_open_flag = PP_FILEOPENFLAG_READ | |
+ PP_FILEOPENFLAG_WRITE | |
+ PP_FILEOPENFLAG_CREATE; |
+ pp::CompletionCallback cb = |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpen); |
+ CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb), |
+ OPEN_ERROR); |
+} |
+ |
+void CdmFileIOImpl::OnFileOpen(int32_t result) { |
+ PP_DCHECK(IsMainThread()); |
+ PP_DCHECK(state_ == OPENING_FILE); |
+ |
+ if (result != PP_OK) { |
+ OnError(OPEN_ERROR, "File open failed."); |
+ return; |
+ } |
+ |
+ state_ = FILE_OPENED; |
+ client_->OnOpenComplete(cdm::CdmFileIOClient::kSuccess); |
+} |
+ |
+void CdmFileIOImpl::ReadFile() { |
+ PP_DCHECK(state_ == READING_FILE); |
+ PP_DCHECK(!buffer_.empty()); |
+ |
+ pp::CompletionCallback cb = |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnFileRead); |
+ CHECK_PP_OK_COMPLETIONPENDING( |
+ file_io_.Read(file_offset_, &buffer_[0], buffer_.size(), cb), READ_ERROR); |
+} |
+ |
+void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { |
+ PP_DCHECK(state_ == READING_FILE); |
ddorwin
2013/12/04 05:27:08
PP_DCHECK(IsMainThread());?
xhwang
2013/12/10 01:24:25
Done.
|
+ |
+ // 0 |bytes_read| indicates end-of-file reached. |
+ if (bytes_read < PP_OK) { |
ddorwin
2013/12/04 05:27:08
|bytes_read| is a |result|?
xhwang
2013/12/10 01:24:25
yes
|
+ OnError(READ_ERROR, "Read file failed."); |
+ return; |
+ } |
+ |
+ PP_DCHECK(!buffer_.empty()); |
ddorwin
2013/12/04 05:27:08
This could be at 212, right? Might be unnecessary
xhwang
2013/12/10 01:24:25
Done.
|
+ PP_DCHECK(static_cast<size_t>(bytes_read) <= buffer_.size()); |
+ // Append |bytes_read| in |buffer_| to the end of |read_buffer_|. |
+ read_buffer_.insert(read_buffer_.end(), |
+ buffer_.begin(), buffer_.begin() + bytes_read); |
+ |
+ buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_read); |
ddorwin
2013/12/04 05:27:08
This seems unnecessarily inefficient. Can we just
xhwang
2013/12/10 01:24:25
Done. Reuse the read buffer and this is not needed
|
+ file_offset_ += bytes_read; |
+ |
+ // Still have data to read and we have not received end-of-file yet. |
ddorwin
2013/12/04 05:27:08
With all the complexity, we should probably includ
xhwang
2013/12/10 01:24:25
I'll add more tests later.
|
+ if (!buffer_.empty() && bytes_read > 0) { |
ddorwin
2013/12/04 05:27:08
Why wouldn't buffer_ be empty here? Shouldn't it b
xhwang
2013/12/10 01:24:25
For example, we ask to read 100 bytes, the only 50
|
+ ReadFile(); |
+ return; |
+ } |
+ |
+ // All requested data read or we hit end-of-file. Return read data to the |
+ // client. |
+ buffer_.clear(); |
+ file_offset_ = 0; |
+ // Clear |read_buffer_| in case OnReadComplete() calls Read() or Write(). |
+ std::vector<char> local_buffer; |
+ std::swap(read_buffer_, local_buffer); |
+ |
+ state_ = FILE_OPENED; |
+ client_->OnReadComplete(cdm::CdmFileIOClient::kSuccess, |
+ reinterpret_cast<const uint8_t*>(&local_buffer[0]), |
+ local_buffer.size()); |
+} |
+ |
+void CdmFileIOImpl::WriteFile() { |
+ PP_DCHECK(state_ == WRITING_FILE); |
+ PP_DCHECK(!buffer_.empty()); |
+ |
+ pp::CompletionCallback cb = |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten); |
+ CHECK_PP_OK_COMPLETIONPENDING( |
+ file_io_.Write(file_offset_, &buffer_[0], buffer_.size(), cb), |
+ WRITE_ERROR); |
+} |
+ |
+void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { |
+ PP_DCHECK(state_ == WRITING_FILE); |
ddorwin
2013/12/04 05:27:08
PP_DCHECK(IsMainThread());?
xhwang
2013/12/10 01:24:25
Done.
|
+ |
+ if (bytes_written <= PP_OK) { |
+ OnError(READ_ERROR, "Write file failed."); |
+ return; |
+ } |
+ |
+ PP_DCHECK(!buffer_.empty()); |
+ PP_DCHECK(static_cast<size_t>(bytes_written) <= buffer_.size()); |
+ buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_written); |
ddorwin
2013/12/04 05:27:08
Same issues as read.
Wouldn't it be easier to just
xhwang
2013/12/10 01:24:25
Done.
|
+ file_offset_ += bytes_written; |
+ |
+ if (!buffer_.empty()) { |
+ WriteFile(); |
ddorwin
2013/12/04 05:27:08
Just to be clear, this probably won't happen, righ
xhwang
2013/12/10 01:24:25
This happens when only part of the data were writt
|
+ return; |
+ } |
+ |
+ file_offset_ = 0; |
+ state_ = FILE_OPENED; |
+ client_->OnWriteComplete(cdm::CdmFileIOClient::kSuccess); |
+} |
+ |
+void CdmFileIOImpl::CloseFile() { |
+ DVLOG(3) << __FUNCTION__; |
+ PP_DCHECK(IsMainThread()); |
+ |
+ state_ = FILE_CLOSED; |
+ |
+ file_io_.Close(); |
+ buffer_.clear(); |
+ file_offset_ = 0; |
+ read_buffer_.clear(); |
+} |
+ |
+void CdmFileIOImpl::OnError(ErrorType error_type, |
+ const std::string& error_msg) { |
+ PostOnMain(callback_factory_.NewCallback( |
+ &CdmFileIOImpl::NotifyClientOfError, error_type, error_msg)); |
+} |
+ |
+void CdmFileIOImpl::NotifyClientOfError(int32_t /* result */, |
ddorwin
2013/12/04 05:27:08
DCHECK(result == PP_OK)?
xhwang
2013/12/10 01:24:25
Done.
|
+ ErrorType error_type, |
+ const std::string& error_msg) { |
+ DVLOG(1) << error_msg; |
+ switch (error_type) { |
ddorwin
2013/12/04 05:27:08
I'm not wild about this conversion, but I can't th
xhwang
2013/12/10 01:24:25
Done.
|
+ case OPEN_ERROR: |
+ client_->OnOpenComplete(cdm::CdmFileIOClient::kError); |
+ break; |
+ case READ_ERROR: |
+ client_->OnReadComplete(cdm::CdmFileIOClient::kError, NULL, 0); |
+ break; |
+ case WRITE_ERROR: |
+ client_->OnWriteComplete(cdm::CdmFileIOClient::kError); |
+ break; |
+ } |
+} |
+ |
+} // namespace media |