FFmpeg
cache.c
Go to the documentation of this file.
1 /*
2  * Input cache protocol.
3  * Copyright (c) 2011,2014 Michael Niedermayer
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * Based on file.c by Fabrice Bellard
22  */
23 
24 /**
25  * @TODO
26  * support keeping files
27  * support filling with a background thread
28  */
29 
30 #include "libavutil/avassert.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/file_open.h"
33 #include "libavutil/mem.h"
34 #include "libavutil/opt.h"
35 #include "libavutil/tree.h"
36 #include "avio.h"
37 #include <fcntl.h>
38 #if HAVE_IO_H
39 #include <io.h>
40 #endif
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #include <sys/stat.h>
45 #include <stdlib.h>
46 #include "os_support.h"
47 #include "url.h"
48 
49 typedef struct CacheEntry {
50  int64_t logical_pos;
51  int64_t physical_pos;
52  int size;
53 } CacheEntry;
54 
55 typedef struct Context {
56  AVClass *class;
57  int fd;
58  char *filename;
59  struct AVTreeNode *root;
60  int64_t logical_pos;
61  int64_t cache_pos;
62  int64_t inner_pos;
63  int64_t end;
68 } Context;
69 
70 static int cmp(const void *key, const void *node)
71 {
72  return FFDIFFSIGN(*(const int64_t *)key, ((const CacheEntry *) node)->logical_pos);
73 }
74 
75 static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
76 {
77  int ret;
78  char *buffername;
79  Context *c= h->priv_data;
80 
81  av_strstart(arg, "cache:", &arg);
82 
83  c->fd = avpriv_tempfile("ffcache", &buffername, 0, h);
84  if (c->fd < 0){
85  av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n");
86  return c->fd;
87  }
88 
89  ret = unlink(buffername);
90 
91  if (ret >= 0)
92  av_freep(&buffername);
93  else
94  c->filename = buffername;
95 
96  return ffurl_open_whitelist(&c->inner, arg, flags, &h->interrupt_callback,
97  options, h->protocol_whitelist, h->protocol_blacklist, h);
98 }
99 
100 static int add_entry(URLContext *h, const unsigned char *buf, int size)
101 {
102  Context *c= h->priv_data;
103  int64_t pos = -1;
104  int ret;
105  CacheEntry *entry = NULL, *next[2] = {NULL, NULL};
106  CacheEntry *entry_ret;
107  struct AVTreeNode *node = NULL;
108 
109  //FIXME avoid lseek
110  pos = lseek(c->fd, 0, SEEK_END);
111  if (pos < 0) {
112  ret = AVERROR(errno);
113  av_log(h, AV_LOG_ERROR, "seek in cache failed\n");
114  goto fail;
115  }
116  c->cache_pos = pos;
117 
118  ret = write(c->fd, buf, size);
119  if (ret < 0) {
120  ret = AVERROR(errno);
121  av_log(h, AV_LOG_ERROR, "write in cache failed\n");
122  goto fail;
123  }
124  c->cache_pos += ret;
125 
126  entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
127 
128  if (!entry)
129  entry = next[0];
130 
131  if (!entry ||
132  entry->logical_pos + entry->size != c->logical_pos ||
133  entry->physical_pos + entry->size != pos
134  ) {
135  entry = av_malloc(sizeof(*entry));
136  node = av_tree_node_alloc();
137  if (!entry || !node) {
138  ret = AVERROR(ENOMEM);
139  goto fail;
140  }
141  entry->logical_pos = c->logical_pos;
142  entry->physical_pos = pos;
143  entry->size = ret;
144 
145  entry_ret = av_tree_insert(&c->root, entry, cmp, &node);
146  if (entry_ret && entry_ret != entry) {
147  ret = -1;
148  av_log(h, AV_LOG_ERROR, "av_tree_insert failed\n");
149  goto fail;
150  }
151  } else
152  entry->size += ret;
153 
154  return 0;
155 fail:
156  //we could truncate the file to pos here if pos >=0 but ftruncate isn't available in VS so
157  //for simplicty we just leave the file a bit larger
158  av_free(entry);
159  av_free(node);
160  return ret;
161 }
162 
163 static int cache_read(URLContext *h, unsigned char *buf, int size)
164 {
165  Context *c= h->priv_data;
166  CacheEntry *entry, *next[2] = {NULL, NULL};
167  int64_t r;
168 
169  entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
170 
171  if (!entry)
172  entry = next[0];
173 
174  if (entry) {
175  int64_t in_block_pos = c->logical_pos - entry->logical_pos;
176  av_assert0(entry->logical_pos <= c->logical_pos);
177  if (in_block_pos < entry->size) {
178  int64_t physical_target = entry->physical_pos + in_block_pos;
179 
180  if (c->cache_pos != physical_target) {
181  r = lseek(c->fd, physical_target, SEEK_SET);
182  } else
183  r = c->cache_pos;
184 
185  if (r >= 0) {
186  c->cache_pos = r;
187  r = read(c->fd, buf, FFMIN(size, entry->size - in_block_pos));
188  }
189 
190  if (r > 0) {
191  c->cache_pos += r;
192  c->logical_pos += r;
193  c->cache_hit ++;
194  return r;
195  }
196  }
197  }
198 
199  // Cache miss or some kind of fault with the cache
200 
201  if (c->logical_pos != c->inner_pos) {
202  r = ffurl_seek(c->inner, c->logical_pos, SEEK_SET);
203  if (r<0) {
204  av_log(h, AV_LOG_ERROR, "Failed to perform internal seek\n");
205  return r;
206  }
207  c->inner_pos = r;
208  }
209 
210  r = ffurl_read(c->inner, buf, size);
211  if (r == AVERROR_EOF && size>0) {
212  c->is_true_eof = 1;
213  av_assert0(c->end >= c->logical_pos);
214  }
215  if (r<=0)
216  return r;
217  c->inner_pos += r;
218 
219  c->cache_miss ++;
220 
221  add_entry(h, buf, r);
222  c->logical_pos += r;
223  c->end = FFMAX(c->end, c->logical_pos);
224 
225  return r;
226 }
227 
228 static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
229 {
230  Context *c= h->priv_data;
231  int64_t ret;
232 
233  if (whence == AVSEEK_SIZE) {
234  pos= ffurl_seek(c->inner, pos, whence);
235  if(pos <= 0){
236  pos= ffurl_seek(c->inner, -1, SEEK_END);
237  if (ffurl_seek(c->inner, c->inner_pos, SEEK_SET) < 0)
238  av_log(h, AV_LOG_ERROR, "Inner protocol failed to seekback end : %"PRId64"\n", pos);
239  }
240  if (pos > 0)
241  c->is_true_eof = 1;
242  c->end = FFMAX(c->end, pos);
243  return pos;
244  }
245 
246  if (whence == SEEK_CUR) {
247  whence = SEEK_SET;
248  pos += c->logical_pos;
249  } else if (whence == SEEK_END && c->is_true_eof) {
250 resolve_eof:
251  whence = SEEK_SET;
252  pos += c->end;
253  }
254 
255  if (whence == SEEK_SET && pos >= 0 && pos < c->end) {
256  //Seems within filesize, assume it will not fail.
257  c->logical_pos = pos;
258  return pos;
259  }
260 
261  //cache miss
262  ret= ffurl_seek(c->inner, pos, whence);
263  if ((whence == SEEK_SET && pos >= c->logical_pos ||
264  whence == SEEK_END && pos <= 0) && ret < 0) {
265  if ( (whence == SEEK_SET && c->read_ahead_limit >= pos - c->logical_pos)
266  || c->read_ahead_limit < 0) {
267  uint8_t tmp[32768];
268  while (c->logical_pos < pos || whence == SEEK_END) {
269  int size = sizeof(tmp);
270  if (whence == SEEK_SET)
271  size = FFMIN(sizeof(tmp), pos - c->logical_pos);
272  ret = cache_read(h, tmp, size);
273  if (ret == AVERROR_EOF && whence == SEEK_END) {
274  av_assert0(c->is_true_eof);
275  goto resolve_eof;
276  }
277  if (ret < 0) {
278  return ret;
279  }
280  }
281  return c->logical_pos;
282  }
283  }
284 
285  if (ret >= 0) {
286  c->logical_pos = ret;
287  c->end = FFMAX(c->end, ret);
288  }
289 
290  return ret;
291 }
292 
293 static int enu_free(void *opaque, void *elem)
294 {
295  av_free(elem);
296  return 0;
297 }
298 
300 {
301  Context *c= h->priv_data;
302  int ret;
303 
304  av_log(h, AV_LOG_INFO, "Statistics, cache hits:%"PRId64" cache misses:%"PRId64"\n",
305  c->cache_hit, c->cache_miss);
306 
307  close(c->fd);
308  if (c->filename) {
309  ret = unlink(c->filename);
310  if (ret < 0)
311  av_log(h, AV_LOG_ERROR, "Could not delete %s.\n", c->filename);
312  av_freep(&c->filename);
313  }
314  ffurl_closep(&c->inner);
316  av_tree_destroy(c->root);
317 
318  return 0;
319 }
320 
321 #define OFFSET(x) offsetof(Context, x)
322 #define D AV_OPT_FLAG_DECODING_PARAM
323 
324 static const AVOption options[] = {
325  { "read_ahead_limit", "Amount in bytes that may be read ahead when seeking isn't supported, -1 for unlimited", OFFSET(read_ahead_limit), AV_OPT_TYPE_INT, { .i64 = 65536 }, -1, INT_MAX, D },
326  {NULL},
327 };
328 
329 static const AVClass cache_context_class = {
330  .class_name = "cache",
331  .item_name = av_default_item_name,
332  .option = options,
333  .version = LIBAVUTIL_VERSION_INT,
334 };
335 
337  .name = "cache",
338  .url_open2 = cache_open,
339  .url_read = cache_read,
340  .url_seek = cache_seek,
341  .url_close = cache_close,
342  .priv_data_size = sizeof(Context),
343  .priv_data_class = &cache_context_class,
344 };
ffurl_seek
static int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
Change the position that will be used by the next read/write operation on the resource accessed by h.
Definition: url.h:222
CacheEntry::physical_pos
int64_t physical_pos
Definition: cache.c:51
entry
#define entry
Definition: aom_film_grain_template.c:66
r
const char * r
Definition: vf_curves.c:127
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
av_tree_insert
void * av_tree_insert(AVTreeNode **tp, void *key, int(*cmp)(const void *key, const void *b), AVTreeNode **next)
Insert or remove an element.
Definition: tree.c:59
cache_read
static int cache_read(URLContext *h, unsigned char *buf, int size)
Definition: cache.c:163
cmp
static int cmp(const void *key, const void *node)
Definition: cache.c:70
tmp
static uint8_t tmp[11]
Definition: aes_ctr.c:28
AVTreeNode::elem
void * elem
Definition: tree.c:28
AVOption
AVOption.
Definition: opt.h:346
AVSEEK_SIZE
#define AVSEEK_SIZE
ORing this as the "whence" parameter to a seek function causes it to return the filesize without seek...
Definition: avio.h:468
ff_cache_protocol
const URLProtocol ff_cache_protocol
Definition: cache.c:336
AVDictionary
Definition: dict.c:34
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
av_tree_node_alloc
struct AVTreeNode * av_tree_node_alloc(void)
Allocate an AVTreeNode.
Definition: tree.c:34
URLProtocol
Definition: url.h:51
os_support.h
av_tree_enumerate
void av_tree_enumerate(AVTreeNode *t, void *opaque, int(*cmp)(void *opaque, void *elem), int(*enu)(void *opaque, void *elem))
Apply enu(opaque, &elem) to all the elements in the tree in a given range.
Definition: tree.c:155
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
Context::end
int64_t end
Definition: cache.c:63
Context::read_ahead_limit
int read_ahead_limit
Definition: cache.c:67
Context
Definition: async.c:56
fail
#define fail()
Definition: checkasm.h:179
cache_close
static int cache_close(URLContext *h)
Definition: cache.c:299
cache_seek
static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
Definition: cache.c:228
FFDIFFSIGN
#define FFDIFFSIGN(x, y)
Comparator.
Definition: macros.h:45
Context::fd
int fd
Definition: cache.c:57
avassert.h
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
cache_open
static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
Definition: cache.c:75
ffurl_open_whitelist
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist, URLContext *parent)
Create an URLContext for accessing to the resource indicated by url, and open it.
Definition: avio.c:362
Context::cache_pos
int64_t cache_pos
Definition: cache.c:61
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:40
CacheEntry::size
int size
Definition: cache.c:52
key
const char * key
Definition: hwcontext_opencl.c:189
file_open.h
arg
const char * arg
Definition: jacosubdec.c:67
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:66
NULL
#define NULL
Definition: coverity.c:32
options
static const AVOption options[]
Definition: cache.c:324
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:237
AVTreeNode
Definition: tree.c:26
OFFSET
#define OFFSET(x)
Definition: cache.c:321
D
#define D
Definition: cache.c:322
Context::inner_pos
int64_t inner_pos
Definition: cache.c:62
Context::cache_miss
int64_t cache_miss
Definition: cache.c:66
av_tree_destroy
void av_tree_destroy(AVTreeNode *t)
Definition: tree.c:146
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
add_entry
static int add_entry(URLContext *h, const unsigned char *buf, int size)
Definition: cache.c:100
avpriv_tempfile
int avpriv_tempfile(const char *prefix, char **filename, int log_offset, void *log_ctx)
Wrapper to work around the lack of mkstemp() on mingw.
Definition: file_open.c:111
Context::cache_hit
int64_t cache_hit
Definition: cache.c:66
size
int size
Definition: twinvq_data.h:10344
avio.h
URLProtocol::name
const char * name
Definition: url.h:52
CacheEntry::logical_pos
int64_t logical_pos
Definition: cache.c:50
tree.h
av_strstart
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
Definition: avstring.c:36
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:191
URLContext
Definition: url.h:35
Context::root
struct AVTreeNode * root
Definition: cache.c:59
enu_free
static int enu_free(void *opaque, void *elem)
Definition: cache.c:293
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
url.h
Context::inner
URLContext * inner
Definition: async.c:58
Context::filename
char * filename
Definition: cache.c:58
ffurl_closep
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
Definition: avio.c:588
ret
ret
Definition: filter_design.txt:187
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:71
pos
unsigned int pos
Definition: spdifenc.c:414
Context::is_true_eof
int is_true_eof
Definition: cache.c:64
Context::logical_pos
int64_t logical_pos
Definition: async.c:70
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Definition: opt.h:235
av_tree_find
void * av_tree_find(const AVTreeNode *t, void *key, int(*cmp)(const void *key, const void *b), void *next[2])
Definition: tree.c:39
cache_context_class
static const AVClass cache_context_class
Definition: cache.c:329
mem.h
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
CacheEntry
@TODO support keeping files support filling with a background thread
Definition: cache.c:49
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:474
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
h
h
Definition: vp9dsp_template.c:2038
avstring.h
read
static uint32_t BS_FUNC() read(BSCTX *bc, unsigned int n)
Return n bits from the buffer, n has to be in the 0-32 range.
Definition: bitstream_template.h:231
ffurl_read
static int ffurl_read(URLContext *h, uint8_t *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf.
Definition: url.h:181