libavformat/cache.c
e9f62a8b
 /*
  * Input cache protocol.
  * Copyright (c) 2011 Michael Niedermayer
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  *
  * Based on file.c by Fabrice Bellard
  */
 
9329d36a
 /**
  * @TODO
  *      support non continuous caching
  *      support keeping files
  *      support filling with a background thread
  */
 
e9f62a8b
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavutil/file.h"
 #include "avformat.h"
 #include <fcntl.h>
af2a17c0
 #if HAVE_IO_H
e9f62a8b
 #include <io.h>
 #endif
af2a17c0
 #if HAVE_UNISTD_H
e9f62a8b
 #include <unistd.h>
af2a17c0
 #endif
e9f62a8b
 #include <sys/stat.h>
 #include <stdlib.h>
 #include "os_support.h"
 #include "url.h"
 
 typedef struct Context {
     int fd;
     int64_t end;
     int64_t pos;
     URLContext *inner;
 } Context;
 
 static int cache_open(URLContext *h, const char *arg, int flags)
 {
e81e0b99
     char *buffername;
7b0b10ce
     Context *c= h->priv_data;
e9f62a8b
 
     av_strstart(arg, "cache:", &arg);
 
74dbb538
     c->fd = av_tempfile("ffcache", &buffername, 0, h);
e9f62a8b
     if (c->fd < 0){
         av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n");
         return c->fd;
     }
 
     unlink(buffername);
576ada79
     av_freep(&buffername);
e9f62a8b
 
5f268ca5
     return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL);
e9f62a8b
 }
 
 static int cache_read(URLContext *h, unsigned char *buf, int size)
 {
     Context *c= h->priv_data;
     int r;
 
     if(c->pos<c->end){
         r = read(c->fd, buf, FFMIN(size, c->end - c->pos));
         if(r>0)
             c->pos += r;
         return (-1 == r)?AVERROR(errno):r;
     }else{
         r = ffurl_read(c->inner, buf, size);
         if(r > 0){
             int r2= write(c->fd, buf, r);
             av_assert0(r2==r); // FIXME handle cache failure
             c->pos += r;
             c->end += r;
         }
         return r;
     }
 }
 
 static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
 {
     Context *c= h->priv_data;
 
     if (whence == AVSEEK_SIZE) {
02b651a7
         pos= ffurl_seek(c->inner, pos, whence);
         if(pos <= 0){
             pos= ffurl_seek(c->inner, -1, SEEK_END);
             ffurl_seek(c->inner, c->end, SEEK_SET);
             if(pos <= 0)
                 return c->end;
         }
         return pos;
e9f62a8b
     }
 
     pos= lseek(c->fd, pos, whence);
     if(pos<0){
         return pos;
     }else if(pos <= c->end){
         c->pos= pos;
         return pos;
     }else{
eb19d89d
         if(lseek(c->fd, c->pos, SEEK_SET) < 0) {
             av_log(h, AV_LOG_ERROR, "Failure to seek in cache\n");
         }
e9f62a8b
         return AVERROR(EPIPE);
     }
 }
 
 static int cache_close(URLContext *h)
 {
     Context *c= h->priv_data;
     close(c->fd);
     ffurl_close(c->inner);
 
     return 0;
 }
 
 URLProtocol ff_cache_protocol = {
     .name                = "cache",
     .url_open            = cache_open,
     .url_read            = cache_read,
     .url_seek            = cache_seek,
     .url_close           = cache_close,
7b0b10ce
     .priv_data_size      = sizeof(Context),
e9f62a8b
 };