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 |
index 3409f16fd581f9258f23f0096aa1c0ea97ec9765..989c05e84d7b5d6b74965940c442ad201b2548bc 100644 |
--- a/media/cdm/ppapi/cdm_file_io_impl.cc |
+++ b/media/cdm/ppapi/cdm_file_io_impl.cc |
@@ -13,7 +13,10 @@ |
namespace media { |
-const int kReadSize = 4 * 1024; // Arbitrary choice. |
+// Arbitrary choice based on the following heuristic ideas: |
+// - not too big to avoid unnecessarily large memory allocation; |
+// - not too small to avoid breaking most reads into multiple read operations. |
+const int kReadSize = 8 * 1024; |
// Call func_call and check the result. If the result is not |
// PP_OK_COMPLETIONPENDING, print out logs, call OnError() and return. |
@@ -23,6 +26,7 @@ const int kReadSize = 4 * 1024; // Arbitrary choice. |
PP_DCHECK(result != PP_OK); \ |
if (result != PP_OK_COMPLETIONPENDING) { \ |
CDM_DLOG() << #func_call << " failed with result: " << result; \ |
+ state_ = STATE_ERROR; \ |
OnError(error_type); \ |
return; \ |
} \ |
@@ -59,37 +63,49 @@ CdmFileIOImpl::CdmFileIOImpl( |
cdm::FileIOClient* client, |
PP_Instance pp_instance, |
const pp::CompletionCallback& first_file_read_cb) |
- : state_(FILE_UNOPENED), |
+ : state_(STATE_UNOPENED), |
client_(client), |
pp_instance_handle_(pp_instance), |
- callback_factory_(this), |
io_offset_(0), |
first_file_read_reported_(false), |
- first_file_read_cb_(first_file_read_cb) { |
+ first_file_read_cb_(first_file_read_cb), |
+ callback_factory_(this) { |
PP_DCHECK(IsMainThread()); |
PP_DCHECK(pp_instance); // 0 indicates a "NULL handle". |
} |
CdmFileIOImpl::~CdmFileIOImpl() { |
- PP_DCHECK(state_ == FILE_CLOSED); |
+ // The dtor is private. |this| can only be destructed through Close(). |
ddorwin
2014/09/16 18:07:36
"destructor" should fit
xhwang
2014/09/17 21:02:31
Done.
|
+ PP_DCHECK(state_ == STATE_CLOSED); |
} |
-// Call sequence: Open() -> OpenFileSystem() -> OpenFile() -> FILE_OPENED. |
+// Call sequence: Open() -> OpenFileSystem() -> FILE_OPENED. |
ddorwin
2014/09/16 18:07:36
FILE_OPENED does not exist anymore and is no longe
xhwang
2014/09/17 21:02:31
Done.
|
+// Note: This only stores file name and opens the file system. The real file |
+// open is deferred to when Read() or Write() is called. |
void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) { |
CDM_DLOG() << __FUNCTION__; |
PP_DCHECK(IsMainThread()); |
- if (state_ != FILE_UNOPENED) { |
+ if (state_ != STATE_UNOPENED) { |
CDM_DLOG() << "Open() called in an invalid state."; |
OnError(OPEN_ERROR); |
return; |
} |
- // File name should not contain any path separators. |
+ // File name should not be empty and should not start with '_'. |
std::string file_name_str(file_name, file_name_size); |
+ if (file_name_str.empty() || file_name_str[0] == '_') { |
+ CDM_DLOG() << "Invalid file name."; |
+ state_ = STATE_ERROR; |
+ OnError(OPEN_ERROR); |
+ return; |
+ } |
+ |
+ // File name should not contain any path separators. |
if (file_name_str.find('/') != std::string::npos || |
ddorwin
2014/09/16 18:07:37
combine into one block with above?
xhwang
2014/09/17 21:02:31
Done.
|
file_name_str.find('\\') != std::string::npos) { |
CDM_DLOG() << "Invalid file name."; |
+ state_ = STATE_ERROR; |
OnError(OPEN_ERROR); |
return; |
} |
@@ -103,59 +119,52 @@ void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) { |
return; |
} |
- state_ = OPENING_FILE_SYSTEM; |
+ state_ = STATE_OPENING_FILE_SYSTEM; |
OpenFileSystem(); |
} |
// Call sequence: |
-// finished |
-// Read() -> ReadFile() -> OnFileRead() ----------> Done. |
-// ^ | |
-// | not finished | |
-// |--------------| |
+// Read() -> OpenFileForRead() -> ReadFile() -> Done. |
void CdmFileIOImpl::Read() { |
CDM_DLOG() << __FUNCTION__; |
PP_DCHECK(IsMainThread()); |
- if (state_ == READING_FILE || state_ == WRITING_FILE) { |
+ if (state_ == STATE_READING || state_ == STATE_WRITING) { |
CDM_DLOG() << "Read() called during pending read/write."; |
OnError(READ_WHILE_IN_USE); |
return; |
} |
- if (state_ != FILE_OPENED) { |
+ if (state_ != STATE_OPENED) { |
CDM_DLOG() << "Read() called in an invalid state."; |
OnError(READ_ERROR); |
return; |
} |
+ PP_DCHECK(io_offset_ == 0); |
PP_DCHECK(io_buffer_.empty()); |
PP_DCHECK(cumulative_read_buffer_.empty()); |
- |
io_buffer_.resize(kReadSize); |
io_offset_ = 0; |
- state_ = READING_FILE; |
- ReadFile(); |
+ state_ = STATE_READING; |
+ OpenFileForRead(); |
} |
// Call sequence: |
-// finished |
-// Write() -> WriteFile() -> OnFileWritten() ----------> Done. |
-// ^ | |
-// | | not finished |
-// |------------------| |
+// Write() -> OpenTempFileForWrite() -> WriteTempFile() -> RenameTempFile(). |
+// The file name of the temparory file is /_<original_file_name>. |
void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) { |
CDM_DLOG() << __FUNCTION__; |
PP_DCHECK(IsMainThread()); |
- if (state_ == READING_FILE || state_ == WRITING_FILE) { |
+ if (state_ == STATE_READING || state_ == STATE_WRITING) { |
CDM_DLOG() << "Write() called during pending read/write."; |
OnError(WRITE_WHILE_IN_USE); |
return; |
} |
- if (state_ != FILE_OPENED) { |
+ if (state_ != STATE_OPENED) { |
CDM_DLOG() << "Write() called in an invalid state."; |
OnError(WRITE_ERROR); |
return; |
@@ -168,17 +177,16 @@ void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) { |
else |
PP_DCHECK(!data); |
- state_ = WRITING_FILE; |
- |
- // Always SetLength() in case |data_size| is less than the file size. |
- SetLength(data_size); |
+ state_ = STATE_WRITING; |
+ OpenTempFileForWrite(); |
} |
void CdmFileIOImpl::Close() { |
CDM_DLOG() << __FUNCTION__; |
PP_DCHECK(IsMainThread()); |
- PP_DCHECK(state_ != FILE_CLOSED); |
- CloseFile(); |
+ PP_DCHECK(state_ != STATE_CLOSED); |
+ Reset(); |
+ state_ = STATE_CLOSED; |
ReleaseFileLock(); |
// All pending callbacks are canceled since |callback_factory_| is destroyed. |
delete this; |
@@ -237,7 +245,7 @@ void CdmFileIOImpl::ReleaseFileLock() { |
} |
void CdmFileIOImpl::OpenFileSystem() { |
- PP_DCHECK(state_ == OPENING_FILE_SYSTEM); |
+ PP_DCHECK(state_ == STATE_OPENING_FILE_SYSTEM); |
pp::CompletionCallbackWithOutput<pp::FileSystem> cb = |
callback_factory_.NewCallbackWithOutput( |
@@ -251,51 +259,62 @@ void CdmFileIOImpl::OpenFileSystem() { |
void CdmFileIOImpl::OnFileSystemOpened(int32_t result, |
pp::FileSystem file_system) { |
PP_DCHECK(IsMainThread()); |
- PP_DCHECK(state_ == OPENING_FILE_SYSTEM); |
+ PP_DCHECK(state_ == STATE_OPENING_FILE_SYSTEM); |
if (result != PP_OK) { |
CDM_DLOG() << "File system open failed asynchronously."; |
ReleaseFileLock(); |
+ state_ = STATE_ERROR; |
OnError(OPEN_ERROR); |
return; |
} |
file_system_ = file_system; |
- state_ = OPENING_FILE; |
- OpenFile(); |
+ |
+ state_ = STATE_OPENED; |
ddorwin
2014/09/16 18:07:37
So it is STATE_FILE_SYSTEM_OPENED
xhwang
2014/09/17 21:02:32
Done.
|
+ client_->OnOpenComplete(cdm::FileIOClient::kSuccess); |
} |
-void CdmFileIOImpl::OpenFile() { |
- PP_DCHECK(state_ == OPENING_FILE); |
+void CdmFileIOImpl::OpenFileForRead() { |
+ PP_DCHECK(state_ == STATE_READING); |
+ PP_DCHECK(file_io_.is_null()); |
+ PP_DCHECK(file_ref_.is_null()); |
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; |
+ |
+ // Open file for read. If file doesn't exist, create a new empty file. |
ddorwin
2014/09/16 18:07:36
We should update the CDM.h API to allow the CDM to
xhwang
2014/09/17 21:02:31
Changed to impl not to create a new file for read
|
+ int32_t file_open_flag = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_CREATE; |
+ |
pp::CompletionCallback cb = |
- callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpened); |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpenedForRead); |
CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb), |
- OPEN_ERROR); |
+ READ_ERROR); |
} |
-void CdmFileIOImpl::OnFileOpened(int32_t result) { |
+void CdmFileIOImpl::OnFileOpenedForRead(int32_t result) { |
PP_DCHECK(IsMainThread()); |
- PP_DCHECK(state_ == OPENING_FILE); |
+ PP_DCHECK(state_ == STATE_READING); |
if (result != PP_OK) { |
CDM_DLOG() << "File open failed."; |
ReleaseFileLock(); |
+ state_ = STATE_ERROR; |
OnError(OPEN_ERROR); |
return; |
} |
- state_ = FILE_OPENED; |
- client_->OnOpenComplete(cdm::FileIOClient::kSuccess); |
+ ReadFile(); |
} |
+// Call sequence: |
+// finished |
ddorwin
2014/09/16 18:07:37
What is the purpose of the finished/not finished p
xhwang
2014/09/17 21:02:31
Changed "not finished" to "partially read"... Is t
ddorwin
2014/09/17 23:16:38
Acknowledged.
|
+// ReadFile() -> OnFileRead() ----------> Done. |
+// ^ | |
+// | not finished | |
+// |--------------| |
void CdmFileIOImpl::ReadFile() { |
- PP_DCHECK(state_ == READING_FILE); |
+ PP_DCHECK(state_ == STATE_READING); |
PP_DCHECK(!io_buffer_.empty()); |
pp::CompletionCallback cb = |
@@ -308,11 +327,12 @@ void CdmFileIOImpl::ReadFile() { |
void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { |
CDM_DLOG() << __FUNCTION__ << ": " << bytes_read; |
PP_DCHECK(IsMainThread()); |
- PP_DCHECK(state_ == READING_FILE); |
+ PP_DCHECK(state_ == STATE_READING); |
// 0 |bytes_read| indicates end-of-file reached. |
if (bytes_read < PP_OK) { |
CDM_DLOG() << "Read file failed."; |
+ state_ = STATE_ERROR; |
OnError(READ_ERROR); |
return; |
} |
@@ -324,21 +344,19 @@ void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { |
io_buffer_.begin() + bytes_read); |
io_offset_ += bytes_read; |
- // Not received end-of-file yet. |
+ // Not received end-of-file yet. Keep reading. |
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]); |
@@ -349,44 +367,71 @@ void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { |
first_file_read_reported_ = true; |
} |
+ Reset(); |
+ |
+ state_ = STATE_OPENED; |
client_->OnReadComplete( |
cdm::FileIOClient::kSuccess, data, local_buffer.size()); |
} |
-void CdmFileIOImpl::SetLength(uint32_t length) { |
- PP_DCHECK(state_ == WRITING_FILE); |
+void CdmFileIOImpl::OpenTempFileForWrite() { |
+ PP_DCHECK(state_ == STATE_WRITING); |
+ |
+ PP_DCHECK(file_name_.size() > 1 && file_name_[0] == '/'); |
+ // Temporary file name format: /_<original_file_name> |
+ std::string temp_file_name = "/_" + file_name_.substr(1); |
+ |
+ PP_DCHECK(file_io_.is_null()); |
+ PP_DCHECK(file_ref_.is_null()); |
+ file_io_ = pp::FileIO(pp_instance_handle_); |
+ file_ref_ = pp::FileRef(file_system_, temp_file_name.c_str()); |
+ |
+ // Create the file if it doesn't exist. Truncate the file to length 0 if it |
ddorwin
2014/09/16 18:07:37
Should we UMA the case where we find a temp file?
xhwang
2014/09/17 21:02:31
Good point. But since we are using TRUNCATE, we ha
|
+ // exists. |
+ int32_t file_open_flag = PP_FILEOPENFLAG_WRITE | |
+ PP_FILEOPENFLAG_TRUNCATE | |
+ PP_FILEOPENFLAG_CREATE; |
pp::CompletionCallback cb = |
- callback_factory_.NewCallback(&CdmFileIOImpl::OnLengthSet); |
- CHECK_PP_OK_COMPLETIONPENDING(file_io_.SetLength(length, cb), WRITE_ERROR); |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnTempFileOpenedForWrite); |
+ CHECK_PP_OK_COMPLETIONPENDING( |
+ file_io_.Open(file_ref_, file_open_flag, cb), WRITE_ERROR); |
} |
-void CdmFileIOImpl::OnLengthSet(int32_t result) { |
+void CdmFileIOImpl::OnTempFileOpenedForWrite(int32_t result) { |
CDM_DLOG() << __FUNCTION__ << ": " << result; |
PP_DCHECK(IsMainThread()); |
- PP_DCHECK(state_ == WRITING_FILE); |
+ PP_DCHECK(state_ == STATE_WRITING); |
if (result != PP_OK) { |
- CDM_DLOG() << "File SetLength failed."; |
+ CDM_DLOG() << "Open temporary file failed."; |
+ state_ = STATE_ERROR; |
OnError(WRITE_ERROR); |
return; |
} |
if (io_buffer_.empty()) { |
ddorwin
2014/09/16 18:07:36
Does this indicate we were told to write 0 bytes?
xhwang
2014/09/17 21:02:31
Done.
|
- state_ = FILE_OPENED; |
- client_->OnWriteComplete(cdm::FileIOClient::kSuccess); |
+ RenameTempFile(); |
return; |
} |
- WriteFile(); |
+ PP_DCHECK(io_offset_ == 0); |
+ io_offset_ = 0; |
+ WriteTempFile(); |
} |
-void CdmFileIOImpl::WriteFile() { |
- PP_DCHECK(state_ == WRITING_FILE); |
+// Call sequence: |
+// finished |
+// WriteTempFile() -> OnTempFileWritten() ----------> RenameTempFile(). |
+// ^ | |
+// | not finished | |
+// |----------------------| |
+void CdmFileIOImpl::WriteTempFile() { |
+ PP_DCHECK(state_ == STATE_WRITING); |
PP_DCHECK(io_offset_ < io_buffer_.size()); |
pp::CompletionCallback cb = |
- callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten); |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnTempFileWritten); |
CHECK_PP_OK_COMPLETIONPENDING(file_io_.Write(io_offset_, |
&io_buffer_[io_offset_], |
io_buffer_.size() - io_offset_, |
@@ -394,14 +439,15 @@ void CdmFileIOImpl::WriteFile() { |
WRITE_ERROR); |
} |
-void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { |
+void CdmFileIOImpl::OnTempFileWritten(int32_t bytes_written) { |
CDM_DLOG() << __FUNCTION__ << ": " << bytes_written; |
PP_DCHECK(IsMainThread()); |
- PP_DCHECK(state_ == WRITING_FILE); |
+ PP_DCHECK(state_ == STATE_WRITING); |
if (bytes_written <= PP_OK) { |
- CDM_DLOG() << "Write file failed."; |
- OnError(READ_ERROR); |
+ CDM_DLOG() << "Write temporary file failed."; |
+ state_ = STATE_ERROR; |
+ OnError(WRITE_ERROR); |
return; |
} |
@@ -409,35 +455,58 @@ void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { |
PP_DCHECK(io_offset_ <= io_buffer_.size()); |
if (io_offset_ < io_buffer_.size()) { |
- WriteFile(); |
+ WriteTempFile(); |
return; |
} |
- io_buffer_.clear(); |
- io_offset_ = 0; |
- state_ = FILE_OPENED; |
- client_->OnWriteComplete(cdm::FileIOClient::kSuccess); |
+ // All data written. Now rename the temporary file to the real file. |
+ RenameTempFile(); |
+} |
+ |
+void CdmFileIOImpl::RenameTempFile() { |
ddorwin
2014/09/16 18:07:37
Comment that this is actually a move and thus over
xhwang
2014/09/17 21:02:31
Done.
|
+ PP_DCHECK(state_ == STATE_WRITING); |
+ |
+ pp::CompletionCallback cb = |
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnTempFileRenamed); |
+ CHECK_PP_OK_COMPLETIONPENDING( |
+ file_ref_.Rename(pp::FileRef(file_system_, file_name_.c_str()), cb), |
+ WRITE_ERROR); |
} |
-void CdmFileIOImpl::CloseFile() { |
+void CdmFileIOImpl::OnTempFileRenamed(int32_t result) { |
+ CDM_DLOG() << __FUNCTION__ << ": " << result; |
PP_DCHECK(IsMainThread()); |
+ PP_DCHECK(state_ == STATE_WRITING); |
- state_ = FILE_CLOSED; |
+ if (result != PP_OK) { |
+ CDM_DLOG() << "Rename temporary file failed."; |
+ state_ = STATE_ERROR; |
+ OnError(WRITE_ERROR); |
+ return; |
+ } |
- file_io_.Close(); |
+ Reset(); |
ddorwin
2014/09/16 18:07:36
Note: The period between line 471 and here is the
xhwang
2014/09/17 21:02:31
Added comment in .h file
|
+ |
+ state_ = STATE_OPENED; |
+ client_->OnWriteComplete(cdm::FileIOClient::kSuccess); |
+} |
+ |
+void CdmFileIOImpl::Reset() { |
+ PP_DCHECK(IsMainThread()); |
io_buffer_.clear(); |
io_offset_ = 0; |
cumulative_read_buffer_.clear(); |
+ file_io_.Close(); |
+ file_io_.detach(); |
ddorwin
2014/09/16 18:07:37
Why detach? This is leaking a reference I believe.
xhwang
2014/09/17 21:02:31
Done.
|
+ file_ref_.detach(); |
} |
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(); |
- } |
+ if (error_type == READ_ERROR || error_type == WRITE_ERROR) |
+ Reset(); |
+ |
PostOnMain(callback_factory_.NewCallback(&CdmFileIOImpl::NotifyClientOfError, |
error_type)); |
} |