Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Side by Side Diff: media/cdm/ppapi/cdm_file_io_impl.cc

Issue 93243003: Add CDM FileIO tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments addressed; adds more tests; fixes impl Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698