00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00031 #include <strings.h>
00032 #include <float.h>
00033 #include "avfilter.h"
00034 #include "libavutil/pixdesc.h"
00035 #include "libavutil/avstring.h"
00036
00037 #define NS(n) n < 0 ? (int)(n*65536.0-0.5+DBL_EPSILON) : (int)(n*65536.0+0.5)
00038 #define CB(n) av_clip_uint8(n)
00039
00040 static const double yuv_coeff[4][3][3] = {
00041 { { +0.7152, +0.0722, +0.2126 },
00042 { -0.3850, +0.5000, -0.1150 },
00043 { -0.4540, -0.0460, +0.5000 } },
00044 { { +0.5900, +0.1100, +0.3000 },
00045 { -0.3310, +0.5000, -0.1690 },
00046 { -0.4210, -0.0790, +0.5000 } },
00047 { { +0.5870, +0.1140, +0.2990 },
00048 { -0.3313, +0.5000, -0.1687 },
00049 { -0.4187, -0.0813, +0.5000 } },
00050 { { +0.7010, +0.0870, +0.2120 },
00051 { -0.3840, +0.5000, -0.1160 },
00052 { -0.4450, -0.0550, +0.5000 } },
00053 };
00054
00055 typedef struct {
00056 int yuv_convert[16][3][3];
00057 int interlaced;
00058 int source, dest, mode;
00059 char src[256];
00060 char dst[256];
00061 int hsub, vsub;
00062 } ColorMatrixContext;
00063
00064 #define ma m[0][0]
00065 #define mb m[0][1]
00066 #define mc m[0][2]
00067 #define md m[1][0]
00068 #define me m[1][1]
00069 #define mf m[1][2]
00070 #define mg m[2][0]
00071 #define mh m[2][1]
00072 #define mi m[2][2]
00073
00074 #define ima im[0][0]
00075 #define imb im[0][1]
00076 #define imc im[0][2]
00077 #define imd im[1][0]
00078 #define ime im[1][1]
00079 #define imf im[1][2]
00080 #define img im[2][0]
00081 #define imh im[2][1]
00082 #define imi im[2][2]
00083
00084 static void inverse3x3(double im[3][3], const double m[3][3])
00085 {
00086 double det = ma * (me * mi - mf * mh) - mb * (md * mi - mf * mg) + mc * (md * mh - me * mg);
00087 det = 1.0 / det;
00088 ima = det * (me * mi - mf * mh);
00089 imb = det * (mc * mh - mb * mi);
00090 imc = det * (mb * mf - mc * me);
00091 imd = det * (mf * mg - md * mi);
00092 ime = det * (ma * mi - mc * mg);
00093 imf = det * (mc * md - ma * mf);
00094 img = det * (md * mh - me * mg);
00095 imh = det * (mb * mg - ma * mh);
00096 imi = det * (ma * me - mb * md);
00097 }
00098
00099 static void solve_coefficients(double cm[3][3], double rgb[3][3], const double yuv[3][3])
00100 {
00101 int i, j;
00102 for (i = 0; i < 3; i++)
00103 for (j = 0; j < 3; j++)
00104 cm[i][j] = yuv[i][0] * rgb[0][j] + yuv[i][1] * rgb[1][j] + yuv[i][2] * rgb[2][j];
00105 }
00106
00107 static void calc_coefficients(AVFilterContext *ctx)
00108 {
00109 ColorMatrixContext *color = ctx->priv;
00110 double rgb_coeffd[4][3][3];
00111 double yuv_convertd[16][3][3];
00112 int v = 0;
00113 int i, j, k;
00114
00115 for (i = 0; i < 4; i++)
00116 inverse3x3(rgb_coeffd[i], yuv_coeff[i]);
00117 for (i = 0; i < 4; i++) {
00118 for (j = 0; j < 4; j++) {
00119 solve_coefficients(yuv_convertd[v], rgb_coeffd[i], yuv_coeff[j]);
00120 for (k = 0; k < 3; k++) {
00121 color->yuv_convert[v][k][0] = NS(yuv_convertd[v][k][0]);
00122 color->yuv_convert[v][k][1] = NS(yuv_convertd[v][k][1]);
00123 color->yuv_convert[v][k][2] = NS(yuv_convertd[v][k][2]);
00124 }
00125 if (color->yuv_convert[v][0][0] != 65536 || color->yuv_convert[v][1][0] != 0 ||
00126 color->yuv_convert[v][2][0] != 0) {
00127 av_log(ctx, AV_LOG_ERROR, "error calculating conversion coefficients\n");
00128 }
00129 v++;
00130 }
00131 }
00132 }
00133
00134 static const char *color_modes[] = {"bt709", "FCC", "bt601", "smpte240m"};
00135
00136 static int get_color_mode_index(const char *name)
00137 {
00138 int i;
00139
00140 for (i = 0; i < FF_ARRAY_ELEMS(color_modes); i++)
00141 if (!av_strcasecmp(color_modes[i], name))
00142 return i;
00143 return -1;
00144 }
00145
00146 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00147 {
00148 ColorMatrixContext *color = ctx->priv;
00149
00150 if (!args)
00151 goto usage;
00152 if (sscanf(args, "%255[^:]:%255[^:]", color->src, color->dst) != 2) {
00153 usage:
00154 av_log(ctx, AV_LOG_ERROR, "usage: <src>:<dst>\n");
00155 av_log(ctx, AV_LOG_ERROR, "possible options: bt709,bt601,smpte240m,fcc\n");
00156 return -1;
00157 }
00158
00159 color->source = get_color_mode_index(color->src);
00160 if (color->source < 0) {
00161 av_log(ctx, AV_LOG_ERROR, "unknown color space %s\n", color->src);
00162 return AVERROR(EINVAL);
00163 }
00164
00165 color->dest = get_color_mode_index(color->dst);
00166 if (color->dest < 0) {
00167 av_log(ctx, AV_LOG_ERROR, "unknown color space %s\n", color->dst);
00168 return AVERROR(EINVAL);
00169 }
00170
00171 if (color->source == color->dest) {
00172 av_log(ctx, AV_LOG_ERROR, "source and destination color space are identical\n");
00173 return AVERROR(EINVAL);
00174 }
00175
00176 color->mode = color->source * 4 + color->dest;
00177
00178 calc_coefficients(ctx);
00179
00180 return 0;
00181 }
00182
00183 static void process_frame_uyvy422(ColorMatrixContext *color,
00184 AVFilterBufferRef *dst, AVFilterBufferRef *src)
00185 {
00186 const unsigned char *srcp = src->data[0];
00187 const int src_pitch = src->linesize[0];
00188 const int height = src->video->h;
00189 const int width = src->video->w*2;
00190 unsigned char *dstp = dst->data[0];
00191 const int dst_pitch = dst->linesize[0];
00192 const int c2 = color->yuv_convert[color->mode][0][1];
00193 const int c3 = color->yuv_convert[color->mode][0][2];
00194 const int c4 = color->yuv_convert[color->mode][1][1];
00195 const int c5 = color->yuv_convert[color->mode][1][2];
00196 const int c6 = color->yuv_convert[color->mode][2][1];
00197 const int c7 = color->yuv_convert[color->mode][2][2];
00198 int x, y;
00199
00200 for (y = 0; y < height; y++) {
00201 for (x = 0; x < width; x += 4) {
00202 const int u = srcp[x + 0] - 128;
00203 const int v = srcp[x + 2] - 128;
00204 const int uvval = c2 * u + c3 * v + 1081344;
00205 dstp[x + 0] = CB((c4 * u + c5 * v + 8421376) >> 16);
00206 dstp[x + 1] = CB((65536 * (srcp[x + 1] - 16) + uvval) >> 16);
00207 dstp[x + 2] = CB((c6 * u + c7 * v + 8421376) >> 16);
00208 dstp[x + 3] = CB((65536 * (srcp[x + 3] - 16) + uvval) >> 16);
00209 }
00210 srcp += src_pitch;
00211 dstp += dst_pitch;
00212 }
00213 }
00214
00215 static void process_frame_yuv422p(ColorMatrixContext *color,
00216 AVFilterBufferRef *dst, AVFilterBufferRef *src)
00217 {
00218 const unsigned char *srcpU = src->data[1];
00219 const unsigned char *srcpV = src->data[2];
00220 const unsigned char *srcpY = src->data[0];
00221 const int src_pitchY = src->linesize[0];
00222 const int src_pitchUV = src->linesize[1];
00223 const int height = src->video->h;
00224 const int width = src->video->w;
00225 unsigned char *dstpU = dst->data[1];
00226 unsigned char *dstpV = dst->data[2];
00227 unsigned char *dstpY = dst->data[0];
00228 const int dst_pitchY = dst->linesize[0];
00229 const int dst_pitchUV = dst->linesize[1];
00230 const int c2 = color->yuv_convert[color->mode][0][1];
00231 const int c3 = color->yuv_convert[color->mode][0][2];
00232 const int c4 = color->yuv_convert[color->mode][1][1];
00233 const int c5 = color->yuv_convert[color->mode][1][2];
00234 const int c6 = color->yuv_convert[color->mode][2][1];
00235 const int c7 = color->yuv_convert[color->mode][2][2];
00236 int x, y;
00237
00238 for (y = 0; y < height; y++) {
00239 for (x = 0; x < width; x += 2) {
00240 const int u = srcpU[x >> 1] - 128;
00241 const int v = srcpV[x >> 1] - 128;
00242 const int uvval = c2 * u + c3 * v + 1081344;
00243 dstpY[x + 0] = CB((65536 * (srcpY[x + 0] - 16) + uvval) >> 16);
00244 dstpY[x + 1] = CB((65536 * (srcpY[x + 1] - 16) + uvval) >> 16);
00245 dstpU[x >> 1] = CB((c4 * u + c5 * v + 8421376) >> 16);
00246 dstpV[x >> 1] = CB((c6 * u + c7 * v + 8421376) >> 16);
00247 }
00248 srcpY += src_pitchY;
00249 dstpY += dst_pitchY;
00250 srcpU += src_pitchUV;
00251 srcpV += src_pitchUV;
00252 dstpU += dst_pitchUV;
00253 dstpV += dst_pitchUV;
00254 }
00255 }
00256
00257 static void process_frame_yuv420p(ColorMatrixContext *color,
00258 AVFilterBufferRef *dst, AVFilterBufferRef *src)
00259 {
00260 const unsigned char *srcpU = src->data[1];
00261 const unsigned char *srcpV = src->data[2];
00262 const unsigned char *srcpY = src->data[0];
00263 const unsigned char *srcpN = src->data[0] + src->linesize[0];
00264 const int src_pitchY = src->linesize[0];
00265 const int src_pitchUV = src->linesize[1];
00266 const int height = src->video->h;
00267 const int width = src->video->w;
00268 unsigned char *dstpU = dst->data[1];
00269 unsigned char *dstpV = dst->data[2];
00270 unsigned char *dstpY = dst->data[0];
00271 unsigned char *dstpN = dst->data[0] + dst->linesize[0];
00272 const int dst_pitchY = dst->linesize[0];
00273 const int dst_pitchUV = dst->linesize[1];
00274 const int c2 = color->yuv_convert[color->mode][0][1];
00275 const int c3 = color->yuv_convert[color->mode][0][2];
00276 const int c4 = color->yuv_convert[color->mode][1][1];
00277 const int c5 = color->yuv_convert[color->mode][1][2];
00278 const int c6 = color->yuv_convert[color->mode][2][1];
00279 const int c7 = color->yuv_convert[color->mode][2][2];
00280 int x, y;
00281
00282 for (y = 0; y < height; y += 2) {
00283 for (x = 0; x < width; x += 2) {
00284 const int u = srcpU[x >> 1] - 128;
00285 const int v = srcpV[x >> 1] - 128;
00286 const int uvval = c2 * u + c3 * v + 1081344;
00287 dstpY[x + 0] = CB((65536 * (srcpY[x + 0] - 16) + uvval) >> 16);
00288 dstpY[x + 1] = CB((65536 * (srcpY[x + 1] - 16) + uvval) >> 16);
00289 dstpN[x + 0] = CB((65536 * (srcpN[x + 0] - 16) + uvval) >> 16);
00290 dstpN[x + 1] = CB((65536 * (srcpN[x + 1] - 16) + uvval) >> 16);
00291 dstpU[x >> 1] = CB((c4 * u + c5 * v + 8421376) >> 16);
00292 dstpV[x >> 1] = CB((c6 * u + c7 * v + 8421376) >> 16);
00293 }
00294 srcpY += src_pitchY << 1;
00295 dstpY += dst_pitchY << 1;
00296 srcpN += src_pitchY << 1;
00297 dstpN += dst_pitchY << 1;
00298 srcpU += src_pitchUV;
00299 srcpV += src_pitchUV;
00300 dstpU += dst_pitchUV;
00301 dstpV += dst_pitchUV;
00302 }
00303 }
00304
00305 static int config_input(AVFilterLink *inlink)
00306 {
00307 AVFilterContext *ctx = inlink->dst;
00308 ColorMatrixContext *color = ctx->priv;
00309 const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
00310
00311 color->hsub = pix_desc->log2_chroma_w;
00312 color->vsub = pix_desc->log2_chroma_h;
00313
00314 av_log(ctx, AV_LOG_INFO, "%s -> %s\n", color->src, color->dst);
00315
00316 return 0;
00317 }
00318
00319 static int query_formats(AVFilterContext *ctx)
00320 {
00321 static const enum PixelFormat pix_fmts[] = {
00322 PIX_FMT_YUV422P,
00323 PIX_FMT_YUV420P,
00324 PIX_FMT_UYVY422,
00325 PIX_FMT_NONE
00326 };
00327
00328 avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
00329
00330 return 0;
00331 }
00332
00333 static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms, int w, int h)
00334 {
00335 AVFilterBufferRef *picref =
00336 avfilter_get_video_buffer(inlink->dst->outputs[0], perms, w, h);
00337 return picref;
00338 }
00339
00340 static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
00341 {
00342 AVFilterBufferRef *outpicref = avfilter_ref_buffer(picref, ~0);
00343
00344 link->dst->outputs[0]->out_buf = outpicref;
00345
00346 avfilter_start_frame(link->dst->outputs[0], outpicref);
00347 }
00348
00349 static void end_frame(AVFilterLink *link)
00350 {
00351 AVFilterContext *ctx = link->dst;
00352 ColorMatrixContext *color = ctx->priv;
00353 AVFilterBufferRef *out = link->dst->outputs[0]->out_buf;
00354
00355 if (link->cur_buf->format == PIX_FMT_YUV422P)
00356 process_frame_yuv422p(color, out, link->cur_buf);
00357 else if (link->cur_buf->format == PIX_FMT_YUV420P)
00358 process_frame_yuv420p(color, out, link->cur_buf);
00359 else
00360 process_frame_uyvy422(color, out, link->cur_buf);
00361
00362 avfilter_draw_slice(ctx->outputs[0], 0, link->dst->outputs[0]->h, 1);
00363 avfilter_end_frame(ctx->outputs[0]);
00364 avfilter_unref_buffer(link->cur_buf);
00365 }
00366
00367 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
00368
00369 AVFilter avfilter_vf_colormatrix = {
00370 .name = "colormatrix",
00371 .description = NULL_IF_CONFIG_SMALL("Color matrix conversion"),
00372
00373 .priv_size = sizeof(ColorMatrixContext),
00374 .init = init,
00375 .query_formats = query_formats,
00376
00377 .inputs = (AVFilterPad[]) {{ .name = "default",
00378 .type = AVMEDIA_TYPE_VIDEO,
00379 .config_props = config_input,
00380 .start_frame = start_frame,
00381 .get_video_buffer = get_video_buffer,
00382 .draw_slice = null_draw_slice,
00383 .end_frame = end_frame, },
00384 { .name = NULL }},
00385
00386 .outputs = (AVFilterPad[]) {{ .name = "default",
00387 .type = AVMEDIA_TYPE_VIDEO, },
00388 { .name = NULL }},
00389 };