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 <inttypes.h>
31 
32 #include "libavutil/avassert.h"
33 #include "libavutil/avstring.h"
34 #include "libavutil/error.h"
35 #include "libavutil/file_open.h"
36 #include "libavutil/mem.h"
37 #include "libavutil/opt.h"
38 #include "libavutil/tree.h"
39 #include "avio.h"
40 #include <fcntl.h>
41 #if HAVE_IO_H
42 #include <io.h>
43 #endif
44 #if HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #include <sys/stat.h>
48 #include "os_support.h"
49 #include "url.h"
50 
51 typedef struct CacheEntry {
54  int size;
55 } CacheEntry;
56 
57 typedef struct CacheContext {
58  AVClass *class;
59  int fd;
60  char *filename;
61  struct AVTreeNode *root;
70 } CacheContext;
71 
72 static int cmp(const void *key, const void *node)
73 {
74  return FFDIFFSIGN(*(const int64_t *)key, ((const CacheEntry *) node)->logical_pos);
75 }
76 
77 static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
78 {
79  CacheContext *c = h->priv_data;
80  int ret;
81  char *buffername;
82 
83  av_strstart(arg, "cache:", &arg);
84 
85  c->fd = avpriv_tempfile("ffcache", &buffername, 0, h);
86  if (c->fd < 0){
87  av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n");
88  return c->fd;
89  }
90 
91  ret = unlink(buffername);
92 
93  if (ret >= 0)
94  av_freep(&buffername);
95  else
96  c->filename = buffername;
97 
98  return ffurl_open_whitelist(&c->inner, arg, flags, &h->interrupt_callback,
99  options, h->protocol_whitelist, h->protocol_blacklist, h);
100 }
101 
102 static int add_entry(URLContext *h, const unsigned char *buf, int size)
103 {
104  CacheContext *c = h->priv_data;
105  int64_t pos = -1;
106  int ret;
107  CacheEntry *entry = NULL, *next[2] = {NULL, NULL};
108  CacheEntry *entry_ret;
109  struct AVTreeNode *node = NULL;
110 
111  //FIXME avoid lseek
112  pos = lseek(c->fd, 0, SEEK_END);
113  if (pos < 0) {
114  ret = AVERROR(errno);
115  av_log(h, AV_LOG_ERROR, "seek in cache failed\n");
116  goto fail;
117  }
118  c->cache_pos = pos;
119 
120  ret = write(c->fd, buf, size);
121  if (ret < 0) {
122  ret = AVERROR(errno);
123  av_log(h, AV_LOG_ERROR, "write in cache failed\n");
124  goto fail;
125  }
126  c->cache_pos += ret;
127 
128  entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
129 
130  if (!entry)
131  entry = next[0];
132 
133  if (!entry ||
134  entry->logical_pos + entry->size != c->logical_pos ||
135  entry->physical_pos + entry->size != pos
136  ) {
137  entry = av_malloc(sizeof(*entry));
138  node = av_tree_node_alloc();
139  if (!entry || !node) {
140  ret = AVERROR(ENOMEM);
141  goto fail;
142  }
143  entry->logical_pos = c->logical_pos;
144  entry->physical_pos = pos;
145  entry->size = ret;
146 
147  entry_ret = av_tree_insert(&c->root, entry, cmp, &node);
148  if (entry_ret && entry_ret != entry) {
149  ret = -1;
150  av_log(h, AV_LOG_ERROR, "av_tree_insert failed\n");
151  goto fail;
152  }
153  } else
154  entry->size += ret;
155 
156  return 0;
157 fail:
158  //we could truncate the file to pos here if pos >=0 but ftruncate isn't available in VS so
159  //for simplicity we just leave the file a bit larger
160  av_free(entry);
161  av_free(node);
162  return ret;
163 }
164 
165 static int cache_read(URLContext *h, unsigned char *buf, int size)
166 {
167  CacheContext *c = h->priv_data;
168  CacheEntry *entry, *next[2] = {NULL, NULL};
169  int64_t r;
170 
171  entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
172 
173  if (!entry)
174  entry = next[0];
175 
176  if (entry) {
177  int64_t in_block_pos = c->logical_pos - entry->logical_pos;
178  av_assert0(entry->logical_pos <= c->logical_pos);
179  if (in_block_pos < entry->size) {
180  int64_t physical_target = entry->physical_pos + in_block_pos;
181 
182  if (c->cache_pos != physical_target) {
183  r = lseek(c->fd, physical_target, SEEK_SET);
184  } else
185  r = c->cache_pos;
186 
187  if (r >= 0) {
188  c->cache_pos = r;
189  r = read(c->fd, buf, FFMIN(size, entry->size - in_block_pos));
190  }
191 
192  if (r > 0) {
193  c->cache_pos += r;
194  c->logical_pos += r;
195  c->cache_hit ++;
196  return r;
197  }
198  }
199  }
200 
201  // Cache miss or some kind of fault with the cache
202 
203  if (c->logical_pos != c->inner_pos) {
204  r = ffurl_seek(c->inner, c->logical_pos, SEEK_SET);
205  if (r<0) {
206  av_log(h, AV_LOG_ERROR, "Failed to perform internal seek\n");
207  return r;
208  }
209  c->inner_pos = r;
210  }
211 
212  r = ffurl_read(c->inner, buf, size);
213  if (r == AVERROR_EOF && size>0) {
214  c->is_true_eof = 1;
215  av_assert0(c->end >= c->logical_pos);
216  }
217  if (r<=0)
218  return r;
219  c->inner_pos += r;
220 
221  c->cache_miss ++;
222 
223  add_entry(h, buf, r);
224  c->logical_pos += r;
225  c->end = FFMAX(c->end, c->logical_pos);
226 
227  return r;
228 }
229 
230 static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
231 {
232  CacheContext *c = h->priv_data;
233  int64_t ret;
234 
235  if (whence == AVSEEK_SIZE) {
236  pos= ffurl_seek(c->inner, pos, whence);
237  if(pos <= 0){
238  pos= ffurl_seek(c->inner, -1, SEEK_END);
239  if (ffurl_seek(c->inner, c->inner_pos, SEEK_SET) < 0)
240  av_log(h, AV_LOG_ERROR, "Inner protocol failed to seekback end : %"PRId64"\n", pos);
241  }
242  if (pos > 0)
243  c->is_true_eof = 1;
244  c->end = FFMAX(c->end, pos);
245  return pos;
246  }
247 
248  if (whence == SEEK_CUR) {
249  whence = SEEK_SET;
250  pos += c->logical_pos;
251  } else if (whence == SEEK_END && c->is_true_eof) {
252 resolve_eof:
253  whence = SEEK_SET;
254  pos += c->end;
255  }
256 
257  if (whence == SEEK_SET && pos >= 0 && pos < c->end) {
258  //Seems within filesize, assume it will not fail.
259  c->logical_pos = pos;
260  return pos;
261  }
262 
263  //cache miss
264  ret= ffurl_seek(c->inner, pos, whence);
265  if ((whence == SEEK_SET && pos >= c->logical_pos ||
266  whence == SEEK_END && pos <= 0) && ret < 0) {
267  if ( (whence == SEEK_SET && c->read_ahead_limit >= pos - c->logical_pos)
268  || c->read_ahead_limit < 0) {
269  uint8_t tmp[32768];
270  while (c->logical_pos < pos || whence == SEEK_END) {
271  int size = sizeof(tmp);
272  if (whence == SEEK_SET)
273  size = FFMIN(sizeof(tmp), pos - c->logical_pos);
274  ret = cache_read(h, tmp, size);
275  if (ret == AVERROR_EOF && whence == SEEK_END) {
276  av_assert0(c->is_true_eof);
277  goto resolve_eof;
278  }
279  if (ret < 0) {
280  return ret;
281  }
282  }
283  return c->logical_pos;
284  }
285  }
286 
287  if (ret >= 0) {
288  c->logical_pos = ret;
289  c->end = FFMAX(c->end, ret);
290  }
291 
292  return ret;
293 }
294 
295 static int enu_free(void *opaque, void *elem)
296 {
297  av_free(elem);
298  return 0;
299 }
300 
302 {
303  CacheContext *c = h->priv_data;
304  int ret;
305 
306  av_log(h, AV_LOG_INFO, "Statistics, cache hits:%"PRId64" cache misses:%"PRId64"\n",
307  c->cache_hit, c->cache_miss);
308 
309  close(c->fd);
310  if (c->filename) {
311  ret = unlink(c->filename);
312  if (ret < 0)
313  av_log(h, AV_LOG_ERROR, "Could not delete %s.\n", c->filename);
314  av_freep(&c->filename);
315  }
316  ffurl_closep(&c->inner);
318  av_tree_destroy(c->root);
319 
320  return 0;
321 }
322 
323 #define OFFSET(x) offsetof(CacheContext, x)
324 #define D AV_OPT_FLAG_DECODING_PARAM
325 
326 static const AVOption options[] = {
327  { "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 },
328  {NULL},
329 };
330 
331 static const AVClass cache_context_class = {
332  .class_name = "cache",
333  .item_name = av_default_item_name,
334  .option = options,
335  .version = LIBAVUTIL_VERSION_INT,
336 };
337 
339  .name = "cache",
340  .url_open2 = cache_open,
341  .url_read = cache_read,
342  .url_seek = cache_seek,
343  .url_close = cache_close,
344  .priv_data_size = sizeof(CacheContext),
345  .priv_data_class = &cache_context_class,
346 };
flags
const SwsFlags flags[]
Definition: swscale.c:85
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:53
entry
#define entry
Definition: aom_film_grain_template.c:66
r
const char * r
Definition: vf_curves.c:127
CacheContext::read_ahead_limit
int read_ahead_limit
Definition: cache.c:69
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
CacheContext::logical_pos
int64_t logical_pos
Definition: cache.c:62
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
int64_t
long long int64_t
Definition: coverity.c:34
cache_read
static int cache_read(URLContext *h, unsigned char *buf, int size)
Definition: cache.c:165
cmp
static int cmp(const void *key, const void *node)
Definition: cache.c:72
CacheContext::cache_miss
int64_t cache_miss
Definition: cache.c:68
AVTreeNode::elem
void * elem
Definition: tree.c:28
AVOption
AVOption.
Definition: opt.h:428
CacheContext::inner_pos
int64_t inner_pos
Definition: cache.c:64
AVSEEK_SIZE
#define AVSEEK_SIZE
Passing this as the "whence" parameter to a seek function causes it to return the filesize without se...
Definition: avio.h:468
ff_cache_protocol
const URLProtocol ff_cache_protocol
Definition: cache.c:338
AVDictionary
Definition: dict.c:32
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
CacheContext
Definition: cache.c:57
CacheContext::end
int64_t end
Definition: cache.c:65
close
static av_cold void close(AVCodecParserContext *s)
Definition: apv_parser.c:197
cache_close
static int cache_close(URLContext *h)
Definition: cache.c:301
cache_seek
static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
Definition: cache.c:230
FFDIFFSIGN
#define FFDIFFSIGN(x, y)
Comparator.
Definition: macros.h:45
avassert.h
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
cache_open
static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
Definition: cache.c:77
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:368
CacheContext::cache_pos
int64_t cache_pos
Definition: cache.c:63
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
CacheEntry::size
int size
Definition: cache.c:54
key
const char * key
Definition: hwcontext_opencl.c:189
file_open.h
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
arg
const char * arg
Definition: jacosubdec.c:65
fail
#define fail
Definition: test.h:478
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
options
static const AVOption options[]
Definition: cache.c:326
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:242
AVTreeNode
Definition: tree.c:26
options
Definition: swscale.c:50
OFFSET
#define OFFSET(x)
Definition: cache.c:323
D
#define D
Definition: cache.c:324
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
error.h
add_entry
static int add_entry(URLContext *h, const unsigned char *buf, int size)
Definition: cache.c:102
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
CacheContext::root
struct AVTreeNode * root
Definition: cache.c:61
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:52
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:221
URLContext
Definition: url.h:35
av_malloc
#define av_malloc(s)
Definition: ops_asmgen.c:44
enu_free
static int enu_free(void *opaque, void *elem)
Definition: cache.c:295
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
url.h
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:594
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:81
CacheContext::filename
char * filename
Definition: cache.c:60
pos
unsigned int pos
Definition: spdifenc.c:414
CacheContext::cache_hit
int64_t cache_hit
Definition: cache.c:68
CacheContext::fd
int fd
Definition: cache.c:59
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:258
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:331
mem.h
CacheContext::is_true_eof
int is_true_eof
Definition: cache.c:66
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
CacheEntry
@TODO support keeping files support filling with a background thread
Definition: cache.c:51
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
CacheContext::inner
URLContext * inner
Definition: cache.c:67
h
h
Definition: vp9dsp_template.c:2070
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:239
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