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 |