00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00031 #include "libavutil/avassert.h"
00032 #include "libavutil/avstring.h"
00033 #include "libavutil/file.h"
00034 #include "avformat.h"
00035 #include <fcntl.h>
00036 #if HAVE_SETMODE
00037 #include <io.h>
00038 #endif
00039 #include <unistd.h>
00040 #include <sys/stat.h>
00041 #include <stdlib.h>
00042 #include "os_support.h"
00043 #include "url.h"
00044 
00045 typedef struct Context {
00046     int fd;
00047     int64_t end;
00048     int64_t pos;
00049     URLContext *inner;
00050 } Context;
00051 
00052 static int cache_open(URLContext *h, const char *arg, int flags)
00053 {
00054     int access;
00055     const char *buffername;
00056     Context *c= h->priv_data;
00057 
00058     av_strstart(arg, "cache:", &arg);
00059 
00060     c->fd = av_tempfile("ffcache", &buffername, 0, h);
00061     if (c->fd < 0){
00062         av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n");
00063         return c->fd;
00064     }
00065 
00066     unlink(buffername);
00067     av_freep(&buffername);
00068 
00069     return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL);
00070 }
00071 
00072 static int cache_read(URLContext *h, unsigned char *buf, int size)
00073 {
00074     Context *c= h->priv_data;
00075     int r;
00076 
00077     if(c->pos<c->end){
00078         r = read(c->fd, buf, FFMIN(size, c->end - c->pos));
00079         if(r>0)
00080             c->pos += r;
00081         return (-1 == r)?AVERROR(errno):r;
00082     }else{
00083         r = ffurl_read(c->inner, buf, size);
00084         if(r > 0){
00085             int r2= write(c->fd, buf, r);
00086             av_assert0(r2==r); 
00087             c->pos += r;
00088             c->end += r;
00089         }
00090         return r;
00091     }
00092 }
00093 
00094 static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
00095 {
00096     Context *c= h->priv_data;
00097 
00098     if (whence == AVSEEK_SIZE) {
00099         pos= ffurl_seek(c->inner, pos, whence);
00100         if(pos <= 0){
00101             pos= ffurl_seek(c->inner, -1, SEEK_END);
00102             ffurl_seek(c->inner, c->end, SEEK_SET);
00103             if(pos <= 0)
00104                 return c->end;
00105         }
00106         return pos;
00107     }
00108 
00109     pos= lseek(c->fd, pos, whence);
00110     if(pos<0){
00111         return pos;
00112     }else if(pos <= c->end){
00113         c->pos= pos;
00114         return pos;
00115     }else{
00116         lseek(c->fd, c->pos, SEEK_SET);
00117         return AVERROR(EPIPE);
00118     }
00119 }
00120 
00121 static int cache_close(URLContext *h)
00122 {
00123     Context *c= h->priv_data;
00124     close(c->fd);
00125     ffurl_close(c->inner);
00126 
00127     return 0;
00128 }
00129 
00130 URLProtocol ff_cache_protocol = {
00131     .name                = "cache",
00132     .url_open            = cache_open,
00133     .url_read            = cache_read,
00134     .url_seek            = cache_seek,
00135     .url_close           = cache_close,
00136     .priv_data_size      = sizeof(Context),
00137 };