DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
file.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/file/file.h"
8 
9 #include <gflags/gflags.h>
10 #include <algorithm>
11 #include <memory>
12 #include "packager/base/files/important_file_writer.h"
13 #include "packager/base/logging.h"
14 #include "packager/base/strings/string_piece.h"
15 #include "packager/media/file/local_file.h"
16 #include "packager/media/file/memory_file.h"
17 #include "packager/media/file/threaded_io_file.h"
18 #include "packager/media/file/udp_file.h"
19 
20 DEFINE_uint64(io_cache_size,
21  32ULL << 20,
22  "Size of the threaded I/O cache, in bytes. Specify 0 to disable "
23  "threaded I/O.");
24 DEFINE_uint64(io_block_size,
25  2ULL << 20,
26  "Size of the block size used for threaded I/O, in bytes.");
27 
28 // Needed for Windows weirdness which somewhere defines CopyFile as CopyFileW.
29 #ifdef CopyFile
30 #undef CopyFile
31 #endif // CopyFile
32 
33 namespace shaka {
34 namespace media {
35 
36 const char* kLocalFilePrefix = "file://";
37 const char* kUdpFilePrefix = "udp://";
38 const char* kMemoryFilePrefix = "memory://";
39 
40 namespace {
41 
42 typedef File* (*FileFactoryFunction)(const char* file_name, const char* mode);
43 typedef bool (*FileDeleteFunction)(const char* file_name);
44 typedef bool (*FileAtomicWriteFunction)(const char* file_name,
45  const std::string& contents);
46 
47 struct FileTypeInfo {
48  const char* type;
49  size_t type_length;
50  const FileFactoryFunction factory_function;
51  const FileDeleteFunction delete_function;
52  const FileAtomicWriteFunction atomic_write_function;
53 };
54 
55 File* CreateLocalFile(const char* file_name, const char* mode) {
56  return new LocalFile(file_name, mode);
57 }
58 
59 bool DeleteLocalFile(const char* file_name) {
60  return LocalFile::Delete(file_name);
61 }
62 
63 bool WriteLocalFileAtomically(const char* file_name,
64  const std::string& contents) {
65  return base::ImportantFileWriter::WriteFileAtomically(
66  base::FilePath::FromUTF8Unsafe(file_name), contents);
67 }
68 
69 File* CreateUdpFile(const char* file_name, const char* mode) {
70  if (strcmp(mode, "r")) {
71  NOTIMPLEMENTED() << "UdpFile only supports read (receive) mode.";
72  return NULL;
73  }
74  return new UdpFile(file_name);
75 }
76 
77 File* CreateMemoryFile(const char* file_name, const char* mode) {
78  return new MemoryFile(file_name, mode);
79 }
80 
81 bool DeleteMemoryFile(const char* file_name) {
82  MemoryFile::Delete(file_name);
83  return true;
84 }
85 
86 static const FileTypeInfo kFileTypeInfo[] = {
87  {
88  kLocalFilePrefix,
89  strlen(kLocalFilePrefix),
90  &CreateLocalFile,
91  &DeleteLocalFile,
92  &WriteLocalFileAtomically,
93  },
94  {
95  kUdpFilePrefix,
96  strlen(kUdpFilePrefix),
97  &CreateUdpFile,
98  nullptr,
99  nullptr
100  },
101  {
102  kMemoryFilePrefix,
103  strlen(kMemoryFilePrefix),
104  &CreateMemoryFile,
105  &DeleteMemoryFile,
106  nullptr
107  },
108 };
109 
110 const FileTypeInfo* GetFileTypeInfo(base::StringPiece file_name,
111  base::StringPiece* real_file_name) {
112  for (const FileTypeInfo& file_type : kFileTypeInfo) {
113  if (strncmp(file_type.type, file_name.data(), file_type.type_length) == 0) {
114  *real_file_name = file_name.substr(file_type.type_length);
115  return &file_type;
116  }
117  }
118  // Otherwise we default to the first file type, which is LocalFile.
119  *real_file_name = file_name;
120  return &kFileTypeInfo[0];
121 }
122 
123 } // namespace
124 
125 File* File::Create(const char* file_name, const char* mode) {
126  std::unique_ptr<File, FileCloser> internal_file(
127  CreateInternalFile(file_name, mode));
128 
129  if (!strncmp(file_name, kMemoryFilePrefix, strlen(kMemoryFilePrefix))) {
130  // Disable caching for memory files.
131  return internal_file.release();
132  }
133 
134  if (FLAGS_io_cache_size) {
135  // Enable threaded I/O for "r", "w", and "a" modes only.
136  if (!strcmp(mode, "r")) {
137  return new ThreadedIoFile(std::move(internal_file),
138  ThreadedIoFile::kInputMode, FLAGS_io_cache_size,
139  FLAGS_io_block_size);
140  } else if (!strcmp(mode, "w") || !strcmp(mode, "a")) {
141  return new ThreadedIoFile(std::move(internal_file),
142  ThreadedIoFile::kOutputMode,
143  FLAGS_io_cache_size, FLAGS_io_block_size);
144  }
145  }
146 
147  // Threaded I/O is disabled.
148  DLOG(WARNING) << "Threaded I/O is disabled. Performance may be decreased.";
149  return internal_file.release();
150 }
151 
152 File* File::CreateInternalFile(const char* file_name, const char* mode) {
153  base::StringPiece real_file_name;
154  const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
155  DCHECK(file_type);
156  return file_type->factory_function(real_file_name.data(), mode);
157 }
158 
159 File* File::Open(const char* file_name, const char* mode) {
160  File* file = File::Create(file_name, mode);
161  if (!file)
162  return NULL;
163  if (!file->Open()) {
164  delete file;
165  return NULL;
166  }
167  return file;
168 }
169 
170 File* File::OpenWithNoBuffering(const char* file_name, const char* mode) {
171  File* file = File::CreateInternalFile(file_name, mode);
172  if (!file)
173  return NULL;
174  if (!file->Open()) {
175  delete file;
176  return NULL;
177  }
178  return file;
179 }
180 
181 bool File::Delete(const char* file_name) {
182  base::StringPiece real_file_name;
183  const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
184  DCHECK(file_type);
185  return file_type->delete_function
186  ? file_type->delete_function(real_file_name.data())
187  : false;
188 }
189 
190 int64_t File::GetFileSize(const char* file_name) {
191  File* file = File::Open(file_name, "r");
192  if (!file)
193  return -1;
194  int64_t res = file->Size();
195  file->Close();
196  return res;
197 }
198 
199 bool File::ReadFileToString(const char* file_name, std::string* contents) {
200  DCHECK(contents);
201 
202  File* file = File::Open(file_name, "r");
203  if (!file)
204  return false;
205 
206  const size_t kBufferSize = 0x40000; // 256KB.
207  std::unique_ptr<char[]> buf(new char[kBufferSize]);
208 
209  int64_t len;
210  while ((len = file->Read(buf.get(), kBufferSize)) > 0)
211  contents->append(buf.get(), len);
212 
213  file->Close();
214  return len == 0;
215 }
216 
217 bool File::WriteFileAtomically(const char* file_name, const std::string& contents) {
218  base::StringPiece real_file_name;
219  const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
220  DCHECK(file_type);
221  if (file_type->atomic_write_function)
222  return file_type->atomic_write_function(real_file_name.data(), contents);
223 
224  // Provide a default implementation which may not be atomic unfortunately.
225 
226  // Skip the warning message for memory files, which is meant for testing
227  // anyway..
228  if (strncmp(file_name, kMemoryFilePrefix, strlen(kMemoryFilePrefix)) != 0) {
229  LOG(WARNING) << "Writing to " << file_name
230  << " is not guaranteed to be atomic.";
231  }
232 
233  std::unique_ptr<File, FileCloser> file(media::File::Open(file_name, "w"));
234  if (!file) {
235  LOG(ERROR) << "Failed to open file " << file_name;
236  return false;
237  }
238  int64_t bytes_written = file->Write(contents.data(), contents.size());
239  if (bytes_written < 0) {
240  LOG(ERROR) << "Failed to write to file '" << file_name << "' ("
241  << bytes_written << ").";
242  return false;
243  }
244  if (static_cast<size_t>(bytes_written) != contents.size()) {
245  LOG(ERROR) << "Failed to write the whole file to " << file_name
246  << ". Wrote " << bytes_written << " but expecting "
247  << contents.size() << " bytes.";
248  return false;
249  }
250  return true;
251 }
252 
253 bool File::Copy(const char* from_file_name, const char* to_file_name) {
254  std::string content;
255  if (!ReadFileToString(from_file_name, &content)) {
256  LOG(ERROR) << "Failed to open file " << from_file_name;
257  return false;
258  }
259 
260  std::unique_ptr<File, FileCloser> output_file(File::Open(to_file_name, "w"));
261  if (!output_file) {
262  LOG(ERROR) << "Failed to write to " << to_file_name;
263  return false;
264  }
265 
266  uint64_t bytes_left = content.size();
267  uint64_t total_bytes_written = 0;
268  const char* content_cstr = content.c_str();
269  while (bytes_left > total_bytes_written) {
270  const int64_t bytes_written =
271  output_file->Write(content_cstr + total_bytes_written, bytes_left);
272  if (bytes_written < 0) {
273  LOG(ERROR) << "Failure while writing to " << to_file_name;
274  return false;
275  }
276 
277  total_bytes_written += bytes_written;
278  }
279  return true;
280 }
281 
282 int64_t File::CopyFile(File* source, File* destination) {
283  return CopyFile(source, destination, kWholeFile);
284 }
285 
286 int64_t File::CopyFile(File* source, File* destination, int64_t max_copy) {
287  DCHECK(source);
288  DCHECK(destination);
289  if (max_copy < 0)
290  max_copy = std::numeric_limits<int64_t>::max();
291 
292  const int64_t kBufferSize = 0x40000; // 256KB.
293  std::unique_ptr<uint8_t[]> buffer(new uint8_t[kBufferSize]);
294  int64_t bytes_copied = 0;
295  while (bytes_copied < max_copy) {
296  const int64_t size = std::min(kBufferSize, max_copy - bytes_copied);
297  const int64_t bytes_read = source->Read(buffer.get(), size);
298  if (bytes_read < 0)
299  return bytes_read;
300  if (bytes_read == 0)
301  break;
302 
303  int64_t total_bytes_written = 0;
304  while (total_bytes_written < bytes_read) {
305  const int64_t bytes_written = destination->Write(
306  buffer.get() + total_bytes_written, bytes_read - total_bytes_written);
307  if (bytes_written < 0)
308  return bytes_written;
309 
310  total_bytes_written += bytes_written;
311  }
312 
313  DCHECK_EQ(total_bytes_written, bytes_read);
314  bytes_copied += bytes_read;
315  }
316 
317  return bytes_copied;
318 }
319 
320 } // namespace media
321 } // namespace shaka
virtual bool Open()=0
Internal open. Should not be used directly.
static File * OpenWithNoBuffering(const char *file_name, const char *mode)
Definition: file.cc:170
static bool Copy(const char *from_file_name, const char *to_file_name)
Definition: file.cc:253
virtual int64_t Size()=0
virtual bool Close()=0
virtual int64_t Write(const void *buffer, uint64_t length)=0
Define an abstract file interface.
Definition: file.h:24
static File * Open(const char *file_name, const char *mode)
Definition: file.cc:159
static void Delete(const std::string &file_name)
Definition: memory_file.cc:135
virtual int64_t Read(void *buffer, uint64_t length)=0
static int64_t CopyFile(File *source, File *destination)
Definition: file.cc:282
static bool WriteFileAtomically(const char *file_name, const std::string &contents)
Definition: file.cc:217
const std::string & file_name() const
Definition: file.h:91
static int64_t GetFileSize(const char *file_name)
Definition: file.cc:190
static bool ReadFileToString(const char *file_name, std::string *contents)
Definition: file.cc:199
static bool Delete(const char *file_name)
Definition: local_file.cc:104
static bool Delete(const char *file_name)
Definition: file.cc:181