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 "base/logging.h" | |
11 #include "ppapi/c/pp_errors.h" | |
12 | |
13 namespace media { | |
14 | |
15 const int kReadSize = 1024; // Arbitrary choice. | |
16 | |
17 // Call func_call and check the result. If the result is not | |
18 // PP_OK_COMPLETIONPENDING, print out logs, call OnError() and return. | |
19 #define CHECK_PP_OK_COMPLETIONPENDING(func_call, error_type) \ | |
20 do { \ | |
21 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.
| |
22 if (result != PP_OK_COMPLETIONPENDING) { \ | |
23 std::ostringstream error_message; \ | |
24 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.
| |
25 OnError((error_type), (error_message).str()); \ | |
26 return; \ | |
27 } \ | |
28 } while (0) | |
29 | |
30 // PPAPI calls should only be made on the main thread. In this file, main thread | |
31 // checking is only performed in public APIs and the completion callback | |
32 // (HandleCompletedTask). This ensures all functions are running on the main | |
33 // thread since internal methods are called either by the public APIs or by the | |
34 // completion callback. | |
35 static bool IsMainThread() { | |
36 return pp::Module::Get()->core()->IsMainThread(); | |
37 } | |
38 | |
39 // Posts a task to run |cb| on the main thread. The task is posted even if the | |
40 // current thread is the main thread. | |
41 static void PostOnMain(pp::CompletionCallback cb) { | |
42 pp::Module::Get()->core()->CallOnMainThread(0, cb, PP_OK); | |
43 } | |
44 | |
45 CdmFileIOImpl::CdmFileIOImpl(cdm::CdmFileIOClient* client, | |
46 PP_Instance pp_instance) | |
ddorwin
2013/12/04 05:27:08
fits?
xhwang
2013/12/10 01:24:25
Done.
| |
47 : 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.
| |
48 client_(client), | |
49 pp_instance_handle_(pp_instance), | |
50 callback_factory_(this), | |
51 file_offset_(0) { | |
52 PP_DCHECK(pp_instance); // 0 indicates a "NULL handle". | |
53 } | |
54 | |
55 CdmFileIOImpl::~CdmFileIOImpl() { | |
56 if (state_ != FILE_CLOSED) | |
57 CloseFile(); | |
58 } | |
59 | |
60 // Call sequence: Open() -> OpenFileSystem() -> OpenFile() -> FILE_OPENED. | |
61 void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) { | |
62 DVLOG(3) << __FUNCTION__; | |
63 PP_DCHECK(IsMainThread()); | |
64 | |
65 if (state_ != FILE_CLOSED) { | |
66 OnError(OPEN_ERROR, "Open() called in an invalid state."); | |
67 return; | |
68 } | |
69 | |
70 state_ = OPENING_FILE_SYSTEM; | |
71 | |
72 // File name should not contain any path separators. | |
73 std::string file_name_str(file_name, file_name_size); | |
74 if (file_name_str.find('/') != std::string::npos || | |
75 file_name_str.find('\\') != std::string::npos) { | |
76 OnError(OPEN_ERROR, "Invalid file name."); | |
77 return; | |
78 } | |
79 | |
80 // pp::FileRef only accepts path that begins with a '/' character. | |
81 file_name_ = '/'; | |
82 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.
| |
83 | |
84 OpenFileSystem(); | |
85 } | |
86 | |
87 // Call sequence: | |
88 // finished | |
89 // Read() -> ReadFile() -> OnFileRead() ----------> Done. | |
90 // ^ | | |
91 // | | 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.
| |
92 // |--------------| | |
93 void CdmFileIOImpl::Read() { | |
94 DVLOG(3) << __FUNCTION__; | |
95 PP_DCHECK(IsMainThread()); | |
96 | |
97 if (state_ != FILE_OPENED) { | |
98 OnError(READ_ERROR, "Read() called in an invalid state."); | |
99 return; | |
100 } | |
101 | |
102 PP_DCHECK(buffer_.empty()); | |
103 PP_DCHECK(read_buffer_.empty()); | |
104 | |
105 buffer_.resize(kReadSize); | |
106 file_offset_ = 0; | |
107 | |
108 state_ = READING_FILE; | |
109 ReadFile(); | |
110 } | |
111 | |
112 // Call sequence: | |
113 // finished | |
114 // Write() -> WriteFile() -> OnFileWritten() ----------> Done. | |
115 // ^ | | |
116 // | | not finished | |
117 // |------------------| | |
118 void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) { | |
119 DVLOG(3) << __FUNCTION__; | |
120 PP_DCHECK(IsMainThread()); | |
121 | |
122 if (state_ != FILE_OPENED) { | |
123 OnError(WRITE_ERROR, "Write() called in an invalid state."); | |
124 return; | |
125 } | |
126 | |
127 PP_DCHECK(state_ == FILE_OPENED); | |
ddorwin
2013/12/04 05:27:08
duplicate with 122?
xhwang
2013/12/10 01:24:25
Done.
| |
128 // TODO(xhwang): Support NULL |data|. | |
129 PP_DCHECK(data); | |
130 | |
131 buffer_.assign(data, data + data_size); | |
132 file_offset_ = 0; | |
133 | |
134 state_ = WRITING_FILE; | |
135 WriteFile(); | |
136 } | |
137 | |
138 void CdmFileIOImpl::Close() { | |
139 if (state_ != FILE_CLOSED) | |
140 CloseFile(); | |
141 delete this; | |
142 } | |
143 | |
144 void CdmFileIOImpl::OpenFileSystem() { | |
145 DVLOG(3) << __FUNCTION__; | |
146 PP_DCHECK(state_ == OPENING_FILE_SYSTEM); | |
147 | |
148 pp::CompletionCallbackWithOutput<pp::FileSystem> cb = | |
149 callback_factory_.NewCallbackWithOutput(&CdmFileIOImpl::OnFileSystemOpen); | |
150 pp::IsolatedFileSystemPrivate isolated_file_system( | |
151 pp_instance_handle_, PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); | |
152 | |
153 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.
| |
154 } | |
155 | |
156 void CdmFileIOImpl::OnFileSystemOpen(int32_t result, | |
157 pp::FileSystem file_system) { | |
ddorwin
2013/12/04 05:27:08
fits
xhwang
2013/12/10 01:24:25
doesn't fit now
| |
158 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.
| |
159 PP_DCHECK(state_ == OPENING_FILE_SYSTEM); | |
160 if (result != PP_OK) { | |
161 OnError(OPEN_ERROR, "File system open failed asynchronously."); | |
162 return; | |
163 } | |
164 | |
165 file_system_ = file_system; | |
166 state_ = OPENING_FILE; | |
167 OpenFile(); | |
168 } | |
169 | |
170 void CdmFileIOImpl::OpenFile() { | |
171 DVLOG(3) << __FUNCTION__; | |
172 PP_DCHECK(state_ == OPENING_FILE); | |
173 | |
174 file_io_ = pp::FileIO(pp_instance_handle_); | |
175 file_ref_ = pp::FileRef(file_system_, file_name_.c_str()); | |
176 // 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
| |
177 // access the same file. | |
178 int32_t file_open_flag = PP_FILEOPENFLAG_READ | | |
179 PP_FILEOPENFLAG_WRITE | | |
180 PP_FILEOPENFLAG_CREATE; | |
181 pp::CompletionCallback cb = | |
182 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpen); | |
183 CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb), | |
184 OPEN_ERROR); | |
185 } | |
186 | |
187 void CdmFileIOImpl::OnFileOpen(int32_t result) { | |
188 PP_DCHECK(IsMainThread()); | |
189 PP_DCHECK(state_ == OPENING_FILE); | |
190 | |
191 if (result != PP_OK) { | |
192 OnError(OPEN_ERROR, "File open failed."); | |
193 return; | |
194 } | |
195 | |
196 state_ = FILE_OPENED; | |
197 client_->OnOpenComplete(cdm::CdmFileIOClient::kSuccess); | |
198 } | |
199 | |
200 void CdmFileIOImpl::ReadFile() { | |
201 PP_DCHECK(state_ == READING_FILE); | |
202 PP_DCHECK(!buffer_.empty()); | |
203 | |
204 pp::CompletionCallback cb = | |
205 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileRead); | |
206 CHECK_PP_OK_COMPLETIONPENDING( | |
207 file_io_.Read(file_offset_, &buffer_[0], buffer_.size(), cb), READ_ERROR); | |
208 } | |
209 | |
210 void CdmFileIOImpl::OnFileRead(int32_t bytes_read) { | |
211 PP_DCHECK(state_ == READING_FILE); | |
ddorwin
2013/12/04 05:27:08
PP_DCHECK(IsMainThread());?
xhwang
2013/12/10 01:24:25
Done.
| |
212 | |
213 // 0 |bytes_read| indicates end-of-file reached. | |
214 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
| |
215 OnError(READ_ERROR, "Read file failed."); | |
216 return; | |
217 } | |
218 | |
219 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.
| |
220 PP_DCHECK(static_cast<size_t>(bytes_read) <= buffer_.size()); | |
221 // Append |bytes_read| in |buffer_| to the end of |read_buffer_|. | |
222 read_buffer_.insert(read_buffer_.end(), | |
223 buffer_.begin(), buffer_.begin() + bytes_read); | |
224 | |
225 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
| |
226 file_offset_ += bytes_read; | |
227 | |
228 // 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.
| |
229 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
| |
230 ReadFile(); | |
231 return; | |
232 } | |
233 | |
234 // All requested data read or we hit end-of-file. Return read data to the | |
235 // client. | |
236 buffer_.clear(); | |
237 file_offset_ = 0; | |
238 // Clear |read_buffer_| in case OnReadComplete() calls Read() or Write(). | |
239 std::vector<char> local_buffer; | |
240 std::swap(read_buffer_, local_buffer); | |
241 | |
242 state_ = FILE_OPENED; | |
243 client_->OnReadComplete(cdm::CdmFileIOClient::kSuccess, | |
244 reinterpret_cast<const uint8_t*>(&local_buffer[0]), | |
245 local_buffer.size()); | |
246 } | |
247 | |
248 void CdmFileIOImpl::WriteFile() { | |
249 PP_DCHECK(state_ == WRITING_FILE); | |
250 PP_DCHECK(!buffer_.empty()); | |
251 | |
252 pp::CompletionCallback cb = | |
253 callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten); | |
254 CHECK_PP_OK_COMPLETIONPENDING( | |
255 file_io_.Write(file_offset_, &buffer_[0], buffer_.size(), cb), | |
256 WRITE_ERROR); | |
257 } | |
258 | |
259 void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) { | |
260 PP_DCHECK(state_ == WRITING_FILE); | |
ddorwin
2013/12/04 05:27:08
PP_DCHECK(IsMainThread());?
xhwang
2013/12/10 01:24:25
Done.
| |
261 | |
262 if (bytes_written <= PP_OK) { | |
263 OnError(READ_ERROR, "Write file failed."); | |
264 return; | |
265 } | |
266 | |
267 PP_DCHECK(!buffer_.empty()); | |
268 PP_DCHECK(static_cast<size_t>(bytes_written) <= buffer_.size()); | |
269 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.
| |
270 file_offset_ += bytes_written; | |
271 | |
272 if (!buffer_.empty()) { | |
273 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
| |
274 return; | |
275 } | |
276 | |
277 file_offset_ = 0; | |
278 state_ = FILE_OPENED; | |
279 client_->OnWriteComplete(cdm::CdmFileIOClient::kSuccess); | |
280 } | |
281 | |
282 void CdmFileIOImpl::CloseFile() { | |
283 DVLOG(3) << __FUNCTION__; | |
284 PP_DCHECK(IsMainThread()); | |
285 | |
286 state_ = FILE_CLOSED; | |
287 | |
288 file_io_.Close(); | |
289 buffer_.clear(); | |
290 file_offset_ = 0; | |
291 read_buffer_.clear(); | |
292 } | |
293 | |
294 void CdmFileIOImpl::OnError(ErrorType error_type, | |
295 const std::string& error_msg) { | |
296 PostOnMain(callback_factory_.NewCallback( | |
297 &CdmFileIOImpl::NotifyClientOfError, error_type, error_msg)); | |
298 } | |
299 | |
300 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.
| |
301 ErrorType error_type, | |
302 const std::string& error_msg) { | |
303 DVLOG(1) << error_msg; | |
304 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.
| |
305 case OPEN_ERROR: | |
306 client_->OnOpenComplete(cdm::CdmFileIOClient::kError); | |
307 break; | |
308 case READ_ERROR: | |
309 client_->OnReadComplete(cdm::CdmFileIOClient::kError, NULL, 0); | |
310 break; | |
311 case WRITE_ERROR: | |
312 client_->OnWriteComplete(cdm::CdmFileIOClient::kError); | |
313 break; | |
314 } | |
315 } | |
316 | |
317 } // namespace media | |
OLD | NEW |