00001
00002
00003
00004 #include <stdlib.h>
00005 #include <stdio.h>
00006
00007 #include "config.h"
00008 #if HAVE_STDINT_H
00009 #include <stdint.h>
00010 #endif
00011
00012 #include <string.h>
00013 #include <math.h>
00014
00015 #include "filter.h"
00016 #include "frame.h"
00017
00018 #if HAVE_MMX
00019
00020 #include "libavutil/mem.h"
00021 #include "libavcodec/dsputil.h"
00022 #include "ffmpeg-mmx.h"
00023
00024 static const mmx_t mm_cpool[] = {
00025 { w: {1, 1, 1, 1} },
00026 { ub: {36, 36, 36, 36, 36, 36, 36, 36} },
00027 { ub: {20, 20, 20, 20, 20, 20, 20, 20} },
00028 { ub: {31, 31, 31, 31, 31, 31, 31, 31} },
00029 { ub: {15, 15, 15, 15, 15, 15, 15, 15} }
00030 };
00031
00032 #endif
00033
00034 typedef struct ThisFilter
00035 {
00036 VideoFilter vf;
00037
00038 #if HAVE_MMX
00039 int yfilt;
00040 int cfilt;
00041
00042 mmx_t yscale;
00043 mmx_t yshift;
00044 mmx_t ymin;
00045
00046 mmx_t cscale;
00047 mmx_t cshift;
00048 mmx_t cmin;
00049 #endif
00050
00051 uint8_t ytable[256];
00052 uint8_t ctable[256];
00053
00054 TF_STRUCT;
00055 } ThisFilter;
00056
00057 static void adjustRegion(uint8_t *buf, uint8_t *end, const uint8_t *table)
00058 {
00059 while (buf < end)
00060 {
00061 *buf = table[*buf];
00062 buf++;
00063 }
00064 }
00065
00066 #if HAVE_MMX
00067 static void adjustRegionMMX(uint8_t *buf, uint8_t *end, const uint8_t *table,
00068 const mmx_t *shift, const mmx_t *scale, const mmx_t *min,
00069 const mmx_t *clamp1, const mmx_t *clamp2)
00070 {
00071 movq_m2r (*scale, mm6);
00072 movq_m2r (*min, mm7);
00073 while (buf < end - 15)
00074 {
00075 movq_m2r (buf[0], mm0);
00076 movq_m2r (buf[8], mm2);
00077 movq_m2r (*shift, mm4);
00078 pxor_r2r (mm5, mm5);
00079
00080 psubusb_r2r (mm7, mm0);
00081 psubusb_r2r (mm7, mm2);
00082
00083 movq_r2r (mm0, mm1);
00084 movq_r2r (mm2, mm3);
00085
00086 punpcklbw_r2r (mm5, mm0);
00087 punpckhbw_r2r (mm5, mm1);
00088 punpcklbw_r2r (mm5, mm2);
00089 punpckhbw_r2r (mm5, mm3);
00090
00091 movq_m2r (mm_cpool[0], mm5);
00092
00093 psllw_r2r (mm4, mm0);
00094 psllw_r2r (mm4, mm1);
00095 psllw_r2r (mm4, mm2);
00096 psllw_r2r (mm4, mm3);
00097
00098 movq_m2r (*clamp1, mm4);
00099
00100 pmulhw_r2r (mm6, mm0);
00101 pmulhw_r2r (mm6, mm1);
00102 pmulhw_r2r (mm6, mm2);
00103 pmulhw_r2r (mm6, mm3);
00104
00105 paddw_r2r (mm5, mm0);
00106 paddw_r2r (mm5, mm1);
00107 paddw_r2r (mm5, mm2);
00108 paddw_r2r (mm5, mm3);
00109
00110 movq_m2r (*clamp2, mm5);
00111
00112 psrlw_i2r (1, mm0);
00113 psrlw_i2r (1, mm1);
00114 psrlw_i2r (1, mm2);
00115 psrlw_i2r (1, mm3);
00116
00117 packuswb_r2r (mm1, mm0);
00118 packuswb_r2r (mm3, mm2);
00119
00120 paddusb_r2r (mm4, mm0);
00121 paddusb_r2r (mm4, mm2);
00122
00123 psubusb_r2r (mm5, mm0);
00124 psubusb_r2r (mm5, mm2);
00125
00126 movq_r2m (mm0, buf[0]);
00127 movq_r2m (mm2, buf[8]);
00128
00129 buf += 16;
00130 }
00131 while (buf < end)
00132 {
00133 *buf = table[*buf];
00134 buf++;
00135 }
00136 }
00137 #endif
00138
00139 static int adjustFilter (VideoFilter *vf, VideoFrame *frame, int field)
00140 {
00141 (void)field;
00142 ThisFilter *filter = (ThisFilter *) vf;
00143 TF_VARS;
00144
00145 TF_START;
00146 {
00147 unsigned char *ybeg = frame->buf + frame->offsets[0];
00148 unsigned char *yend = ybeg + (frame->pitches[0] * frame->height);
00149 int cheight = (frame->codec == FMT_YV12) ?
00150 (frame->height >> 1) : frame->height;
00151 unsigned char *ubeg = frame->buf + frame->offsets[1];
00152 unsigned char *uend = ubeg + (frame->pitches[1] * cheight);
00153 unsigned char *vbeg = frame->buf + frame->offsets[2];
00154 unsigned char *vend = ubeg + (frame->pitches[2] * cheight);
00155
00156 #if HAVE_MMX
00157 if (filter->yfilt)
00158 adjustRegionMMX(ybeg, yend, filter->ytable,
00159 &(filter->yshift), &(filter->yscale),
00160 &(filter->ymin), mm_cpool + 1, mm_cpool + 2);
00161 else
00162 adjustRegion(ybeg, yend, filter->ytable);
00163
00164 if (filter->cfilt)
00165 {
00166 adjustRegionMMX(ubeg, uend, filter->ctable,
00167 &(filter->cshift), &(filter->cscale),
00168 &(filter->cmin), mm_cpool + 3, mm_cpool + 4);
00169 adjustRegionMMX(vbeg, vend, filter->ctable,
00170 &(filter->cshift), &(filter->cscale),
00171 &(filter->cmin), mm_cpool + 3, mm_cpool + 4);
00172 }
00173 else
00174 {
00175 adjustRegion(ubeg, uend, filter->ctable);
00176 adjustRegion(vbeg, vend, filter->ctable);
00177 }
00178
00179 if (filter->yfilt || filter->cfilt)
00180 emms();
00181
00182 #else
00183 adjustRegion(ybeg, yend, filter->ytable);
00184 adjustRegion(ubeg, uend, filter->ctable);
00185 adjustRegion(vbeg, vend, filter->ctable);
00186 #endif
00187 }
00188 TF_END(filter, "Adjust: ");
00189 return 0;
00190 }
00191
00192 static void fillTable(uint8_t *table, int in_min, int in_max, int out_min,
00193 int out_max, float gamma)
00194 {
00195 int i;
00196 float f;
00197
00198 for (i = 0; i < 256; i++)
00199 {
00200 f = ((float)i - in_min) / (in_max - in_min);
00201 f = f < 0.0 ? 0.0 : f;
00202 f = f > 1.0 ? 1.0 : f;
00203 table[i] = (pow (f, gamma) * (out_max - out_min) + out_min + 0.5);
00204 }
00205 }
00206
00207 #if HAVE_MMX
00208 static int fillTableMMX(uint8_t *table, mmx_t *shift, mmx_t *scale, mmx_t *min,
00209 int in_min, int in_max, int out_min, int out_max,
00210 float gamma)
00211 {
00212 int shiftc, scalec, i;
00213
00214 fillTable(table, in_min, in_max, out_min, out_max, gamma);
00215 scalec = ((out_max - out_min) << 15)/(in_max - in_min);
00216 if ((av_get_cpu_flags() & AV_CPU_FLAG_MMX) == 0 || gamma < 0.9999 ||
00217 gamma > 1.00001 || scalec > 32767 << 7)
00218 return 0;
00219 shiftc = 2;
00220 while (scalec > 32767)
00221 {
00222 shiftc++;
00223 scalec >>= 1;
00224 }
00225 if (shiftc > 7)
00226 return 0;
00227 for (i = 0; i < 4; i++)
00228 {
00229 scale->w[i] = scalec;
00230 }
00231 for (i = 0; i < 8; i++)
00232 min->b[i] = in_min;
00233 shift->q = shiftc;
00234 return 1;
00235 }
00236 #endif
00237
00238 static VideoFilter *
00239 newAdjustFilter (VideoFrameType inpixfmt, VideoFrameType outpixfmt,
00240 int *width, int *height, char *options, int threads)
00241 {
00242 ThisFilter *filter;
00243 int numopts = 0, ymin = 16, ymax = 253, cmin = 16, cmax = 240;
00244 float ygamma = 1.0f, cgamma = 1.0f;
00245 (void) width;
00246 (void) height;
00247 (void) threads;
00248
00249 if (inpixfmt != outpixfmt ||
00250 (inpixfmt != FMT_YV12 && inpixfmt != FMT_YUV422P))
00251 {
00252 fprintf(stderr, "adjust: only YV12->YV12 and YUV422P->YUV422P"
00253 " conversions are supported\n");
00254 return NULL;
00255 }
00256
00257 if (options)
00258 {
00259 numopts = sscanf(options, "%20d:%20d:%20f:%20d:%20d:%20f",
00260 &ymin, &ymax, &ygamma,
00261 &cmin, &cmax, &cgamma);
00262 }
00263
00264 if (numopts != 6 && (numopts !=1 && ymin != -1))
00265 {
00266 ymin = 16;
00267 ymax = 253;
00268 ygamma = 1.0f;
00269 cmin = 16;
00270 cmax = 240;
00271 cgamma = 1.0f;
00272 }
00273
00274 filter = malloc (sizeof (ThisFilter));
00275
00276 if (filter == NULL)
00277 {
00278 fprintf (stderr, "adjust: failed to allocate memory for filter\n");
00279 return NULL;
00280 }
00281
00282 if (ymin == -1)
00283 {
00284 filter->vf.filter = NULL;
00285 filter->vf.cleanup = NULL;
00286 return (VideoFilter *) filter;
00287 }
00288
00289 #if HAVE_MMX
00290 filter->yfilt = fillTableMMX (filter->ytable, &(filter->yshift),
00291 &(filter->yscale), &(filter->ymin),
00292 ymin, ymax, 16, 235, ygamma);
00293 filter->cfilt = fillTableMMX (filter->ctable, &(filter->cshift),
00294 &(filter->cscale), &(filter->cmin),
00295 cmin, cmax, 16, 240, cgamma);
00296 #else
00297 fillTable (filter->ytable, ymin, ymax, 16, 235, ygamma);
00298 fillTable (filter->ctable, cmin, cmax, 16, 240, cgamma);
00299 #endif
00300
00301 filter->vf.filter = &adjustFilter;
00302 filter->vf.cleanup = NULL;
00303
00304 TF_INIT(filter);
00305 return (VideoFilter *) filter;
00306 }
00307
00308 static FmtConv FmtList[] =
00309 {
00310 { FMT_YV12, FMT_YV12 },
00311 { FMT_YUV422P, FMT_YUV422P },
00312 FMT_NULL
00313 };
00314
00315 ConstFilterInfo filter_table[] =
00316 {
00317 {
00318 filter_init: &newAdjustFilter,
00319 name: "adjust",
00320 descript: "adjust range and gamma of video",
00321 formats: FmtList,
00322 libname: NULL
00323 },
00324 FILT_NULL
00325 };