Merge remote-tracking branch 'qatar/master'
[ffmpeg.git] / libavformat / cache.c
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_SETMODE
37 #include <io.h>
38 #endif
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include <stdlib.h>
42 #include "os_support.h"
43 #include "url.h"
44
45 typedef struct Context {
46     int fd;
47     int64_t end;
48     int64_t pos;
49     URLContext *inner;
50 } Context;
51
52 static int cache_open(URLContext *h, const char *arg, int flags)
53 {
54     int access;
55     const char *buffername;
56     Context *c;
57
58     c = av_mallocz(sizeof(Context));
59     if (!c) {
60         return AVERROR(ENOMEM);
61     }
62     h->priv_data = c;
63
64     av_strstart(arg, "cache:", &arg);
65
66     c->fd = av_tempfile("ffcache", &buffername, 0, h);
67     if (c->fd < 0){
68         av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n");
69         return c->fd;
70     }
71
72     unlink(buffername);
73     av_free(buffername);
74
75     return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL);
76 }
77
78 static int cache_read(URLContext *h, unsigned char *buf, int size)
79 {
80     Context *c= h->priv_data;
81     int r;
82
83     if(c->pos<c->end){
84         r = read(c->fd, buf, FFMIN(size, c->end - c->pos));
85         if(r>0)
86             c->pos += r;
87         return (-1 == r)?AVERROR(errno):r;
88     }else{
89         r = ffurl_read(c->inner, buf, size);
90         if(r > 0){
91             int r2= write(c->fd, buf, r);
92             av_assert0(r2==r); // FIXME handle cache failure
93             c->pos += r;
94             c->end += r;
95         }
96         return r;
97     }
98 }
99
100 static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
101 {
102     Context *c= h->priv_data;
103
104     if (whence == AVSEEK_SIZE) {
105         pos= ffurl_seek(c->inner, pos, whence);
106         if(pos <= 0){
107             pos= ffurl_seek(c->inner, -1, SEEK_END);
108             ffurl_seek(c->inner, c->end, SEEK_SET);
109             if(pos <= 0)
110                 return c->end;
111         }
112         return pos;
113     }
114
115     pos= lseek(c->fd, pos, whence);
116     if(pos<0){
117         return pos;
118     }else if(pos <= c->end){
119         c->pos= pos;
120         return pos;
121     }else{
122         lseek(c->fd, c->pos, SEEK_SET);
123         return AVERROR(EPIPE);
124     }
125 }
126
127 static int cache_close(URLContext *h)
128 {
129     Context *c= h->priv_data;
130     close(c->fd);
131     ffurl_close(c->inner);
132
133     av_freep(&h->priv_data);
134
135     return 0;
136 }
137
138 URLProtocol ff_cache_protocol = {
139     .name                = "cache",
140     .url_open            = cache_open,
141     .url_read            = cache_read,
142     .url_seek            = cache_seek,
143     .url_close           = cache_close,
144 };