Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52867 views
1
/*****************************************************************************
2
* depth.c: bit-depth conversion video filter
3
*****************************************************************************
4
* Copyright (C) 2010-2016 x264 project
5
*
6
* Authors: Oskar Arvidsson <[email protected]>
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 [email protected].
24
*****************************************************************************/
25
26
#include "video.h"
27
#define NAME "depth"
28
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
29
30
cli_vid_filter_t depth_filter;
31
32
typedef struct
33
{
34
hnd_t prev_hnd;
35
cli_vid_filter_t prev_filter;
36
37
int bit_depth;
38
int dst_csp;
39
cli_pic_t buffer;
40
int16_t *error_buf;
41
} depth_hnd_t;
42
43
static int depth_filter_csp_is_supported( int csp )
44
{
45
int csp_mask = csp & X264_CSP_MASK;
46
return csp_mask == X264_CSP_I420 ||
47
csp_mask == X264_CSP_I422 ||
48
csp_mask == X264_CSP_I444 ||
49
csp_mask == X264_CSP_YV12 ||
50
csp_mask == X264_CSP_YV16 ||
51
csp_mask == X264_CSP_YV24 ||
52
csp_mask == X264_CSP_NV12 ||
53
csp_mask == X264_CSP_NV21 ||
54
csp_mask == X264_CSP_NV16 ||
55
csp_mask == X264_CSP_BGR ||
56
csp_mask == X264_CSP_RGB ||
57
csp_mask == X264_CSP_BGRA;
58
}
59
60
static int csp_num_interleaved( int csp, int plane )
61
{
62
int csp_mask = csp & X264_CSP_MASK;
63
return (csp_mask == X264_CSP_NV12 || csp_mask == X264_CSP_NV21 || csp_mask == X264_CSP_NV16) && plane == 1 ? 2 :
64
csp_mask == X264_CSP_BGR || csp_mask == X264_CSP_RGB ? 3 :
65
csp_mask == X264_CSP_BGRA ? 4 :
66
1;
67
}
68
69
/* The dithering algorithm is based on Sierra-2-4A error diffusion. It has been
70
* written in such a way so that if the source has been upconverted using the
71
* same algorithm as used in scale_image, dithering down to the source bit
72
* depth again is lossless. */
73
#define DITHER_PLANE( pitch ) \
74
static void dither_plane_##pitch( pixel *dst, int dst_stride, uint16_t *src, int src_stride, \
75
int width, int height, int16_t *errors ) \
76
{ \
77
const int lshift = 16-X264_BIT_DEPTH; \
78
const int rshift = 16-X264_BIT_DEPTH+2; \
79
const int half = 1 << (16-X264_BIT_DEPTH+1); \
80
const int pixel_max = (1 << X264_BIT_DEPTH)-1; \
81
memset( errors, 0, (width+1) * sizeof(int16_t) ); \
82
for( int y = 0; y < height; y++, src += src_stride, dst += dst_stride ) \
83
{ \
84
int err = 0; \
85
for( int x = 0; x < width; x++ ) \
86
{ \
87
err = err*2 + errors[x] + errors[x+1]; \
88
dst[x*pitch] = x264_clip3( ((src[x*pitch]<<2)+err+half) >> rshift, 0, pixel_max ); \
89
errors[x] = err = src[x*pitch] - (dst[x*pitch] << lshift); \
90
} \
91
} \
92
}
93
94
DITHER_PLANE( 1 )
95
DITHER_PLANE( 2 )
96
DITHER_PLANE( 3 )
97
DITHER_PLANE( 4 )
98
99
static void dither_image( cli_image_t *out, cli_image_t *img, int16_t *error_buf )
100
{
101
int csp_mask = img->csp & X264_CSP_MASK;
102
for( int i = 0; i < img->planes; i++ )
103
{
104
int num_interleaved = csp_num_interleaved( img->csp, i );
105
int height = x264_cli_csps[csp_mask].height[i] * img->height;
106
int width = x264_cli_csps[csp_mask].width[i] * img->width / num_interleaved;
107
108
#define CALL_DITHER_PLANE( pitch, off ) \
109
dither_plane_##pitch( ((pixel*)out->plane[i])+off, out->stride[i]/sizeof(pixel), \
110
((uint16_t*)img->plane[i])+off, img->stride[i]/2, width, height, error_buf )
111
112
if( num_interleaved == 4 )
113
{
114
CALL_DITHER_PLANE( 4, 0 );
115
CALL_DITHER_PLANE( 4, 1 );
116
CALL_DITHER_PLANE( 4, 2 );
117
CALL_DITHER_PLANE( 4, 3 ); //we probably can skip this one
118
}
119
else if( num_interleaved == 3 )
120
{
121
CALL_DITHER_PLANE( 3, 0 );
122
CALL_DITHER_PLANE( 3, 1 );
123
CALL_DITHER_PLANE( 3, 2 );
124
}
125
else if( num_interleaved == 2 )
126
{
127
CALL_DITHER_PLANE( 2, 0 );
128
CALL_DITHER_PLANE( 2, 1 );
129
}
130
else //if( num_interleaved == 1 )
131
{
132
CALL_DITHER_PLANE( 1, 0 );
133
}
134
}
135
}
136
137
static void scale_image( cli_image_t *output, cli_image_t *img )
138
{
139
int csp_mask = img->csp & X264_CSP_MASK;
140
const int shift = X264_BIT_DEPTH - 8;
141
for( int i = 0; i < img->planes; i++ )
142
{
143
uint8_t *src = img->plane[i];
144
uint16_t *dst = (uint16_t*)output->plane[i];
145
int height = x264_cli_csps[csp_mask].height[i] * img->height;
146
int width = x264_cli_csps[csp_mask].width[i] * img->width;
147
148
for( int j = 0; j < height; j++ )
149
{
150
for( int k = 0; k < width; k++ )
151
dst[k] = src[k] << shift;
152
153
src += img->stride[i];
154
dst += output->stride[i]/2;
155
}
156
}
157
}
158
159
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
160
{
161
depth_hnd_t *h = handle;
162
163
if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
164
return -1;
165
166
if( h->bit_depth < 16 && output->img.csp & X264_CSP_HIGH_DEPTH )
167
{
168
dither_image( &h->buffer.img, &output->img, h->error_buf );
169
output->img = h->buffer.img;
170
}
171
else if( h->bit_depth > 8 && !(output->img.csp & X264_CSP_HIGH_DEPTH) )
172
{
173
scale_image( &h->buffer.img, &output->img );
174
output->img = h->buffer.img;
175
}
176
return 0;
177
}
178
179
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
180
{
181
depth_hnd_t *h = handle;
182
return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
183
}
184
185
static void free_filter( hnd_t handle )
186
{
187
depth_hnd_t *h = handle;
188
h->prev_filter.free( h->prev_hnd );
189
x264_cli_pic_clean( &h->buffer );
190
x264_free( h );
191
}
192
193
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info,
194
x264_param_t *param, char *opt_string )
195
{
196
int ret = 0;
197
int change_fmt = (info->csp ^ param->i_csp) & X264_CSP_HIGH_DEPTH;
198
int csp = ~(~info->csp ^ change_fmt);
199
int bit_depth = 8*x264_cli_csp_depth_factor( csp );
200
201
if( opt_string )
202
{
203
static const char *optlist[] = { "bit_depth", NULL };
204
char **opts = x264_split_options( opt_string, optlist );
205
206
if( opts )
207
{
208
char *str_bit_depth = x264_get_option( "bit_depth", opts );
209
bit_depth = x264_otoi( str_bit_depth, -1 );
210
211
ret = bit_depth < 8 || bit_depth > 16;
212
csp = bit_depth > 8 ? csp | X264_CSP_HIGH_DEPTH : csp & ~X264_CSP_HIGH_DEPTH;
213
change_fmt = (info->csp ^ csp) & X264_CSP_HIGH_DEPTH;
214
x264_free_string_array( opts );
215
}
216
else
217
ret = 1;
218
}
219
220
FAIL_IF_ERROR( bit_depth != X264_BIT_DEPTH, "this build supports only bit depth %d\n", X264_BIT_DEPTH )
221
FAIL_IF_ERROR( ret, "unsupported bit depth conversion.\n" )
222
223
/* only add the filter to the chain if it's needed */
224
if( change_fmt || bit_depth != 8 * x264_cli_csp_depth_factor( csp ) )
225
{
226
FAIL_IF_ERROR( !depth_filter_csp_is_supported(csp), "unsupported colorspace.\n" )
227
depth_hnd_t *h = x264_malloc( sizeof(depth_hnd_t) + (info->width+1)*sizeof(int16_t) );
228
229
if( !h )
230
return -1;
231
232
h->error_buf = (int16_t*)(h + 1);
233
h->dst_csp = csp;
234
h->bit_depth = bit_depth;
235
h->prev_hnd = *handle;
236
h->prev_filter = *filter;
237
238
if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, info->width, info->height ) )
239
{
240
x264_free( h );
241
return -1;
242
}
243
244
*handle = h;
245
*filter = depth_filter;
246
info->csp = h->dst_csp;
247
}
248
249
return 0;
250
}
251
252
cli_vid_filter_t depth_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };
253
254