Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Input cache protocol. | |
3 | * Copyright (c) 2011 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 non continuous caching | |
27 | * support keeping files | |
28 | * support filling with a background thread | |
29 | */ | |
30 | ||
31 | #include "libavutil/avassert.h" | |
32 | #include "libavutil/avstring.h" | |
33 | #include "libavutil/file.h" | |
34 | #include "avformat.h" | |
35 | #include <fcntl.h> | |
36 | #if HAVE_IO_H | |
37 | #include <io.h> | |
38 | #endif | |
39 | #if HAVE_UNISTD_H | |
40 | #include <unistd.h> | |
41 | #endif | |
42 | #include <sys/stat.h> | |
43 | #include <stdlib.h> | |
44 | #include "os_support.h" | |
45 | #include "url.h" | |
46 | ||
47 | typedef struct Context { | |
48 | int fd; | |
49 | int64_t end; | |
50 | int64_t pos; | |
51 | URLContext *inner; | |
52 | } Context; | |
53 | ||
54 | static int cache_open(URLContext *h, const char *arg, int flags) | |
55 | { | |
56 | char *buffername; | |
57 | Context *c= h->priv_data; | |
58 | ||
59 | av_strstart(arg, "cache:", &arg); | |
60 | ||
61 | c->fd = av_tempfile("ffcache", &buffername, 0, h); | |
62 | if (c->fd < 0){ | |
63 | av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n"); | |
64 | return c->fd; | |
65 | } | |
66 | ||
67 | unlink(buffername); | |
68 | av_freep(&buffername); | |
69 | ||
70 | return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL); | |
71 | } | |
72 | ||
73 | static int cache_read(URLContext *h, unsigned char *buf, int size) | |
74 | { | |
75 | Context *c= h->priv_data; | |
76 | int r; | |
77 | ||
78 | if(c->pos<c->end){ | |
79 | r = read(c->fd, buf, FFMIN(size, c->end - c->pos)); | |
80 | if(r>0) | |
81 | c->pos += r; | |
82 | return (-1 == r)?AVERROR(errno):r; | |
83 | }else{ | |
84 | r = ffurl_read(c->inner, buf, size); | |
85 | if(r > 0){ | |
86 | int r2= write(c->fd, buf, r); | |
87 | av_assert0(r2==r); // FIXME handle cache failure | |
88 | c->pos += r; | |
89 | c->end += r; | |
90 | } | |
91 | return r; | |
92 | } | |
93 | } | |
94 | ||
95 | static int64_t cache_seek(URLContext *h, int64_t pos, int whence) | |
96 | { | |
97 | Context *c= h->priv_data; | |
98 | ||
99 | if (whence == AVSEEK_SIZE) { | |
100 | pos= ffurl_seek(c->inner, pos, whence); | |
101 | if(pos <= 0){ | |
102 | pos= ffurl_seek(c->inner, -1, SEEK_END); | |
103 | ffurl_seek(c->inner, c->end, SEEK_SET); | |
104 | if(pos <= 0) | |
105 | return c->end; | |
106 | } | |
107 | return pos; | |
108 | } | |
109 | ||
110 | pos= lseek(c->fd, pos, whence); | |
111 | if(pos<0){ | |
112 | return pos; | |
113 | }else if(pos <= c->end){ | |
114 | c->pos= pos; | |
115 | return pos; | |
116 | }else{ | |
117 | if(lseek(c->fd, c->pos, SEEK_SET) < 0) { | |
118 | av_log(h, AV_LOG_ERROR, "Failure to seek in cache\n"); | |
119 | } | |
120 | return AVERROR(EPIPE); | |
121 | } | |
122 | } | |
123 | ||
124 | static int cache_close(URLContext *h) | |
125 | { | |
126 | Context *c= h->priv_data; | |
127 | close(c->fd); | |
128 | ffurl_close(c->inner); | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | URLProtocol ff_cache_protocol = { | |
134 | .name = "cache", | |
135 | .url_open = cache_open, | |
136 | .url_read = cache_read, | |
137 | .url_seek = cache_seek, | |
138 | .url_close = cache_close, | |
139 | .priv_data_size = sizeof(Context), | |
140 | }; |