1 /*****************************************************************************
2  * cache.c: cache video filter
3  *****************************************************************************
4  * Copyright (C) 2010-2014 x264 project
5  *
6  * Authors: Steven Walters <kemuri9@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
21  *
22  * This program is also available under a commercial proprietary license.
23  * For more information, contact us at licensing@x264.com.
24  *****************************************************************************/
25 
26 #include "video.h"
27 #include "internal.h"
28 #define NAME "cache"
29 #define LAST_FRAME (h->first_frame + h->cur_size - 1)
30 
31 typedef struct
32 {
33     hnd_t prev_hnd;
34     cli_vid_filter_t prev_filter;
35 
36     int max_size;
37     int first_frame; /* first cached frame */
38     cli_pic_t **cache;
39     int cur_size;
40     int eof;         /* frame beyond end of the file */
41 } cache_hnd_t;
42 
43 cli_vid_filter_t cache_filter;
44 
init(hnd_t * handle,cli_vid_filter_t * filter,video_info_t * info,x264_param_t * param,char * opt_string)45 static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
46 {
47     intptr_t size = (intptr_t)opt_string;
48     cache_hnd_t *h;
49 	int i;
50 
51 	/* upon a <= 0 cache request, do nothing */
52     if( size <= 0 )
53         return 0;
54     h = calloc( 1, sizeof(cache_hnd_t) );
55     if( !h )
56         return -1;
57 
58     h->max_size = size;
59     h->cache = malloc( (h->max_size+1) * sizeof(cli_pic_t*) );
60     if( !h->cache )
61         return -1;
62 
63     for( i = 0; i < h->max_size; i++ )
64     {
65         h->cache[i] = malloc( sizeof(cli_pic_t) );
66         if( !h->cache[i] || x264_cli_pic_alloc( h->cache[i], info->csp, info->width, info->height ) )
67             return -1;
68     }
69     h->cache[h->max_size] = NULL; /* require null terminator for list methods */
70 
71     h->prev_filter = *filter;
72     h->prev_hnd = *handle;
73     *handle = h;
74     *filter = cache_filter;
75 
76     return 0;
77 }
78 
fill_cache(cache_hnd_t * h,int frame)79 static void fill_cache( cache_hnd_t *h, int frame )
80 {
81     /* shift frames out of the cache as the frame request is beyond the filled cache */
82     int shift = frame - LAST_FRAME;
83     int cur_frame;
84 
85 	/* no frames to shift or no frames left to read */
86     if( shift <= 0 || h->eof )
87         return;
88     /* the next frames to read are either
89      * A) starting at the end of the current cache, or
90      * B) starting at a new frame that has the end of the cache at the desired frame
91      * and proceeding to fill the entire cache */
92     cur_frame = X264_MAX( h->first_frame + h->cur_size, frame - h->max_size + 1 );
93     /* the new starting point is either
94      * A) the current one shifted the number of frames entering/leaving the cache, or
95      * B) at a new frame that has the end of the cache at the desired frame. */
96     h->first_frame = X264_MIN( h->first_frame + shift, cur_frame );
97     h->cur_size = X264_MAX( h->cur_size - shift, 0 );
98     while( h->cur_size < h->max_size )
99     {
100         cli_pic_t temp;
101         /* the old front frame is going to shift off, overwrite it with the new frame */
102         cli_pic_t *cache = h->cache[0];
103         if( h->prev_filter.get_frame( h->prev_hnd, &temp, cur_frame ) ||
104             x264_cli_pic_copy( cache, &temp ) ||
105             h->prev_filter.release_frame( h->prev_hnd, &temp, cur_frame ) )
106         {
107             h->eof = cur_frame;
108             return;
109         }
110         /* the read was successful, shift the frame off the front to the end */
111         x264_frame_push( (void*)h->cache, x264_frame_shift( (void*)h->cache ) );
112         cur_frame++;
113         h->cur_size++;
114     }
115 }
116 
get_frame(hnd_t handle,cli_pic_t * output,int frame)117 static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
118 {
119     cache_hnd_t *h = handle;
120     int idx;
121 
122 	FAIL_IF_ERR( frame < h->first_frame, NAME, "frame %d is before first cached frame %d \n", frame, h->first_frame );
123     fill_cache( h, frame );
124     if( frame > LAST_FRAME ) /* eof */
125         return -1;
126     idx = frame - (h->eof ? h->eof - h->max_size : h->first_frame);
127     *output = *h->cache[idx];
128     return 0;
129 }
130 
release_frame(hnd_t handle,cli_pic_t * pic,int frame)131 static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
132 {
133     /* the parent filter's frame has already been released so do nothing here */
134     return 0;
135 }
136 
free_filter(hnd_t handle)137 static void free_filter( hnd_t handle )
138 {
139 	cache_hnd_t *h = handle;
140 	int i;
141 
142 	h->prev_filter.free( h->prev_hnd );
143     for( i = 0; i < h->max_size; i++ )
144     {
145         x264_cli_pic_clean( h->cache[i] );
146         free( h->cache[i] );
147     }
148     free( h->cache );
149     free( h );
150 }
151 
152 cli_vid_filter_t cache_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };
153