/*****************************************************************************1* cache.c: cache video filter2*****************************************************************************3* Copyright (C) 2010-2016 x264 project4*5* Authors: Steven Walters <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* (at your option) any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.20*21* This program is also available under a commercial proprietary license.22* For more information, contact us at [email protected].23*****************************************************************************/2425#include "video.h"26#include "internal.h"27#define NAME "cache"28#define LAST_FRAME (h->first_frame + h->cur_size - 1)2930typedef struct31{32hnd_t prev_hnd;33cli_vid_filter_t prev_filter;3435int max_size;36int first_frame; /* first cached frame */37cli_pic_t **cache;38int cur_size;39int eof; /* frame beyond end of the file */40} cache_hnd_t;4142cli_vid_filter_t cache_filter;4344static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )45{46intptr_t size = (intptr_t)opt_string;47/* upon a <= 0 cache request, do nothing */48if( size <= 0 )49return 0;50cache_hnd_t *h = calloc( 1, sizeof(cache_hnd_t) );51if( !h )52return -1;5354h->max_size = size;55h->cache = malloc( (h->max_size+1) * sizeof(cli_pic_t*) );56if( !h->cache )57return -1;5859for( int i = 0; i < h->max_size; i++ )60{61h->cache[i] = malloc( sizeof(cli_pic_t) );62if( !h->cache[i] || x264_cli_pic_alloc( h->cache[i], info->csp, info->width, info->height ) )63return -1;64}65h->cache[h->max_size] = NULL; /* require null terminator for list methods */6667h->prev_filter = *filter;68h->prev_hnd = *handle;69*handle = h;70*filter = cache_filter;7172return 0;73}7475static void fill_cache( cache_hnd_t *h, int frame )76{77/* shift frames out of the cache as the frame request is beyond the filled cache */78int shift = frame - LAST_FRAME;79/* no frames to shift or no frames left to read */80if( shift <= 0 || h->eof )81return;82/* the next frames to read are either83* A) starting at the end of the current cache, or84* B) starting at a new frame that has the end of the cache at the desired frame85* and proceeding to fill the entire cache */86int cur_frame = X264_MAX( h->first_frame + h->cur_size, frame - h->max_size + 1 );87/* the new starting point is either88* A) the current one shifted the number of frames entering/leaving the cache, or89* B) at a new frame that has the end of the cache at the desired frame. */90h->first_frame = X264_MIN( h->first_frame + shift, cur_frame );91h->cur_size = X264_MAX( h->cur_size - shift, 0 );92while( h->cur_size < h->max_size )93{94cli_pic_t temp;95/* the old front frame is going to shift off, overwrite it with the new frame */96cli_pic_t *cache = h->cache[0];97if( h->prev_filter.get_frame( h->prev_hnd, &temp, cur_frame ) ||98x264_cli_pic_copy( cache, &temp ) ||99h->prev_filter.release_frame( h->prev_hnd, &temp, cur_frame ) )100{101h->eof = cur_frame;102return;103}104/* the read was successful, shift the frame off the front to the end */105x264_frame_push( (void*)h->cache, x264_frame_shift( (void*)h->cache ) );106cur_frame++;107h->cur_size++;108}109}110111static int get_frame( hnd_t handle, cli_pic_t *output, int frame )112{113cache_hnd_t *h = handle;114FAIL_IF_ERR( frame < h->first_frame, NAME, "frame %d is before first cached frame %d \n", frame, h->first_frame );115fill_cache( h, frame );116if( frame > LAST_FRAME ) /* eof */117return -1;118int idx = frame - (h->eof ? h->eof - h->max_size : h->first_frame);119*output = *h->cache[idx];120return 0;121}122123static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )124{125/* the parent filter's frame has already been released so do nothing here */126return 0;127}128129static void free_filter( hnd_t handle )130{131cache_hnd_t *h = handle;132h->prev_filter.free( h->prev_hnd );133for( int i = 0; i < h->max_size; i++ )134{135x264_cli_pic_clean( h->cache[i] );136free( h->cache[i] );137}138free( h->cache );139free( h );140}141142cli_vid_filter_t cache_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };143144145