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