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_ != FILE_OPENED) { | |
100 CDM_DLOG() << "Read() called in an invalid state."; | |
101 bool in_use = state_ == READING_FILE || state_ == WRITING_FILE; | |
ddorwin
2013/12/14 20:44:04
You share the logging this way, but it might be si
xhwang
2013/12/16 23:04:29
Done.
| |
102 OnError(in_use ? READ_IN_USE : READ_ERROR); | |
ddorwin
2013/12/14 20:44:04
READ_WHILE_IN_USE?
xhwang
2013/12/16 23:04:29
Done.
| |
103 return; | |
104 } | |
105 | |
106 PP_DCHECK(io_buffer_.empty()); | |
107 PP_DCHECK(cumulative_read_buffer_.empty()); | |
108 | |
109 io_buffer_.resize(kReadSize); | |
110 io_offset_ = 0; | |
111 | |
112 state_ = READING_FILE; | |
113 ReadFile(); | |
114 } | |
115 | |
116 // Call sequence: | |
117 // finished | |
118 // Write() -> WriteFile() -> OnFileWritten() ----------> Done. | |
119 // ^ | | |
120 // | | not finished | |
121 // |------------------| | |
122 void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) { | |
123 CDM_DLOG() << __FUNCTION__; | |
124 PP_DCHECK(IsMainThread()); | |
125 | |
126 if (state_ != FILE_OPENED) { | |
127 CDM_DLOG() << "Write() called in an invalid state."; | |
128 bool in_use = state_ == READING_FILE || state_ == WRITING_FILE; | |
129 OnError(in_use ? WRITE_IN_USE : WRITE_ERROR); | |
130 return; | |
131 } | |
132 | |
133 PP_DCHECK(io_offset_ == 0); | |
134 PP_DCHECK(io_buffer_.empty()); | |
135 if (data_size > 0) | |
136 io_buffer_.assign(data, data + data_size); | |
137 else | |
138 PP_DCHECK(!data); | |
139 | |
140 state_ = WRITING_FILE; | |
141 SetLength(data_size); | |
ddorwin
2013/12/14 20:44:04
You might explain why you need to do this.
xhwang
2013/12/16 23:04:29
Done.
| |
142 } | |
143 | |
144 void CdmFileIOImpl::Close() { | |
145 CDM_DLOG() << __FUNCTION__; | |
146 PP_DCHECK(IsMainThread()); | |
147 if (state_ != FILE_CLOSED) | |
148 CloseFile(); | |
149 delete this; | |
150 } | |
151 | |
152 void CdmFileIOImpl::OpenFileSystem() { | |
153 PP_DCHECK(state_ == OPENING_FILE_SYSTEM); | |
154 | |
155 pp::CompletionCallbackWithOutput<pp::FileSystem> cb = | |
156 callback_factory_.NewCallbackWithOutput( | |
157 &CdmFileIOImpl::OnFileSystemOpened); | |
158 isolated_file_system_ = pp::IsolatedFileSystemPrivate( | |
159 pp_instance_handle_, PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); | |
160 | |
161 CHECK_PP_OK_COMPLETIONPENDING(isolated_file_system_.Open(cb), OPEN_ERROR); | |
162 } | |
163 | |
164 void CdmFileIOImpl::OnFileSystemOpened(int32_t result, | |
165 pp::FileSystem file_system) { | |
166 PP_DCHECK(IsMainThread()); | |
167 PP_DCHECK(state_ == OPENING_FILE_SYSTEM); | |
168 | |
169 if (result != PP_OK) { | |
170 CDM_DLOG() << "File system open failed asynchronously."; | |
171 OnError(OPEN_ERROR); | |
172 return; | |
173 } | |
174 | |
175 file_system_ = file_system; | |
176 state_ = OPENING_FILE; | |
177 OpenFile(); | |
178 } | |
179 | |
180 void CdmFileIOImpl::OpenFile() { | |
181 PP_DCHECK(state_ == OPENING_FILE); | |
182 | |
183 file_io_ = pp::FileIO(pp_instance_handle_); | |
184 file_ref_ = pp::FileRef(file_system_, file_name_.c_str()); | |
185 int32_t file_open_flag = PP_FILEOPENFLAG_READ | | |
186 PP_FILEOPENFLAG_WRITE | | |
187 PP_FILEOPENFLAG_CREATE; | |
188 pp::CompletionCallback cb = | |
189 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpened); | |
190 CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb), | |
191 OPEN_ERROR); | |
192 } | |
193 | |
194 void CdmFileIOImpl::OnFileOpened(int32_t result) { | |
195 PP_DCHECK(IsMainThread()); | |
196 PP_DCHECK(state_ == OPENING_FILE); | |
197 | |
198 if (result != PP_OK) { | |
199 CDM_DLOG() << "File open failed."; | |
200 OnError(OPEN_ERROR); | |
201 return; | |
202 } | |
203 | |
204 state_ = FILE_OPENED; | |
205 client_->OnOpenComplete(cdm::FileIOClient::kSuccess); | |
206 } | |
207 | |
208 void CdmFileIOImpl::ReadFile() { | |
209 PP_DCHECK(state_ == READING_FILE); | |
210 PP_DCHECK(!io_buffer_.empty()); | |
211 | |
212 pp::CompletionCallback cb = | |
213 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileRead); | |
214 CHECK_PP_OK_COMPLETIONPENDING( | |
215 file_io_.Read(io_offset_, &io_buffer_[0], io_buffer_.size(), cb), | |
216 READ_ERROR); | |
217 } | |
218 | |
219 void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { | |
220 CDM_DLOG() << __FUNCTION__ << ": " << bytes_read; | |
221 PP_DCHECK(IsMainThread()); | |
222 PP_DCHECK(state_ == READING_FILE); | |
223 | |
224 // 0 |bytes_read| indicates end-of-file reached. | |
225 if (bytes_read < PP_OK) { | |
226 CDM_DLOG() << "Read file failed."; | |
227 OnError(READ_ERROR); | |
228 return; | |
229 } | |
230 | |
231 PP_DCHECK(static_cast<size_t>(bytes_read) <= io_buffer_.size()); | |
232 // Append |bytes_read| in |io_buffer_| to |cumulative_read_buffer_|. | |
233 cumulative_read_buffer_.insert(cumulative_read_buffer_.end(), | |
234 io_buffer_.begin(), | |
235 io_buffer_.begin() + bytes_read); | |
236 io_offset_ += bytes_read; | |
237 | |
238 // Not received end-of-file yet. | |
239 if (bytes_read > 0) { | |
240 ReadFile(); | |
241 return; | |
242 } | |
243 | |
244 // We hit end-of-file. Return read data to the client. | |
245 io_buffer_.clear(); | |
246 io_offset_ = 0; | |
247 // Clear |cumulative_read_buffer_| in case OnReadComplete() calls Read() or | |
248 // Write(). | |
249 std::vector<char> local_buffer; | |
250 std::swap(cumulative_read_buffer_, local_buffer); | |
251 | |
252 state_ = FILE_OPENED; | |
253 const uint8_t* data = local_buffer.empty() ? | |
254 NULL : reinterpret_cast<const uint8_t*>(&local_buffer[0]); | |
255 client_->OnReadComplete( | |
256 cdm::FileIOClient::kSuccess, data, local_buffer.size()); | |
257 } | |
258 | |
259 void CdmFileIOImpl::SetLength(uint32_t length) { | |
260 PP_DCHECK(state_ == WRITING_FILE); | |
261 | |
262 pp::CompletionCallback cb = | |
263 callback_factory_.NewCallback(&CdmFileIOImpl::OnLengthSet); | |
264 CHECK_PP_OK_COMPLETIONPENDING(file_io_.SetLength(length, cb), WRITE_ERROR); | |
265 } | |
266 | |
267 void CdmFileIOImpl::OnLengthSet(int32_t result) { | |
268 CDM_DLOG() << __FUNCTION__ << ": " << result; | |
269 PP_DCHECK(IsMainThread()); | |
270 PP_DCHECK(state_ == WRITING_FILE); | |
271 | |
272 if (result != PP_OK) { | |
273 CDM_DLOG() << "File SetLength failed."; | |
274 OnError(WRITE_ERROR); | |
275 return; | |
276 } | |
277 | |
278 if (io_buffer_.empty()) { | |
279 state_ = FILE_OPENED; | |
280 client_->OnWriteComplete(cdm::FileIOClient::kSuccess); | |
281 return; | |
282 } | |
283 | |
284 WriteFile(); | |
285 } | |
286 | |
287 void CdmFileIOImpl::WriteFile() { | |
288 PP_DCHECK(state_ == WRITING_FILE); | |
289 PP_DCHECK(io_offset_ < io_buffer_.size()); | |
290 | |
291 pp::CompletionCallback cb = | |
292 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten); | |
293 CHECK_PP_OK_COMPLETIONPENDING(file_io_.Write(io_offset_, | |
294 &io_buffer_[io_offset_], | |
295 io_buffer_.size() - io_offset_, | |
296 cb), | |
297 WRITE_ERROR); | |
298 } | |
299 | |
300 void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { | |
301 CDM_DLOG() << __FUNCTION__ << ": " << bytes_written; | |
302 PP_DCHECK(IsMainThread()); | |
303 PP_DCHECK(state_ == WRITING_FILE); | |
304 | |
305 if (bytes_written <= PP_OK) { | |
306 CDM_DLOG() << "Write file failed."; | |
307 OnError(READ_ERROR); | |
308 return; | |
309 } | |
310 | |
311 io_offset_ += bytes_written; | |
312 PP_DCHECK(io_offset_ <= io_buffer_.size()); | |
313 | |
314 if (io_offset_ < io_buffer_.size()) { | |
315 WriteFile(); | |
316 return; | |
317 } | |
318 | |
319 io_buffer_.clear(); | |
320 io_offset_ = 0; | |
321 state_ = FILE_OPENED; | |
322 client_->OnWriteComplete(cdm::FileIOClient::kSuccess); | |
323 } | |
324 | |
325 void CdmFileIOImpl::CloseFile() { | |
326 PP_DCHECK(IsMainThread()); | |
327 | |
328 state_ = FILE_CLOSED; | |
329 | |
330 file_io_.Close(); | |
331 io_buffer_.clear(); | |
332 io_offset_ = 0; | |
333 cumulative_read_buffer_.clear(); | |
334 } | |
335 | |
336 void CdmFileIOImpl::OnError(ErrorType error_type) { | |
337 if (error_type == READ_ERROR || error_type == WRITE_ERROR) { | |
ddorwin
2013/12/14 20:44:04
Is there any harm in always resetting these values
xhwang
2013/12/16 23:04:29
Yes, for *_WHILE_IN_USE, we don't want to interrup
| |
338 io_buffer_.clear(); | |
339 io_offset_ = 0; | |
340 cumulative_read_buffer_.clear(); | |
341 } | |
342 PostOnMain(callback_factory_.NewCallback(&CdmFileIOImpl::NotifyClientOfError, | |
343 error_type)); | |
344 } | |
345 | |
346 void CdmFileIOImpl::NotifyClientOfError(int32_t result, | |
347 ErrorType error_type) { | |
348 PP_DCHECK(result == PP_OK); | |
349 switch (error_type) { | |
350 case OPEN_ERROR: | |
351 client_->OnOpenComplete(cdm::FileIOClient::kError); | |
352 break; | |
353 case READ_ERROR: | |
354 client_->OnReadComplete(cdm::FileIOClient::kError, NULL, 0); | |
355 break; | |
356 case WRITE_ERROR: | |
357 client_->OnWriteComplete(cdm::FileIOClient::kError); | |
358 break; | |
359 case OPEN_IN_USE: | |
360 client_->OnOpenComplete(cdm::FileIOClient::kInUse); | |
361 break; | |
362 case READ_IN_USE: | |
363 client_->OnReadComplete(cdm::FileIOClient::kInUse, NULL, 0); | |
364 break; | |
365 case WRITE_IN_USE: | |
366 client_->OnWriteComplete(cdm::FileIOClient::kInUse); | |
367 break; | |
368 } | |
369 } | |
370 | |
371 } // namespace media | |
OLD | NEW |