Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/cdm/ppapi/cdm_file_io_impl.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <sstream> | |
| 9 | |
| 10 #include "ppapi/c/pp_errors.h" | |
| 11 | |
| 12 // TODO(xhwang): Rebase and remove this. | |
| 13 #include "base/logging.h" | |
| 14 #define CDM_DLOG() DVLOG(3) | |
| 15 | |
| 16 namespace media { | |
| 17 | |
| 18 const int kReadSize = 4 * 1024; // Arbitrary choice. | |
| 19 | |
| 20 // Call func_call and check the result. If the result is not | |
| 21 // PP_OK_COMPLETIONPENDING, print out logs, call OnError() and return. | |
| 22 #define CHECK_PP_OK_COMPLETIONPENDING(func_call, error_type) \ | |
| 23 do { \ | |
| 24 int32_t result = func_call; \ | |
| 25 PP_DCHECK(result != PP_OK); \ | |
| 26 if (result != PP_OK_COMPLETIONPENDING) { \ | |
| 27 CDM_DLOG() << #func_call << " failed with result: " << result; \ | |
| 28 OnError(error_type); \ | |
| 29 return; \ | |
| 30 } \ | |
| 31 } while (0) | |
| 32 | |
| 33 // PPAPI calls should only be made on the main thread. In this file, main thread | |
| 34 // checking is only performed in public APIs and the completion callbacks. This | |
| 35 // ensures all functions are running on the main thread since internal methods | |
| 36 // are called either by the public APIs or by the completion callbacks. | |
| 37 static bool IsMainThread() { | |
| 38 return pp::Module::Get()->core()->IsMainThread(); | |
| 39 } | |
| 40 | |
| 41 // Posts a task to run |cb| on the main thread. The task is posted even if the | |
| 42 // current thread is the main thread. | |
| 43 static void PostOnMain(pp::CompletionCallback cb) { | |
| 44 pp::Module::Get()->core()->CallOnMainThread(0, cb, PP_OK); | |
| 45 } | |
| 46 | |
| 47 CdmFileIOImpl::CdmFileIOImpl(cdm::FileIOClient* client, PP_Instance pp_instance) | |
| 48 : state_(FILE_UNOPENED), | |
| 49 client_(client), | |
| 50 pp_instance_handle_(pp_instance), | |
| 51 callback_factory_(this), | |
| 52 io_offset_(0) { | |
| 53 PP_DCHECK(IsMainThread()); | |
| 54 PP_DCHECK(pp_instance); // 0 indicates a "NULL handle". | |
| 55 } | |
| 56 | |
| 57 CdmFileIOImpl::~CdmFileIOImpl() { | |
| 58 if (state_ != FILE_CLOSED) | |
| 59 CloseFile(); | |
| 60 } | |
| 61 | |
| 62 // Call sequence: Open() -> OpenFileSystem() -> OpenFile() -> FILE_OPENED. | |
| 63 void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) { | |
| 64 CDM_DLOG() << __FUNCTION__; | |
| 65 PP_DCHECK(IsMainThread()); | |
| 66 | |
| 67 if (state_ != FILE_UNOPENED) { | |
| 68 CDM_DLOG() << "Open() called in an invalid state."; | |
| 69 OnError(OPEN_ERROR); | |
| 70 return; | |
| 71 } | |
| 72 | |
| 73 state_ = OPENING_FILE_SYSTEM; | |
| 74 | |
| 75 // File name should not contain any path separators. | |
| 76 std::string file_name_str(file_name, file_name_size); | |
| 77 if (file_name_str.find('/') != std::string::npos || | |
| 78 file_name_str.find('\\') != std::string::npos) { | |
| 79 CDM_DLOG() << "Invalid file name."; | |
| 80 OnError(OPEN_ERROR); | |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 // pp::FileRef only accepts path that begins with a '/' character. | |
| 85 file_name_ = '/' + file_name_str; | |
| 86 OpenFileSystem(); | |
| 87 } | |
| 88 | |
| 89 // Call sequence: | |
| 90 // finished | |
| 91 // Read() -> ReadFile() -> OnFileRead() ----------> Done. | |
| 92 // ^ | | |
| 93 // | not finished | | |
| 94 // |--------------| | |
| 95 void CdmFileIOImpl::Read() { | |
| 96 CDM_DLOG() << __FUNCTION__; | |
| 97 PP_DCHECK(IsMainThread()); | |
| 98 | |
| 99 if (state_ == READING_FILE || state_ == WRITING_FILE) { | |
| 100 CDM_DLOG() << "Read() called during pending read/write."; | |
| 101 OnError(READ_WHILE_IN_USE); | |
| 102 return; | |
| 103 } | |
| 104 | |
| 105 if (state_ != FILE_OPENED) { | |
| 106 CDM_DLOG() << "Read() called in an invalid state."; | |
| 107 OnError(READ_ERROR); | |
| 108 return; | |
| 109 } | |
| 110 | |
| 111 PP_DCHECK(io_buffer_.empty()); | |
| 112 PP_DCHECK(cumulative_read_buffer_.empty()); | |
| 113 | |
| 114 io_buffer_.resize(kReadSize); | |
| 115 io_offset_ = 0; | |
| 116 | |
| 117 state_ = READING_FILE; | |
| 118 ReadFile(); | |
| 119 } | |
| 120 | |
| 121 // Call sequence: | |
| 122 // finished | |
| 123 // Write() -> WriteFile() -> OnFileWritten() ----------> Done. | |
| 124 // ^ | | |
| 125 // | | not finished | |
| 126 // |------------------| | |
| 127 void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) { | |
| 128 CDM_DLOG() << __FUNCTION__; | |
| 129 PP_DCHECK(IsMainThread()); | |
| 130 | |
| 131 if (state_ == READING_FILE || state_ == WRITING_FILE) { | |
| 132 CDM_DLOG() << "Write() called during pending read/write."; | |
| 133 OnError(WRITE_WHILE_IN_USE); | |
| 134 return; | |
| 135 } | |
| 136 | |
| 137 if (state_ != FILE_OPENED) { | |
| 138 CDM_DLOG() << "Write() called in an invalid state."; | |
| 139 OnError(WRITE_ERROR); | |
| 140 return; | |
| 141 } | |
| 142 | |
| 143 PP_DCHECK(io_offset_ == 0); | |
| 144 PP_DCHECK(io_buffer_.empty()); | |
| 145 if (data_size > 0) | |
| 146 io_buffer_.assign(data, data + data_size); | |
| 147 else | |
| 148 PP_DCHECK(!data); | |
| 149 | |
| 150 state_ = WRITING_FILE; | |
| 151 | |
| 152 // Always SetLength() in case |data_size| is less than the file size. | |
| 153 SetLength(data_size); | |
| 154 } | |
| 155 | |
| 156 void CdmFileIOImpl::Close() { | |
| 157 CDM_DLOG() << __FUNCTION__; | |
| 158 PP_DCHECK(IsMainThread()); | |
| 159 if (state_ != FILE_CLOSED) | |
| 160 CloseFile(); | |
| 161 delete this; | |
| 162 } | |
| 163 | |
| 164 void CdmFileIOImpl::OpenFileSystem() { | |
| 165 PP_DCHECK(state_ == OPENING_FILE_SYSTEM); | |
| 166 | |
| 167 pp::CompletionCallbackWithOutput<pp::FileSystem> cb = | |
| 168 callback_factory_.NewCallbackWithOutput( | |
| 169 &CdmFileIOImpl::OnFileSystemOpened); | |
| 170 isolated_file_system_ = pp::IsolatedFileSystemPrivate( | |
| 171 pp_instance_handle_, PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); | |
| 172 | |
| 173 CHECK_PP_OK_COMPLETIONPENDING(isolated_file_system_.Open(cb), OPEN_ERROR); | |
| 174 } | |
| 175 | |
| 176 void CdmFileIOImpl::OnFileSystemOpened(int32_t result, | |
| 177 pp::FileSystem file_system) { | |
| 178 PP_DCHECK(IsMainThread()); | |
| 179 PP_DCHECK(state_ == OPENING_FILE_SYSTEM); | |
| 180 | |
| 181 if (result != PP_OK) { | |
| 182 CDM_DLOG() << "File system open failed asynchronously."; | |
| 183 OnError(OPEN_ERROR); | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 file_system_ = file_system; | |
| 188 state_ = OPENING_FILE; | |
| 189 OpenFile(); | |
| 190 } | |
| 191 | |
| 192 void CdmFileIOImpl::OpenFile() { | |
| 193 PP_DCHECK(state_ == OPENING_FILE); | |
| 194 | |
| 195 file_io_ = pp::FileIO(pp_instance_handle_); | |
| 196 file_ref_ = pp::FileRef(file_system_, file_name_.c_str()); | |
| 197 int32_t file_open_flag = PP_FILEOPENFLAG_READ | | |
| 198 PP_FILEOPENFLAG_WRITE | | |
| 199 PP_FILEOPENFLAG_CREATE; | |
| 200 pp::CompletionCallback cb = | |
| 201 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpened); | |
| 202 CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb), | |
| 203 OPEN_ERROR); | |
| 204 } | |
| 205 | |
| 206 void CdmFileIOImpl::OnFileOpened(int32_t result) { | |
| 207 PP_DCHECK(IsMainThread()); | |
| 208 PP_DCHECK(state_ == OPENING_FILE); | |
| 209 | |
| 210 if (result != PP_OK) { | |
| 211 CDM_DLOG() << "File open failed."; | |
| 212 OnError(OPEN_ERROR); | |
| 213 return; | |
| 214 } | |
| 215 | |
| 216 state_ = FILE_OPENED; | |
| 217 client_->OnOpenComplete(cdm::FileIOClient::kSuccess); | |
| 218 } | |
| 219 | |
| 220 void CdmFileIOImpl::ReadFile() { | |
| 221 PP_DCHECK(state_ == READING_FILE); | |
| 222 PP_DCHECK(!io_buffer_.empty()); | |
| 223 | |
| 224 pp::CompletionCallback cb = | |
| 225 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileRead); | |
| 226 CHECK_PP_OK_COMPLETIONPENDING( | |
| 227 file_io_.Read(io_offset_, &io_buffer_[0], io_buffer_.size(), cb), | |
| 228 READ_ERROR); | |
| 229 } | |
| 230 | |
| 231 void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { | |
| 232 CDM_DLOG() << __FUNCTION__ << ": " << bytes_read; | |
| 233 PP_DCHECK(IsMainThread()); | |
| 234 PP_DCHECK(state_ == READING_FILE); | |
| 235 | |
| 236 // 0 |bytes_read| indicates end-of-file reached. | |
| 237 if (bytes_read < PP_OK) { | |
| 238 CDM_DLOG() << "Read file failed."; | |
| 239 OnError(READ_ERROR); | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 PP_DCHECK(static_cast<size_t>(bytes_read) <= io_buffer_.size()); | |
| 244 // Append |bytes_read| in |io_buffer_| to |cumulative_read_buffer_|. | |
| 245 cumulative_read_buffer_.insert(cumulative_read_buffer_.end(), | |
| 246 io_buffer_.begin(), | |
| 247 io_buffer_.begin() + bytes_read); | |
| 248 io_offset_ += bytes_read; | |
| 249 | |
| 250 // Not received end-of-file yet. | |
| 251 if (bytes_read > 0) { | |
| 252 ReadFile(); | |
| 253 return; | |
| 254 } | |
| 255 | |
| 256 // We hit end-of-file. Return read data to the client. | |
| 257 io_buffer_.clear(); | |
| 258 io_offset_ = 0; | |
| 259 // Clear |cumulative_read_buffer_| in case OnReadComplete() calls Read() or | |
| 260 // Write(). | |
| 261 std::vector<char> local_buffer; | |
| 262 std::swap(cumulative_read_buffer_, local_buffer); | |
| 263 | |
| 264 state_ = FILE_OPENED; | |
| 265 const uint8_t* data = local_buffer.empty() ? | |
| 266 NULL : reinterpret_cast<const uint8_t*>(&local_buffer[0]); | |
| 267 client_->OnReadComplete( | |
| 268 cdm::FileIOClient::kSuccess, data, local_buffer.size()); | |
| 269 } | |
| 270 | |
| 271 void CdmFileIOImpl::SetLength(uint32_t length) { | |
| 272 PP_DCHECK(state_ == WRITING_FILE); | |
| 273 | |
| 274 pp::CompletionCallback cb = | |
| 275 callback_factory_.NewCallback(&CdmFileIOImpl::OnLengthSet); | |
| 276 CHECK_PP_OK_COMPLETIONPENDING(file_io_.SetLength(length, cb), WRITE_ERROR); | |
| 277 } | |
| 278 | |
| 279 void CdmFileIOImpl::OnLengthSet(int32_t result) { | |
| 280 CDM_DLOG() << __FUNCTION__ << ": " << result; | |
| 281 PP_DCHECK(IsMainThread()); | |
| 282 PP_DCHECK(state_ == WRITING_FILE); | |
| 283 | |
| 284 if (result != PP_OK) { | |
| 285 CDM_DLOG() << "File SetLength failed."; | |
| 286 OnError(WRITE_ERROR); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 if (io_buffer_.empty()) { | |
| 291 state_ = FILE_OPENED; | |
| 292 client_->OnWriteComplete(cdm::FileIOClient::kSuccess); | |
| 293 return; | |
| 294 } | |
| 295 | |
| 296 WriteFile(); | |
| 297 } | |
| 298 | |
| 299 void CdmFileIOImpl::WriteFile() { | |
| 300 PP_DCHECK(state_ == WRITING_FILE); | |
| 301 PP_DCHECK(io_offset_ < io_buffer_.size()); | |
| 302 | |
| 303 pp::CompletionCallback cb = | |
| 304 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten); | |
| 305 CHECK_PP_OK_COMPLETIONPENDING(file_io_.Write(io_offset_, | |
| 306 &io_buffer_[io_offset_], | |
| 307 io_buffer_.size() - io_offset_, | |
| 308 cb), | |
| 309 WRITE_ERROR); | |
| 310 } | |
| 311 | |
| 312 void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { | |
| 313 CDM_DLOG() << __FUNCTION__ << ": " << bytes_written; | |
| 314 PP_DCHECK(IsMainThread()); | |
| 315 PP_DCHECK(state_ == WRITING_FILE); | |
| 316 | |
| 317 if (bytes_written <= PP_OK) { | |
| 318 CDM_DLOG() << "Write file failed."; | |
| 319 OnError(READ_ERROR); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 io_offset_ += bytes_written; | |
| 324 PP_DCHECK(io_offset_ <= io_buffer_.size()); | |
| 325 | |
| 326 if (io_offset_ < io_buffer_.size()) { | |
| 327 WriteFile(); | |
| 328 return; | |
| 329 } | |
| 330 | |
| 331 io_buffer_.clear(); | |
| 332 io_offset_ = 0; | |
| 333 state_ = FILE_OPENED; | |
| 334 client_->OnWriteComplete(cdm::FileIOClient::kSuccess); | |
| 335 } | |
| 336 | |
| 337 void CdmFileIOImpl::CloseFile() { | |
| 338 PP_DCHECK(IsMainThread()); | |
| 339 | |
| 340 state_ = FILE_CLOSED; | |
| 341 | |
| 342 file_io_.Close(); | |
| 343 io_buffer_.clear(); | |
| 344 io_offset_ = 0; | |
| 345 cumulative_read_buffer_.clear(); | |
| 346 } | |
| 347 | |
| 348 void CdmFileIOImpl::OnError(ErrorType error_type) { | |
| 349 // For *_WHILE_IN_USE errors, do not reset these values. Otherwise, the | |
|
ddorwin
2013/12/17 00:17:04
nit: The comment and check don't match. This would
xhwang
2013/12/17 00:31:29
Hmm, this appears to be the cleanest I can get now
| |
| 350 // existing read/write operation will fail. | |
| 351 if (error_type == READ_ERROR || error_type == WRITE_ERROR) { | |
|
ddorwin
2013/12/17 00:17:04
On an OPEN_ERROR,these are already at the reset va
xhwang
2013/12/17 00:31:29
OPEN_ERROR is only reported during opening a file.
| |
| 352 io_buffer_.clear(); | |
| 353 io_offset_ = 0; | |
| 354 cumulative_read_buffer_.clear(); | |
| 355 } | |
| 356 PostOnMain(callback_factory_.NewCallback(&CdmFileIOImpl::NotifyClientOfError, | |
| 357 error_type)); | |
| 358 } | |
| 359 | |
| 360 void CdmFileIOImpl::NotifyClientOfError(int32_t result, | |
| 361 ErrorType error_type) { | |
| 362 PP_DCHECK(result == PP_OK); | |
| 363 switch (error_type) { | |
| 364 case OPEN_ERROR: | |
| 365 client_->OnOpenComplete(cdm::FileIOClient::kError); | |
| 366 break; | |
| 367 case READ_ERROR: | |
| 368 client_->OnReadComplete(cdm::FileIOClient::kError, NULL, 0); | |
| 369 break; | |
| 370 case WRITE_ERROR: | |
| 371 client_->OnWriteComplete(cdm::FileIOClient::kError); | |
| 372 break; | |
| 373 case OPEN_WHILE_IN_USE: | |
| 374 client_->OnOpenComplete(cdm::FileIOClient::kInUse); | |
| 375 break; | |
| 376 case READ_WHILE_IN_USE: | |
| 377 client_->OnReadComplete(cdm::FileIOClient::kInUse, NULL, 0); | |
| 378 break; | |
| 379 case WRITE_WHILE_IN_USE: | |
| 380 client_->OnWriteComplete(cdm::FileIOClient::kInUse); | |
| 381 break; | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 } // namespace media | |
| OLD | NEW |