summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/csputils.c391
-rw-r--r--video/csputils.h151
-rw-r--r--video/decode/dec_video.c448
-rw-r--r--video/decode/dec_video.h51
-rw-r--r--video/decode/vd.c273
-rw-r--r--video/decode/vd.h60
-rw-r--r--video/decode/vd_lavc.c857
-rw-r--r--video/filter/pullup.c817
-rw-r--r--video/filter/pullup.h102
-rw-r--r--video/filter/vf.c830
-rw-r--r--video/filter/vf.h189
-rw-r--r--video/filter/vf_crop.c196
-rw-r--r--video/filter/vf_delogo.c379
-rw-r--r--video/filter/vf_divtc.c723
-rw-r--r--video/filter/vf_dlopen.c389
-rw-r--r--video/filter/vf_dlopen.h88
-rw-r--r--video/filter/vf_down3dright.c166
-rw-r--r--video/filter/vf_dsize.c125
-rw-r--r--video/filter/vf_eq2.c519
-rw-r--r--video/filter/vf_expand.c350
-rw-r--r--video/filter/vf_flip.c110
-rw-r--r--video/filter/vf_format.c91
-rw-r--r--video/filter/vf_gradfun.c450
-rw-r--r--video/filter/vf_hqdn3d.c373
-rw-r--r--video/filter/vf_ilpack.c457
-rw-r--r--video/filter/vf_mirror.c131
-rw-r--r--video/filter/vf_noformat.c77
-rw-r--r--video/filter/vf_noise.c470
-rw-r--r--video/filter/vf_phase.c303
-rw-r--r--video/filter/vf_pp.c196
-rw-r--r--video/filter/vf_pullup.c333
-rw-r--r--video/filter/vf_rotate.c152
-rw-r--r--video/filter/vf_scale.c718
-rw-r--r--video/filter/vf_screenshot.c219
-rw-r--r--video/filter/vf_softpulldown.c182
-rw-r--r--video/filter/vf_stereo3d.c527
-rw-r--r--video/filter/vf_sub.c309
-rw-r--r--video/filter/vf_swapuv.c106
-rw-r--r--video/filter/vf_unsharp.c321
-rw-r--r--video/filter/vf_vo.c202
-rw-r--r--video/filter/vf_yadif.c533
-rw-r--r--video/fmt-conversion.c144
-rw-r--r--video/fmt-conversion.h27
-rw-r--r--video/image_writer.c327
-rw-r--r--video/image_writer.h53
-rw-r--r--video/img_format.c233
-rw-r--r--video/img_format.h243
-rw-r--r--video/memcpy_pic.h77
-rw-r--r--video/mp_image.c280
-rw-r--r--video/mp_image.h162
-rw-r--r--video/out/aspect.c148
-rw-r--r--video/out/aspect.h37
-rw-r--r--video/out/bitmap_packer.c227
-rw-r--r--video/out/bitmap_packer.h68
-rw-r--r--video/out/cocoa_common.h55
-rw-r--r--video/out/cocoa_common.m865
-rw-r--r--video/out/d3d_shader_yuv.h142
-rw-r--r--video/out/d3d_shader_yuv.hlsl44
-rw-r--r--video/out/d3d_shader_yuv_2ch.h170
-rw-r--r--video/out/filter_kernels.c279
-rw-r--r--video/out/filter_kernels.h45
-rw-r--r--video/out/geometry.c112
-rw-r--r--video/out/geometry.h29
-rw-r--r--video/out/gl_common.c2654
-rw-r--r--video/out/gl_common.h396
-rw-r--r--video/out/gl_header_fixes.h257
-rw-r--r--video/out/gl_osd.c324
-rw-r--r--video/out/gl_osd.h43
-rw-r--r--video/out/osx_common.c145
-rw-r--r--video/out/osx_common.h27
-rw-r--r--video/out/pnm_loader.c97
-rw-r--r--video/out/pnm_loader.h52
-rw-r--r--video/out/vo.c530
-rw-r--r--video/out/vo.h352
-rw-r--r--video/out/vo_caca.c395
-rw-r--r--video/out/vo_corevideo.h28
-rw-r--r--video/out/vo_corevideo.m457
-rw-r--r--video/out/vo_direct3d.c2101
-rw-r--r--video/out/vo_image.c198
-rw-r--r--video/out/vo_lavc.c553
-rw-r--r--video/out/vo_null.c103
-rw-r--r--video/out/vo_opengl.c2419
-rw-r--r--video/out/vo_opengl_old.c1166
-rw-r--r--video/out/vo_opengl_shaders.glsl355
-rw-r--r--video/out/vo_vdpau.c1718
-rw-r--r--video/out/vo_x11.c620
-rw-r--r--video/out/vo_xv.c716
-rw-r--r--video/out/w32_common.c757
-rw-r--r--video/out/w32_common.h66
-rw-r--r--video/out/x11_common.c2404
-rw-r--r--video/out/x11_common.h184
-rw-r--r--video/sws_utils.c199
-rw-r--r--video/sws_utils.h33
-rw-r--r--video/vfcap.h46
94 files changed, 36526 insertions, 0 deletions
diff --git a/video/csputils.c b/video/csputils.c
new file mode 100644
index 0000000000..23eb099f69
--- /dev/null
+++ b/video/csputils.c
@@ -0,0 +1,391 @@
+/*
+ * Common code related to colorspaces and conversion
+ *
+ * Copyleft (C) 2009 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
+ *
+ * mp_invert_yuv2rgb based on DarkPlaces engine, original code (GPL2 or later)
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * You can alternatively redistribute this file and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include <stdint.h>
+#include <math.h>
+#include <assert.h>
+#include <libavutil/common.h>
+
+#include "csputils.h"
+
+char * const mp_csp_names[MP_CSP_COUNT] = {
+ "Autoselect",
+ "BT.601 (SD)",
+ "BT.709 (HD)",
+ "SMPTE-240M",
+ "RGB",
+};
+
+char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
+ "brightness",
+ "contrast",
+ "hue",
+ "saturation",
+ "gamma",
+};
+
+enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace)
+{
+ switch (colorspace) {
+ case AVCOL_SPC_BT709: return MP_CSP_BT_709;
+ case AVCOL_SPC_BT470BG: return MP_CSP_BT_601;
+ case AVCOL_SPC_SMPTE170M: return MP_CSP_BT_601;
+ case AVCOL_SPC_SMPTE240M: return MP_CSP_SMPTE_240M;
+ case AVCOL_SPC_RGB: return MP_CSP_RGB;
+ default: return MP_CSP_AUTO;
+ }
+}
+
+enum mp_csp_levels avcol_range_to_mp_csp_levels(enum AVColorRange range)
+{
+ switch (range) {
+ case AVCOL_RANGE_MPEG: return MP_CSP_LEVELS_TV;
+ case AVCOL_RANGE_JPEG: return MP_CSP_LEVELS_PC;
+ default: return MP_CSP_LEVELS_AUTO;
+ }
+}
+
+enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp colorspace)
+{
+ switch (colorspace) {
+ case MP_CSP_BT_709: return AVCOL_SPC_BT709;
+ case MP_CSP_BT_601: return AVCOL_SPC_BT470BG;
+ case MP_CSP_SMPTE_240M: return AVCOL_SPC_SMPTE240M;
+ case MP_CSP_RGB: return AVCOL_SPC_RGB;
+ default: return AVCOL_SPC_UNSPECIFIED;
+ }
+}
+
+enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range)
+{
+ switch (range) {
+ case MP_CSP_LEVELS_TV: return AVCOL_RANGE_MPEG;
+ case MP_CSP_LEVELS_PC: return AVCOL_RANGE_JPEG;
+ default: return AVCOL_RANGE_UNSPECIFIED;
+ }
+}
+
+enum mp_csp mp_csp_guess_colorspace(int width, int height)
+{
+ return width >= 1280 || height > 576 ? MP_CSP_BT_709 : MP_CSP_BT_601;
+}
+
+/**
+ * \brief little helper function to create a lookup table for gamma
+ * \param map buffer to create map into
+ * \param size size of buffer
+ * \param gamma gamma value
+ */
+void mp_gen_gamma_map(uint8_t *map, int size, float gamma)
+{
+ if (gamma == 1.0) {
+ for (int i = 0; i < size; i++)
+ map[i] = 255 * i / (size - 1);
+ return;
+ }
+ gamma = 1.0 / gamma;
+ for (int i = 0; i < size; i++) {
+ float tmp = (float)i / (size - 1.0);
+ tmp = pow(tmp, gamma);
+ if (tmp > 1.0)
+ tmp = 1.0;
+ if (tmp < 0.0)
+ tmp = 0.0;
+ map[i] = 255 * tmp;
+ }
+}
+
+/* Fill in the Y, U, V vectors of a yuv2rgb conversion matrix
+ * based on the given luma weights of the R, G and B components (lr, lg, lb).
+ * lr+lg+lb is assumed to equal 1.
+ * This function is meant for colorspaces satisfying the following
+ * conditions (which are true for common YUV colorspaces):
+ * - The mapping from input [Y, U, V] to output [R, G, B] is linear.
+ * - Y is the vector [1, 1, 1]. (meaning input Y component maps to 1R+1G+1B)
+ * - U maps to a value with zero R and positive B ([0, x, y], y > 0;
+ * i.e. blue and green only).
+ * - V maps to a value with zero B and positive R ([x, y, 0], x > 0;
+ * i.e. red and green only).
+ * - U and V are orthogonal to the luma vector [lr, lg, lb].
+ * - The magnitudes of the vectors U and V are the minimal ones for which
+ * the image of the set Y=[0...1],U=[-0.5...0.5],V=[-0.5...0.5] under the
+ * conversion function will cover the set R=[0...1],G=[0...1],B=[0...1]
+ * (the resulting matrix can be converted for other input/output ranges
+ * outside this function).
+ * Under these conditions the given parameters lr, lg, lb uniquely
+ * determine the mapping of Y, U, V to R, G, B.
+ */
+static void luma_coeffs(float m[3][4], float lr, float lg, float lb)
+{
+ assert(fabs(lr+lg+lb - 1) < 1e-6);
+ m[0][0] = m[1][0] = m[2][0] = 1;
+ m[0][1] = 0;
+ m[1][1] = -2 * (1-lb) * lb/lg;
+ m[2][1] = 2 * (1-lb);
+ m[0][2] = 2 * (1-lr);
+ m[1][2] = -2 * (1-lr) * lr/lg;
+ m[2][2] = 0;
+ // Constant coefficients (m[x][3]) not set here
+}
+
+/**
+ * \brief get the coefficients of the yuv -> rgb conversion matrix
+ * \param params struct specifying the properties of the conversion like
+ * brightness, ...
+ * \param m array to store coefficients into
+ */
+void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
+{
+ int format = params->colorspace.format;
+ if (format <= MP_CSP_AUTO || format >= MP_CSP_COUNT)
+ format = MP_CSP_BT_601;
+ switch (format) {
+ case MP_CSP_BT_601: luma_coeffs(m, 0.299, 0.587, 0.114 ); break;
+ case MP_CSP_BT_709: luma_coeffs(m, 0.2126, 0.7152, 0.0722); break;
+ case MP_CSP_SMPTE_240M: luma_coeffs(m, 0.2122, 0.7013, 0.0865); break;
+ default:
+ abort();
+ };
+
+ // Hue is equivalent to rotating input [U, V] subvector around the origin.
+ // Saturation scales [U, V].
+ float huecos = params->saturation * cos(params->hue);
+ float huesin = params->saturation * sin(params->hue);
+ for (int i = 0; i < 3; i++) {
+ float u = m[i][COL_U];
+ m[i][COL_U] = huecos * u - huesin * m[i][COL_V];
+ m[i][COL_V] = huesin * u + huecos * m[i][COL_V];
+ }
+
+ int levels_in = params->colorspace.levels_in;
+ if (levels_in <= MP_CSP_LEVELS_AUTO || levels_in >= MP_CSP_LEVELS_COUNT)
+ levels_in = MP_CSP_LEVELS_TV;
+ assert(params->input_bits >= 8);
+ assert(params->texture_bits >= params->input_bits);
+ double s = (1 << params->input_bits-8) / ((1<<params->texture_bits)-1.);
+ // The values below are written in 0-255 scale
+ struct yuvlevels { double ymin, ymax, cmin, cmid; }
+ yuvlim = { 16*s, 235*s, 16*s, 128*s },
+ yuvfull = { 0*s, 255*s, 1*s, 128*s }, // '1' for symmetry around 128
+ yuvlev;
+ switch (levels_in) {
+ case MP_CSP_LEVELS_TV: yuvlev = yuvlim; break;
+ case MP_CSP_LEVELS_PC: yuvlev = yuvfull; break;
+ default:
+ abort();
+ }
+
+ int levels_out = params->colorspace.levels_out;
+ if (levels_out <= MP_CSP_LEVELS_AUTO || levels_out >= MP_CSP_LEVELS_COUNT)
+ levels_out = MP_CSP_LEVELS_PC;
+ struct rgblevels { double min, max; }
+ rgblim = { 16/255., 235/255. },
+ rgbfull = { 0, 1 },
+ rgblev;
+ switch (levels_out) {
+ case MP_CSP_LEVELS_TV: rgblev = rgblim; break;
+ case MP_CSP_LEVELS_PC: rgblev = rgbfull; break;
+ default:
+ abort();
+ }
+
+ double ymul = (rgblev.max - rgblev.min) / (yuvlev.ymax - yuvlev.ymin);
+ double cmul = (rgblev.max - rgblev.min) / (yuvlev.cmid - yuvlev.cmin) / 2;
+ for (int i = 0; i < 3; i++) {
+ m[i][COL_Y] *= ymul;
+ m[i][COL_U] *= cmul;
+ m[i][COL_V] *= cmul;
+ // Set COL_C so that Y=umin,UV=cmid maps to RGB=min (black to black)
+ m[i][COL_C] = rgblev.min - m[i][COL_Y] * yuvlev.ymin
+ -(m[i][COL_U] + m[i][COL_V]) * yuvlev.cmid;
+ }
+
+ // Brightness adds a constant to output R,G,B.
+ // Contrast scales Y around 1/2 (not 0 in this implementation).
+ for (int i = 0; i < 3; i++) {
+ m[i][COL_C] += params->brightness;
+ m[i][COL_Y] *= params->contrast;
+ m[i][COL_C] += (rgblev.max-rgblev.min) * (1 - params->contrast)/2;
+ }
+
+ int in_bits = FFMAX(params->int_bits_in, 1);
+ int out_bits = FFMAX(params->int_bits_out, 1);
+ double in_scale = (1 << in_bits) - 1.0;
+ double out_scale = (1 << out_bits) - 1.0;
+ for (int i = 0; i < 3; i++) {
+ m[i][COL_C] *= out_scale; // constant is 1.0
+ for (int x = 0; x < 3; x++)
+ m[i][x] *= out_scale / in_scale;
+ }
+}
+
+//! size of gamma map use to avoid slow exp function in gen_yuv2rgb_map
+#define GMAP_SIZE (1024)
+/**
+ * \brief generate a 3D YUV -> RGB map
+ * \param params struct containing parameters like brightness, gamma, ...
+ * \param map where to store map. Must provide space for (size + 2)^3 elements
+ * \param size size of the map, excluding border
+ */
+void mp_gen_yuv2rgb_map(struct mp_csp_params *params, unsigned char *map, int size)
+{
+ int i, j, k, l;
+ float step = 1.0 / size;
+ float y, u, v;
+ float yuv2rgb[3][4];
+ unsigned char gmaps[3][GMAP_SIZE];
+ mp_gen_gamma_map(gmaps[0], GMAP_SIZE, params->rgamma);
+ mp_gen_gamma_map(gmaps[1], GMAP_SIZE, params->ggamma);
+ mp_gen_gamma_map(gmaps[2], GMAP_SIZE, params->bgamma);
+ mp_get_yuv2rgb_coeffs(params, yuv2rgb);
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 4; j++)
+ yuv2rgb[i][j] *= GMAP_SIZE - 1;
+ v = 0;
+ for (i = -1; i <= size; i++) {
+ u = 0;
+ for (j = -1; j <= size; j++) {
+ y = 0;
+ for (k = -1; k <= size; k++) {
+ for (l = 0; l < 3; l++) {
+ float rgb = yuv2rgb[l][COL_Y] * y + yuv2rgb[l][COL_U] * u +
+ yuv2rgb[l][COL_V] * v + yuv2rgb[l][COL_C];
+ *map++ = gmaps[l][av_clip(rgb, 0, GMAP_SIZE - 1)];
+ }
+ y += (k == -1 || k == size - 1) ? step / 2 : step;
+ }
+ u += (j == -1 || j == size - 1) ? step / 2 : step;
+ }
+ v += (i == -1 || i == size - 1) ? step / 2 : step;
+ }
+}
+
+// Copy settings from eq into params.
+void mp_csp_copy_equalizer_values(struct mp_csp_params *params,
+ const struct mp_csp_equalizer *eq)
+{
+ params->brightness = eq->values[MP_CSP_EQ_BRIGHTNESS] / 100.0;
+ params->contrast = (eq->values[MP_CSP_EQ_CONTRAST] + 100) / 100.0;
+ params->hue = eq->values[MP_CSP_EQ_HUE] / 100.0 * 3.1415927;
+ params->saturation = (eq->values[MP_CSP_EQ_SATURATION] + 100) / 100.0;
+ float gamma = exp(log(8.0) * eq->values[MP_CSP_EQ_GAMMA] / 100.0);
+ params->rgamma = gamma;
+ params->ggamma = gamma;
+ params->bgamma = gamma;
+}
+
+static int find_eq(int capabilities, const char *name)
+{
+ for (int i = 0; i < MP_CSP_EQ_COUNT; i++) {
+ if (strcmp(name, mp_csp_equalizer_names[i]) == 0)
+ return ((1 << i) & capabilities) ? i : -1;
+ }
+ return -1;
+}
+
+int mp_csp_equalizer_get(struct mp_csp_equalizer *eq, const char *property,
+ int *out_value)
+{
+ int index = find_eq(eq->capabilities, property);
+ if (index < 0)
+ return -1;
+
+ *out_value = eq->values[index];
+
+ return 0;
+}
+
+int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property,
+ int value)
+{
+ int index = find_eq(eq->capabilities, property);
+ if (index < 0)
+ return 0;
+
+ eq->values[index] = value;
+
+ return 1;
+}
+
+void mp_invert_yuv2rgb(float out[3][4], float in[3][4])
+{
+ float m00 = in[0][0], m01 = in[0][1], m02 = in[0][2], m03 = in[0][3],
+ m10 = in[1][0], m11 = in[1][1], m12 = in[1][2], m13 = in[1][3],
+ m20 = in[2][0], m21 = in[2][1], m22 = in[2][2], m23 = in[2][3];
+
+ // calculate the adjoint
+ out[0][0] = (m11 * m22 - m21 * m12);
+ out[0][1] = -(m01 * m22 - m21 * m02);
+ out[0][2] = (m01 * m12 - m11 * m02);
+ out[1][0] = -(m10 * m22 - m20 * m12);
+ out[1][1] = (m00 * m22 - m20 * m02);
+ out[1][2] = -(m00 * m12 - m10 * m02);
+ out[2][0] = (m10 * m21 - m20 * m11);
+ out[2][1] = -(m00 * m21 - m20 * m01);
+ out[2][2] = (m00 * m11 - m10 * m01);
+
+ // calculate the determinant (as inverse == 1/det * adjoint,
+ // adjoint * m == identity * det, so this calculates the det)
+ float det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2];
+ det = 1.0f / det;
+
+ out[0][0] *= det;
+ out[0][1] *= det;
+ out[0][2] *= det;
+ out[1][0] *= det;
+ out[1][1] *= det;
+ out[1][2] *= det;
+ out[2][0] *= det;
+ out[2][1] *= det;
+ out[2][2] *= det;
+
+ // fix the constant coefficient
+ // rgb = M * yuv + C
+ // M^-1 * rgb = yuv + M^-1 * C
+ // yuv = M^-1 * rgb - M^-1 * C
+ // ^^^^^^^^^^
+ out[0][3] = -(out[0][0] * m03 + out[0][1] * m13 + out[0][2] * m23);
+ out[1][3] = -(out[1][0] * m03 + out[1][1] * m13 + out[1][2] * m23);
+ out[2][3] = -(out[2][0] * m03 + out[2][1] * m13 + out[2][2] * m23);
+}
+
+// Multiply the color in c with the given matrix.
+// c is {R, G, B} or {Y, U, V} (depending on input/output and matrix).
+// Output is clipped to the given number of bits.
+void mp_map_int_color(float matrix[3][4], int clip_bits, int c[3])
+{
+ int in[3] = {c[0], c[1], c[2]};
+ for (int i = 0; i < 3; i++) {
+ double val = matrix[i][3];
+ for (int x = 0; x < 3; x++)
+ val += matrix[i][x] * in[x];
+ int ival = lrint(val);
+ c[i] = av_clip(ival, 0, (1 << clip_bits) - 1);
+ }
+}
diff --git a/video/csputils.h b/video/csputils.h
new file mode 100644
index 0000000000..d66bb86fa3
--- /dev/null
+++ b/video/csputils.h
@@ -0,0 +1,151 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * You can alternatively redistribute this file and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef MPLAYER_CSPUTILS_H
+#define MPLAYER_CSPUTILS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "libavcodec/avcodec.h"
+
+/* NOTE: the csp and levels AUTO values are converted to specific ones
+ * above vf/vo level. At least vf_scale relies on all valid settings being
+ * nonzero at vf/vo level.
+ */
+
+enum mp_csp {
+ MP_CSP_AUTO,
+ MP_CSP_BT_601,
+ MP_CSP_BT_709,
+ MP_CSP_SMPTE_240M,
+ MP_CSP_RGB,
+ MP_CSP_COUNT
+};
+
+// Any enum mp_csp value is a valid index (except MP_CSP_COUNT)
+extern char * const mp_csp_names[MP_CSP_COUNT];
+
+enum mp_csp_levels {
+ MP_CSP_LEVELS_AUTO,
+ MP_CSP_LEVELS_TV,
+ MP_CSP_LEVELS_PC,
+ MP_CSP_LEVELS_COUNT,
+};
+
+struct mp_csp_details {
+ enum mp_csp format;
+ enum mp_csp_levels levels_in; // encoded video
+ enum mp_csp_levels levels_out; // output device
+};
+
+// initializer for struct mp_csp_details that contains reasonable defaults
+#define MP_CSP_DETAILS_DEFAULTS {MP_CSP_BT_601, MP_CSP_LEVELS_TV, MP_CSP_LEVELS_PC}
+
+struct mp_csp_params {
+ struct mp_csp_details colorspace;
+ float brightness;
+ float contrast;
+ float hue;
+ float saturation;
+ float rgamma;
+ float ggamma;
+ float bgamma;
+ // texture_bits/input_bits is for rescaling fixed point input to range [0,1]
+ int texture_bits;
+ int input_bits;
+ // for scaling integer input and output (if 0, assume range [0,1])
+ int int_bits_in;
+ int int_bits_out;
+};
+
+#define MP_CSP_PARAMS_DEFAULTS { \
+ .colorspace = MP_CSP_DETAILS_DEFAULTS, \
+ .brightness = 0, .contrast = 1, .hue = 0, .saturation = 1, \
+ .rgamma = 1, .ggamma = 1, .bgamma = 1, \
+ .texture_bits = 8, .input_bits = 8}
+
+enum mp_csp_equalizer_param {
+ MP_CSP_EQ_BRIGHTNESS,
+ MP_CSP_EQ_CONTRAST,
+ MP_CSP_EQ_HUE,
+ MP_CSP_EQ_SATURATION,
+ MP_CSP_EQ_GAMMA,
+ MP_CSP_EQ_COUNT,
+};
+
+#define MP_CSP_EQ_CAPS_COLORMATRIX \
+ ( (1 << MP_CSP_EQ_BRIGHTNESS) \
+ | (1 << MP_CSP_EQ_CONTRAST) \
+ | (1 << MP_CSP_EQ_HUE) \
+ | (1 << MP_CSP_EQ_SATURATION) )
+
+#define MP_CSP_EQ_CAPS_GAMMA (1 << MP_CSP_EQ_GAMMA)
+
+extern char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT];
+
+// Default initialization with 0 is enough, except for the capabilities field
+struct mp_csp_equalizer {
+ // Bit field of capabilities. For example (1 << MP_CSP_EQ_HUE) means hue
+ // support is available.
+ int capabilities;
+ // Value for each property is in the range [-100, 100].
+ // 0 is default, meaning neutral or no change.
+ int values[MP_CSP_EQ_COUNT];
+};
+
+
+void mp_csp_copy_equalizer_values(struct mp_csp_params *params,
+ const struct mp_csp_equalizer *eq);
+
+int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property,
+ int value);
+
+int mp_csp_equalizer_get(struct mp_csp_equalizer *eq, const char *property,
+ int *out_value);
+
+enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace);
+
+enum mp_csp_levels avcol_range_to_mp_csp_levels(enum AVColorRange range);
+
+enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp colorspace);
+
+enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range);
+
+enum mp_csp mp_csp_guess_colorspace(int width, int height);
+
+void mp_gen_gamma_map(unsigned char *map, int size, float gamma);
+#define ROW_R 0
+#define ROW_G 1
+#define ROW_B 2
+#define COL_Y 0
+#define COL_U 1
+#define COL_V 2
+#define COL_C 3
+void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float yuv2rgb[3][4]);
+void mp_gen_yuv2rgb_map(struct mp_csp_params *params, uint8_t *map, int size);
+
+void mp_invert_yuv2rgb(float out[3][4], float in[3][4]);
+void mp_map_int_color(float matrix[3][4], int clip_bits, int c[3]);
+
+#endif /* MPLAYER_CSPUTILS_H */
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
new file mode 100644
index 0000000000..5c6d3113da
--- /dev/null
+++ b/video/decode/dec_video.c
@@ -0,0 +1,448 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "options.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "mp_msg.h"
+
+#include "osdep/timer.h"
+#include "osdep/shmem.h"
+
+#include "stream/stream.h"
+#include "libmpdemux/demuxer.h"
+
+#include "codec-cfg.h"
+
+#include "libvo/video_out.h"
+#include "libvo/csputils.h"
+
+#include "libmpdemux/stheader.h"
+#include "vd.h"
+#include "vf.h"
+
+#include "dec_video.h"
+
+// ===================================================================
+
+#include "cpudetect.h"
+
+int field_dominance = -1;
+
+int divx_quality = 0;
+
+int get_video_quality_max(sh_video_t *sh_video)
+{
+ vf_instance_t *vf = sh_video->vfilter;
+ if (vf) {
+ int ret = vf->control(vf, VFCTRL_QUERY_MAX_PP_LEVEL, NULL);
+ if (ret > 0) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "[PP] Using external postprocessing filter, max q = %d.\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int set_video_colors(sh_video_t *sh_video, const char *item, int value)
+{
+ vf_instance_t *vf = sh_video->vfilter;
+ vf_equalizer_t data;
+
+ data.item = item;
+ data.value = value;
+
+ mp_dbg(MSGT_DECVIDEO, MSGL_V, "set video colors %s=%d \n", item, value);
+ if (vf) {
+ int ret = vf->control(vf, VFCTRL_SET_EQUALIZER, &data);
+ if (ret == CONTROL_TRUE)
+ return 1;
+ }
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Video attribute '%s' is not supported by selected vo.\n",
+ item);
+ return 0;
+}
+
+int get_video_colors(sh_video_t *sh_video, const char *item, int *value)
+{
+ vf_instance_t *vf = sh_video->vfilter;
+ vf_equalizer_t data;
+
+ data.item = item;
+
+ mp_dbg(MSGT_DECVIDEO, MSGL_V, "get video colors %s \n", item);
+ if (vf) {
+ int ret = vf->control(vf, VFCTRL_GET_EQUALIZER, &data);
+ if (ret == CONTROL_TRUE) {
+ *value = data.value;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void get_detected_video_colorspace(struct sh_video *sh, struct mp_csp_details *csp)
+{
+ struct MPOpts *opts = sh->opts;
+ struct vf_instance *vf = sh->vfilter;
+
+ csp->format = opts->requested_colorspace;
+ csp->levels_in = opts->requested_input_range;
+ csp->levels_out = opts->requested_output_range;
+
+ if (csp->format == MP_CSP_AUTO)
+ csp->format = sh->colorspace;
+ if (csp->format == MP_CSP_AUTO)
+ csp->format = mp_csp_guess_colorspace(vf->w, vf->h);
+
+ if (csp->levels_in == MP_CSP_LEVELS_AUTO)
+ csp->levels_in = sh->color_range;
+ if (csp->levels_in == MP_CSP_LEVELS_AUTO)
+ csp->levels_in = MP_CSP_LEVELS_TV;
+
+ if (csp->levels_out == MP_CSP_LEVELS_AUTO)
+ csp->levels_out = MP_CSP_LEVELS_PC;
+}
+
+void set_video_colorspace(struct sh_video *sh)
+{
+ struct vf_instance *vf = sh->vfilter;
+
+ struct mp_csp_details requested;
+ get_detected_video_colorspace(sh, &requested);
+ vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, &requested);
+
+ struct mp_csp_details actual = MP_CSP_DETAILS_DEFAULTS;
+ vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual);
+
+ int success = actual.format == requested.format
+ && actual.levels_in == requested.levels_in
+ && actual.levels_out == requested.levels_out;
+
+ if (!success)
+ mp_tmsg(MSGT_DECVIDEO, MSGL_WARN,
+ "Colorspace details not fully supported by selected vo.\n");
+
+ if (actual.format != requested.format
+ && requested.format == MP_CSP_SMPTE_240M) {
+ // BT.709 is pretty close, much better than BT.601
+ requested.format = MP_CSP_BT_709;
+ vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, &requested);
+ }
+
+}
+
+void resync_video_stream(sh_video_t *sh_video)
+{
+ const struct vd_functions *vd = sh_video->vd_driver;
+ if (vd)
+ vd->control(sh_video, VDCTRL_RESYNC_STREAM, NULL);
+ sh_video->prev_codec_reordered_pts = MP_NOPTS_VALUE;
+ sh_video->prev_sorted_pts = MP_NOPTS_VALUE;
+}
+
+void video_reset_aspect(struct sh_video *sh_video)
+{
+ sh_video->vd_driver->control(sh_video, VDCTRL_RESET_ASPECT, NULL);
+}
+
+int get_current_video_decoder_lag(sh_video_t *sh_video)
+{
+ const struct vd_functions *vd = sh_video->vd_driver;
+ if (!vd)
+ return -1;
+ int ret = vd->control(sh_video, VDCTRL_QUERY_UNSEEN_FRAMES, NULL);
+ if (ret >= 10)
+ return ret - 10;
+ return -1;
+}
+
+void uninit_video(sh_video_t *sh_video)
+{
+ if (!sh_video->initialized)
+ return;
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Uninit video: %s\n", sh_video->codec->drv);
+ sh_video->vd_driver->uninit(sh_video);
+ vf_uninit_filter_chain(sh_video->vfilter);
+ sh_video->initialized = 0;
+}
+
+void vfm_help(void)
+{
+ int i;
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Available (compiled-in) video codec families/drivers:\n");
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_DRIVERS\n");
+ mp_msg(MSGT_DECVIDEO, MSGL_INFO, " vfm: info: (comment)\n");
+ for (i = 0; mpcodecs_vd_drivers[i] != NULL; i++)
+ mp_msg(MSGT_DECVIDEO, MSGL_INFO, "%8s %s (%s)\n",
+ mpcodecs_vd_drivers[i]->info->short_name,
+ mpcodecs_vd_drivers[i]->info->name,
+ mpcodecs_vd_drivers[i]->info->comment);
+}
+
+static int init_video(sh_video_t *sh_video, char *codecname, char *vfm,
+ int status, stringset_t *selected)
+{
+ int force = 0;
+ unsigned int orig_fourcc =
+ sh_video->bih ? sh_video->bih->biCompression : 0;
+ sh_video->codec = NULL;
+ sh_video->vf_initialized = 0;
+ if (codecname && codecname[0] == '+') {
+ codecname = &codecname[1];
+ force = 1;
+ }
+
+ while (1) {
+ int i;
+ int orig_w, orig_h;
+ // restore original fourcc:
+ if (sh_video->bih)
+ sh_video->bih->biCompression = orig_fourcc;
+ if (!
+ (sh_video->codec =
+ find_video_codec(sh_video->format,
+ sh_video->bih ? ((unsigned int *) &sh_video->
+ bih->biCompression) : NULL,
+ sh_video->codec, force)))
+ break;
+ // ok we found one codec
+ if (stringset_test(selected, sh_video->codec->name))
+ continue; // already tried & failed
+ if (codecname && strcmp(sh_video->codec->name, codecname))
+ continue; // -vc
+ if (vfm && strcmp(sh_video->codec->drv, vfm))
+ continue; // vfm doesn't match
+ if (!force && sh_video->codec->status < status)
+ continue; // too unstable
+ stringset_add(selected, sh_video->codec->name); // tagging it
+ // ok, it matches all rules, let's find the driver!
+ for (i = 0; mpcodecs_vd_drivers[i] != NULL; i++)
+ if (!strcmp(mpcodecs_vd_drivers[i]->info->short_name,
+ sh_video->codec->drv))
+ break;
+ sh_video->vd_driver = mpcodecs_vd_drivers[i];
+ if (!sh_video->vd_driver) { // driver not available (==compiled in)
+ mp_tmsg(MSGT_DECVIDEO, MSGL_WARN,
+ _("Requested video codec family [%s] (vfm=%s) not available.\nEnable it at compilation.\n"),
+ sh_video->codec->name, sh_video->codec->drv);
+ continue;
+ }
+ orig_w = sh_video->bih ? sh_video->bih->biWidth : sh_video->disp_w;
+ orig_h = sh_video->bih ? sh_video->bih->biHeight : sh_video->disp_h;
+ sh_video->disp_w = orig_w;
+ sh_video->disp_h = orig_h;
+ // it's available, let's try to init!
+ if (sh_video->codec->flags & CODECS_FLAG_ALIGN16) {
+ // align width/height to n*16
+ sh_video->disp_w = (sh_video->disp_w + 15) & (~15);
+ sh_video->disp_h = (sh_video->disp_h + 15) & (~15);
+ }
+ if (sh_video->bih) {
+ sh_video->bih->biWidth = sh_video->disp_w;
+ sh_video->bih->biHeight = sh_video->disp_h;
+ }
+
+ // init()
+ const struct vd_functions *vd = sh_video->vd_driver;
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Opening video decoder: [%s] %s\n",
+ vd->info->short_name, vd->info->name);
+ // clear vf init error, it is no longer relevant
+ if (sh_video->vf_initialized < 0)
+ sh_video->vf_initialized = 0;
+ if (!vd->init(sh_video)) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Video decoder init failed for "
+ "codecs.conf entry \"%s\".\n", sh_video->codec->name);
+ sh_video->disp_w = orig_w;
+ sh_video->disp_h = orig_h;
+ if (sh_video->bih) {
+ sh_video->bih->biWidth = sh_video->disp_w;
+ sh_video->bih->biHeight = sh_video->disp_h;
+ }
+ continue; // try next...
+ }
+ // Yeah! We got it!
+ sh_video->initialized = 1;
+ sh_video->prev_codec_reordered_pts = MP_NOPTS_VALUE;
+ sh_video->prev_sorted_pts = MP_NOPTS_VALUE;
+ return 1;
+ }
+ return 0;
+}
+
+int init_best_video_codec(sh_video_t *sh_video, char **video_codec_list,
+ char **video_fm_list)
+{
+ char *vc_l_default[2] = { "", (char *) NULL };
+ stringset_t selected;
+ // hack:
+ if (!video_codec_list)
+ video_codec_list = vc_l_default;
+ // Go through the codec.conf and find the best codec...
+ sh_video->initialized = 0;
+ stringset_init(&selected);
+ while (!sh_video->initialized && *video_codec_list) {
+ char *video_codec = *(video_codec_list++);
+ if (video_codec[0]) {
+ if (video_codec[0] == '-') {
+ // disable this codec:
+ stringset_add(&selected, video_codec + 1);
+ } else {
+ // forced codec by name:
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Forced video codec: %s\n",
+ video_codec);
+ init_video(sh_video, video_codec, NULL, -1, &selected);
+ }
+ } else {
+ int status;
+ // try in stability order: UNTESTED, WORKING, BUGGY. never try CRASHING.
+ if (video_fm_list) {
+ char **fmlist = video_fm_list;
+ // try first the preferred codec families:
+ while (!sh_video->initialized && *fmlist) {
+ char *video_fm = *(fmlist++);
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Trying to force video codec driver family %s...\n",
+ video_fm);
+ for (status = CODECS_STATUS__MAX;
+ status >= CODECS_STATUS__MIN; --status)
+ if (init_video
+ (sh_video, NULL, video_fm, status, &selected))
+ break;
+ }
+ }
+ if (!sh_video->initialized)
+ for (status = CODECS_STATUS__MAX; status >= CODECS_STATUS__MIN;
+ --status)
+ if (init_video(sh_video, NULL, NULL, status, &selected))
+ break;
+ }
+ }
+ stringset_free(&selected);
+
+ if (!sh_video->initialized) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Cannot find codec matching selected -vo and video format 0x%X.\n",
+ sh_video->format);
+ return 0; // failed
+ }
+
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Selected video codec: %s [%s]\n",
+ sh_video->codecname ? sh_video->codecname : sh_video->codec->info,
+ sh_video->vd_driver->info->print_name ?
+ sh_video->vd_driver->info->print_name :
+ sh_video->vd_driver->info->short_name);
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V,
+ "Video codecs.conf entry: %s (%s) vfm: %s\n",
+ sh_video->codec->name, sh_video->codec->info, sh_video->codec->drv);
+ return 1; // success
+}
+
+void *decode_video(sh_video_t *sh_video, struct demux_packet *packet,
+ unsigned char *start, int in_size,
+ int drop_frame, double pts)
+{
+ mp_image_t *mpi = NULL;
+ struct MPOpts *opts = sh_video->opts;
+
+ if (opts->correct_pts && pts != MP_NOPTS_VALUE) {
+ int delay = get_current_video_decoder_lag(sh_video);
+ if (delay >= 0) {
+ if (delay > sh_video->num_buffered_pts)
+#if 0
+ // this is disabled because vd_ffmpeg reports the same lag
+ // after seek even when there are no buffered frames,
+ // leading to incorrect error messages
+ mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Not enough buffered pts\n");
+#else
+ ;
+#endif
+ else
+ sh_video->num_buffered_pts = delay;
+ }
+ if (sh_video->num_buffered_pts ==
+ sizeof(sh_video->buffered_pts) / sizeof(double))
+ mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Too many buffered pts\n");
+ else {
+ int i, j;
+ for (i = 0; i < sh_video->num_buffered_pts; i++)
+ if (sh_video->buffered_pts[i] < pts)
+ break;
+ for (j = sh_video->num_buffered_pts; j > i; j--)
+ sh_video->buffered_pts[j] = sh_video->buffered_pts[j - 1];
+ sh_video->buffered_pts[i] = pts;
+ sh_video->num_buffered_pts++;
+ }
+ }
+
+ mpi = sh_video->vd_driver->decode(sh_video, packet, start, in_size,
+ drop_frame, &pts);
+
+ //------------------------ frame decoded. --------------------
+
+#if HAVE_MMX
+ // some codecs are broken, and doesn't restore MMX state :(
+ // it happens usually with broken/damaged files.
+ if (gCpuCaps.hasMMX) {
+ __asm__ volatile("emms\n\t":::"memory");
+ }
+#endif
+
+ if (!mpi || drop_frame)
+ return NULL; // error / skipped frame
+
+ if (field_dominance == 0)
+ mpi->fields |= MP_IMGFIELD_TOP_FIRST;
+ else if (field_dominance == 1)
+ mpi->fields &= ~MP_IMGFIELD_TOP_FIRST;
+
+ double prevpts = sh_video->codec_reordered_pts;
+ sh_video->prev_codec_reordered_pts = prevpts;
+ sh_video->codec_reordered_pts = pts;
+ if (prevpts != MP_NOPTS_VALUE && pts <= prevpts
+ || pts == MP_NOPTS_VALUE)
+ sh_video->num_reordered_pts_problems++;
+ prevpts = sh_video->sorted_pts;
+ if (opts->correct_pts) {
+ if (sh_video->num_buffered_pts) {
+ sh_video->num_buffered_pts--;
+ sh_video->sorted_pts =
+ sh_video->buffered_pts[sh_video->num_buffered_pts];
+ } else {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR,
+ "No pts value from demuxer to use for frame!\n");
+ sh_video->sorted_pts = MP_NOPTS_VALUE;
+ }
+ }
+ pts = sh_video->sorted_pts;
+ if (prevpts != MP_NOPTS_VALUE && pts <= prevpts
+ || pts == MP_NOPTS_VALUE)
+ sh_video->num_sorted_pts_problems++;
+ return mpi;
+}
+
+int filter_video(sh_video_t *sh_video, void *frame, double pts)
+{
+ mp_image_t *mpi = frame;
+ vf_instance_t *vf = sh_video->vfilter;
+ // apply video filters and call the leaf vo/ve
+ return vf->put_image(vf, mpi, pts);
+}
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
new file mode 100644
index 0000000000..f871198988
--- /dev/null
+++ b/video/decode/dec_video.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_DEC_VIDEO_H
+#define MPLAYER_DEC_VIDEO_H
+
+#include "libmpdemux/stheader.h"
+
+struct osd_state;
+
+// dec_video.c:
+void vfm_help(void);
+
+int init_best_video_codec(sh_video_t *sh_video, char** video_codec_list, char** video_fm_list);
+void uninit_video(sh_video_t *sh_video);
+
+struct demux_packet;
+void *decode_video(sh_video_t *sh_video, struct demux_packet *packet,
+ unsigned char *start, int in_size, int drop_frame,
+ double pts);
+int filter_video(sh_video_t *sh_video, void *frame, double pts);
+
+int get_video_quality_max(sh_video_t *sh_video);
+
+int get_video_colors(sh_video_t *sh_video, const char *item, int *value);
+int set_video_colors(sh_video_t *sh_video, const char *item, int value);
+struct mp_csp_details;
+void get_detected_video_colorspace(struct sh_video *sh, struct mp_csp_details *csp);
+void set_video_colorspace(struct sh_video *sh);
+void resync_video_stream(sh_video_t *sh_video);
+void video_reset_aspect(struct sh_video *sh_video);
+int get_current_video_decoder_lag(sh_video_t *sh_video);
+
+extern int divx_quality;
+
+#endif /* MPLAYER_DEC_VIDEO_H */
diff --git a/video/decode/vd.c b/video/decode/vd.c
new file mode 100644
index 0000000000..3bfc17c895
--- /dev/null
+++ b/video/decode/vd.c
@@ -0,0 +1,273 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "codec-cfg.h"
+
+#include "img_format.h"
+
+#include "stream/stream.h"
+#include "libmpdemux/demuxer.h"
+#include "libmpdemux/stheader.h"
+#include "dec_video.h"
+
+#include "vd.h"
+#include "vf.h"
+#include "libvo/video_out.h"
+
+extern const vd_functions_t mpcodecs_vd_ffmpeg;
+
+/* Please do not add any new decoders here. If you want to implement a new
+ * decoder, add it to libavcodec, except for wrappers around external
+ * libraries and decoders requiring binary support. */
+
+const vd_functions_t * const mpcodecs_vd_drivers[] = {
+ &mpcodecs_vd_ffmpeg,
+ /* Please do not add any new decoders here. If you want to implement a new
+ * decoder, add it to libavcodec, except for wrappers around external
+ * libraries and decoders requiring binary support. */
+ NULL
+};
+
+int mpcodecs_config_vo(sh_video_t *sh, int w, int h,
+ const unsigned int *outfmts,
+ unsigned int preferred_outfmt)
+{
+ struct MPOpts *opts = sh->opts;
+ int j;
+ unsigned int out_fmt = 0;
+ int screen_size_x = 0;
+ int screen_size_y = 0;
+ vf_instance_t *vf = sh->vfilter;
+ int vocfg_flags = 0;
+
+ if (w)
+ sh->disp_w = w;
+ if (h)
+ sh->disp_h = h;
+
+ mp_msg(MSGT_DECVIDEO, MSGL_V,
+ "VIDEO: %dx%d %5.3f fps %5.1f kbps (%4.1f kB/s)\n",
+ sh->disp_w, sh->disp_h, sh->fps, sh->i_bps * 0.008,
+ sh->i_bps / 1000.0);
+
+ if (!sh->disp_w || !sh->disp_h)
+ return 0;
+
+ mp_msg(MSGT_DECVIDEO, MSGL_V,
+ "VDec: vo config request - %d x %d (preferred colorspace: %s)\n",
+ w, h, vo_format_name(preferred_outfmt));
+
+ if (get_video_quality_max(sh) <= 0 && divx_quality) {
+ // user wants postprocess but no pp filter yet:
+ sh->vfilter = vf = vf_open_filter(opts, vf, "pp", NULL);
+ }
+
+ if (!outfmts || sh->codec->outfmt[0] != 0xffffffff)
+ outfmts = sh->codec->outfmt;
+
+ // check if libvo and codec has common outfmt (no conversion):
+ csp_again:
+
+ if (mp_msg_test(MSGT_DECVIDEO, MSGL_V)) {
+ mp_msg(MSGT_DECVIDEO, MSGL_V, "Trying filter chain:");
+ for (vf_instance_t *f = vf; f; f = f->next)
+ mp_msg(MSGT_DECVIDEO, MSGL_V, " %s", f->info->name);
+ mp_msg(MSGT_DECVIDEO, MSGL_V, "\n");
+ }
+
+ j = -1;
+ for (int i = 0; i < CODECS_MAX_OUTFMT; i++) {
+ int flags;
+ out_fmt = outfmts[i];
+ if (out_fmt == (unsigned int) 0xFFFFFFFF)
+ break;
+ flags = vf->query_format(vf, out_fmt);
+ mp_msg(MSGT_CPLAYER, MSGL_DBG2,
+ "vo_debug: query(%s) returned 0x%X (i=%d) \n",
+ vo_format_name(out_fmt), flags, i);
+ if ((flags & VFCAP_CSP_SUPPORTED_BY_HW)
+ || (flags & VFCAP_CSP_SUPPORTED && j < 0)) {
+ // check (query) if codec really support this outfmt...
+ sh->outfmtidx = j; // pass index to the control() function this way
+ if (sh->vd_driver->control(sh, VDCTRL_QUERY_FORMAT, &out_fmt) ==
+ CONTROL_FALSE) {
+ mp_msg(MSGT_CPLAYER, MSGL_DBG2,
+ "vo_debug: codec query_format(%s) returned FALSE\n",
+ vo_format_name(out_fmt));
+ continue;
+ }
+ j = i;
+ sh->output_flags = flags;
+ if (flags & VFCAP_CSP_SUPPORTED_BY_HW)
+ break;
+ }
+ }
+ if (j < 0) {
+ // TODO: no match - we should use conversion...
+ if (strcmp(vf->info->name, "scale")) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Could not find matching colorspace - retrying with -vf scale...\n");
+ vf = vf_open_filter(opts, vf, "scale", NULL);
+ goto csp_again;
+ }
+ mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
+ "The selected video_out device is incompatible with this codec.\n"\
+ "Try appending the scale filter to your filter list,\n"\
+ "e.g. -vf spp,scale instead of -vf spp.\n");
+ sh->vf_initialized = -1;
+ return 0; // failed
+ }
+ out_fmt = outfmts[j];
+ sh->outfmt = out_fmt;
+ mp_msg(MSGT_CPLAYER, MSGL_V, "VDec: using %s as output csp (no %d)\n",
+ vo_format_name(out_fmt), j);
+ sh->outfmtidx = j;
+ sh->vfilter = vf;
+
+ // autodetect flipping
+ if (opts->flip == -1) {
+ opts->flip = 0;
+ if (sh->codec->outflags[j] & CODECS_FLAG_FLIP)
+ if (!(sh->codec->outflags[j] & CODECS_FLAG_NOFLIP))
+ opts->flip = 1;
+ }
+ if (opts->flip && !(sh->output_flags & VFCAP_FLIP)) {
+ // we need to flip, but no flipping filter avail.
+ vf_add_before_vo(&vf, "flip", NULL);
+ sh->vfilter = vf;
+ }
+ // time to do aspect ratio corrections...
+
+ if (opts->movie_aspect > -1.0)
+ sh->aspect = opts->movie_aspect; // cmdline overrides autodetect
+ else if (sh->stream_aspect != 0.0)
+ sh->aspect = sh->stream_aspect;
+
+ if (opts->screen_size_x || opts->screen_size_y) {
+ screen_size_x = opts->screen_size_x;
+ screen_size_y = opts->screen_size_y;
+ if (!opts->vidmode) {
+ if (!screen_size_x)
+ screen_size_x = 1;
+ if (!screen_size_y)
+ screen_size_y = 1;
+ if (screen_size_x <= 8)
+ screen_size_x *= sh->disp_w;
+ if (screen_size_y <= 8)
+ screen_size_y *= sh->disp_h;
+ }
+ } else {
+ // check source format aspect, calculate prescale ::atmos
+ screen_size_x = sh->disp_w;
+ screen_size_y = sh->disp_h;
+ if (opts->screen_size_xy >= 0.001) {
+ if (opts->screen_size_xy <= 8) {
+ // -xy means x+y scale
+ screen_size_x *= opts->screen_size_xy;
+ screen_size_y *= opts->screen_size_xy;
+ } else {
+ // -xy means forced width while keeping correct aspect
+ screen_size_x = opts->screen_size_xy;
+ screen_size_y = opts->screen_size_xy * sh->disp_h / sh->disp_w;
+ }
+ }
+ if (sh->aspect > 0.01) {
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_ASPECT=%1.4f\n",
+ sh->aspect);
+ int w = screen_size_y * sh->aspect;
+ int h = screen_size_y;
+ // we don't like horizontal downscale || user forced width:
+ if (w < screen_size_x || opts->screen_size_xy > 8) {
+ w = screen_size_x;
+ h = screen_size_x / sh->aspect;
+ }
+ if (abs(screen_size_x - w) >= 4 || abs(screen_size_y - h) >= 4) {
+ screen_size_x = w;
+ screen_size_y = h;
+ mp_tmsg(MSGT_CPLAYER, MSGL_V, "Aspect ratio is %.2f:1 - "
+ "scaling to correct movie aspect.\n", sh->aspect);
+ }
+ } else {
+ mp_tmsg(MSGT_CPLAYER, MSGL_V, "Movie-Aspect is undefined - no prescaling applied.\n");
+ }
+ }
+
+ vocfg_flags = (opts->fullscreen ? VOFLAG_FULLSCREEN : 0)
+ | (opts->vidmode ? VOFLAG_MODESWITCHING : 0)
+ | (opts->softzoom ? VOFLAG_SWSCALE : 0)
+ | (opts->flip ? VOFLAG_FLIPPING : 0);
+
+ // Time to config libvo!
+ mp_msg(MSGT_CPLAYER, MSGL_V,
+ "VO Config (%dx%d->%dx%d,flags=%d,0x%X)\n", sh->disp_w,
+ sh->disp_h, screen_size_x, screen_size_y, vocfg_flags, out_fmt);
+
+ vf->w = sh->disp_w;
+ vf->h = sh->disp_h;
+
+ if (vf_config_wrapper
+ (vf, sh->disp_w, sh->disp_h, screen_size_x, screen_size_y, vocfg_flags,
+ out_fmt) == 0) {
+ mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "FATAL: Cannot initialize video driver.\n");
+ sh->vf_initialized = -1;
+ return 0;
+ }
+
+ sh->vf_initialized = 1;
+
+ set_video_colorspace(sh);
+
+ if (opts->vo_gamma_gamma != 1000)
+ set_video_colors(sh, "gamma", opts->vo_gamma_gamma);
+ if (opts->vo_gamma_brightness != 1000)
+ set_video_colors(sh, "brightness", opts->vo_gamma_brightness);
+ if (opts->vo_gamma_contrast != 1000)
+ set_video_colors(sh, "contrast", opts->vo_gamma_contrast);
+ if (opts->vo_gamma_saturation != 1000)
+ set_video_colors(sh, "saturation", opts->vo_gamma_saturation);
+ if (opts->vo_gamma_hue != 1000)
+ set_video_colors(sh, "hue", opts->vo_gamma_hue);
+
+ return 1;
+}
+
+// mp_imgtype: buffering type, see mp_image.h
+// mp_imgflag: buffer requirements (read/write, preserve, stride limits), see mp_image.h
+// returns NULL or allocated mp_image_t*
+// Note: buffer allocation may be moved to mpcodecs_config_vo() later...
+mp_image_t *mpcodecs_get_image(sh_video_t *sh, int mp_imgtype, int mp_imgflag,
+ int w, int h)
+{
+ return vf_get_image(sh->vfilter, sh->outfmt, mp_imgtype, mp_imgflag, w, h);
+}
+
+void mpcodecs_draw_slice(sh_video_t *sh, unsigned char **src, int *stride,
+ int w, int h, int x, int y)
+{
+ struct vf_instance *vf = sh->vfilter;
+
+ if (vf->draw_slice)
+ vf->draw_slice(vf, src, stride, w, h, x, y);
+}
diff --git a/video/decode/vd.h b/video/decode/vd.h
new file mode 100644
index 0000000000..6b9803a611
--- /dev/null
+++ b/video/decode/vd.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_VD_H
+#define MPLAYER_VD_H
+
+#include "mp_image.h"
+#include "mpc_info.h"
+#include "libmpdemux/stheader.h"
+
+typedef struct mp_codec_info vd_info_t;
+
+struct demux_packet;
+
+/* interface of video decoder drivers */
+typedef struct vd_functions
+{
+ const vd_info_t *info;
+ int (*init)(sh_video_t *sh);
+ void (*uninit)(sh_video_t *sh);
+ int (*control)(sh_video_t *sh, int cmd, void *arg);
+ struct mp_image *(*decode)(struct sh_video *sh, struct demux_packet *pkt,
+ void *data, int len, int flags,
+ double *reordered_pts);
+} vd_functions_t;
+
+// NULL terminated array of all drivers
+extern const vd_functions_t *const mpcodecs_vd_drivers[];
+
+#define VDCTRL_QUERY_FORMAT 3 // test for availabilty of a format
+#define VDCTRL_RESYNC_STREAM 8 // reset decode state after seeking
+#define VDCTRL_QUERY_UNSEEN_FRAMES 9 // current decoder lag
+#define VDCTRL_RESET_ASPECT 10 // reinit filter/VO chain for new aspect ratio
+
+// callbacks:
+int mpcodecs_config_vo(sh_video_t *sh, int w, int h,
+ const unsigned int *outfmts,
+ unsigned int preferred_outfmt);
+
+mp_image_t *mpcodecs_get_image(sh_video_t *sh, int mp_imgtype, int mp_imgflag,
+ int w, int h);
+void mpcodecs_draw_slice(sh_video_t *sh, unsigned char **src, int *stride,
+ int w, int h, int x, int y);
+
+#endif /* MPLAYER_VD_H */
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
new file mode 100644
index 0000000000..e078de4419
--- /dev/null
+++ b/video/decode/vd_lavc.c
@@ -0,0 +1,857 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <time.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <libavutil/common.h>
+#include <libavutil/opt.h>
+#include <libavutil/intreadwrite.h>
+
+#include "talloc.h"
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+#include "av_opts.h"
+
+#include "mpbswap.h"
+#include "fmt-conversion.h"
+
+#include "vd.h"
+#include "img_format.h"
+#include "libmpdemux/stheader.h"
+#include "libmpdemux/demux_packet.h"
+#include "codec-cfg.h"
+#include "osdep/numcores.h"
+#include "libvo/csputils.h"
+
+static const vd_info_t info = {
+ "libavcodec video codecs",
+ "ffmpeg",
+ "",
+ "",
+ "native codecs",
+ .print_name = "libavcodec",
+};
+
+#include "libavcodec/avcodec.h"
+
+#if AVPALETTE_SIZE > 1024
+#error palette too large, adapt libmpcodecs/vf.c:vf_get_image
+#endif
+
+typedef struct {
+ AVCodecContext *avctx;
+ AVFrame *pic;
+ enum PixelFormat pix_fmt;
+ int do_slices;
+ int do_dr1;
+ int vo_initialized;
+ int best_csp;
+ int qp_stat[32];
+ double qp_sum;
+ double inv_qp_sum;
+ int ip_count;
+ int b_count;
+ AVRational last_sample_aspect_ratio;
+ enum AVDiscard skip_frame;
+} vd_ffmpeg_ctx;
+
+#include "m_option.h"
+
+static int get_buffer(AVCodecContext *avctx, AVFrame *pic);
+static void release_buffer(AVCodecContext *avctx, AVFrame *pic);
+static void draw_slice(struct AVCodecContext *s, const AVFrame *src,
+ int offset[4], int y, int type, int height);
+
+static enum PixelFormat get_format(struct AVCodecContext *avctx,
+ const enum PixelFormat *pix_fmt);
+static void uninit(struct sh_video *sh);
+
+const m_option_t lavc_decode_opts_conf[] = {
+ OPT_INTRANGE("bug", lavc_param.workaround_bugs, 0, -1, 999999),
+ OPT_FLAG_ON("gray", lavc_param.gray, 0),
+ OPT_INTRANGE("idct", lavc_param.idct_algo, 0, 0, 99),
+ OPT_INTRANGE("ec", lavc_param.error_concealment, 0, 0, 99),
+ OPT_FLAG_ON("vstats", lavc_param.vstats, 0),
+ OPT_INTRANGE("debug", lavc_param.debug, 0, 0, 9999999),
+ OPT_INTRANGE("vismv", lavc_param.vismv, 0, 0, 9999999),
+ OPT_INTRANGE("st", lavc_param.skip_top, 0, 0, 999),
+ OPT_INTRANGE("sb", lavc_param.skip_bottom, 0, 0, 999),
+ OPT_FLAG_CONSTANTS("fast", lavc_param.fast, 0, 0, CODEC_FLAG2_FAST),
+ OPT_STRING("lowres", lavc_param.lowres_str, 0),
+ OPT_STRING("skiploopfilter", lavc_param.skip_loop_filter_str, 0),
+ OPT_STRING("skipidct", lavc_param.skip_idct_str, 0),
+ OPT_STRING("skipframe", lavc_param.skip_frame_str, 0),
+ OPT_INTRANGE("threads", lavc_param.threads, 0, 0, 16),
+ OPT_FLAG_CONSTANTS("bitexact", lavc_param.bitexact, 0, 0, CODEC_FLAG_BITEXACT),
+ OPT_STRING("o", lavc_param.avopt, 0),
+ {NULL, NULL, 0, 0, 0, 0, NULL}
+};
+
+static enum AVDiscard str2AVDiscard(char *str)
+{
+ if (!str) return AVDISCARD_DEFAULT;
+ if (strcasecmp(str, "none" ) == 0) return AVDISCARD_NONE;
+ if (strcasecmp(str, "default") == 0) return AVDISCARD_DEFAULT;
+ if (strcasecmp(str, "nonref" ) == 0) return AVDISCARD_NONREF;
+ if (strcasecmp(str, "bidir" ) == 0) return AVDISCARD_BIDIR;
+ if (strcasecmp(str, "nonkey" ) == 0) return AVDISCARD_NONKEY;
+ if (strcasecmp(str, "all" ) == 0) return AVDISCARD_ALL;
+ mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Unknown discard value %s\n", str);
+ return AVDISCARD_DEFAULT;
+}
+
+static int init(sh_video_t *sh)
+{
+ struct lavc_param *lavc_param = &sh->opts->lavc_param;
+ AVCodecContext *avctx;
+ vd_ffmpeg_ctx *ctx;
+ AVCodec *lavc_codec = NULL;
+ enum PixelFormat rawfmt = PIX_FMT_NONE;
+ int do_vis_debug = lavc_param->vismv ||
+ (lavc_param->debug & (FF_DEBUG_VIS_MB_TYPE | FF_DEBUG_VIS_QP));
+
+ ctx = sh->context = talloc_zero(NULL, vd_ffmpeg_ctx);
+
+ if (sh->codec->dll) {
+ lavc_codec = avcodec_find_decoder_by_name(sh->codec->dll);
+ if (!lavc_codec) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_ERR,
+ "Cannot find codec '%s' in libavcodec...\n",
+ sh->codec->dll);
+ uninit(sh);
+ return 0;
+ }
+ } else if (sh->libav_codec_id) {
+ lavc_codec = avcodec_find_decoder(sh->libav_codec_id);
+ if (!lavc_codec) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Libavcodec has no decoder "
+ "for this codec\n");
+ uninit(sh);
+ return 0;
+ }
+ } else if (!IMGFMT_IS_HWACCEL(sh->format)) {
+ rawfmt = imgfmt2pixfmt(sh->format);
+ if (rawfmt != PIX_FMT_NONE)
+ lavc_codec = avcodec_find_decoder_by_name("rawvideo");
+ }
+ if (!lavc_codec) {
+ uninit(sh);
+ return 0;
+ }
+
+ sh->codecname = lavc_codec->long_name;
+ if (!sh->codecname)
+ sh->codecname = lavc_codec->name;
+
+ if (sh->opts->vd_use_slices
+ && (lavc_codec->capabilities & CODEC_CAP_DRAW_HORIZ_BAND)
+ && !do_vis_debug)
+ ctx->do_slices = 1;
+
+ if (lavc_codec->capabilities & CODEC_CAP_DR1 && !do_vis_debug
+ && lavc_codec->id != CODEC_ID_H264
+ && lavc_codec->id != CODEC_ID_INTERPLAY_VIDEO
+ && lavc_codec->id != CODEC_ID_ROQ && lavc_codec->id != CODEC_ID_VP8
+ && lavc_codec->id != CODEC_ID_LAGARITH)
+ ctx->do_dr1 = sh->opts->vd_use_dr1;
+ ctx->ip_count = ctx->b_count = 0;
+
+ ctx->pic = avcodec_alloc_frame();
+ ctx->avctx = avcodec_alloc_context3(lavc_codec);
+ avctx = ctx->avctx;
+ avctx->opaque = sh;
+ avctx->codec_type = AVMEDIA_TYPE_VIDEO;
+ avctx->codec_id = lavc_codec->id;
+
+ if (lavc_codec->capabilities & CODEC_CAP_HWACCEL // XvMC
+ || lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU) {
+ ctx->do_dr1 = true;
+ ctx->do_slices = true;
+ lavc_param->threads = 1;
+ avctx->get_format = get_format;
+ avctx->get_buffer = get_buffer;
+ avctx->release_buffer = release_buffer;
+ avctx->reget_buffer = get_buffer;
+ avctx->draw_horiz_band = draw_slice;
+ if (lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU)
+ mp_msg(MSGT_DECVIDEO, MSGL_V, "[VD_FFMPEG] VDPAU hardware "
+ "decoding.\n");
+ avctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
+ }
+
+ if (lavc_param->threads == 0) {
+ int threads = default_thread_count();
+ if (threads < 1) {
+ mp_msg(MSGT_DECVIDEO, MSGL_WARN, "[VD_FFMPEG] Could not determine "
+ "thread count to use, defaulting to 1.\n");
+ threads = 1;
+ }
+ threads = FFMIN(threads, 16);
+ lavc_param->threads = threads;
+ }
+ /* Our get_buffer and draw_horiz_band callbacks are not safe to call
+ * from other threads. */
+ if (lavc_param->threads > 1) {
+ ctx->do_dr1 = false;
+ ctx->do_slices = false;
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Asking decoder to use "
+ "%d threads if supported.\n", lavc_param->threads);
+ }
+
+ if (ctx->do_dr1) {
+ avctx->flags |= CODEC_FLAG_EMU_EDGE;
+ avctx->get_buffer = get_buffer;
+ avctx->release_buffer = release_buffer;
+ avctx->reget_buffer = get_buffer;
+ }
+
+ avctx->flags |= lavc_param->bitexact;
+
+ avctx->coded_width = sh->disp_w;
+ avctx->coded_height = sh->disp_h;
+ avctx->workaround_bugs = lavc_param->workaround_bugs;
+ if (lavc_param->gray)
+ avctx->flags |= CODEC_FLAG_GRAY;
+ avctx->flags2 |= lavc_param->fast;
+ if (rawfmt == PIX_FMT_NONE) {
+ avctx->codec_tag = sh->format;
+ } else {
+ avctx->pix_fmt = rawfmt;
+ }
+ if (sh->gsh->lavf_codec_tag)
+ avctx->codec_tag = sh->gsh->lavf_codec_tag;
+ avctx->stream_codec_tag = sh->video.fccHandler;
+ avctx->idct_algo = lavc_param->idct_algo;
+ avctx->error_concealment = lavc_param->error_concealment;
+ avctx->debug = lavc_param->debug;
+ if (lavc_param->debug)
+ av_log_set_level(AV_LOG_DEBUG);
+ avctx->debug_mv = lavc_param->vismv;
+ avctx->skip_top = lavc_param->skip_top;
+ avctx->skip_bottom = lavc_param->skip_bottom;
+ if (lavc_param->lowres_str != NULL) {
+ int lowres, lowres_w;
+ sscanf(lavc_param->lowres_str, "%d,%d", &lowres, &lowres_w);
+ if (lowres < 1 || lowres > 16 ||
+ lowres_w > 0 && avctx->width < lowres_w)
+ lowres = 0;
+ avctx->lowres = lowres;
+ }
+ avctx->skip_loop_filter = str2AVDiscard(lavc_param->skip_loop_filter_str);
+ avctx->skip_idct = str2AVDiscard(lavc_param->skip_idct_str);
+ avctx->skip_frame = str2AVDiscard(lavc_param->skip_frame_str);
+
+ if (lavc_param->avopt) {
+ if (parse_avopts(avctx, lavc_param->avopt) < 0) {
+ mp_msg(MSGT_DECVIDEO, MSGL_ERR,
+ "Your options /%s/ look like gibberish to me pal\n",
+ lavc_param->avopt);
+ uninit(sh);
+ return 0;
+ }
+ }
+
+ // Do this after the above avopt handling in case it changes values
+ ctx->skip_frame = avctx->skip_frame;
+
+ mp_dbg(MSGT_DECVIDEO, MSGL_DBG2,
+ "libavcodec.size: %d x %d\n", avctx->width, avctx->height);
+ switch (sh->format) {
+ case mmioFOURCC('S','V','Q','3'):
+ case mmioFOURCC('A','V','R','n'):
+ case mmioFOURCC('M','J','P','G'):
+ /* AVRn stores huffman table in AVI header */
+ /* Pegasus MJPEG stores it also in AVI header, but it uses the common
+ * MJPG fourcc :( */
+ if (!sh->bih || sh->bih->biSize <= sizeof(*sh->bih))
+ break;
+ av_opt_set_int(avctx, "extern_huff", 1, AV_OPT_SEARCH_CHILDREN);
+ avctx->extradata_size = sh->bih->biSize - sizeof(*sh->bih);
+ avctx->extradata = av_mallocz(avctx->extradata_size +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ memcpy(avctx->extradata, sh->bih + 1, avctx->extradata_size);
+ break;
+
+ case mmioFOURCC('R','V','1','0'):
+ case mmioFOURCC('R','V','1','3'):
+ case mmioFOURCC('R','V','2','0'):
+ case mmioFOURCC('R','V','3','0'):
+ case mmioFOURCC('R','V','4','0'):
+ if (sh->bih->biSize < sizeof(*sh->bih) + 8) {
+ // only 1 packet per frame & sub_id from fourcc
+ avctx->extradata_size = 8;
+ avctx->extradata = av_mallocz(avctx->extradata_size +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ ((uint32_t *)avctx->extradata)[0] = 0;
+ ((uint32_t *)avctx->extradata)[1] =
+ sh->format == mmioFOURCC('R','V','1','3') ?
+ 0x10003001 : 0x10000000;
+ } else {
+ // has extra slice header (demux_rm or rm->avi streamcopy)
+ avctx->extradata_size = sh->bih->biSize - sizeof(*sh->bih);
+ avctx->extradata = av_mallocz(avctx->extradata_size +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ memcpy(avctx->extradata, sh->bih + 1, avctx->extradata_size);
+ }
+ break;
+
+ default:
+ if (!sh->bih || sh->bih->biSize <= sizeof(*sh->bih))
+ break;
+ avctx->extradata_size = sh->bih->biSize - sizeof(*sh->bih);
+ avctx->extradata = av_mallocz(avctx->extradata_size +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ memcpy(avctx->extradata, sh->bih + 1, avctx->extradata_size);
+ break;
+ }
+
+ if (sh->bih)
+ avctx->bits_per_coded_sample = sh->bih->biBitCount;
+
+ avctx->thread_count = lavc_param->threads;
+
+ /* open it */
+ if (avcodec_open2(avctx, lavc_codec, NULL) < 0) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Could not open codec.\n");
+ uninit(sh);
+ return 0;
+ }
+ return 1;
+}
+
+static void uninit(sh_video_t *sh)
+{
+ vd_ffmpeg_ctx *ctx = sh->context;
+ AVCodecContext *avctx = ctx->avctx;
+
+ sh->codecname = NULL;
+ if (sh->opts->lavc_param.vstats && avctx->coded_frame) {
+ for (int i = 1; i < 32; i++)
+ mp_msg(MSGT_DECVIDEO, MSGL_INFO,
+ "QP: %d, count: %d\n", i, ctx->qp_stat[i]);
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "[VD_FFMPEG] Arithmetic mean of QP: "
+ "%2.4f, Harmonic mean of QP: %2.4f\n",
+ ctx->qp_sum / avctx->coded_frame->coded_picture_number,
+ 1.0 / (ctx->inv_qp_sum / avctx->coded_frame->coded_picture_number));
+ }
+
+ if (avctx) {
+ if (avctx->codec && avcodec_close(avctx) < 0)
+ mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Could not close codec.\n");
+
+ av_freep(&avctx->extradata);
+ av_freep(&avctx->slice_offset);
+ }
+
+ av_freep(&avctx);
+ avcodec_free_frame(&ctx->pic);
+ talloc_free(ctx);
+}
+
+static void draw_slice(struct AVCodecContext *s,
+ const AVFrame *src, int offset[4],
+ int y, int type, int height)
+{
+ sh_video_t *sh = s->opaque;
+ uint8_t *source[MP_MAX_PLANES] = {
+ src->data[0] + offset[0], src->data[1] + offset[1],
+ src->data[2] + offset[2]
+ };
+ int strides[MP_MAX_PLANES] = {
+ src->linesize[0], src->linesize[1], src->linesize[2]
+ };
+ if (height < 0) {
+ int i;
+ height = -height;
+ y -= height;
+ for (i = 0; i < MP_MAX_PLANES; i++) {
+ strides[i] = -strides[i];
+ source[i] -= strides[i];
+ }
+ }
+ if (y < sh->disp_h) {
+ height = FFMIN(height, sh->disp_h - y);
+ mpcodecs_draw_slice(sh, source, strides, sh->disp_w, height, 0, y);
+ }
+}
+
+
+static int init_vo(sh_video_t *sh, enum PixelFormat pix_fmt)
+{
+ vd_ffmpeg_ctx *ctx = sh->context;
+ AVCodecContext *avctx = ctx->avctx;
+ float aspect = av_q2d(avctx->sample_aspect_ratio) *
+ avctx->width / avctx->height;
+ int width, height;
+
+ width = avctx->width;
+ height = avctx->height;
+
+ /* Reconfiguring filter/VO chain may invalidate direct rendering buffers
+ * we have allocated for libavcodec (including the VDPAU HW decoding
+ * case). Is it guaranteed that the code below only triggers in a situation
+ * with no busy direct rendering buffers for reference frames?
+ */
+ if (av_cmp_q(avctx->sample_aspect_ratio, ctx->last_sample_aspect_ratio) ||
+ width != sh->disp_w || height != sh->disp_h ||
+ pix_fmt != ctx->pix_fmt || !ctx->vo_initialized) {
+ ctx->vo_initialized = 0;
+ mp_msg(MSGT_DECVIDEO, MSGL_V, "[ffmpeg] aspect_ratio: %f\n", aspect);
+
+ // Do not overwrite s->aspect on the first call, so that a container
+ // aspect if available is preferred.
+ // But set it even if the sample aspect did not change, since a
+ // resolution change can cause an aspect change even if the
+ // _sample_ aspect is unchanged.
+ if (sh->aspect == 0 || ctx->last_sample_aspect_ratio.den)
+ sh->aspect = aspect;
+ ctx->last_sample_aspect_ratio = avctx->sample_aspect_ratio;
+ sh->disp_w = width;
+ sh->disp_h = height;
+ ctx->pix_fmt = pix_fmt;
+ ctx->best_csp = pixfmt2imgfmt(pix_fmt);
+ const unsigned int *supported_fmts;
+ if (ctx->best_csp == IMGFMT_YV12)
+ supported_fmts = (const unsigned int[]){
+ IMGFMT_YV12, IMGFMT_I420, IMGFMT_IYUV, 0xffffffff
+ };
+ else if (ctx->best_csp == IMGFMT_422P)
+ supported_fmts = (const unsigned int[]){
+ IMGFMT_422P, IMGFMT_YV12, IMGFMT_I420, IMGFMT_IYUV, 0xffffffff
+ };
+ else
+ supported_fmts = (const unsigned int[]){ctx->best_csp, 0xffffffff};
+
+ sh->colorspace = avcol_spc_to_mp_csp(avctx->colorspace);
+ sh->color_range = avcol_range_to_mp_csp_levels(avctx->color_range);
+
+ if (!mpcodecs_config_vo(sh, sh->disp_w, sh->disp_h, supported_fmts,
+ ctx->best_csp))
+ return -1;
+ ctx->vo_initialized = 1;
+ }
+ return 0;
+}
+
+static int get_buffer(AVCodecContext *avctx, AVFrame *pic)
+{
+ sh_video_t *sh = avctx->opaque;
+ vd_ffmpeg_ctx *ctx = sh->context;
+ mp_image_t *mpi = NULL;
+ int flags = MP_IMGFLAG_ACCEPT_ALIGNED_STRIDE |
+ MP_IMGFLAG_PREFER_ALIGNED_STRIDE;
+ int type = MP_IMGTYPE_IPB;
+ int width = avctx->width;
+ int height = avctx->height;
+ // special case to handle reget_buffer without buffer hints
+ if (pic->opaque && pic->data[0] && !pic->buffer_hints)
+ return 0;
+ avcodec_align_dimensions(avctx, &width, &height);
+
+ if (pic->buffer_hints) {
+ mp_msg(MSGT_DECVIDEO, MSGL_DBG2, "Buffer hints: %u\n",
+ pic->buffer_hints);
+ type = MP_IMGTYPE_TEMP;
+ if (pic->buffer_hints & FF_BUFFER_HINTS_READABLE)
+ flags |= MP_IMGFLAG_READABLE;
+ if (pic->buffer_hints & FF_BUFFER_HINTS_PRESERVE) {
+ type = MP_IMGTYPE_STATIC;
+ flags |= MP_IMGFLAG_PRESERVE;
+ }
+ if (pic->buffer_hints & FF_BUFFER_HINTS_REUSABLE) {
+ type = MP_IMGTYPE_STATIC;
+ flags |= MP_IMGFLAG_PRESERVE;
+ }
+ flags |= ctx->do_slices ? MP_IMGFLAG_DRAW_CALLBACK : 0;
+ mp_msg(MSGT_DECVIDEO, MSGL_DBG2,
+ type == MP_IMGTYPE_STATIC ? "using STATIC\n" : "using TEMP\n");
+ } else {
+ if (!pic->reference) {
+ ctx->b_count++;
+ flags |= ctx->do_slices ? MP_IMGFLAG_DRAW_CALLBACK : 0;
+ } else {
+ ctx->ip_count++;
+ flags |= MP_IMGFLAG_PRESERVE | MP_IMGFLAG_READABLE
+ | (ctx->do_slices ? MP_IMGFLAG_DRAW_CALLBACK : 0);
+ }
+ }
+
+ if (init_vo(sh, avctx->pix_fmt) < 0) {
+ avctx->release_buffer = avcodec_default_release_buffer;
+ avctx->get_buffer = avcodec_default_get_buffer;
+ avctx->reget_buffer = avcodec_default_reget_buffer;
+ if (pic->data[0])
+ release_buffer(avctx, pic);
+ return avctx->get_buffer(avctx, pic);
+ }
+
+ if (IMGFMT_IS_HWACCEL(ctx->best_csp))
+ type = MP_IMGTYPE_NUMBERED | (0xffff << 16);
+ else if (!pic->buffer_hints) {
+ if (ctx->b_count > 1 || ctx->ip_count > 2) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "[VD_FFMPEG] DRI failure.\n");
+
+ ctx->do_dr1 = 0; //FIXME
+ avctx->get_buffer = avcodec_default_get_buffer;
+ avctx->reget_buffer = avcodec_default_reget_buffer;
+ if (pic->data[0])
+ release_buffer(avctx, pic);
+ return avctx->get_buffer(avctx, pic);
+ }
+
+ if (avctx->has_b_frames || ctx->b_count)
+ type = MP_IMGTYPE_IPB;
+ else
+ type = MP_IMGTYPE_IP;
+ mp_msg(MSGT_DECVIDEO, MSGL_DBG2,
+ type == MP_IMGTYPE_IPB ? "using IPB\n" : "using IP\n");
+ }
+
+ if (ctx->best_csp == IMGFMT_RGB8 || ctx->best_csp == IMGFMT_BGR8)
+ flags |= MP_IMGFLAG_RGB_PALETTE;
+ mpi = mpcodecs_get_image(sh, type, flags, width, height);
+ if (!mpi)
+ return -1;
+
+ // ok, let's see what did we get:
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK &&
+ !(mpi->flags & MP_IMGFLAG_DIRECT)) {
+ // nice, filter/vo likes draw_callback :)
+ avctx->draw_horiz_band = draw_slice;
+ } else
+ avctx->draw_horiz_band = NULL;
+ if (IMGFMT_IS_HWACCEL(mpi->imgfmt))
+ avctx->draw_horiz_band = draw_slice;
+
+ pic->data[0] = mpi->planes[0];
+ pic->data[1] = mpi->planes[1];
+ pic->data[2] = mpi->planes[2];
+ pic->data[3] = mpi->planes[3];
+
+ /* Note: some (many) codecs in libavcodec require
+ * linesize[1] == linesize[2] and no changes between frames.
+ * Lavc will check that and die with an error message if it's not true.
+ */
+ pic->linesize[0] = mpi->stride[0];
+ pic->linesize[1] = mpi->stride[1];
+ pic->linesize[2] = mpi->stride[2];
+ pic->linesize[3] = mpi->stride[3];
+
+ pic->opaque = mpi;
+
+ pic->type = FF_BUFFER_TYPE_USER;
+
+ /* The libavcodec reordered_opaque functionality is implemented by
+ * a similar copy in avcodec_default_get_buffer() and without a
+ * workaround like this it'd stop working when a custom buffer
+ * callback is used.
+ */
+ pic->reordered_opaque = avctx->reordered_opaque;
+ return 0;
+}
+
+static void release_buffer(struct AVCodecContext *avctx, AVFrame *pic)
+{
+ mp_image_t *mpi = pic->opaque;
+ sh_video_t *sh = avctx->opaque;
+ vd_ffmpeg_ctx *ctx = sh->context;
+
+ if (ctx->ip_count <= 2 && ctx->b_count <= 1) {
+ if (mpi->flags & MP_IMGFLAG_PRESERVE)
+ ctx->ip_count--;
+ else
+ ctx->b_count--;
+ }
+
+ if (mpi) {
+ // release mpi (in case MPI_IMGTYPE_NUMBERED is used, e.g. for VDPAU)
+ mpi->usage_count--;
+ if (mpi->usage_count < 0) {
+ mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Bad mp_image usage count, please report!\n");
+ mpi->usage_count = 0;
+ }
+ }
+
+ if (pic->type != FF_BUFFER_TYPE_USER) {
+ avcodec_default_release_buffer(avctx, pic);
+ return;
+ }
+
+ for (int i = 0; i < 4; i++)
+ pic->data[i] = NULL;
+}
+
+static av_unused void swap_palette(void *pal)
+{
+ int i;
+ uint32_t *p = pal;
+ for (i = 0; i < AVPALETTE_COUNT; i++)
+ p[i] = le2me_32(p[i]);
+}
+
+static struct mp_image *decode(struct sh_video *sh, struct demux_packet *packet,
+ void *data, int len, int flags,
+ double *reordered_pts)
+{
+ int got_picture = 0;
+ int ret;
+ vd_ffmpeg_ctx *ctx = sh->context;
+ AVFrame *pic = ctx->pic;
+ AVCodecContext *avctx = ctx->avctx;
+ struct lavc_param *lavc_param = &sh->opts->lavc_param;
+ mp_image_t *mpi = NULL;
+ int dr1 = ctx->do_dr1;
+ AVPacket pkt;
+
+ if (!dr1)
+ avctx->draw_horiz_band = NULL;
+
+ if (flags & 2)
+ avctx->skip_frame = AVDISCARD_ALL;
+ else if (flags & 1)
+ avctx->skip_frame = AVDISCARD_NONREF;
+ else
+ avctx->skip_frame = ctx->skip_frame;
+
+ av_init_packet(&pkt);
+ pkt.data = data;
+ pkt.size = len;
+ /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
+ * from demuxer. */
+ if (packet && packet->keyframe)
+ pkt.flags |= AV_PKT_FLAG_KEY;
+ if (packet && packet->avpacket) {
+ pkt.side_data = packet->avpacket->side_data;
+ pkt.side_data_elems = packet->avpacket->side_data_elems;
+ }
+ // The avcodec opaque field stupidly supports only int64_t type
+ union pts { int64_t i; double d; };
+ avctx->reordered_opaque = (union pts){.d = *reordered_pts}.i;
+ ret = avcodec_decode_video2(avctx, pic, &got_picture, &pkt);
+ *reordered_pts = (union pts){.i = pic->reordered_opaque}.d;
+
+ dr1 = ctx->do_dr1;
+ if (ret < 0)
+ mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Error while decoding frame!\n");
+ //-- vstats generation
+ while (lavc_param->vstats) { // always one time loop
+ static FILE *fvstats = NULL;
+ char filename[20];
+ static long long int all_len = 0;
+ static int frame_number = 0;
+ static double all_frametime = 0.0;
+ AVFrame *pic = avctx->coded_frame;
+ double quality = 0.0;
+
+ if (!pic)
+ break;
+
+ if (!fvstats) {
+ time_t today2;
+ struct tm *today;
+ today2 = time(NULL);
+ today = localtime(&today2);
+ sprintf(filename, "vstats_%02d%02d%02d.log", today->tm_hour,
+ today->tm_min, today->tm_sec);
+ fvstats = fopen(filename, "w");
+ if (!fvstats) {
+ perror("fopen");
+ lavc_param->vstats = 0; // disable block
+ break;
+ /*exit(1);*/
+ }
+ }
+
+ // average MB quantizer
+ {
+ int x, y;
+ int w = ((avctx->width << avctx->lowres) + 15) >> 4;
+ int h = ((avctx->height << avctx->lowres) + 15) >> 4;
+ int8_t *q = pic->qscale_table;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++)
+ quality += (double)*(q + x);
+ q += pic->qstride;
+ }
+ quality /= w * h;
+ }
+
+ all_len += len;
+ all_frametime += sh->frametime;
+ fprintf(fvstats, "frame= %5d q= %2.2f f_size= %6d s_size= %8.0fkB ",
+ ++frame_number, quality, len, (double)all_len / 1024);
+ fprintf(fvstats, "time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ",
+ all_frametime, (double)(len * 8) / sh->frametime / 1000.0,
+ (double)(all_len * 8) / all_frametime / 1000.0);
+ switch (pic->pict_type) {
+ case AV_PICTURE_TYPE_I:
+ fprintf(fvstats, "type= I\n");
+ break;
+ case AV_PICTURE_TYPE_P:
+ fprintf(fvstats, "type= P\n");
+ break;
+ case AV_PICTURE_TYPE_S:
+ fprintf(fvstats, "type= S\n");
+ break;
+ case AV_PICTURE_TYPE_B:
+ fprintf(fvstats, "type= B\n");
+ break;
+ default:
+ fprintf(fvstats, "type= ? (%d)\n", pic->pict_type);
+ break;
+ }
+
+ ctx->qp_stat[(int)(quality + 0.5)]++;
+ ctx->qp_sum += quality;
+ ctx->inv_qp_sum += 1.0 / (double)quality;
+
+ break;
+ }
+ //--
+
+ if (!got_picture)
+ return NULL; // skipped image
+
+ if (init_vo(sh, avctx->pix_fmt) < 0)
+ return NULL;
+
+ if (dr1 && pic->opaque)
+ mpi = (mp_image_t *)pic->opaque;
+
+ if (!mpi)
+ mpi = mpcodecs_get_image(sh, MP_IMGTYPE_EXPORT, MP_IMGFLAG_PRESERVE,
+ avctx->width, avctx->height);
+ if (!mpi) { // temporary error?
+ mp_tmsg(MSGT_DECVIDEO, MSGL_WARN,
+ "[VD_FFMPEG] Couldn't allocate image for codec.\n");
+ return NULL;
+ }
+
+ if (!dr1) {
+ mpi->planes[0] = pic->data[0];
+ mpi->planes[1] = pic->data[1];
+ mpi->planes[2] = pic->data[2];
+ mpi->planes[3] = pic->data[3];
+ mpi->stride[0] = pic->linesize[0];
+ mpi->stride[1] = pic->linesize[1];
+ mpi->stride[2] = pic->linesize[2];
+ mpi->stride[3] = pic->linesize[3];
+ }
+
+ if (!mpi->planes[0])
+ return NULL;
+
+ if (ctx->best_csp == IMGFMT_422P && mpi->chroma_y_shift == 1) {
+ // we have 422p but user wants 420p
+ mpi->stride[1] *= 2;
+ mpi->stride[2] *= 2;
+ }
+
+#if BYTE_ORDER == BIG_ENDIAN
+ // FIXME: this might cause problems for buffers with FF_BUFFER_HINTS_PRESERVE
+ if (mpi->bpp == 8)
+ swap_palette(mpi->planes[1]);
+#endif
+
+ mpi->colorspace = sh->colorspace;
+ mpi->levels = sh->color_range;
+ mpi->qscale = pic->qscale_table;
+ mpi->qstride = pic->qstride;
+ mpi->pict_type = pic->pict_type;
+ mpi->qscale_type = pic->qscale_type;
+ mpi->fields = MP_IMGFIELD_ORDERED;
+ if (pic->interlaced_frame)
+ mpi->fields |= MP_IMGFIELD_INTERLACED;
+ if (pic->top_field_first)
+ mpi->fields |= MP_IMGFIELD_TOP_FIRST;
+ if (pic->repeat_pict == 1)
+ mpi->fields |= MP_IMGFIELD_REPEAT_FIRST;
+
+ return mpi;
+}
+
+static enum PixelFormat get_format(struct AVCodecContext *avctx,
+ const enum PixelFormat *fmt)
+{
+ sh_video_t *sh = avctx->opaque;
+ int i;
+
+ for (i = 0; fmt[i] != PIX_FMT_NONE; i++) {
+ int imgfmt = pixfmt2imgfmt(fmt[i]);
+ if (!IMGFMT_IS_HWACCEL(imgfmt))
+ continue;
+ mp_msg(MSGT_DECVIDEO, MSGL_V, "[VD_FFMPEG] Trying pixfmt=%d.\n", i);
+ if (init_vo(sh, fmt[i]) >= 0)
+ break;
+ }
+ return fmt[i];
+}
+
+static int control(sh_video_t *sh, int cmd, void *arg)
+{
+ vd_ffmpeg_ctx *ctx = sh->context;
+ AVCodecContext *avctx = ctx->avctx;
+ switch (cmd) {
+ case VDCTRL_QUERY_FORMAT: {
+ int format = (*((int *)arg));
+ if (format == ctx->best_csp)
+ return CONTROL_TRUE;
+ // possible conversions:
+ switch (format) {
+ case IMGFMT_YV12:
+ case IMGFMT_IYUV:
+ case IMGFMT_I420:
+ // "converted" using pointer/stride modification
+ if (ctx->best_csp == IMGFMT_YV12)
+ return CONTROL_TRUE; // u/v swap
+ if (ctx->best_csp == IMGFMT_422P && !ctx->do_dr1)
+ return CONTROL_TRUE; // half stride
+ break;
+ }
+ return CONTROL_FALSE;
+ }
+ case VDCTRL_RESYNC_STREAM:
+ avcodec_flush_buffers(avctx);
+ return CONTROL_TRUE;
+ case VDCTRL_QUERY_UNSEEN_FRAMES:;
+ int delay = avctx->has_b_frames;
+ if (avctx->active_thread_type & FF_THREAD_FRAME)
+ delay += avctx->thread_count - 1;
+ return delay + 10;
+ case VDCTRL_RESET_ASPECT:
+ if (ctx->vo_initialized)
+ ctx->vo_initialized = false;
+ init_vo(sh, avctx->pix_fmt);
+ return true;
+ }
+ return CONTROL_UNKNOWN;
+}
+
+const struct vd_functions mpcodecs_vd_ffmpeg = {
+ .info = &info,
+ .init = init,
+ .uninit = uninit,
+ .control = control,
+ .decode = decode,
+};
diff --git a/video/filter/pullup.c b/video/filter/pullup.c
new file mode 100644
index 0000000000..bd25c187d6
--- /dev/null
+++ b/video/filter/pullup.c
@@ -0,0 +1,817 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "pullup.h"
+#include "cpudetect.h"
+#include "mpcommon.h"
+
+
+
+#if ARCH_X86
+#if HAVE_MMX
+static int diff_y_mmx(unsigned char *a, unsigned char *b, int s)
+{
+ int ret;
+ __asm__ volatile (
+ "movl $4, %%ecx \n\t"
+ "pxor %%mm4, %%mm4 \n\t"
+ "pxor %%mm7, %%mm7 \n\t"
+
+ "1: \n\t"
+
+ "movq (%%"REG_S"), %%mm0 \n\t"
+ "movq (%%"REG_S"), %%mm2 \n\t"
+ "add %%"REG_a", %%"REG_S" \n\t"
+ "movq (%%"REG_D"), %%mm1 \n\t"
+ "add %%"REG_a", %%"REG_D" \n\t"
+ "psubusb %%mm1, %%mm2 \n\t"
+ "psubusb %%mm0, %%mm1 \n\t"
+ "movq %%mm2, %%mm0 \n\t"
+ "movq %%mm1, %%mm3 \n\t"
+ "punpcklbw %%mm7, %%mm0 \n\t"
+ "punpcklbw %%mm7, %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm2 \n\t"
+ "punpckhbw %%mm7, %%mm3 \n\t"
+ "paddw %%mm0, %%mm4 \n\t"
+ "paddw %%mm1, %%mm4 \n\t"
+ "paddw %%mm2, %%mm4 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+
+ "decl %%ecx \n\t"
+ "jnz 1b \n\t"
+
+ "movq %%mm4, %%mm3 \n\t"
+ "punpcklwd %%mm7, %%mm4 \n\t"
+ "punpckhwd %%mm7, %%mm3 \n\t"
+ "paddd %%mm4, %%mm3 \n\t"
+ "movd %%mm3, %%eax \n\t"
+ "psrlq $32, %%mm3 \n\t"
+ "movd %%mm3, %%edx \n\t"
+ "addl %%edx, %%eax \n\t"
+ "emms \n\t"
+ : "=a" (ret)
+ : "S" (a), "D" (b), "a" (s)
+ : "%ecx", "%edx"
+ );
+ return ret;
+}
+
+static int licomb_y_mmx(unsigned char *a, unsigned char *b, int s)
+{
+ int ret;
+ __asm__ volatile (
+ "movl $4, %%ecx \n\t"
+ "pxor %%mm6, %%mm6 \n\t"
+ "pxor %%mm7, %%mm7 \n\t"
+ "sub %%"REG_a", %%"REG_D" \n\t"
+
+ "2: \n\t"
+
+ "movq (%%"REG_D"), %%mm0 \n\t"
+ "movq (%%"REG_D"), %%mm1 \n\t"
+ "punpcklbw %%mm7, %%mm0 \n\t"
+ "movq (%%"REG_D",%%"REG_a"), %%mm2 \n\t"
+ "punpcklbw %%mm7, %%mm1 \n\t"
+ "punpcklbw %%mm7, %%mm2 \n\t"
+ "paddw %%mm0, %%mm0 \n\t"
+ "paddw %%mm2, %%mm1 \n\t"
+ "movq %%mm0, %%mm2 \n\t"
+ "psubusw %%mm1, %%mm0 \n\t"
+ "psubusw %%mm2, %%mm1 \n\t"
+ "paddw %%mm0, %%mm6 \n\t"
+ "paddw %%mm1, %%mm6 \n\t"
+
+ "movq (%%"REG_S"), %%mm0 \n\t"
+ "movq (%%"REG_D"), %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm0 \n\t"
+ "movq (%%"REG_D",%%"REG_a"), %%mm2 \n\t"
+ "punpckhbw %%mm7, %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm2 \n\t"
+ "paddw %%mm0, %%mm0 \n\t"
+ "paddw %%mm2, %%mm1 \n\t"
+ "movq %%mm0, %%mm2 \n\t"
+ "psubusw %%mm1, %%mm0 \n\t"
+ "psubusw %%mm2, %%mm1 \n\t"
+ "paddw %%mm0, %%mm6 \n\t"
+ "paddw %%mm1, %%mm6 \n\t"
+
+ "movq (%%"REG_D",%%"REG_a"), %%mm0 \n\t"
+ "movq (%%"REG_S"), %%mm1 \n\t"
+ "punpcklbw %%mm7, %%mm0 \n\t"
+ "movq (%%"REG_S",%%"REG_a"), %%mm2 \n\t"
+ "punpcklbw %%mm7, %%mm1 \n\t"
+ "punpcklbw %%mm7, %%mm2 \n\t"
+ "paddw %%mm0, %%mm0 \n\t"
+ "paddw %%mm2, %%mm1 \n\t"
+ "movq %%mm0, %%mm2 \n\t"
+ "psubusw %%mm1, %%mm0 \n\t"
+ "psubusw %%mm2, %%mm1 \n\t"
+ "paddw %%mm0, %%mm6 \n\t"
+ "paddw %%mm1, %%mm6 \n\t"
+
+ "movq (%%"REG_D",%%"REG_a"), %%mm0 \n\t"
+ "movq (%%"REG_S"), %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm0 \n\t"
+ "movq (%%"REG_S",%%"REG_a"), %%mm2 \n\t"
+ "punpckhbw %%mm7, %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm2 \n\t"
+ "paddw %%mm0, %%mm0 \n\t"
+ "paddw %%mm2, %%mm1 \n\t"
+ "movq %%mm0, %%mm2 \n\t"
+ "psubusw %%mm1, %%mm0 \n\t"
+ "psubusw %%mm2, %%mm1 \n\t"
+ "paddw %%mm0, %%mm6 \n\t"
+ "paddw %%mm1, %%mm6 \n\t"
+
+ "add %%"REG_a", %%"REG_S" \n\t"
+ "add %%"REG_a", %%"REG_D" \n\t"
+ "decl %%ecx \n\t"
+ "jnz 2b \n\t"
+
+ "movq %%mm6, %%mm5 \n\t"
+ "punpcklwd %%mm7, %%mm6 \n\t"
+ "punpckhwd %%mm7, %%mm5 \n\t"
+ "paddd %%mm6, %%mm5 \n\t"
+ "movd %%mm5, %%eax \n\t"
+ "psrlq $32, %%mm5 \n\t"
+ "movd %%mm5, %%edx \n\t"
+ "addl %%edx, %%eax \n\t"
+
+ "emms \n\t"
+ : "=a" (ret)
+ : "S" (a), "D" (b), "a" (s)
+ : "%ecx", "%edx"
+ );
+ return ret;
+}
+
+static int var_y_mmx(unsigned char *a, unsigned char *b, int s)
+{
+ int ret;
+ __asm__ volatile (
+ "movl $3, %%ecx \n\t"
+ "pxor %%mm4, %%mm4 \n\t"
+ "pxor %%mm7, %%mm7 \n\t"
+
+ "1: \n\t"
+
+ "movq (%%"REG_S"), %%mm0 \n\t"
+ "movq (%%"REG_S"), %%mm2 \n\t"
+ "movq (%%"REG_S",%%"REG_a"), %%mm1 \n\t"
+ "add %%"REG_a", %%"REG_S" \n\t"
+ "psubusb %%mm1, %%mm2 \n\t"
+ "psubusb %%mm0, %%mm1 \n\t"
+ "movq %%mm2, %%mm0 \n\t"
+ "movq %%mm1, %%mm3 \n\t"
+ "punpcklbw %%mm7, %%mm0 \n\t"
+ "punpcklbw %%mm7, %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm2 \n\t"
+ "punpckhbw %%mm7, %%mm3 \n\t"
+ "paddw %%mm0, %%mm4 \n\t"
+ "paddw %%mm1, %%mm4 \n\t"
+ "paddw %%mm2, %%mm4 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+
+ "decl %%ecx \n\t"
+ "jnz 1b \n\t"
+
+ "movq %%mm4, %%mm3 \n\t"
+ "punpcklwd %%mm7, %%mm4 \n\t"
+ "punpckhwd %%mm7, %%mm3 \n\t"
+ "paddd %%mm4, %%mm3 \n\t"
+ "movd %%mm3, %%eax \n\t"
+ "psrlq $32, %%mm3 \n\t"
+ "movd %%mm3, %%edx \n\t"
+ "addl %%edx, %%eax \n\t"
+ "emms \n\t"
+ : "=a" (ret)
+ : "S" (a), "a" (s)
+ : "%ecx", "%edx"
+ );
+ return 4*ret;
+}
+#endif
+#endif
+
+#define ABS(a) (((a)^((a)>>31))-((a)>>31))
+
+static int diff_y(unsigned char *a, unsigned char *b, int s)
+{
+ int i, j, diff=0;
+ for (i=4; i; i--) {
+ for (j=0; j<8; j++) diff += ABS(a[j]-b[j]);
+ a+=s; b+=s;
+ }
+ return diff;
+}
+
+static int licomb_y(unsigned char *a, unsigned char *b, int s)
+{
+ int i, j, diff=0;
+ for (i=4; i; i--) {
+ for (j=0; j<8; j++)
+ diff += ABS((a[j]<<1) - b[j-s] - b[j])
+ + ABS((b[j]<<1) - a[j] - a[j+s]);
+ a+=s; b+=s;
+ }
+ return diff;
+}
+
+#if 0
+static int qpcomb_y(unsigned char *a, unsigned char *b, int s)
+{
+ int i, j, diff=0;
+ for (i=4; i; i--) {
+ for (j=0; j<8; j++)
+ diff += ABS(a[j] - 3*b[j-s] + 3*a[j+s] - b[j]);
+ a+=s; b+=s;
+ }
+ return diff;
+}
+
+static int licomb_y_test(unsigned char *a, unsigned char *b, int s)
+{
+ int c = licomb_y(a,b,s);
+ int m = licomb_y_mmx(a,b,s);
+ if (c != m) printf("%d != %d\n", c, m);
+ return m;
+}
+#endif
+
+static int var_y(unsigned char *a, unsigned char *b, int s)
+{
+ int i, j, var=0;
+ for (i=3; i; i--) {
+ for (j=0; j<8; j++) {
+ var += ABS(a[j]-a[j+s]);
+ }
+ a+=s; b+=s;
+ }
+ return 4*var; /* match comb scaling */
+}
+
+
+
+
+
+
+
+
+
+static void alloc_buffer(struct pullup_context *c, struct pullup_buffer *b)
+{
+ int i;
+ if (b->planes) return;
+ b->planes = calloc(c->nplanes, sizeof(unsigned char *));
+ for (i = 0; i < c->nplanes; i++) {
+ b->planes[i] = malloc(c->h[i]*c->stride[i]);
+ /* Deal with idiotic 128=0 for chroma: */
+ memset(b->planes[i], c->background[i], c->h[i]*c->stride[i]);
+ }
+}
+
+struct pullup_buffer *pullup_lock_buffer(struct pullup_buffer *b, int parity)
+{
+ if (!b) return 0;
+ if ((parity+1) & 1) b->lock[0]++;
+ if ((parity+1) & 2) b->lock[1]++;
+ return b;
+}
+
+void pullup_release_buffer(struct pullup_buffer *b, int parity)
+{
+ if (!b) return;
+ if ((parity+1) & 1) b->lock[0]--;
+ if ((parity+1) & 2) b->lock[1]--;
+}
+
+struct pullup_buffer *pullup_get_buffer(struct pullup_context *c, int parity)
+{
+ int i;
+
+ /* Try first to get the sister buffer for the previous field */
+ if (parity < 2 && c->last && parity != c->last->parity
+ && !c->last->buffer->lock[parity]) {
+ alloc_buffer(c, c->last->buffer);
+ return pullup_lock_buffer(c->last->buffer, parity);
+ }
+
+ /* Prefer a buffer with both fields open */
+ for (i = 0; i < c->nbuffers; i++) {
+ if (c->buffers[i].lock[0]) continue;
+ if (c->buffers[i].lock[1]) continue;
+ alloc_buffer(c, &c->buffers[i]);
+ return pullup_lock_buffer(&c->buffers[i], parity);
+ }
+
+ if (parity == 2) return 0;
+
+ /* Search for any half-free buffer */
+ for (i = 0; i < c->nbuffers; i++) {
+ if (((parity+1) & 1) && c->buffers[i].lock[0]) continue;
+ if (((parity+1) & 2) && c->buffers[i].lock[1]) continue;
+ alloc_buffer(c, &c->buffers[i]);
+ return pullup_lock_buffer(&c->buffers[i], parity);
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+static void compute_metric(struct pullup_context *c,
+ struct pullup_field *fa, int pa,
+ struct pullup_field *fb, int pb,
+ int (*func)(unsigned char *, unsigned char *, int), int *dest)
+{
+ unsigned char *a, *b;
+ int x, y;
+ int mp = c->metric_plane;
+ int xstep = c->bpp[mp];
+ int ystep = c->stride[mp]<<3;
+ int s = c->stride[mp]<<1; /* field stride */
+ int w = c->metric_w*xstep;
+
+ if (!fa->buffer || !fb->buffer) return;
+
+ /* Shortcut for duplicate fields (e.g. from RFF flag) */
+ if (fa->buffer == fb->buffer && pa == pb) {
+ memset(dest, 0, c->metric_len * sizeof(int));
+ return;
+ }
+
+ a = fa->buffer->planes[mp] + pa * c->stride[mp] + c->metric_offset;
+ b = fb->buffer->planes[mp] + pb * c->stride[mp] + c->metric_offset;
+
+ for (y = c->metric_h; y; y--) {
+ for (x = 0; x < w; x += xstep) {
+ *dest++ = func(a + x, b + x, s);
+ }
+ a += ystep; b += ystep;
+ }
+}
+
+
+
+
+
+static void alloc_metrics(struct pullup_context *c, struct pullup_field *f)
+{
+ f->diffs = calloc(c->metric_len, sizeof(int));
+ f->comb = calloc(c->metric_len, sizeof(int));
+ f->var = calloc(c->metric_len, sizeof(int));
+ /* add more metrics here as needed */
+}
+
+static struct pullup_field *make_field_queue(struct pullup_context *c, int len)
+{
+ struct pullup_field *head, *f;
+ f = head = calloc(1, sizeof(struct pullup_field));
+ alloc_metrics(c, f);
+ for (; len > 0; len--) {
+ f->next = calloc(1, sizeof(struct pullup_field));
+ f->next->prev = f;
+ f = f->next;
+ alloc_metrics(c, f);
+ }
+ f->next = head;
+ head->prev = f;
+ return head;
+}
+
+static void check_field_queue(struct pullup_context *c)
+{
+ if (c->head->next == c->first) {
+ struct pullup_field *f = calloc(1, sizeof(struct pullup_field));
+ alloc_metrics(c, f);
+ f->prev = c->head;
+ f->next = c->first;
+ c->head->next = f;
+ c->first->prev = f;
+ }
+}
+
+void pullup_submit_field(struct pullup_context *c, struct pullup_buffer *b,
+ int parity, double pts)
+{
+ struct pullup_field *f;
+
+ /* Grow the circular list if needed */
+ check_field_queue(c);
+
+ /* Cannot have two fields of same parity in a row; drop the new one */
+ if (c->last && c->last->parity == parity) return;
+
+ f = c->head;
+ f->parity = parity;
+ f->buffer = pullup_lock_buffer(b, parity);
+ f->flags = 0;
+ f->breaks = 0;
+ f->affinity = 0;
+ f->pts = pts;
+
+ compute_metric(c, f, parity, f->prev->prev, parity, c->diff, f->diffs);
+ compute_metric(c, parity?f->prev:f, 0, parity?f:f->prev, 1, c->comb, f->comb);
+ compute_metric(c, f, parity, f, -1, c->var, f->var);
+
+ /* Advance the circular list */
+ if (!c->first) c->first = c->head;
+ c->last = c->head;
+ c->head = c->head->next;
+}
+
+
+
+
+#define F_HAVE_BREAKS 1
+#define F_HAVE_AFFINITY 2
+
+
+#define BREAK_LEFT 1
+#define BREAK_RIGHT 2
+
+
+
+
+static int queue_length(struct pullup_field *begin, struct pullup_field *end)
+{
+ int count = 1;
+ struct pullup_field *f;
+
+ if (!begin || !end) return 0;
+ for (f = begin; f != end; f = f->next) count++;
+ return count;
+}
+
+static int find_first_break(struct pullup_field *f, int max)
+{
+ int i;
+ for (i = 0; i < max; i++) {
+ if (f->breaks & BREAK_RIGHT || f->next->breaks & BREAK_LEFT)
+ return i+1;
+ f = f->next;
+ }
+ return 0;
+}
+
+static void compute_breaks(struct pullup_context *c, struct pullup_field *f0)
+{
+ int i;
+ struct pullup_field *f1 = f0->next;
+ struct pullup_field *f2 = f1->next;
+ struct pullup_field *f3 = f2->next;
+ int l, max_l=0, max_r=0;
+ //struct pullup_field *ff;
+ //for (i=0, ff=c->first; ff != f0; i++, ff=ff->next);
+
+ if (f0->flags & F_HAVE_BREAKS) return;
+ //printf("\n%d: ", i);
+ f0->flags |= F_HAVE_BREAKS;
+
+ /* Special case when fields are 100% identical */
+ if (f0->buffer == f2->buffer && f1->buffer != f3->buffer) {
+ f2->breaks |= BREAK_RIGHT;
+ return;
+ }
+ if (f0->buffer != f2->buffer && f1->buffer == f3->buffer) {
+ f1->breaks |= BREAK_LEFT;
+ return;
+ }
+
+ for (i = 0; i < c->metric_len; i++) {
+ l = f2->diffs[i] - f3->diffs[i];
+ if (l > max_l) max_l = l;
+ if (-l > max_r) max_r = -l;
+ }
+ /* Don't get tripped up when differences are mostly quant error */
+ //printf("%d %d\n", max_l, max_r);
+ if (max_l + max_r < 128) return;
+ if (max_l > 4*max_r) f1->breaks |= BREAK_LEFT;
+ if (max_r > 4*max_l) f2->breaks |= BREAK_RIGHT;
+}
+
+static void compute_affinity(struct pullup_context *c, struct pullup_field *f)
+{
+ int i;
+ int max_l=0, max_r=0, l;
+ if (f->flags & F_HAVE_AFFINITY) return;
+ f->flags |= F_HAVE_AFFINITY;
+ if (f->buffer == f->next->next->buffer) {
+ f->affinity = 1;
+ f->next->affinity = 0;
+ f->next->next->affinity = -1;
+ f->next->flags |= F_HAVE_AFFINITY;
+ f->next->next->flags |= F_HAVE_AFFINITY;
+ return;
+ }
+ if (1) {
+ for (i = 0; i < c->metric_len; i++) {
+ int lv = f->prev->var[i];
+ int rv = f->next->var[i];
+ int v = f->var[i];
+ int lc = f->comb[i] - (v+lv) + ABS(v-lv);
+ int rc = f->next->comb[i] - (v+rv) + ABS(v-rv);
+ lc = lc>0 ? lc : 0;
+ rc = rc>0 ? rc : 0;
+ l = lc - rc;
+ if (l > max_l) max_l = l;
+ if (-l > max_r) max_r = -l;
+ }
+ if (max_l + max_r < 64) return;
+ if (max_r > 6*max_l) f->affinity = -1;
+ else if (max_l > 6*max_r) f->affinity = 1;
+ } else {
+ for (i = 0; i < c->metric_len; i++) {
+ l = f->comb[i] - f->next->comb[i];
+ if (l > max_l) max_l = l;
+ if (-l > max_r) max_r = -l;
+ }
+ if (max_l + max_r < 64) return;
+ if (max_r > 2*max_l) f->affinity = -1;
+ else if (max_l > 2*max_r) f->affinity = 1;
+ }
+}
+
+static void foo(struct pullup_context *c)
+{
+ struct pullup_field *f = c->first;
+ int i, n = queue_length(f, c->last);
+ for (i = 0; i < n-1; i++) {
+ if (i < n-3) compute_breaks(c, f);
+ compute_affinity(c, f);
+ f = f->next;
+ }
+}
+
+static int decide_frame_length(struct pullup_context *c)
+{
+ struct pullup_field *f0 = c->first;
+ struct pullup_field *f1 = f0->next;
+ struct pullup_field *f2 = f1->next;
+ int l;
+
+ if (queue_length(c->first, c->last) < 4) return 0;
+ foo(c);
+
+ if (f0->affinity == -1) return 1;
+
+ l = find_first_break(f0, 3);
+ if (l == 1 && c->strict_breaks < 0) l = 0;
+
+ switch (l) {
+ case 1:
+ if (c->strict_breaks < 1 && f0->affinity == 1 && f1->affinity == -1)
+ return 2;
+ else return 1;
+ case 2:
+ /* FIXME: strictly speaking, f0->prev is no longer valid... :) */
+ if (c->strict_pairs
+ && (f0->prev->breaks & BREAK_RIGHT) && (f2->breaks & BREAK_LEFT)
+ && (f0->affinity != 1 || f1->affinity != -1) )
+ return 1;
+ if (f1->affinity == 1) return 1;
+ else return 2;
+ case 3:
+ if (f2->affinity == 1) return 2;
+ else return 3;
+ default:
+ /* 9 possibilities covered before switch */
+ if (f1->affinity == 1) return 1; /* covers 6 */
+ else if (f1->affinity == -1) return 2; /* covers 6 */
+ else if (f2->affinity == -1) { /* covers 2 */
+ if (f0->affinity == 1) return 3;
+ else return 1;
+ }
+ else return 2; /* the remaining 6 */
+ }
+}
+
+
+static void print_aff_and_breaks(struct pullup_context *c, struct pullup_field *f)
+{
+ int i;
+ struct pullup_field *f0 = f;
+ const char aff_l[] = "+..", aff_r[] = "..+";
+ printf("\naffinity: ");
+ for (i = 0; i < 4; i++) {
+ printf("%c%d%c", aff_l[1+f->affinity], i, aff_r[1+f->affinity]);
+ f = f->next;
+ }
+ f = f0;
+ printf("\nbreaks: ");
+ for (i=0; i<4; i++) {
+ printf("%c%d%c", f->breaks & BREAK_LEFT ? '|' : '.', i, f->breaks & BREAK_RIGHT ? '|' : '.');
+ f = f->next;
+ }
+ printf("\n");
+}
+
+
+
+
+
+struct pullup_frame *pullup_get_frame(struct pullup_context *c)
+{
+ int i;
+ struct pullup_frame *fr = c->frame;
+ int n = decide_frame_length(c);
+ int aff = c->first->next->affinity;
+
+ if (!n) return 0;
+ if (fr->lock) return 0;
+
+ if (c->verbose) {
+ print_aff_and_breaks(c, c->first);
+ printf("duration: %d \n", n);
+ }
+
+ fr->lock++;
+ fr->length = n;
+ fr->parity = c->first->parity;
+ fr->buffer = 0;
+ fr->pts = 0;
+ for (i = 0; i < n; i++) {
+ /* We cheat and steal the buffer without release+relock */
+ fr->ifields[i] = c->first->buffer;
+ c->first->buffer = 0;
+ if (c->first->pts == MP_NOPTS_VALUE || fr->pts == MP_NOPTS_VALUE)
+ fr->pts = MP_NOPTS_VALUE;
+ else
+ fr->pts += c->first->pts;
+ c->first = c->first->next;
+ }
+ if (fr->pts != MP_NOPTS_VALUE)
+ fr->pts /= n;
+
+ if (n == 1) {
+ fr->ofields[fr->parity] = fr->ifields[0];
+ fr->ofields[fr->parity^1] = 0;
+ } else if (n == 2) {
+ fr->ofields[fr->parity] = fr->ifields[0];
+ fr->ofields[fr->parity^1] = fr->ifields[1];
+ } else if (n == 3) {
+ if (aff == 0)
+ aff = (fr->ifields[0] == fr->ifields[1]) ? -1 : 1;
+ /* else if (c->verbose) printf("forced aff: %d \n", aff); */
+ fr->ofields[fr->parity] = fr->ifields[1+aff];
+ fr->ofields[fr->parity^1] = fr->ifields[1];
+ }
+ pullup_lock_buffer(fr->ofields[0], 0);
+ pullup_lock_buffer(fr->ofields[1], 1);
+
+ if (fr->ofields[0] == fr->ofields[1]) {
+ fr->buffer = fr->ofields[0];
+ pullup_lock_buffer(fr->buffer, 2);
+ return fr;
+ }
+ return fr;
+}
+
+static void copy_field(struct pullup_context *c, struct pullup_buffer *dest,
+ struct pullup_buffer *src, int parity)
+{
+ int i, j;
+ unsigned char *d, *s;
+ for (i = 0; i < c->nplanes; i++) {
+ s = src->planes[i] + parity*c->stride[i];
+ d = dest->planes[i] + parity*c->stride[i];
+ for (j = c->h[i]>>1; j; j--) {
+ memcpy(d, s, c->stride[i]);
+ s += c->stride[i]<<1;
+ d += c->stride[i]<<1;
+ }
+ }
+}
+
+void pullup_pack_frame(struct pullup_context *c, struct pullup_frame *fr)
+{
+ int i;
+ if (fr->buffer) return;
+ if (fr->length < 2) return; /* FIXME: deal with this */
+ for (i = 0; i < 2; i++)
+ {
+ if (fr->ofields[i]->lock[i^1]) continue;
+ fr->buffer = fr->ofields[i];
+ pullup_lock_buffer(fr->buffer, 2);
+ copy_field(c, fr->buffer, fr->ofields[i^1], i^1);
+ return;
+ }
+ fr->buffer = pullup_get_buffer(c, 2);
+ copy_field(c, fr->buffer, fr->ofields[0], 0);
+ copy_field(c, fr->buffer, fr->ofields[1], 1);
+}
+
+void pullup_release_frame(struct pullup_frame *fr)
+{
+ int i;
+ for (i = 0; i < fr->length; i++)
+ pullup_release_buffer(fr->ifields[i], fr->parity ^ (i&1));
+ pullup_release_buffer(fr->ofields[0], 0);
+ pullup_release_buffer(fr->ofields[1], 1);
+ if (fr->buffer) pullup_release_buffer(fr->buffer, 2);
+ fr->lock--;
+}
+
+
+
+
+
+
+struct pullup_context *pullup_alloc_context(void)
+{
+ struct pullup_context *c;
+
+ c = calloc(1, sizeof(struct pullup_context));
+
+ return c;
+}
+
+void pullup_preinit_context(struct pullup_context *c)
+{
+ c->bpp = calloc(c->nplanes, sizeof(int));
+ c->w = calloc(c->nplanes, sizeof(int));
+ c->h = calloc(c->nplanes, sizeof(int));
+ c->stride = calloc(c->nplanes, sizeof(int));
+ c->background = calloc(c->nplanes, sizeof(int));
+}
+
+void pullup_init_context(struct pullup_context *c)
+{
+ int mp = c->metric_plane;
+ if (c->nbuffers < 10) c->nbuffers = 10;
+ c->buffers = calloc(c->nbuffers, sizeof (struct pullup_buffer));
+
+ c->metric_w = (c->w[mp] - ((c->junk_left + c->junk_right) << 3)) >> 3;
+ c->metric_h = (c->h[mp] - ((c->junk_top + c->junk_bottom) << 1)) >> 3;
+ c->metric_offset = c->junk_left*c->bpp[mp] + (c->junk_top<<1)*c->stride[mp];
+ c->metric_len = c->metric_w * c->metric_h;
+
+ c->head = make_field_queue(c, 8);
+
+ c->frame = calloc(1, sizeof (struct pullup_frame));
+ c->frame->ifields = calloc(3, sizeof (struct pullup_buffer *));
+
+ switch(c->format) {
+ case PULLUP_FMT_Y:
+ c->diff = diff_y;
+ c->comb = licomb_y;
+ c->var = var_y;
+#if ARCH_X86
+#if HAVE_MMX
+ if (c->cpu & PULLUP_CPU_MMX) {
+ c->diff = diff_y_mmx;
+ c->comb = licomb_y_mmx;
+ c->var = var_y_mmx;
+ }
+#endif
+#endif
+ /* c->comb = qpcomb_y; */
+ break;
+#if 0
+ case PULLUP_FMT_YUY2:
+ c->diff = diff_yuy2;
+ break;
+ case PULLUP_FMT_RGB32:
+ c->diff = diff_rgb32;
+ break;
+#endif
+ }
+}
+
+void pullup_free_context(struct pullup_context *c)
+{
+ struct pullup_field *f;
+ free(c->buffers);
+ f = c->head;
+ do {
+ if (!f) break;
+ free(f->diffs);
+ free(f->comb);
+ f = f->next;
+ free(f->prev);
+ } while (f != c->head);
+ free(c->frame);
+ free(c);
+}
diff --git a/video/filter/pullup.h b/video/filter/pullup.h
new file mode 100644
index 0000000000..0948737919
--- /dev/null
+++ b/video/filter/pullup.h
@@ -0,0 +1,102 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_PULLUP_H
+#define MPLAYER_PULLUP_H
+
+#define PULLUP_CPU_MMX 1
+#define PULLUP_CPU_MMX2 2
+#define PULLUP_CPU_SSE 16
+#define PULLUP_CPU_SSE2 32
+
+#define PULLUP_FMT_Y 1
+#define PULLUP_FMT_YUY2 2
+#define PULLUP_FMT_UYVY 3
+#define PULLUP_FMT_RGB32 4
+
+struct pullup_buffer
+{
+ int lock[2];
+ unsigned char **planes;
+};
+
+struct pullup_field
+{
+ int parity;
+ double pts;
+ struct pullup_buffer *buffer;
+ unsigned int flags;
+ int breaks;
+ int affinity;
+ int *diffs;
+ int *comb;
+ int *var;
+ struct pullup_field *prev, *next;
+};
+
+struct pullup_frame
+{
+ int lock;
+ int length;
+ int parity;
+ double pts;
+ struct pullup_buffer **ifields, *ofields[2];
+ struct pullup_buffer *buffer;
+};
+
+struct pullup_context
+{
+ /* Public interface */
+ int format;
+ int nplanes;
+ int *bpp, *w, *h, *stride, *background;
+ unsigned int cpu;
+ int junk_left, junk_right, junk_top, junk_bottom;
+ int verbose;
+ int metric_plane;
+ int strict_breaks;
+ int strict_pairs;
+ /* Internal data */
+ struct pullup_field *first, *last, *head;
+ struct pullup_buffer *buffers;
+ int nbuffers;
+ int (*diff)(unsigned char *, unsigned char *, int);
+ int (*comb)(unsigned char *, unsigned char *, int);
+ int (*var)(unsigned char *, unsigned char *, int);
+ int metric_w, metric_h, metric_len, metric_offset;
+ struct pullup_frame *frame;
+};
+
+
+struct pullup_buffer *pullup_lock_buffer(struct pullup_buffer *b, int parity);
+void pullup_release_buffer(struct pullup_buffer *b, int parity);
+struct pullup_buffer *pullup_get_buffer(struct pullup_context *c, int parity);
+
+void pullup_submit_field(struct pullup_context *c, struct pullup_buffer *b,
+ int parity, double pts);
+
+struct pullup_frame *pullup_get_frame(struct pullup_context *c);
+void pullup_pack_frame(struct pullup_context *c, struct pullup_frame *fr);
+void pullup_release_frame(struct pullup_frame *fr);
+
+struct pullup_context *pullup_alloc_context(void);
+void pullup_preinit_context(struct pullup_context *c);
+void pullup_init_context(struct pullup_context *c);
+void pullup_free_context(struct pullup_context *c);
+
+#endif /* MPLAYER_PULLUP_H */
diff --git a/video/filter/vf.c b/video/filter/vf.c
new file mode 100644
index 0000000000..10b9fa546f
--- /dev/null
+++ b/video/filter/vf.c
@@ -0,0 +1,830 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <libavutil/common.h>
+#include <libavutil/mem.h>
+
+#include "config.h"
+
+#include "mp_msg.h"
+#include "m_option.h"
+#include "m_struct.h"
+
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+extern const vf_info_t vf_info_vo;
+extern const vf_info_t vf_info_crop;
+extern const vf_info_t vf_info_expand;
+extern const vf_info_t vf_info_pp;
+extern const vf_info_t vf_info_scale;
+extern const vf_info_t vf_info_format;
+extern const vf_info_t vf_info_noformat;
+extern const vf_info_t vf_info_flip;
+extern const vf_info_t vf_info_rotate;
+extern const vf_info_t vf_info_mirror;
+extern const vf_info_t vf_info_noise;
+extern const vf_info_t vf_info_eq2;
+extern const vf_info_t vf_info_gradfun;
+extern const vf_info_t vf_info_unsharp;
+extern const vf_info_t vf_info_swapuv;
+extern const vf_info_t vf_info_down3dright;
+extern const vf_info_t vf_info_hqdn3d;
+extern const vf_info_t vf_info_ilpack;
+extern const vf_info_t vf_info_dsize;
+extern const vf_info_t vf_info_softpulldown;
+extern const vf_info_t vf_info_pullup;
+extern const vf_info_t vf_info_delogo;
+extern const vf_info_t vf_info_phase;
+extern const vf_info_t vf_info_divtc;
+extern const vf_info_t vf_info_softskip;
+extern const vf_info_t vf_info_screenshot;
+extern const vf_info_t vf_info_screenshot_force;
+extern const vf_info_t vf_info_sub;
+extern const vf_info_t vf_info_yadif;
+extern const vf_info_t vf_info_stereo3d;
+extern const vf_info_t vf_info_dlopen;
+
+// list of available filters:
+static const vf_info_t *const filter_list[] = {
+ &vf_info_crop,
+ &vf_info_expand,
+ &vf_info_scale,
+ &vf_info_vo,
+ &vf_info_format,
+ &vf_info_noformat,
+ &vf_info_flip,
+ &vf_info_rotate,
+ &vf_info_mirror,
+
+#ifdef CONFIG_LIBPOSTPROC
+ &vf_info_pp,
+#endif
+
+ &vf_info_screenshot,
+ &vf_info_screenshot_force,
+
+ &vf_info_noise,
+ &vf_info_eq2,
+ &vf_info_gradfun,
+ &vf_info_unsharp,
+ &vf_info_swapuv,
+ &vf_info_down3dright,
+ &vf_info_hqdn3d,
+ &vf_info_ilpack,
+ &vf_info_dsize,
+ &vf_info_softpulldown,
+ &vf_info_pullup,
+ &vf_info_delogo,
+ &vf_info_phase,
+ &vf_info_divtc,
+ &vf_info_sub,
+ &vf_info_yadif,
+ &vf_info_stereo3d,
+ &vf_info_dlopen,
+ NULL
+};
+
+// For the vf option
+const m_obj_list_t vf_obj_list = {
+ (void **)filter_list,
+ M_ST_OFF(vf_info_t, name),
+ M_ST_OFF(vf_info_t, info),
+ M_ST_OFF(vf_info_t, opts)
+};
+
+//============================================================================
+// mpi stuff:
+
+void vf_mpi_clear(mp_image_t *mpi, int x0, int y0, int w, int h)
+{
+ int y;
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ y0 &= ~1;
+ h += h & 1;
+ if (x0 == 0 && w == mpi->width) {
+ // full width clear:
+ memset(mpi->planes[0] + mpi->stride[0] * y0, 0, mpi->stride[0] * h);
+ memset(mpi->planes[1] + mpi->stride[1] *(y0 >> mpi->chroma_y_shift),
+ 128, mpi->stride[1] * (h >> mpi->chroma_y_shift));
+ memset(mpi->planes[2] + mpi->stride[2] *(y0 >> mpi->chroma_y_shift),
+ 128, mpi->stride[2] * (h >> mpi->chroma_y_shift));
+ } else
+ for (y = y0; y < y0 + h; y += 2) {
+ memset(mpi->planes[0] + x0 + mpi->stride[0] * y, 0, w);
+ memset(mpi->planes[0] + x0 + mpi->stride[0] * (y + 1), 0, w);
+ memset(mpi->planes[1] + (x0 >> mpi->chroma_x_shift) +
+ mpi->stride[1] * (y >> mpi->chroma_y_shift),
+ 128, (w >> mpi->chroma_x_shift));
+ memset(mpi->planes[2] + (x0 >> mpi->chroma_x_shift) +
+ mpi->stride[2] * (y >> mpi->chroma_y_shift),
+ 128, (w >> mpi->chroma_x_shift));
+ }
+ return;
+ }
+ // packed:
+ for (y = y0; y < y0 + h; y++) {
+ unsigned char *dst = mpi->planes[0] + mpi->stride[0] * y +
+ (mpi->bpp >> 3) * x0;
+ if (mpi->flags & MP_IMGFLAG_YUV) {
+ unsigned int *p = (unsigned int *) dst;
+ int size = (mpi->bpp >> 3) * w / 4;
+ int i;
+#ifdef BIG_ENDIAN
+#define CLEAR_PACKEDYUV_PATTERN 0x00800080
+#define CLEAR_PACKEDYUV_PATTERN_SWAPPED 0x80008000
+#else
+#define CLEAR_PACKEDYUV_PATTERN 0x80008000
+#define CLEAR_PACKEDYUV_PATTERN_SWAPPED 0x00800080
+#endif
+ if (mpi->flags & MP_IMGFLAG_SWAPPED) {
+ for (i = 0; i < size - 3; i += 4)
+ p[i] = p[i + 1] = p[i + 2] = p[i + 3] = CLEAR_PACKEDYUV_PATTERN_SWAPPED;
+ for (; i < size; i++)
+ p[i] = CLEAR_PACKEDYUV_PATTERN_SWAPPED;
+ } else {
+ for (i = 0; i < size - 3; i += 4)
+ p[i] = p[i + 1] = p[i + 2] = p[i + 3] = CLEAR_PACKEDYUV_PATTERN;
+ for (; i < size; i++)
+ p[i] = CLEAR_PACKEDYUV_PATTERN;
+ }
+ } else
+ memset(dst, 0, (mpi->bpp >> 3) * w);
+ }
+}
+
+mp_image_t *vf_get_image(vf_instance_t *vf, unsigned int outfmt,
+ int mp_imgtype, int mp_imgflag, int w, int h)
+{
+ mp_image_t *mpi = NULL;
+ int w2;
+ int number = mp_imgtype >> 16;
+
+ assert(w == -1 || w >= vf->w);
+ assert(h == -1 || h >= vf->h);
+ assert(vf->w > 0);
+ assert(vf->h > 0);
+
+ if (w == -1)
+ w = vf->w;
+ if (h == -1)
+ h = vf->h;
+
+ w2 = (mp_imgflag & MP_IMGFLAG_ACCEPT_ALIGNED_STRIDE) ? FFALIGN(w, 32) : w;
+
+ if (vf->put_image == vf_next_put_image) {
+ // passthru mode, if the filter uses the fallback/default put_image()
+ mpi = vf_get_image(vf->next,outfmt,mp_imgtype,mp_imgflag,w,h);
+ mpi->usage_count++;
+ return mpi;
+ }
+
+ // Note: we should call libvo first to check if it supports direct rendering
+ // and if not, then fallback to software buffers:
+ switch (mp_imgtype & 0xff) {
+ case MP_IMGTYPE_EXPORT:
+ if (!vf->imgctx.export_images[0])
+ vf->imgctx.export_images[0] = new_mp_image(w2, h);
+ mpi = vf->imgctx.export_images[0];
+ break;
+ case MP_IMGTYPE_STATIC:
+ if (!vf->imgctx.static_images[0])
+ vf->imgctx.static_images[0] = new_mp_image(w2, h);
+ mpi = vf->imgctx.static_images[0];
+ break;
+ case MP_IMGTYPE_TEMP:
+ if (!vf->imgctx.temp_images[0])
+ vf->imgctx.temp_images[0] = new_mp_image(w2, h);
+ mpi = vf->imgctx.temp_images[0];
+ break;
+ case MP_IMGTYPE_IPB:
+ if (!(mp_imgflag & MP_IMGFLAG_READABLE)) { // B frame:
+ if (!vf->imgctx.temp_images[0])
+ vf->imgctx.temp_images[0] = new_mp_image(w2, h);
+ mpi = vf->imgctx.temp_images[0];
+ break;
+ }
+ case MP_IMGTYPE_IP:
+ if (!vf->imgctx.static_images[vf->imgctx.static_idx])
+ vf->imgctx.static_images[vf->imgctx.static_idx] = new_mp_image(w2, h);
+ mpi = vf->imgctx.static_images[vf->imgctx.static_idx];
+ vf->imgctx.static_idx ^= 1;
+ break;
+ case MP_IMGTYPE_NUMBERED:
+ if (number == -1) {
+ int i;
+ for (i = 0; i < NUM_NUMBERED_MPI; i++)
+ if (!vf->imgctx.numbered_images[i] ||
+ !vf->imgctx.numbered_images[i]->usage_count)
+ break;
+ number = i;
+ }
+ if (number < 0 || number >= NUM_NUMBERED_MPI)
+ return NULL;
+ if (!vf->imgctx.numbered_images[number])
+ vf->imgctx.numbered_images[number] = new_mp_image(w2, h);
+ mpi = vf->imgctx.numbered_images[number];
+ mpi->number = number;
+ break;
+ }
+ if (mpi) {
+ int missing_palette = !(mpi->flags & MP_IMGFLAG_RGB_PALETTE) &&
+ (mp_imgflag & MP_IMGFLAG_RGB_PALETTE);
+ mpi->type = mp_imgtype;
+ mpi->w = vf->w;
+ mpi->h = vf->h;
+ // keep buffer allocation status & color flags only:
+ mpi->flags &= MP_IMGFLAG_ALLOCATED | MP_IMGFLAG_TYPE_DISPLAYED |
+ MP_IMGFLAGMASK_COLORS;
+ // accept restrictions, draw_slice and palette flags only:
+ mpi->flags |= mp_imgflag & (MP_IMGFLAGMASK_RESTRICTIONS |
+ MP_IMGFLAG_DRAW_CALLBACK | MP_IMGFLAG_RGB_PALETTE);
+ if (!vf->draw_slice)
+ mpi->flags &= ~MP_IMGFLAG_DRAW_CALLBACK;
+ if (mpi->width != w2 || mpi->height != h || missing_palette) {
+ if (mpi->flags & MP_IMGFLAG_ALLOCATED) {
+ if (mpi->width < w2 || mpi->height < h || missing_palette) {
+ // need to re-allocate buffer memory:
+ av_free(mpi->planes[0]);
+ if (mpi->flags & MP_IMGFLAG_RGB_PALETTE)
+ av_free(mpi->planes[1]);
+ for (int n = 0; n < MP_MAX_PLANES; n++)
+ mpi->planes[n] = NULL;
+ mpi->flags &= ~MP_IMGFLAG_ALLOCATED;
+ mp_msg(MSGT_VFILTER, MSGL_V,
+ "vf.c: have to REALLOCATE buffer memory :(\n");
+ }
+ }
+ mpi->width = w2;
+ mpi->chroma_width = (w2 + (1 << mpi->chroma_x_shift) - 1) >>
+ mpi->chroma_x_shift;
+ mpi->height = h;
+ mpi->chroma_height = (h + (1 << mpi->chroma_y_shift) - 1) >>
+ mpi->chroma_y_shift;
+ }
+ if (!mpi->bpp)
+ mp_image_setfmt(mpi, outfmt);
+ if (!(mpi->flags & MP_IMGFLAG_ALLOCATED) &&
+ mpi->type > MP_IMGTYPE_EXPORT) {
+ // check libvo first!
+ if (vf->get_image)
+ vf->get_image(vf, mpi);
+
+ if (!(mpi->flags & MP_IMGFLAG_DIRECT)) {
+ // non-direct and not yet allocated image. allocate it!
+ if (!mpi->bpp) { // no way we can allocate this
+ mp_msg(MSGT_DECVIDEO, MSGL_FATAL,
+ "vf_get_image: Tried to allocate a format that "
+ "can not be allocated!\n");
+ return NULL;
+ }
+
+ // check if codec prefer aligned stride:
+ if (mp_imgflag & MP_IMGFLAG_PREFER_ALIGNED_STRIDE) {
+ int align = (mpi->flags & MP_IMGFLAG_PLANAR &&
+ mpi->flags & MP_IMGFLAG_YUV) ?
+ (16 << mpi->chroma_x_shift) - 1 : 32; // OK?
+ w2 = FFALIGN(w, align);
+ if (mpi->width != w2) {
+ // we have to change width... check if we CAN co it:
+ int flags = vf->query_format(vf, outfmt);
+ // should not fail
+ if (!(flags & (VFCAP_CSP_SUPPORTED |
+ VFCAP_CSP_SUPPORTED_BY_HW)))
+ mp_msg(MSGT_DECVIDEO, MSGL_WARN,
+ "??? vf_get_image{vf->query_format(outfmt)} "
+ "failed!\n");
+ if (flags & VFCAP_ACCEPT_STRIDE) {
+ mpi->width = w2;
+ mpi->chroma_width =
+ (w2 + (1 << mpi->chroma_x_shift) - 1) >>
+ mpi->chroma_x_shift;
+ }
+ }
+ }
+
+ mp_image_alloc_planes(mpi);
+ vf_mpi_clear(mpi, 0, 0, mpi->width, mpi->height);
+ }
+ }
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ if (vf->start_slice)
+ vf->start_slice(vf, mpi);
+ if (!(mpi->flags & MP_IMGFLAG_TYPE_DISPLAYED)) {
+ mp_msg(MSGT_DECVIDEO, MSGL_V,
+ "*** [%s] %s%s mp_image_t, %dx%dx%dbpp %s %s, %d bytes\n",
+ vf->info->name,
+ (mpi->type == MP_IMGTYPE_EXPORT) ? "Exporting" :
+ ((mpi->flags & MP_IMGFLAG_DIRECT) ?
+ "Direct Rendering" : "Allocating"),
+ (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) ? " (slices)" : "",
+ mpi->width, mpi->height, mpi->bpp,
+ (mpi->flags & MP_IMGFLAG_YUV) ? "YUV" :
+ ((mpi->flags & MP_IMGFLAG_SWAPPED) ? "BGR" : "RGB"),
+ (mpi->flags & MP_IMGFLAG_PLANAR) ? "planar" : "packed",
+ mpi->bpp * mpi->width * mpi->height / 8);
+ mp_msg(MSGT_DECVIDEO, MSGL_DBG2, "(imgfmt: %x, planes: %p,%p,%p "
+ "strides: %d,%d,%d, chroma: %dx%d, shift: h:%d,v:%d)\n",
+ mpi->imgfmt, mpi->planes[0], mpi->planes[1], mpi->planes[2],
+ mpi->stride[0], mpi->stride[1], mpi->stride[2],
+ mpi->chroma_width, mpi->chroma_height,
+ mpi->chroma_x_shift, mpi->chroma_y_shift);
+ mpi->flags |= MP_IMGFLAG_TYPE_DISPLAYED;
+ }
+ mpi->qscale = NULL;
+ mpi->usage_count++;
+ }
+ return mpi;
+}
+
+//============================================================================
+
+static int vf_default_query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ return vf_next_query_format(vf, fmt);
+}
+
+struct vf_instance *vf_open_plugin_noerr(struct MPOpts *opts,
+ const vf_info_t *const *filter_list,
+ vf_instance_t *next, const char *name,
+ char **args, int *retcode)
+{
+ vf_instance_t *vf;
+ int i;
+ for (i = 0;; i++) {
+ if (!filter_list[i]) {
+ mp_tmsg(MSGT_VFILTER, MSGL_ERR,
+ "Couldn't find video filter '%s'.\n", name);
+ return NULL; // no such filter!
+ }
+ if (!strcmp(filter_list[i]->name, name))
+ break;
+ }
+ vf = calloc(1, sizeof *vf);
+ vf->opts = opts;
+ vf->info = filter_list[i];
+ vf->next = next;
+ vf->config = vf_next_config;
+ vf->control = vf_next_control;
+ vf->query_format = vf_default_query_format;
+ vf->put_image = vf_next_put_image;
+ vf->default_caps = VFCAP_ACCEPT_STRIDE;
+ vf->default_reqs = 0;
+ if (vf->info->opts) { // vf_vo get some special argument
+ const m_struct_t *st = vf->info->opts;
+ void *vf_priv = m_struct_alloc(st);
+ int n;
+ for (n = 0; args && args[2 * n]; n++)
+ m_struct_set(st, vf_priv, args[2 * n], bstr0(args[2 * n + 1]));
+ vf->priv = vf_priv;
+ args = NULL;
+ } else // Otherwise we should have the '_oldargs_'
+ if (args && !strcmp(args[0], "_oldargs_"))
+ args = (char **)args[1];
+ else
+ args = NULL;
+ *retcode = vf->info->vf_open(vf, (char *)args);
+ if (*retcode > 0)
+ return vf;
+ free(vf);
+ return NULL;
+}
+
+struct vf_instance *vf_open_plugin(struct MPOpts *opts,
+ const vf_info_t *const *filter_list,
+ vf_instance_t *next, const char *name,
+ char **args)
+{
+ struct vf_instance *vf = vf_open_plugin_noerr(opts, filter_list, next,
+ name, args, &(int){0});
+ if (!vf)
+ mp_tmsg(MSGT_VFILTER, MSGL_ERR, "Couldn't open video filter '%s'.\n",
+ name);
+ return vf;
+}
+
+vf_instance_t *vf_open_filter(struct MPOpts *opts, vf_instance_t *next,
+ const char *name, char **args)
+{
+ if (args && strcmp(args[0], "_oldargs_")) {
+ int i, l = 0;
+ for (i = 0; args && args[2 * i]; i++)
+ l += 1 + strlen(args[2 * i]) + 1 + strlen(args[2 * i + 1]);
+ l += strlen(name);
+ {
+ char str[l + 1];
+ char *p = str;
+ p += sprintf(str, "%s", name);
+ for (i = 0; args && args[2 * i]; i++)
+ p += sprintf(p, " %s=%s", args[2 * i], args[2 * i + 1]);
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "%s[%s]\n",
+ mp_gtext("Opening video filter: "), str);
+ }
+ } else if (strcmp(name, "vo")) {
+ if (args && strcmp(args[0], "_oldargs_") == 0)
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "%s[%s=%s]\n",
+ mp_gtext("Opening video filter: "), name, args[1]);
+ else
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "%s[%s]\n",
+ mp_gtext("Opening video filter: "), name);
+ }
+ return vf_open_plugin(opts, filter_list, next, name, args);
+}
+
+/**
+ * \brief adds a filter before the last one (which should be the vo filter).
+ * \param vf start of the filter chain.
+ * \param name name of the filter to add.
+ * \param args argument list for the filter.
+ * \return pointer to the filter instance that was created.
+ */
+vf_instance_t *vf_add_before_vo(vf_instance_t **vf, char *name, char **args)
+{
+ struct MPOpts *opts = (*vf)->opts;
+ vf_instance_t *vo, *prev = NULL, *new;
+ // Find the last filter (should be vf_vo)
+ for (vo = *vf; vo->next; vo = vo->next)
+ prev = vo;
+ new = vf_open_filter(opts, vo, name, args);
+ if (prev)
+ prev->next = new;
+ else
+ *vf = new;
+ return new;
+}
+
+//============================================================================
+
+unsigned int vf_match_csp(vf_instance_t **vfp, const unsigned int *list,
+ unsigned int preferred)
+{
+ vf_instance_t *vf = *vfp;
+ struct MPOpts *opts = vf->opts;
+ const unsigned int *p;
+ unsigned int best = 0;
+ int ret;
+ if ((p = list))
+ while (*p) {
+ ret = vf->query_format(vf, *p);
+ mp_msg(MSGT_VFILTER, MSGL_V, "[%s] query(%s) -> %x\n",
+ vf->info->name, vo_format_name(*p), ret);
+ if (ret & VFCAP_CSP_SUPPORTED_BY_HW) {
+ best = *p;
+ break;
+ }
+ if (ret & VFCAP_CSP_SUPPORTED && !best)
+ best = *p;
+ ++p;
+ }
+ if (best)
+ return best; // bingo, they have common csp!
+ // ok, then try with scale:
+ if (vf->info == &vf_info_scale)
+ return 0; // avoid infinite recursion!
+ vf = vf_open_filter(opts, vf, "scale", NULL);
+ if (!vf)
+ return 0; // failed to init "scale"
+ // try the preferred csp first:
+ if (preferred && vf->query_format(vf, preferred))
+ best = preferred;
+ else
+ // try the list again, now with "scaler" :
+ if ((p = list))
+ while (*p) {
+ ret = vf->query_format(vf, *p);
+ mp_msg(MSGT_VFILTER, MSGL_V, "[%s] query(%s) -> %x\n",
+ vf->info->name, vo_format_name(*p), ret);
+ if (ret & VFCAP_CSP_SUPPORTED_BY_HW) {
+ best = *p;
+ break;
+ }
+ if (ret & VFCAP_CSP_SUPPORTED && !best)
+ best = *p;
+ ++p;
+ }
+ if (best)
+ *vfp = vf; // else uninit vf !FIXME!
+ return best;
+}
+
+void vf_clone_mpi_attributes(mp_image_t *dst, mp_image_t *src)
+{
+ dst->pict_type = src->pict_type;
+ dst->fields = src->fields;
+ dst->qscale_type = src->qscale_type;
+ if (dst->width == src->width && dst->height == src->height) {
+ dst->qstride = src->qstride;
+ dst->qscale = src->qscale;
+ dst->display_w = src->display_w;
+ dst->display_h = src->display_h;
+ }
+ if ((dst->flags & MP_IMGFLAG_YUV) == (src->flags & MP_IMGFLAG_YUV)) {
+ dst->colorspace = src->colorspace;
+ dst->levels = src->levels;
+ }
+}
+
+void vf_queue_frame(vf_instance_t *vf, int (*func)(vf_instance_t *))
+{
+ vf->continue_buffered_image = func;
+}
+
+// Output the next buffered image (if any) from the filter chain.
+// The queue could be kept as a simple stack/list instead avoiding the
+// looping here, but there's currently no good context variable where
+// that could be stored so this was easier to implement.
+
+int vf_output_queued_frame(vf_instance_t *vf)
+{
+ while (1) {
+ int ret;
+ vf_instance_t *current;
+ vf_instance_t *last = NULL;
+ int (*tmp)(vf_instance_t *);
+ for (current = vf; current; current = current->next)
+ if (current->continue_buffered_image)
+ last = current;
+ if (!last)
+ return 0;
+ tmp = last->continue_buffered_image;
+ last->continue_buffered_image = NULL;
+ ret = tmp(last);
+ if (ret)
+ return ret;
+ }
+}
+
+
+/**
+ * \brief Video config() function wrapper
+ *
+ * Blocks config() calls with different size or format for filters
+ * with VFCAP_CONSTANT
+ *
+ * First call is redirected to vf->config.
+ *
+ * In following calls, it verifies that the configuration parameters
+ * are unchanged, and returns either success or error.
+ *
+ */
+int vf_config_wrapper(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ vf->fmt.have_configured = 1;
+ vf->fmt.orig_height = height;
+ vf->fmt.orig_width = width;
+ vf->fmt.orig_fmt = outfmt;
+ int r = vf->config(vf, width, height, d_width, d_height, flags, outfmt);
+ if (!r)
+ vf->fmt.have_configured = 0;
+ return r;
+}
+
+int vf_next_config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int voflags, unsigned int outfmt)
+{
+ struct MPOpts *opts = vf->opts;
+ int miss;
+ int flags = vf->next->query_format(vf->next, outfmt);
+ if (!flags) {
+ // hmm. colorspace mismatch!!!
+ // let's insert the 'scale' filter, it does the job for us:
+ vf_instance_t *vf2;
+ if (vf->next->info == &vf_info_scale)
+ return 0; // scale->scale
+ vf2 = vf_open_filter(opts, vf->next, "scale", NULL);
+ if (!vf2)
+ return 0; // shouldn't happen!
+ vf->next = vf2;
+ flags = vf->next->query_format(vf->next, outfmt);
+ if (!flags) {
+ mp_tmsg(MSGT_VFILTER, MSGL_ERR, "Cannot find matching colorspace, "
+ "even by inserting 'scale' :(\n");
+ return 0; // FAIL
+ }
+ }
+ mp_msg(MSGT_VFILTER, MSGL_V, "REQ: flags=0x%X req=0x%X \n",
+ flags, vf->default_reqs);
+ miss = vf->default_reqs - (flags & vf->default_reqs);
+ if (miss & VFCAP_ACCEPT_STRIDE) {
+ // vf requires stride support but vf->next doesn't support it!
+ // let's insert the 'expand' filter, it does the job for us:
+ vf_instance_t *vf2 = vf_open_filter(opts, vf->next, "expand", NULL);
+ if (!vf2)
+ return 0; // shouldn't happen!
+ vf->next = vf2;
+ }
+ vf->next->w = width;
+ vf->next->h = height;
+ return vf_config_wrapper(vf->next, width, height, d_width, d_height,
+ voflags, outfmt);
+}
+
+int vf_next_control(struct vf_instance *vf, int request, void *data)
+{
+ return vf->next->control(vf->next, request, data);
+}
+
+int vf_next_query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ int flags = vf->next->query_format(vf->next, fmt);
+ if (flags)
+ flags |= vf->default_caps;
+ return flags;
+}
+
+int vf_next_put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ return vf->next->put_image(vf->next, mpi, pts);
+}
+
+void vf_next_draw_slice(struct vf_instance *vf, unsigned char **src,
+ int *stride, int w, int h, int x, int y)
+{
+ if (vf->next->draw_slice) {
+ vf->next->draw_slice(vf->next, src, stride, w, h, x, y);
+ return;
+ }
+ if (!vf->dmpi) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "draw_slice: dmpi not stored by vf_%s\n", vf->info->name);
+ return;
+ }
+ if (!(vf->dmpi->flags & MP_IMGFLAG_PLANAR)) {
+ memcpy_pic(vf->dmpi->planes[0] + y * vf->dmpi->stride[0] +
+ vf->dmpi->bpp / 8 * x,
+ src[0], vf->dmpi->bpp / 8 * w, h, vf->dmpi->stride[0],
+ stride[0]);
+ return;
+ }
+ memcpy_pic(vf->dmpi->planes[0] + y * vf->dmpi->stride[0] + x, src[0],
+ w, h, vf->dmpi->stride[0], stride[0]);
+ memcpy_pic(vf->dmpi->planes[1]
+ + (y >> vf->dmpi->chroma_y_shift) * vf->dmpi->stride[1]
+ + (x >> vf->dmpi->chroma_x_shift),
+ src[1], w >> vf->dmpi->chroma_x_shift,
+ h >> vf->dmpi->chroma_y_shift, vf->dmpi->stride[1], stride[1]);
+ memcpy_pic(vf->dmpi->planes[2]
+ + (y >> vf->dmpi->chroma_y_shift) * vf->dmpi->stride[2]
+ + (x >> vf->dmpi->chroma_x_shift),
+ src[2], w >> vf->dmpi->chroma_x_shift,
+ h >> vf->dmpi->chroma_y_shift, vf->dmpi->stride[2], stride[2]);
+}
+
+//============================================================================
+
+vf_instance_t *append_filters(vf_instance_t *last,
+ struct m_obj_settings *vf_settings)
+{
+ struct MPOpts *opts = last->opts;
+ vf_instance_t *vf;
+ int i;
+
+ if (vf_settings) {
+ // We want to add them in the 'right order'
+ for (i = 0; vf_settings[i].name; i++)
+ /* NOP */;
+ for (i--; i >= 0; i--) {
+ //printf("Open filter %s\n",vf_settings[i].name);
+ vf = vf_open_filter(opts, last, vf_settings[i].name,
+ vf_settings[i].attribs);
+ if (vf)
+ last = vf;
+ }
+ }
+ return last;
+}
+
+//============================================================================
+
+void vf_uninit_filter(vf_instance_t *vf)
+{
+ if (vf->uninit)
+ vf->uninit(vf);
+ free_mp_image(vf->imgctx.static_images[0]);
+ free_mp_image(vf->imgctx.static_images[1]);
+ free_mp_image(vf->imgctx.temp_images[0]);
+ free_mp_image(vf->imgctx.export_images[0]);
+ for (int i = 0; i < NUM_NUMBERED_MPI; i++)
+ free_mp_image(vf->imgctx.numbered_images[i]);
+ free(vf);
+}
+
+void vf_uninit_filter_chain(vf_instance_t *vf)
+{
+ while (vf) {
+ vf_instance_t *next = vf->next;
+ vf_uninit_filter(vf);
+ vf = next;
+ }
+}
+
+void vf_detc_init_pts_buf(struct vf_detc_pts_buf *p)
+{
+ p->inpts_prev = MP_NOPTS_VALUE;
+ p->outpts_prev = MP_NOPTS_VALUE;
+ p->lastdelta = 0;
+}
+
+static double vf_detc_adjust_pts_internal(struct vf_detc_pts_buf *p,
+ double pts, bool reset_pattern,
+ bool skip_frame, double delta,
+ double boundfactor_minus,
+ double increasefactor,
+ double boundfactor_plus)
+{
+ double newpts;
+
+ if (pts == MP_NOPTS_VALUE)
+ return pts;
+
+ if (delta <= 0) {
+ if (p->inpts_prev == MP_NOPTS_VALUE)
+ delta = 0;
+ else if (pts == p->inpts_prev)
+ delta = p->lastdelta;
+ else
+ delta = pts - p->inpts_prev;
+ }
+ p->inpts_prev = pts;
+ p->lastdelta = delta;
+
+ if (skip_frame)
+ return MP_NOPTS_VALUE;
+
+ /* detect bogus deltas and then passthru pts (possibly caused by seeking,
+ * or bad input) */
+ if (p->outpts_prev == MP_NOPTS_VALUE || reset_pattern || delta <= 0.0 ||
+ delta >= 0.5)
+ newpts = pts;
+ else {
+ // turn 5 frames into 4
+ newpts = p->outpts_prev + delta * increasefactor;
+
+ // bound to input pts in a sensible way; these numbers come because we
+ // map frames the following way when ivtc'ing:
+ // 0/30 -> 0/24 diff=0
+ // 1/30 -> 1/24 diff=1/120
+ // 2/30 -> -
+ // 3/30 -> 2/24 diff=-1/60
+ // 4/30 -> 3/24 diff=-1/120
+ if (newpts < pts - delta * boundfactor_minus)
+ newpts = pts - delta * boundfactor_minus;
+ if (newpts > pts + delta * boundfactor_plus)
+ newpts = pts + delta * boundfactor_plus;
+ if (newpts < p->outpts_prev)
+ newpts = p->outpts_prev; // damage control
+ }
+ p->outpts_prev = newpts;
+
+ return newpts;
+}
+
+double vf_detc_adjust_pts(struct vf_detc_pts_buf *p, double pts,
+ bool reset_pattern, bool skip_frame)
+{
+ // standard telecine (see above)
+ return vf_detc_adjust_pts_internal(p, pts, reset_pattern, skip_frame,
+ 0, 0.5, 1.25, 0.25);
+}
+
+double vf_softpulldown_adjust_pts(struct vf_detc_pts_buf *p, double pts,
+ bool reset_pattern, bool skip_frame,
+ int last_frame_duration)
+{
+ // for the softpulldown filter we get:
+ // 0/60 -> 0/30
+ // 2/60 -> 1/30
+ // 5/60 -> 2/30
+ // 7/60 -> 3/30, 4/30
+ return vf_detc_adjust_pts_internal(p, pts, reset_pattern, skip_frame,
+ 0, 1.0 / last_frame_duration,
+ 2.0 / last_frame_duration,
+ 1.0 / last_frame_duration);
+}
diff --git a/video/filter/vf.h b/video/filter/vf.h
new file mode 100644
index 0000000000..4c50f0e9cc
--- /dev/null
+++ b/video/filter/vf.h
@@ -0,0 +1,189 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_VF_H
+#define MPLAYER_VF_H
+
+#include "mp_image.h"
+#include "mpcommon.h"
+#include "stdbool.h"
+
+#include "mpc_info.h"
+#include "vfcap.h"
+
+struct MPOpts;
+struct vf_instance;
+struct vf_priv_s;
+
+typedef struct vf_info {
+ const char *info;
+ const char *name;
+ const char *author;
+ const char *comment;
+ int (*vf_open)(struct vf_instance *vf, char *args);
+ // Ptr to a struct dscribing the options
+ const void *opts;
+} vf_info_t;
+
+#define NUM_NUMBERED_MPI 50
+
+struct vf_image_context {
+ mp_image_t *static_images[2];
+ mp_image_t *temp_images[1];
+ mp_image_t *export_images[1];
+ mp_image_t *numbered_images[NUM_NUMBERED_MPI];
+ int static_idx;
+};
+
+struct vf_format_context {
+ int have_configured;
+ int orig_width, orig_height, orig_fmt;
+};
+
+typedef struct vf_instance {
+ const vf_info_t *info;
+ // funcs:
+ int (*config)(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt);
+ int (*control)(struct vf_instance *vf, int request, void *data);
+ int (*query_format)(struct vf_instance *vf, unsigned int fmt);
+ void (*get_image)(struct vf_instance *vf, mp_image_t *mpi);
+ int (*put_image)(struct vf_instance *vf, mp_image_t *mpi, double pts);
+ void (*start_slice)(struct vf_instance *vf, mp_image_t *mpi);
+ void (*draw_slice)(struct vf_instance *vf, unsigned char **src,
+ int *stride, int w, int h, int x, int y);
+ void (*uninit)(struct vf_instance *vf);
+
+ int (*continue_buffered_image)(struct vf_instance *vf);
+ // caps:
+ unsigned int default_caps; // used by default query_format()
+ unsigned int default_reqs; // used by default config()
+ // data:
+ int w, h;
+ struct vf_image_context imgctx;
+ struct vf_format_context fmt;
+ struct vf_instance *next;
+ mp_image_t *dmpi;
+ struct vf_priv_s *priv;
+ struct MPOpts *opts;
+} vf_instance_t;
+
+typedef struct vf_seteq {
+ const char *item;
+ int value;
+} vf_equalizer_t;
+
+struct vf_ctrl_screenshot {
+ // When the screenshot is complete, pass it to this callback.
+ void (*image_callback)(void *, mp_image_t *);
+ void *image_callback_ctx;
+};
+
+#define VFCTRL_QUERY_MAX_PP_LEVEL 4 // query max postprocessing level (if any)
+#define VFCTRL_SET_PP_LEVEL 5 // set postprocessing level
+#define VFCTRL_SET_EQUALIZER 6 // set color options (brightness,contrast etc)
+#define VFCTRL_GET_EQUALIZER 8 // get color options (brightness,contrast etc)
+#define VFCTRL_DUPLICATE_FRAME 11 // For encoding - encode zero-change frame
+#define VFCTRL_SKIP_NEXT_FRAME 12 // For encoding - drop the next frame that passes thru
+#define VFCTRL_FLUSH_FRAMES 13 // For encoding - flush delayed frames
+#define VFCTRL_SCREENSHOT 14 // Take screenshot, arg is vf_ctrl_screenshot
+#define VFCTRL_INIT_OSD 15 // Filter OSD renderer present?
+#define VFCTRL_SET_DEINTERLACE 18 // Set deinterlacing status
+#define VFCTRL_GET_DEINTERLACE 19 // Get deinterlacing status
+/* Hack to make the OSD state object available to vf_sub which
+ * access OSD/subtitle state outside of normal OSD draw time. */
+#define VFCTRL_SET_OSD_OBJ 20
+#define VFCTRL_SET_YUV_COLORSPACE 22 // arg is struct mp_csp_details*
+#define VFCTRL_GET_YUV_COLORSPACE 23 // arg is struct mp_csp_details*
+
+// functions:
+void vf_mpi_clear(mp_image_t *mpi, int x0, int y0, int w, int h);
+mp_image_t *vf_get_image(vf_instance_t *vf, unsigned int outfmt,
+ int mp_imgtype, int mp_imgflag, int w, int h);
+
+vf_instance_t *vf_open_plugin(struct MPOpts *opts,
+ const vf_info_t * const *filter_list, vf_instance_t *next,
+ const char *name, char **args);
+struct vf_instance *vf_open_plugin_noerr(struct MPOpts *opts,
+ const vf_info_t *const *filter_list, vf_instance_t *next,
+ const char *name, char **args, int *retcode);
+vf_instance_t *vf_open_filter(struct MPOpts *opts, vf_instance_t *next,
+ const char *name, char **args);
+vf_instance_t *vf_add_before_vo(vf_instance_t **vf, char *name, char **args);
+vf_instance_t *vf_open_encoder(struct MPOpts *opts, vf_instance_t *next,
+ const char *name, char *args);
+
+unsigned int vf_match_csp(vf_instance_t **vfp, const unsigned int *list,
+ unsigned int preferred);
+void vf_clone_mpi_attributes(mp_image_t *dst, mp_image_t *src);
+void vf_queue_frame(vf_instance_t *vf, int (*)(vf_instance_t *));
+int vf_output_queued_frame(vf_instance_t *vf);
+
+// default wrappers:
+int vf_next_config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt);
+int vf_next_control(struct vf_instance *vf, int request, void *data);
+int vf_next_query_format(struct vf_instance *vf, unsigned int fmt);
+int vf_next_put_image(struct vf_instance *vf, mp_image_t *mpi, double pts);
+void vf_next_draw_slice(struct vf_instance *vf, unsigned char **src,
+ int *stride, int w, int h, int x, int y);
+
+struct m_obj_settings;
+vf_instance_t *append_filters(vf_instance_t *last,
+ struct m_obj_settings *vf_settings);
+
+void vf_uninit_filter(vf_instance_t *vf);
+void vf_uninit_filter_chain(vf_instance_t *vf);
+
+int vf_config_wrapper(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt);
+
+static inline int norm_qscale(int qscale, int type)
+{
+ switch (type) {
+ case 0: // MPEG-1
+ return qscale;
+ case 1: // MPEG-2
+ return qscale >> 1;
+ case 2: // H264
+ return qscale >> 2;
+ case 3: // VP56
+ return (63 - qscale + 2) >> 2;
+ }
+ return qscale;
+}
+
+struct vf_detc_pts_buf {
+ double inpts_prev, outpts_prev;
+ double lastdelta;
+};
+void vf_detc_init_pts_buf(struct vf_detc_pts_buf *p);
+/* Adjust pts when detelecining.
+ * skip_frame: do not render this frame
+ * reset_pattern: set to 1 if the telecine pattern has reset due to scene cut
+ */
+double vf_detc_adjust_pts(struct vf_detc_pts_buf *p, double pts,
+ bool reset_pattern, bool skip_frame);
+double vf_softpulldown_adjust_pts(struct vf_detc_pts_buf *p, double pts,
+ bool reset_pattern, bool skip_frame,
+ int last_frame_duration);
+
+#endif /* MPLAYER_VF_H */
diff --git a/video/filter/vf_crop.c b/video/filter/vf_crop.c
new file mode 100644
index 0000000000..c4e0b4253b
--- /dev/null
+++ b/video/filter/vf_crop.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+static const struct vf_priv_s {
+ int crop_w,crop_h;
+ int crop_x,crop_y;
+} vf_priv_dflt = {
+ -1,-1,
+ -1,-1
+};
+
+//===========================================================================//
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+ struct MPOpts *opts = vf->opts;
+ // calculate the missing parameters:
+ if(vf->priv->crop_w<=0 || vf->priv->crop_w>width) vf->priv->crop_w=width;
+ if(vf->priv->crop_h<=0 || vf->priv->crop_h>height) vf->priv->crop_h=height;
+ if(vf->priv->crop_x<0) vf->priv->crop_x=(width-vf->priv->crop_w)/2;
+ if(vf->priv->crop_y<0) vf->priv->crop_y=(height-vf->priv->crop_h)/2;
+ // rounding:
+ if(!IMGFMT_IS_RGB(outfmt) && !IMGFMT_IS_BGR(outfmt)){
+ switch(outfmt){
+ case IMGFMT_444P:
+ case IMGFMT_Y800:
+ case IMGFMT_Y8:
+ break;
+ case IMGFMT_YVU9:
+ case IMGFMT_IF09:
+ vf->priv->crop_y&=~3;
+ case IMGFMT_411P:
+ vf->priv->crop_x&=~3;
+ break;
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ vf->priv->crop_y&=~1;
+ default:
+ vf->priv->crop_x&=~1;
+ }
+ }
+ // check:
+ if(vf->priv->crop_w+vf->priv->crop_x>width ||
+ vf->priv->crop_h+vf->priv->crop_y>height){
+ mp_tmsg(MSGT_VFILTER, MSGL_WARN, "[CROP] Bad position/width/height - cropped area outside of the original!\n");
+ return 0;
+ }
+ if(!opts->screen_size_x && !opts->screen_size_y){
+ d_width=d_width*vf->priv->crop_w/width;
+ d_height=d_height*vf->priv->crop_h/height;
+ }
+ return vf_next_config(vf,vf->priv->crop_w,vf->priv->crop_h,d_width,d_height,flags,outfmt);
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi;
+ if (mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)
+ return vf_next_put_image(vf,vf->dmpi, pts);
+ dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_EXPORT, 0,
+ vf->priv->crop_w, vf->priv->crop_h);
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ dmpi->planes[0]=mpi->planes[0]+
+ vf->priv->crop_y*mpi->stride[0]+vf->priv->crop_x;
+ dmpi->planes[1]=mpi->planes[1]+
+ (vf->priv->crop_y>>mpi->chroma_y_shift)*mpi->stride[1]+(vf->priv->crop_x>>mpi->chroma_x_shift);
+ dmpi->planes[2]=mpi->planes[2]+
+ (vf->priv->crop_y>>mpi->chroma_y_shift)*mpi->stride[2]+(vf->priv->crop_x>>mpi->chroma_x_shift);
+ dmpi->stride[1]=mpi->stride[1];
+ dmpi->stride[2]=mpi->stride[2];
+ } else {
+ dmpi->planes[0]=mpi->planes[0]+
+ vf->priv->crop_y*mpi->stride[0]+
+ vf->priv->crop_x*(mpi->bpp/8);
+ dmpi->planes[1]=mpi->planes[1]; // passthrough rgb8 palette
+ }
+ dmpi->stride[0]=mpi->stride[0];
+ dmpi->width=mpi->width;
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+static void start_slice(struct vf_instance *vf, mp_image_t *mpi){
+ vf->dmpi = vf_get_image(vf->next, mpi->imgfmt, mpi->type, mpi->flags,
+ vf->priv->crop_w, vf->priv->crop_h);
+}
+
+static void draw_slice(struct vf_instance *vf,
+ unsigned char** src, int* stride, int w,int h, int x, int y){
+ unsigned char *src2[3];
+ src2[0] = src[0];
+ if (vf->dmpi->flags & MP_IMGFLAG_PLANAR) {
+ src2[1] = src[1];
+ src2[2] = src[2];
+ }
+ //mp_msg(MSGT_VFILTER, MSGL_V, "crop slice %d %d %d %d ->", w,h,x,y);
+ if ((x -= vf->priv->crop_x) < 0) {
+ x = -x;
+ src2[0] += x;
+ if (vf->dmpi->flags & MP_IMGFLAG_PLANAR) {
+ src2[1] += x>>vf->dmpi->chroma_x_shift;
+ src2[2] += x>>vf->dmpi->chroma_x_shift;
+ }
+ w -= x;
+ x = 0;
+ }
+ if ((y -= vf->priv->crop_y) < 0) {
+ y = -y;
+ src2[0] += y*stride[0];
+ if (vf->dmpi->flags & MP_IMGFLAG_PLANAR) {
+ src2[1] += (y>>vf->dmpi->chroma_y_shift)*stride[1];
+ src2[2] += (y>>vf->dmpi->chroma_y_shift)*stride[2];
+ }
+ h -= y;
+ y = 0;
+ }
+ if (x+w > vf->priv->crop_w) w = vf->priv->crop_w-x;
+ if (y+h > vf->priv->crop_h) h = vf->priv->crop_h-y;
+ //mp_msg(MSGT_VFILTER, MSGL_V, "%d %d %d %d\n", w,h,x,y);
+ if (w <= 0 || h <= 0) return;
+ vf_next_draw_slice(vf,src2,stride,w,h,x,y);
+}
+
+//===========================================================================//
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->put_image=put_image;
+ vf->start_slice=start_slice;
+ vf->draw_slice=draw_slice;
+ vf->default_reqs=VFCAP_ACCEPT_STRIDE;
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "Crop: %d x %d, %d ; %d\n",
+ vf->priv->crop_w,
+ vf->priv->crop_h,
+ vf->priv->crop_x,
+ vf->priv->crop_y);
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"w", ST_OFF(crop_w), CONF_TYPE_INT, M_OPT_MIN,0 ,0, NULL},
+ {"h", ST_OFF(crop_h), CONF_TYPE_INT, M_OPT_MIN,0 ,0, NULL},
+ {"x", ST_OFF(crop_x), CONF_TYPE_INT, M_OPT_MIN,-1 ,0, NULL},
+ {"y", ST_OFF(crop_y), CONF_TYPE_INT, M_OPT_MIN,-1 ,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "crop",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_crop = {
+ "cropping",
+ "crop",
+ "A'rpi",
+ "",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_delogo.c b/video/filter/vf_delogo.c
new file mode 100644
index 0000000000..add6dc6b0c
--- /dev/null
+++ b/video/filter/vf_delogo.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2002 Jindrich Makovicka <makovick@gmail.com>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* A very simple tv station logo remover */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <errno.h>
+#include <math.h>
+
+#include "mp_msg.h"
+#include "cpudetect.h"
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libvo/fastmemcpy.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+//===========================================================================//
+
+static struct vf_priv_s {
+ unsigned int outfmt;
+ int xoff, yoff, lw, lh, band, show;
+ const char *file;
+ struct timed_rectangle {
+ int ts, x, y, w, h, b;
+ } *timed_rect;
+ int n_timed_rect;
+ int cur_timed_rect;
+} const vf_priv_dflt = {
+ 0,
+ 0, 0, 0, 0, 0, 0,
+ NULL, NULL, 0, 0,
+};
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+/**
+ * Adjust the coordinates to suit the band width
+ * Also print a notice in verbose mode
+ */
+static void fix_band(struct vf_priv_s *p)
+{
+ p->show = 0;
+ if (p->band < 0) {
+ p->band = 4;
+ p->show = 1;
+ }
+ p->lw += p->band*2;
+ p->lh += p->band*2;
+ p->xoff -= p->band;
+ p->yoff -= p->band;
+ mp_msg(MSGT_VFILTER, MSGL_V, "delogo: %d x %d, %d x %d, band = %d\n",
+ p->xoff, p->yoff, p->lw, p->lh, p->band);
+}
+
+static void update_sub(struct vf_priv_s *p, double pts)
+{
+ int ipts = pts * 1000;
+ int tr = p->cur_timed_rect;
+ while (tr < p->n_timed_rect - 1 && ipts >= p->timed_rect[tr + 1].ts)
+ tr++;
+ while (tr >= 0 && ipts < p->timed_rect[tr].ts)
+ tr--;
+ if (tr == p->cur_timed_rect)
+ return;
+ p->cur_timed_rect = tr;
+ if (tr >= 0) {
+ p->xoff = p->timed_rect[tr].x;
+ p->yoff = p->timed_rect[tr].y;
+ p->lw = p->timed_rect[tr].w;
+ p->lh = p->timed_rect[tr].h;
+ p->band = p->timed_rect[tr].b;
+ } else {
+ p->xoff = p->yoff = p->lw = p->lh = p->band = 0;
+ }
+ fix_band(p);
+}
+
+static void delogo(uint8_t *dst, uint8_t *src, int dstStride, int srcStride, int width, int height,
+ int logo_x, int logo_y, int logo_w, int logo_h, int band, int show, int direct) {
+ int y, x;
+ int interp, dist;
+ uint8_t *xdst, *xsrc;
+
+ uint8_t *topleft, *botleft, *topright;
+ int xclipl, xclipr, yclipt, yclipb;
+ int logo_x1, logo_x2, logo_y1, logo_y2;
+
+ xclipl = MAX(-logo_x, 0);
+ xclipr = MAX(logo_x+logo_w-width, 0);
+ yclipt = MAX(-logo_y, 0);
+ yclipb = MAX(logo_y+logo_h-height, 0);
+
+ logo_x1 = logo_x + xclipl;
+ logo_x2 = logo_x + logo_w - xclipr;
+ logo_y1 = logo_y + yclipt;
+ logo_y2 = logo_y + logo_h - yclipb;
+
+ topleft = src+logo_y1*srcStride+logo_x1;
+ topright = src+logo_y1*srcStride+logo_x2-1;
+ botleft = src+(logo_y2-1)*srcStride+logo_x1;
+
+ if (!direct) memcpy_pic(dst, src, width, height, dstStride, srcStride);
+
+ dst += (logo_y1+1)*dstStride;
+ src += (logo_y1+1)*srcStride;
+
+ for(y = logo_y1+1; y < logo_y2-1; y++)
+ {
+ for (x = logo_x1+1, xdst = dst+logo_x1+1, xsrc = src+logo_x1+1; x < logo_x2-1; x++, xdst++, xsrc++) {
+ interp = ((topleft[srcStride*(y-logo_y-yclipt)]
+ + topleft[srcStride*(y-logo_y-1-yclipt)]
+ + topleft[srcStride*(y-logo_y+1-yclipt)])*(logo_w-(x-logo_x))/logo_w
+ + (topright[srcStride*(y-logo_y-yclipt)]
+ + topright[srcStride*(y-logo_y-1-yclipt)]
+ + topright[srcStride*(y-logo_y+1-yclipt)])*(x-logo_x)/logo_w
+ + (topleft[x-logo_x-xclipl]
+ + topleft[x-logo_x-1-xclipl]
+ + topleft[x-logo_x+1-xclipl])*(logo_h-(y-logo_y))/logo_h
+ + (botleft[x-logo_x-xclipl]
+ + botleft[x-logo_x-1-xclipl]
+ + botleft[x-logo_x+1-xclipl])*(y-logo_y)/logo_h
+ )/6;
+/* interp = (topleft[srcStride*(y-logo_y)]*(logo_w-(x-logo_x))/logo_w
+ + topright[srcStride*(y-logo_y)]*(x-logo_x)/logo_w
+ + topleft[x-logo_x]*(logo_h-(y-logo_y))/logo_h
+ + botleft[x-logo_x]*(y-logo_y)/logo_h
+ )/2;*/
+ if (y >= logo_y+band && y < logo_y+logo_h-band && x >= logo_x+band && x < logo_x+logo_w-band) {
+ *xdst = interp;
+ } else {
+ dist = 0;
+ if (x < logo_x+band) dist = MAX(dist, logo_x-x+band);
+ else if (x >= logo_x+logo_w-band) dist = MAX(dist, x-(logo_x+logo_w-1-band));
+ if (y < logo_y+band) dist = MAX(dist, logo_y-y+band);
+ else if (y >= logo_y+logo_h-band) dist = MAX(dist, y-(logo_y+logo_h-1-band));
+ *xdst = (*xsrc*dist + interp*(band-dist))/band;
+ if (show && (dist == band-1)) *xdst = 0;
+ }
+ }
+
+ dst+= dstStride;
+ src+= srcStride;
+ }
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi){
+ if(mpi->flags&MP_IMGFLAG_PRESERVE) return; // don't change
+ if(mpi->imgfmt!=vf->priv->outfmt) return; // colorspace differ
+ // ok, we can do pp in-place (or pp disabled):
+ mpi->priv =
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ mpi->type, mpi->flags, mpi->w, mpi->h);
+ mpi->planes[0]=vf->dmpi->planes[0];
+ mpi->stride[0]=vf->dmpi->stride[0];
+ mpi->width=vf->dmpi->width;
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ mpi->planes[1]=vf->dmpi->planes[1];
+ mpi->planes[2]=vf->dmpi->planes[2];
+ mpi->stride[1]=vf->dmpi->stride[1];
+ mpi->stride[2]=vf->dmpi->stride[2];
+ }
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi;
+
+ if(mpi->flags&MP_IMGFLAG_DIRECT) {
+ vf->dmpi = mpi->priv;
+ mpi->priv = NULL;
+ } else {
+ // no DR, so get a new image! hope we'll get DR buffer:
+ vf->dmpi=vf_get_image(vf->next,vf->priv->outfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->w,mpi->h);
+ }
+ dmpi= vf->dmpi;
+
+ if (vf->priv->timed_rect)
+ update_sub(vf->priv, pts);
+ delogo(dmpi->planes[0], mpi->planes[0], dmpi->stride[0], mpi->stride[0], mpi->w, mpi->h,
+ vf->priv->xoff, vf->priv->yoff, vf->priv->lw, vf->priv->lh, vf->priv->band, vf->priv->show,
+ mpi->flags&MP_IMGFLAG_DIRECT);
+ delogo(dmpi->planes[1], mpi->planes[1], dmpi->stride[1], mpi->stride[1], mpi->w/2, mpi->h/2,
+ vf->priv->xoff/2, vf->priv->yoff/2, vf->priv->lw/2, vf->priv->lh/2, vf->priv->band/2, vf->priv->show,
+ mpi->flags&MP_IMGFLAG_DIRECT);
+ delogo(dmpi->planes[2], mpi->planes[2], dmpi->stride[2], mpi->stride[2], mpi->w/2, mpi->h/2,
+ vf->priv->xoff/2, vf->priv->yoff/2, vf->priv->lw/2, vf->priv->lh/2, vf->priv->band/2, vf->priv->show,
+ mpi->flags&MP_IMGFLAG_DIRECT);
+
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+static void uninit(struct vf_instance *vf){
+ if(!vf->priv) return;
+
+ free(vf->priv);
+ vf->priv=NULL;
+}
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ switch(fmt)
+ {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ return vf_next_query_format(vf,vf->priv->outfmt);
+ }
+ return 0;
+}
+
+static const unsigned int fmt_list[]={
+ IMGFMT_YV12,
+ IMGFMT_I420,
+ IMGFMT_IYUV,
+ 0
+};
+
+static int load_timed_rectangles(struct vf_priv_s *delogo)
+{
+ FILE *f;
+ char line[2048];
+ int lineno = 0, p;
+ double ts, last_ts = 0;
+ struct timed_rectangle *rect = NULL, *nr;
+ int n_rect = 0, alloc_rect = 0;
+
+ f = fopen(delogo->file, "r");
+ if (!f) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "delogo: unable to load %s: %s\n",
+ delogo->file, strerror(errno));
+ return -1;
+ }
+ while (fgets(line, sizeof(line), f)) {
+ lineno++;
+ if (*line == '#' || *line == '\n')
+ continue;
+ if (n_rect == alloc_rect) {
+ if (alloc_rect > INT_MAX / 2 / (int)sizeof(*rect)) {
+ mp_msg(MSGT_VFILTER, MSGL_WARN,
+ "delogo: too many rectangles\n");
+ goto load_error;
+ }
+ alloc_rect = alloc_rect ? 2 * alloc_rect : 256;
+ nr = realloc(rect, alloc_rect * sizeof(*rect));
+ if (!nr) {
+ mp_msg(MSGT_VFILTER, MSGL_WARN, "delogo: out of memory\n");
+ goto load_error;
+ }
+ rect = nr;
+ }
+ nr = rect + n_rect;
+ memset(nr, 0, sizeof(*nr));
+ p = sscanf(line, "%lf %d:%d:%d:%d:%d",
+ &ts, &nr->x, &nr->y, &nr->w, &nr->h, &nr->b);
+ if ((p == 2 && !nr->x) || p == 5 || p == 6) {
+ if (ts <= last_ts)
+ mp_msg(MSGT_VFILTER, MSGL_WARN, "delogo: %s:%d: wrong time\n",
+ delogo->file, lineno);
+ nr->ts = 1000 * ts + 0.5;
+ n_rect++;
+ } else {
+ mp_msg(MSGT_VFILTER, MSGL_WARN, "delogo: %s:%d: syntax error\n",
+ delogo->file, lineno);
+ }
+ }
+ fclose(f);
+ if (!n_rect) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "delogo: %s: no rectangles found\n",
+ delogo->file);
+ free(rect);
+ return -1;
+ }
+ nr = realloc(rect, n_rect * sizeof(*rect));
+ if (nr)
+ rect = nr;
+ delogo->timed_rect = rect;
+ delogo->n_timed_rect = n_rect;
+ return 0;
+
+load_error:
+ free(rect);
+ fclose(f);
+ return -1;
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->put_image=put_image;
+ vf->get_image=get_image;
+ vf->query_format=query_format;
+ vf->uninit=uninit;
+
+ if (vf->priv->file) {
+ if (load_timed_rectangles(vf->priv))
+ return 0;
+ mp_msg(MSGT_VFILTER, MSGL_V, "delogo: %d from %s\n",
+ vf->priv->n_timed_rect, vf->priv->file);
+ vf->priv->cur_timed_rect = -1;
+ }
+ fix_band(vf->priv);
+
+ // check csp:
+ vf->priv->outfmt=vf_match_csp(&vf->next,fmt_list,IMGFMT_YV12);
+ if(!vf->priv->outfmt)
+ {
+ uninit(vf);
+ return 0; // no csp match :(
+ }
+
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ { "x", ST_OFF(xoff), CONF_TYPE_INT, 0, 0, 0, NULL },
+ { "y", ST_OFF(yoff), CONF_TYPE_INT, 0, 0, 0, NULL },
+ { "w", ST_OFF(lw), CONF_TYPE_INT, 0, 0, 0, NULL },
+ { "h", ST_OFF(lh), CONF_TYPE_INT, 0, 0, 0, NULL },
+ { "t", ST_OFF(band), CONF_TYPE_INT, 0, 0, 0, NULL },
+ { "band", ST_OFF(band), CONF_TYPE_INT, 0, 0, 0, NULL }, // alias
+ { "file", ST_OFF(file), CONF_TYPE_STRING, 0, 0, 0, NULL },
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "delogo",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_delogo = {
+ "simple logo remover",
+ "delogo",
+ "Jindrich Makovicka, Alex Beregszaszi",
+ "",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_divtc.c b/video/filter/vf_divtc.c
new file mode 100644
index 0000000000..6faeb7c411
--- /dev/null
+++ b/video/filter/vf_divtc.c
@@ -0,0 +1,723 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+#include "libavutil/common.h"
+#include "mpbswap.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+const vf_info_t vf_info_divtc;
+
+struct vf_priv_s
+ {
+ int deghost, pass, phase, window, fcount, bcount, frameno, misscount,
+ ocount, sum[5];
+ double threshold;
+ FILE *file;
+ int8_t *bdata;
+ unsigned int *csdata;
+ int *history;
+ struct vf_detc_pts_buf ptsbuf;
+ };
+
+/*
+ * diff_MMX and diff_C stolen from vf_decimate.c
+ */
+
+#if HAVE_MMX && HAVE_EBX_AVAILABLE
+static int diff_MMX(unsigned char *old, unsigned char *new, int os, int ns)
+ {
+ volatile short out[4];
+ __asm__ (
+ "movl $8, %%ecx \n\t"
+ "pxor %%mm4, %%mm4 \n\t"
+ "pxor %%mm7, %%mm7 \n\t"
+
+ ASMALIGN(4)
+ "1: \n\t"
+
+ "movq (%%"REG_S"), %%mm0 \n\t"
+ "movq (%%"REG_S"), %%mm2 \n\t"
+ "add %%"REG_a", %%"REG_S" \n\t"
+ "movq (%%"REG_D"), %%mm1 \n\t"
+ "add %%"REG_b", %%"REG_D" \n\t"
+ "psubusb %%mm1, %%mm2 \n\t"
+ "psubusb %%mm0, %%mm1 \n\t"
+ "movq %%mm2, %%mm0 \n\t"
+ "movq %%mm1, %%mm3 \n\t"
+ "punpcklbw %%mm7, %%mm0 \n\t"
+ "punpcklbw %%mm7, %%mm1 \n\t"
+ "punpckhbw %%mm7, %%mm2 \n\t"
+ "punpckhbw %%mm7, %%mm3 \n\t"
+ "paddw %%mm0, %%mm4 \n\t"
+ "paddw %%mm1, %%mm4 \n\t"
+ "paddw %%mm2, %%mm4 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+
+ "decl %%ecx \n\t"
+ "jnz 1b \n\t"
+ "movq %%mm4, (%%"REG_d") \n\t"
+ "emms \n\t"
+ :
+ : "S" (old), "D" (new), "a" ((long)os), "b" ((long)ns), "d" (out)
+ : "%ecx", "memory"
+ );
+ return out[0]+out[1]+out[2]+out[3];
+ }
+#endif
+
+static int diff_C(unsigned char *old, unsigned char *new, int os, int ns)
+ {
+ int x, y, d=0;
+
+ for(y=8; y; y--, new+=ns, old+=os)
+ for(x=8; x; x--)
+ d+=abs(new[x]-old[x]);
+
+ return d;
+ }
+
+static int (*diff)(unsigned char *, unsigned char *, int, int);
+
+static int diff_plane(unsigned char *old, unsigned char *new,
+ int w, int h, int os, int ns, int arg)
+ {
+ int x, y, d, max=0, sum=0, n=0;
+
+ for(y=0; y<h-7; y+=8)
+ {
+ for(x=0; x<w-7; x+=8)
+ {
+ d=diff(old+x+y*os, new+x+y*ns, os, ns);
+ if(d>max) max=d;
+ sum+=d;
+ n++;
+ }
+ }
+
+ return (sum+n*max)/2;
+ }
+
+/*
+static unsigned int checksum_plane(unsigned char *p, unsigned char *z,
+ int w, int h, int s, int zs, int arg)
+ {
+ unsigned int shift, sum;
+ unsigned char *e;
+
+ for(sum=0; h; h--, p+=s-w)
+ for(e=p+w, shift=32; p<e;)
+ sum^=(*p++)<<(shift=(shift-8)&31);
+
+ return sum;
+ }
+*/
+
+static unsigned int checksum_plane(unsigned char *p, unsigned char *z,
+ int w, int h, int s, int zs, int arg)
+ {
+ unsigned int shift;
+ uint32_t sum, t;
+ unsigned char *e, *e2;
+#if HAVE_FAST_64BIT
+ typedef uint64_t wsum_t;
+#else
+ typedef uint32_t wsum_t;
+#endif
+ wsum_t wsum;
+
+ for(sum=0; h; h--, p+=s-w)
+ {
+ for(shift=0, e=p+w; (size_t)p&(sizeof(wsum_t)-1) && p<e;)
+ sum^=*p++<<(shift=(shift-8)&31);
+
+ for(wsum=0, e2=e-sizeof(wsum_t)+1; p<e2; p+=sizeof(wsum_t))
+ wsum^=*(wsum_t *)p;
+
+#if HAVE_FAST_64BIT
+ t=be2me_32((uint32_t)(wsum>>32^wsum));
+#else
+ t=be2me_32(wsum);
+#endif
+
+ for(sum^=(t<<shift|t>>(32-shift)); p<e;)
+ sum^=*p++<<(shift=(shift-8)&31);
+ }
+
+ return sum;
+ }
+
+static int deghost_plane(unsigned char *d, unsigned char *s,
+ int w, int h, int ds, int ss, int threshold)
+ {
+ int t;
+ unsigned char *e;
+
+ for(; h; h--, s+=ss-w, d+=ds-w)
+ for(e=d+w; d<e; d++, s++)
+ if(abs(*d-*s)>=threshold)
+ *d=(t=(*d<<1)-*s)<0?0:t>255?255:t;
+
+ return 0;
+ }
+
+static int copyop(unsigned char *d, unsigned char *s, int bpl, int h, int dstride, int sstride, int dummy) {
+ memcpy_pic(d, s, bpl, h, dstride, sstride);
+ return 0;
+}
+
+static int imgop(int(*planeop)(unsigned char *, unsigned char *,
+ int, int, int, int, int),
+ mp_image_t *dst, mp_image_t *src, int arg)
+ {
+ if(dst->flags&MP_IMGFLAG_PLANAR)
+ return planeop(dst->planes[0], src?src->planes[0]:0,
+ dst->w, dst->h,
+ dst->stride[0], src?src->stride[0]:0, arg)+
+ planeop(dst->planes[1], src?src->planes[1]:0,
+ dst->chroma_width, dst->chroma_height,
+ dst->stride[1], src?src->stride[1]:0, arg)+
+ planeop(dst->planes[2], src?src->planes[2]:0,
+ dst->chroma_width, dst->chroma_height,
+ dst->stride[2], src?src->stride[2]:0, arg);
+
+ return planeop(dst->planes[0], src?src->planes[0]:0,
+ dst->w*(dst->bpp/8), dst->h,
+ dst->stride[0], src?src->stride[0]:0, arg);
+ }
+
+/*
+ * Find the phase in which the telecine pattern fits best to the
+ * given 5 frame slice of frame difference measurements.
+ *
+ * If phase1 and phase2 are not negative, only the two specified
+ * phases are tested.
+ */
+
+static int match(struct vf_priv_s *p, int *diffs,
+ int phase1, int phase2, double *strength)
+ {
+ const int pattern1[]={ -4, 1, 1, 1, 1 },
+ pattern2[]={ -2, -3, 4, 4, -3 }, *pattern;
+ int f, m, n, t[5];
+
+ pattern=p->deghost>0?pattern2:pattern1;
+
+ for(f=0; f<5; f++)
+ {
+ if(phase1<0 || phase2<0 || f==phase1 || f==phase2)
+ {
+ for(n=t[f]=0; n<5; n++)
+ t[f]+=diffs[n]*pattern[(n-f+5)%5];
+ }
+ else
+ t[f]=INT_MIN;
+ }
+
+ /* find the best match */
+ for(m=0, n=1; n<5; n++)
+ if(t[n]>t[m]) m=n;
+
+ if(strength)
+ {
+ /* the second best match */
+ for(f=m?0:1, n=f+1; n<5; n++)
+ if(n!=m && t[n]>t[f]) f=n;
+
+ *strength=(t[m]>0?(double)(t[m]-t[f])/t[m]:0.0);
+ }
+
+ return m;
+ }
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+ {
+ mp_image_t *dmpi, *tmpi=0;
+ int n, m, f, newphase;
+ struct vf_priv_s *p=vf->priv;
+ unsigned int checksum;
+ double d;
+
+ dmpi=vf_get_image(vf->next, mpi->imgfmt,
+ MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE |
+ MP_IMGFLAG_PRESERVE | MP_IMGFLAG_READABLE,
+ mpi->width, mpi->height);
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+ newphase=p->phase;
+
+ switch(p->pass)
+ {
+ case 1:
+ fprintf(p->file, "%08x %d\n",
+ (unsigned int)imgop((void *)checksum_plane, mpi, 0, 0),
+ p->frameno?imgop(diff_plane, dmpi, mpi, 0):0);
+ break;
+
+ case 2:
+ if(p->frameno/5>p->bcount)
+ {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "\n%s: Log file ends prematurely! "
+ "Switching to one pass mode.\n", vf->info->name);
+ p->pass=0;
+ break;
+ }
+
+ checksum=(unsigned int)imgop((void *)checksum_plane, mpi, 0, 0);
+
+ if(checksum!=p->csdata[p->frameno])
+ {
+ for(f=0; f<100; f++)
+ if(p->frameno+f<p->fcount && p->csdata[p->frameno+f]==checksum)
+ break;
+ else if(p->frameno-f>=0 && p->csdata[p->frameno-f]==checksum)
+ {
+ f=-f;
+ break;
+ }
+
+ if(f<100)
+ {
+ mp_msg(MSGT_VFILTER, MSGL_INFO,
+ "\n%s: Mismatch with pass-1: %+d frame(s).\n",
+ vf->info->name, f);
+
+ p->frameno+=f;
+ p->misscount=0;
+ }
+ else if(p->misscount++>=30)
+ {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "\n%s: Sync with pass-1 lost! "
+ "Switching to one pass mode.\n", vf->info->name);
+ p->pass=0;
+ break;
+ }
+ }
+
+ n=(p->frameno)/5;
+ if(n>=p->bcount) n=p->bcount-1;
+
+ newphase=p->bdata[n];
+ break;
+
+ default:
+ if(p->frameno)
+ {
+ int *sump=p->sum+p->frameno%5,
+ *histp=p->history+p->frameno%p->window;
+
+ *sump-=*histp;
+ *sump+=(*histp=imgop(diff_plane, dmpi, mpi, 0));
+ }
+
+ m=match(p, p->sum, -1, -1, &d);
+
+ if(d>=p->threshold)
+ newphase=m;
+ }
+
+ n=p->ocount++%5;
+
+ if(newphase!=p->phase && ((p->phase+4)%5<n)==((newphase+4)%5<n))
+ {
+ p->phase=newphase;
+ mp_msg(MSGT_VFILTER, MSGL_STATUS,
+ "\n%s: Telecine phase %d.\n", vf->info->name, p->phase);
+ }
+
+ switch((p->frameno++-p->phase+10)%5)
+ {
+ case 0:
+ imgop(copyop, dmpi, mpi, 0);
+ vf_detc_adjust_pts(&p->ptsbuf, pts, 0, 1);
+ return 0;
+
+ case 4:
+ if(p->deghost>0)
+ {
+ tmpi=vf_get_image(vf->next, mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE |
+ MP_IMGFLAG_READABLE,
+ mpi->width, mpi->height);
+ vf_clone_mpi_attributes(tmpi, mpi);
+
+ imgop(copyop, tmpi, mpi, 0);
+ imgop(deghost_plane, tmpi, dmpi, p->deghost);
+ imgop(copyop, dmpi, mpi, 0);
+ return vf_next_put_image(vf, tmpi, vf_detc_adjust_pts(&p->ptsbuf, pts, 0, 0));
+ }
+ }
+
+ imgop(copyop, dmpi, mpi, 0);
+ return vf_next_put_image(vf, dmpi, vf_detc_adjust_pts(&p->ptsbuf, pts, 0, 0));
+ }
+
+static int analyze(struct vf_priv_s *p)
+ {
+ int *buf=0, *bp, bufsize=0, n, b, f, i, j, m, s;
+ unsigned int *cbuf=0, *cp;
+ int8_t *pbuf;
+ int8_t lbuf[256];
+ int sum[5];
+ double d;
+
+ /* read the file */
+
+ n=15;
+ while(fgets(lbuf, 256, p->file))
+ {
+ if(n>=bufsize-19)
+ {
+ bufsize=bufsize?bufsize*2:30000;
+ if((bp=realloc(buf, bufsize*sizeof *buf))) buf=bp;
+ if((cp=realloc(cbuf, bufsize*sizeof *cbuf))) cbuf=cp;
+
+ if(!bp || !cp)
+ {
+ mp_msg(MSGT_VFILTER, MSGL_FATAL, "%s: Not enough memory.\n",
+ vf_info_divtc.name);
+ free(buf);
+ free(cbuf);
+ return 0;
+ }
+ }
+ sscanf(lbuf, "%x %d", cbuf+n, buf+n);
+ n++;
+ }
+
+ if(n <= 15)
+ {
+ mp_msg(MSGT_VFILTER, MSGL_FATAL, "%s: Empty 2-pass log file.\n",
+ vf_info_divtc.name);
+ free(buf);
+ free(cbuf);
+ return 0;
+ }
+
+ /* generate some dummy data past the beginning and end of the array */
+
+ buf+=15, cbuf+=15;
+ n-=15;
+
+ memcpy(buf-15, buf, 15*sizeof *buf);
+ memset(cbuf-15, 0, 15*sizeof *cbuf);
+
+ while(n%5)
+ buf[n]=buf[n-5], cbuf[n]=0, n++;
+
+ memcpy(buf+n, buf+n-15, 15*sizeof *buf);
+ memset(cbuf+n, 0, 15*sizeof *cbuf);
+
+ p->csdata=cbuf;
+ p->fcount=n;
+
+ /* array with one slot for each slice of 5 frames */
+
+ p->bdata=pbuf=malloc(p->bcount=b=(n/5));
+ memset(pbuf, 255, b);
+
+ /* resolve the automatic mode */
+
+ if(p->deghost<0)
+ {
+ int deghost=-p->deghost;
+ double s0=0.0, s1=0.0;
+
+ for(f=0; f<n; f+=5)
+ {
+ p->deghost=0; match(p, buf+f, -1, -1, &d); s0+=d;
+ p->deghost=1; match(p, buf+f, -1, -1, &d); s1+=d;
+ }
+
+ p->deghost=s1>s0?deghost:0;
+
+ mp_msg(MSGT_VFILTER, MSGL_INFO,
+ "%s: Deghosting %-3s (relative pattern strength %+.2fdB).\n",
+ vf_info_divtc.name,
+ p->deghost?"ON":"OFF",
+ 10.0*log10(s1/s0));
+ }
+
+ /* analyze the data */
+
+ for(f=0; f<5; f++)
+ for(sum[f]=0, n=-15; n<20; n+=5)
+ sum[f]+=buf[n+f];
+
+ for(f=0; f<b; f++)
+ {
+ m=match(p, sum, -1, -1, &d);
+
+ if(d>=p->threshold)
+ pbuf[f]=m;
+
+ if(f<b-1)
+ for(n=0; n<5; n++)
+ sum[n]=sum[n]-buf[5*(f-3)+n]+buf[5*(f+4)+n];
+ }
+
+ /* fill in the gaps */
+
+ /* the beginning */
+ for(f=0; f<b && pbuf[f]==-1; f++);
+
+ if(f==b)
+ {
+ free(buf-15);
+ mp_msg(MSGT_VFILTER, MSGL_FATAL, "%s: No telecine pattern found!\n",
+ vf_info_divtc.name);
+ return 0;
+ }
+
+ for(n=0; n<f; pbuf[n++]=pbuf[f]);
+
+ /* the end */
+ for(f=b-1; pbuf[f]==-1; f--);
+ for(n=f+1; n<b; pbuf[n++]=pbuf[f]);
+
+ /* the rest */
+ for(f=0;;)
+ {
+ while(f<b && pbuf[f]!=-1) f++;
+ if(f==b) break;
+ for(n=f; pbuf[n]==-1; n++);
+
+ if(pbuf[f-1]==pbuf[n])
+ {
+ /* just a gap */
+ while(f<n) pbuf[f++]=pbuf[n];
+ }
+ else
+ {
+ /* phase change, reanalyze the original data in the gap with zero
+ threshold for only the two phases that appear at the ends */
+
+ for(i=0; i<5; i++)
+ for(sum[i]=0, j=5*f-15; j<5*f; j+=5)
+ sum[i]+=buf[i+j];
+
+ for(i=f; i<n; i++)
+ {
+ pbuf[i]=match(p, sum, pbuf[f-1], pbuf[n], 0);
+
+ for(j=0; j<5; j++)
+ sum[j]=sum[j]-buf[5*(i-3)+j]+buf[5*(i+4)+j];
+ }
+
+ /* estimate the transition point by dividing the gap
+ in the same proportion as the number of matches of each kind */
+
+ for(i=f, m=f; i<n; i++)
+ if(pbuf[i]==pbuf[f-1]) m++;
+
+ /* find the transition of the right direction nearest to the
+ estimated point */
+
+ if(m>f && m<n)
+ {
+ for(j=m; j>f; j--)
+ if(pbuf[j-1]==pbuf[f-1] && pbuf[j]==pbuf[n]) break;
+ for(s=m; s<n; s++)
+ if(pbuf[s-1]==pbuf[f-1] && pbuf[s]==pbuf[n]) break;
+
+ m=(s-m<m-j)?s:j;
+ }
+
+ /* and rewrite the data to allow only this one transition */
+
+ for(i=f; i<m; i++)
+ pbuf[i]=pbuf[f-1];
+
+ for(; i<n; i++)
+ pbuf[i]=pbuf[n];
+
+ f=n;
+ }
+ }
+
+ free(buf-15);
+
+ return 1;
+ }
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+ {
+ switch(fmt)
+ {
+ case IMGFMT_444P: case IMGFMT_IYUV: case IMGFMT_RGB24:
+ case IMGFMT_422P: case IMGFMT_UYVY: case IMGFMT_BGR24:
+ case IMGFMT_411P: case IMGFMT_YUY2: case IMGFMT_IF09:
+ case IMGFMT_YV12: case IMGFMT_I420: case IMGFMT_YVU9:
+ case IMGFMT_IUYV: case IMGFMT_Y800: case IMGFMT_Y8:
+ return vf_next_query_format(vf,fmt);
+ }
+
+ return 0;
+ }
+
+static void uninit(struct vf_instance *vf)
+ {
+ if(vf->priv)
+ {
+ if(vf->priv->file) fclose(vf->priv->file);
+ if(vf->priv->csdata) free(vf->priv->csdata-15);
+ free(vf->priv->bdata);
+ free(vf->priv->history);
+ free(vf->priv);
+ }
+ }
+
+static int vf_open(vf_instance_t *vf, char *args)
+ {
+ struct vf_priv_s *p;
+ char *filename="framediff.log", *ap, *q, *a;
+
+ if(args && !(args=strdup(args)))
+ {
+ nomem:
+ mp_msg(MSGT_VFILTER, MSGL_FATAL,
+ "%s: Not enough memory.\n", vf->info->name);
+ fail:
+ uninit(vf);
+ free(args);
+ return 0;
+ }
+
+ vf->put_image=put_image;
+ vf->uninit=uninit;
+ vf->query_format=query_format;
+ vf->default_reqs=VFCAP_ACCEPT_STRIDE;
+ if(!(vf->priv=p=calloc(1, sizeof(struct vf_priv_s))))
+ goto nomem;
+
+ p->phase=5;
+ p->threshold=0.5;
+ p->window=30;
+
+ if((ap=args))
+ while(*ap)
+ {
+ q=ap;
+ if((ap=strchr(q, ':'))) *ap++=0; else ap=q+strlen(q);
+ if((a=strchr(q, '='))) *a++=0; else a=q+strlen(q);
+
+ switch(*q)
+ {
+ case 0: break;
+ case 'f': filename=a; break;
+ case 't': p->threshold=atof(a); break;
+ case 'w': p->window=5*(atoi(a)+4)/5; break;
+ case 'd': p->deghost=atoi(a); break;
+ case 'p':
+ if(q[1]=='h') p->phase=atoi(a);
+ else p->pass=atoi(a);
+ break;
+
+ case 'h':
+ mp_msg(MSGT_VFILTER, MSGL_INFO,
+ "\n%s options:\n\n"
+ "pass=1|2 - Use 2-pass mode.\n"
+ "file=filename - Set the 2-pass log file name "
+ "(default %s).\n"
+ "threshold=value - Set the pattern recognition "
+ "sensitivity (default %g).\n"
+ "deghost=value - Select deghosting threshold "
+ "(default %d).\n"
+ "window=numframes - Set the statistics window "
+ "for 1-pass mode (default %d).\n"
+ "phase=0|1|2|3|4 - Set the initial phase "
+ "for 1-pass mode (default %d).\n\n"
+ "The option names can be abbreviated to the shortest "
+ "unique prefix.\n\n",
+ vf->info->name, filename, p->threshold, p->deghost,
+ p->window, p->phase%5);
+ break;
+
+ default:
+ mp_msg(MSGT_VFILTER, MSGL_FATAL,
+ "%s: Unknown argument %s.\n", vf->info->name, q);
+ goto fail;
+ }
+ }
+
+ switch(p->pass)
+ {
+ case 1:
+ if(!(p->file=fopen(filename, "w")))
+ {
+ mp_msg(MSGT_VFILTER, MSGL_FATAL,
+ "%s: Can't create file %s.\n", vf->info->name, filename);
+ goto fail;
+ }
+
+ break;
+
+ case 2:
+ if(!(p->file=fopen(filename, "r")))
+ {
+ mp_msg(MSGT_VFILTER, MSGL_FATAL,
+ "%s: Can't open file %s.\n", vf->info->name, filename);
+ goto fail;
+ }
+
+ if(!analyze(p))
+ goto fail;
+
+ fclose(p->file);
+ p->file=0;
+ break;
+ }
+
+ if(p->window<5) p->window=5;
+ if(!(p->history=calloc(sizeof *p->history, p->window)))
+ goto nomem;
+
+ diff = diff_C;
+#if HAVE_MMX && HAVE_EBX_AVAILABLE
+ if(gCpuCaps.hasMMX) diff = diff_MMX;
+#endif
+
+ free(args);
+ vf_detc_init_pts_buf(&p->ptsbuf);
+ return 1;
+ }
+
+const vf_info_t vf_info_divtc =
+ {
+ "inverse telecine for deinterlaced video",
+ "divtc",
+ "Ville Saari",
+ "",
+ vf_open,
+ NULL
+ };
diff --git a/video/filter/vf_dlopen.c b/video/filter/vf_dlopen.c
new file mode 100644
index 0000000000..183b31be84
--- /dev/null
+++ b/video/filter/vf_dlopen.c
@@ -0,0 +1,389 @@
+/*
+ * This file is part of mplayer.
+ *
+ * mplayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+#include "vf_dlopen.h"
+
+#ifdef _WIN32
+# include <windows.h>
+# define DLLOpen(name) LoadLibrary(name)
+# define DLLClose(handle) FreeLibrary(handle)
+# define DLLSymbol(handle, name) ((void *)GetProcAddress(handle, name))
+#else
+# include <dlfcn.h>
+# define DLLOpen(name) dlopen(name, RTLD_NOW)
+# define DLLClose(handle) dlclose(handle)
+# define DLLSymbol(handle, name) dlsym(handle, name)
+#endif
+
+static struct vf_priv_s {
+ const char *cfg_dllname;
+ int cfg_argc;
+ const char *cfg_argv[4];
+ void *dll;
+ struct vf_dlopen_context filter;
+
+ // output mp_image_t stuff
+ mp_image_t *outpic[FILTER_MAX_OUTCNT];
+
+ // generic
+ unsigned int out_cnt, out_width, out_height;
+
+ // multi frame output
+ unsigned int outbufferpos;
+ unsigned int outbufferlen;
+ mp_image_t *outbuffermpi;
+
+ // qscale buffer
+ unsigned char *qbuffer;
+ size_t qbuffersize;
+
+ unsigned int outfmt;
+
+ int argc;
+} const vf_priv_dflt = {};
+
+//===========================================================================//
+
+static void set_imgprop(struct vf_dlopen_picdata *out, const mp_image_t *mpi)
+{
+ int i;
+ out->planes = mpi->num_planes;
+ for (i = 0; i < mpi->num_planes; ++i) {
+ out->plane[i] = mpi->planes[i];
+ out->planestride[i] = mpi->stride[i];
+ out->planewidth[i] =
+ i ? (/*mpi->chroma_width*/ mpi->w >> mpi->chroma_x_shift) : mpi->w;
+ out->planeheight[i] =
+ i ? (/*mpi->chroma_height*/ mpi->h >> mpi->chroma_y_shift) : mpi->h;
+ out->planexshift[i] = i ? mpi->chroma_x_shift : 0;
+ out->planeyshift[i] = i ? mpi->chroma_y_shift : 0;
+ }
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int fmt)
+{
+ vf->priv->filter.in_width = width;
+ vf->priv->filter.in_height = height;
+ vf->priv->filter.in_d_width = d_width;
+ vf->priv->filter.in_d_height = d_height;
+ vf->priv->filter.in_fmt = mp_imgfmt_to_name(fmt);
+ vf->priv->filter.out_width = width;
+ vf->priv->filter.out_height = height;
+ vf->priv->filter.out_d_width = d_width;
+ vf->priv->filter.out_d_height = d_height;
+ vf->priv->filter.out_fmt = NULL;
+ vf->priv->filter.out_cnt = 1;
+
+ if (!vf->priv->filter.in_fmt) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "invalid input/output format\n");
+ return 0;
+ }
+ if (vf->priv->filter.config && vf->priv->filter.config(&vf->priv->filter) < 0) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "filter config failed\n");
+ return 0;
+ }
+
+ // copy away stuff to sanity island
+ vf->priv->out_cnt = vf->priv->filter.out_cnt;
+ vf->priv->out_width = vf->priv->filter.out_width;
+ vf->priv->out_height = vf->priv->filter.out_height;
+
+ if (vf->priv->filter.out_fmt)
+ vf->priv->outfmt = mp_imgfmt_from_name(bstr0(vf->priv->filter.out_fmt),
+ false);
+ else {
+ struct vf_dlopen_formatpair *p = vf->priv->filter.format_mapping;
+ vf->priv->outfmt = 0;
+ if (p) {
+ for (; p->from; ++p) {
+ // TODO support pixel format classes in matching
+ if (!strcmp(p->from, vf->priv->filter.in_fmt)) {
+ vf->priv->outfmt = mp_imgfmt_from_name(bstr0(p->to), false);
+ break;
+ }
+ }
+ } else
+ vf->priv->outfmt = fmt;
+ vf->priv->filter.out_fmt = mp_imgfmt_to_name(vf->priv->outfmt);
+ }
+
+ if (!vf->priv->outfmt) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "filter config wants an unsupported output format\n");
+ return 0;
+ }
+ if (!vf->priv->out_cnt || vf->priv->out_cnt > FILTER_MAX_OUTCNT) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "filter config wants to yield zero or too many output frames\n");
+ return 0;
+ }
+
+ if (vf->priv->out_cnt >= 2) {
+ int i;
+ for (i = 0; i < vf->priv->out_cnt; ++i) {
+ vf->priv->outpic[i] =
+ alloc_mpi(vf->priv->out_width, vf->priv->out_height,
+ vf->priv->outfmt);
+ set_imgprop(&vf->priv->filter.outpic[i], vf->priv->outpic[i]);
+ }
+ }
+
+ return vf_next_config(vf, vf->priv->out_width,
+ vf->priv->out_height,
+ vf->priv->filter.out_d_width,
+ vf->priv->filter.out_d_height,
+ flags, vf->priv->outfmt);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ if (vf->priv->filter.uninit)
+ vf->priv->filter.uninit(&vf->priv->filter);
+ memset(&vf->priv->filter, 0, sizeof(&vf->priv->filter));
+ if (vf->priv->dll) {
+ DLLClose(vf->priv->dll);
+ vf->priv->dll = NULL;
+ }
+ if (vf->priv->out_cnt >= 2) {
+ int i;
+ for (i = 0; i < vf->priv->out_cnt; ++i) {
+ free_mp_image(vf->priv->outpic[i]);
+ vf->priv->outpic[i] = NULL;
+ }
+ }
+ if (vf->priv->qbuffer) {
+ free(vf->priv->qbuffer);
+ vf->priv->qbuffer = NULL;
+ }
+}
+
+// NOTE: only called if (vf->priv->out_cnt >= 2) {
+static int continue_put_image(struct vf_instance *vf)
+{
+ int k;
+ int ret = 0;
+
+ mp_image_t *dmpi =
+ vf_get_image(vf->next, vf->priv->outfmt, MP_IMGTYPE_EXPORT, 0,
+ vf->priv->outpic[vf->priv->outbufferpos]->w,
+ vf->priv->outpic[vf->priv->outbufferpos]->h);
+ for (k = 0; k < vf->priv->outpic[vf->priv->outbufferpos]->num_planes;
+ ++k) {
+ dmpi->planes[k] = vf->priv->outpic[vf->priv->outbufferpos]->planes[k];
+ dmpi->stride[k] = vf->priv->outpic[vf->priv->outbufferpos]->stride[k];
+ }
+
+ // pass through qscale if we can
+ vf_clone_mpi_attributes(dmpi, vf->priv->outbuffermpi);
+
+ ret =
+ vf_next_put_image(vf, dmpi,
+ vf->priv->filter.outpic[vf->priv->outbufferpos].pts);
+
+ ++vf->priv->outbufferpos;
+
+ // more frames left?
+ if (vf->priv->outbufferpos < vf->priv->outbufferlen)
+ vf_queue_frame(vf, continue_put_image);
+
+ return ret;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ int i, k;
+
+ set_imgprop(&vf->priv->filter.inpic, mpi);
+ if (mpi->qscale) {
+ if (mpi->qscale_type != 0) {
+ k = mpi->qstride * ((mpi->height + 15) >> 4);
+ if (vf->priv->qbuffersize != k) {
+ vf->priv->qbuffer = realloc(vf->priv->qbuffer, k);
+ vf->priv->qbuffersize = k;
+ }
+ for (i = 0; i < k; ++i)
+ vf->priv->qbuffer[i] = norm_qscale(mpi->qscale[i],
+ mpi->qscale_type);
+ vf->priv->filter.inpic_qscale = vf->priv->qbuffer;
+ } else
+ vf->priv->filter.inpic_qscale = mpi->qscale;
+ vf->priv->filter.inpic_qscalestride = mpi->qstride;
+ vf->priv->filter.inpic_qscaleshift = 4;
+ } else {
+ vf->priv->filter.inpic_qscale = NULL;
+ vf->priv->filter.inpic_qscalestride = 0;
+ vf->priv->filter.inpic_qscaleshift = 0;
+ }
+ vf->priv->filter.inpic.pts = pts;
+
+ if (vf->priv->out_cnt >= 2) {
+ // more than one out pic
+ int ret = vf->priv->filter.put_image(&vf->priv->filter);
+ if (ret <= 0)
+ return ret;
+
+ vf->priv->outbuffermpi = mpi;
+ vf->priv->outbufferlen = ret;
+ vf->priv->outbufferpos = 0;
+ return continue_put_image(vf);
+ } else {
+ // efficient case: exactly one out pic
+ mp_image_t *dmpi =
+ vf_get_image(vf->next, vf->priv->outfmt,
+ MP_IMGTYPE_TEMP,
+ MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
+ vf->priv->out_width, vf->priv->out_height);
+ set_imgprop(&vf->priv->filter.outpic[0], dmpi);
+
+ int ret = vf->priv->filter.put_image(&vf->priv->filter);
+ if (ret <= 0)
+ return ret;
+
+ // pass through qscale if we can
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+ return vf_next_put_image(vf, dmpi, vf->priv->filter.outpic[0].pts);
+ }
+}
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ if (IMGFMT_IS_HWACCEL(fmt))
+ return 0; // these can't really be filtered
+ if (fmt == IMGFMT_RGB8 || fmt == IMGFMT_BGR8)
+ return 0; // we don't have palette support, sorry
+ const char *fmtname = mp_imgfmt_to_name(fmt);
+ if (!fmtname)
+ return 0;
+ struct vf_dlopen_formatpair *p = vf->priv->filter.format_mapping;
+ unsigned int outfmt = 0;
+ if (p) {
+ for (; p->from; ++p) {
+ // TODO support pixel format classes in matching
+ if (!strcmp(p->from, fmtname)) {
+ outfmt = mp_imgfmt_from_name(bstr0(p->to), false);
+ break;
+ }
+ }
+ } else
+ outfmt = fmt;
+ if (!outfmt)
+ return 0;
+ return vf_next_query_format(vf, outfmt);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ if (!vf->priv->cfg_dllname) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "usage: -vf dlopen=filename.so:function:args\n");
+ return 0;
+ }
+
+ vf->priv->dll = DLLOpen(vf->priv->cfg_dllname);
+ if (!vf->priv->dll) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "library not found: %s\n",
+ vf->priv->cfg_dllname);
+ return 0;
+ }
+
+ vf_dlopen_getcontext_func *func =
+ (vf_dlopen_getcontext_func *) DLLSymbol(vf->priv->dll, "vf_dlopen_getcontext");
+ if (!func) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "library is not a filter: %s\n",
+ vf->priv->cfg_dllname);
+ return 0;
+ }
+
+ memset(&vf->priv->filter, 0, sizeof(vf->priv->filter));
+ vf->priv->filter.major_version = VF_DLOPEN_MAJOR_VERSION;
+ vf->priv->filter.minor_version = VF_DLOPEN_MINOR_VERSION;
+
+ // count arguments
+ for (vf->priv->cfg_argc = 0;
+ vf->priv->cfg_argc < sizeof(vf->priv->cfg_argv) / sizeof(vf->priv->cfg_argv[0]) && vf->priv->cfg_argv[vf->priv->cfg_argc];
+ ++vf->priv->cfg_argc)
+ ;
+
+ if (func(&vf->priv->filter, vf->priv->cfg_argc, vf->priv->cfg_argv) < 0) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "function did not create a filter: %s\n",
+ vf->priv->cfg_dllname);
+ return 0;
+ }
+
+ if (!vf->priv->filter.put_image) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR,
+ "function did not create a filter that can put images: %s\n",
+ vf->priv->cfg_dllname);
+ return 0;
+ }
+
+ vf->put_image = put_image;
+ vf->query_format = query_format;
+ vf->config = config;
+ vf->uninit = uninit;
+
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s, f)
+static m_option_t vf_opts_fields[] = {
+ {"dll", ST_OFF(cfg_dllname), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a0", ST_OFF(cfg_argv[0]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a1", ST_OFF(cfg_argv[1]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a2", ST_OFF(cfg_argv[2]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ {"a3", ST_OFF(cfg_argv[3]), CONF_TYPE_STRING, 0, 0, 0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "dlopen",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_dlopen = {
+ "Dynamic library filter",
+ "dlopen",
+ "Rudolf Polzer",
+ "",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_dlopen.h b/video/filter/vf_dlopen.h
new file mode 100644
index 0000000000..962605ca28
--- /dev/null
+++ b/video/filter/vf_dlopen.h
@@ -0,0 +1,88 @@
+#ifndef VF_DLOPEN_H
+#define VF_DLOPEN_H
+
+// when doing a two-way compatible change, don't change these
+// when doing a backwards compatible change, bump minor version
+// when doing an incompatible change, bump major version and zero minor version
+#define VF_DLOPEN_MAJOR_VERSION 1
+#define VF_DLOPEN_MINOR_VERSION 0
+
+#if VF_DLOPEN_MINOR_VERSION > 0
+# define VF_DLOPEN_CHECK_VERSION(ctx) \
+ do { \
+ if (ctx->major_version != VF_DLOPEN_MAJOR_VERSION || \
+ ctx->minor_version < VF_DLOPEN_MINOR_VERSION) \
+ return -1; \
+ } while (0)
+#else
+// workaround for "comparison is always false" warning
+# define VF_DLOPEN_CHECK_VERSION(ctx) \
+ do { \
+ if (ctx->major_version != VF_DLOPEN_MAJOR_VERSION) \
+ return -1; \
+ } while (0)
+#endif
+
+// valid pixel format names:
+// "yv12": planar YUV, U and V planes have an xshift and yshift of 1
+// "rgb24": packed RGB24
+struct vf_dlopen_formatpair {
+ const char *from; // (LATER) can also be a name of a format class
+ const char *to; // if NULL, this means identical format as source
+};
+
+#define FILTER_MAX_OUTCNT 16
+
+struct vf_dlopen_picdata {
+ unsigned int planes;
+ unsigned char *plane[4];
+ signed int planestride[4];
+ unsigned int planewidth[4];
+ unsigned int planeheight[4];
+ unsigned int planexshift[4];
+ unsigned int planeyshift[4];
+ double pts;
+};
+
+struct vf_dlopen_context {
+ unsigned short major_version;
+ unsigned short minor_version;
+
+ void *priv;
+
+ struct vf_dlopen_formatpair *format_mapping;
+ // {NULL, NULL} terminated list of supported format pairs
+ // if NULL, anything goes
+
+ int (*config)(struct vf_dlopen_context *ctx); // -1 = error
+ // image config is put into the in_* members before calling this
+ // fills in the out_* members (which are preinitialized for an identity vf_dlopen_context)
+
+ int (*put_image)(struct vf_dlopen_context *ctx); // returns number of images written, or negative on error
+ // before this is called, inpic_* and outpic_* are filled
+
+ void (*uninit)(struct vf_dlopen_context *ctx);
+
+ unsigned int in_width;
+ unsigned int in_height;
+ unsigned int in_d_width;
+ unsigned int in_d_height;
+ const char *in_fmt;
+ unsigned int out_width;
+ unsigned int out_height;
+ unsigned int out_d_width;
+ unsigned int out_d_height;
+ const char *out_fmt;
+ unsigned int out_cnt;
+
+ struct vf_dlopen_picdata inpic;
+ char *inpic_qscale;
+ unsigned int inpic_qscalestride;
+ unsigned int inpic_qscaleshift;
+
+ struct vf_dlopen_picdata outpic[FILTER_MAX_OUTCNT];
+};
+typedef int (vf_dlopen_getcontext_func)(struct vf_dlopen_context *ctx, int argc, const char **argv); // negative on error
+vf_dlopen_getcontext_func vf_dlopen_getcontext;
+
+#endif
diff --git a/video/filter/vf_down3dright.c b/video/filter/vf_down3dright.c
new file mode 100644
index 0000000000..561bc898d0
--- /dev/null
+++ b/video/filter/vf_down3dright.c
@@ -0,0 +1,166 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+struct vf_priv_s {
+ int skipline;
+ int scalew;
+ int scaleh;
+};
+
+static void toright(unsigned char *dst[3], unsigned char *src[3],
+ int dststride[3], int srcstride[3],
+ int w, int h, struct vf_priv_s* p)
+{
+ int k;
+
+ for (k = 0; k < 3; k++) {
+ unsigned char* fromL = src[k];
+ unsigned char* fromR = src[k];
+ unsigned char* to = dst[k];
+ int src = srcstride[k];
+ int dst = dststride[k];
+ int ss;
+ unsigned int dd;
+ int i;
+
+ if (k > 0) {
+ i = h / 4 - p->skipline / 2;
+ ss = src * (h / 4 + p->skipline / 2);
+ dd = w / 4;
+ } else {
+ i = h / 2 - p->skipline;
+ ss = src * (h / 2 + p->skipline);
+ dd = w / 2;
+ }
+ fromR += ss;
+ for ( ; i > 0; i--) {
+ int j;
+ unsigned char* t = to;
+ unsigned char* sL = fromL;
+ unsigned char* sR = fromR;
+
+ if (p->scalew == 1) {
+ for (j = dd; j > 0; j--) {
+ *t++ = (sL[0] + sL[1]) / 2;
+ sL+=2;
+ }
+ for (j = dd ; j > 0; j--) {
+ *t++ = (sR[0] + sR[1]) / 2;
+ sR+=2;
+ }
+ } else {
+ for (j = dd * 2 ; j > 0; j--)
+ *t++ = *sL++;
+ for (j = dd * 2 ; j > 0; j--)
+ *t++ = *sR++;
+ }
+ if (p->scaleh == 1) {
+ memcpy(to + dst, to, dst);
+ to += dst;
+ }
+ to += dst;
+ fromL += src;
+ fromR += src;
+ }
+ //printf("K %d %d %d %d %d \n", k, w, h, src, dst);
+ }
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ mp_image_t *dmpi;
+
+ // hope we'll get DR buffer:
+ dmpi=vf_get_image(vf->next, IMGFMT_YV12,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE |
+ ((vf->priv->scaleh == 1) ? MP_IMGFLAG_READABLE : 0),
+ mpi->w * vf->priv->scalew,
+ mpi->h / vf->priv->scaleh - vf->priv->skipline);
+
+ toright(dmpi->planes, mpi->planes, dmpi->stride,
+ mpi->stride, mpi->w, mpi->h, vf->priv);
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ /* FIXME - also support UYVY output? */
+ return vf_next_config(vf, width * vf->priv->scalew,
+ height / vf->priv->scaleh - vf->priv->skipline, d_width, d_height, flags, IMGFMT_YV12);
+}
+
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ /* FIXME - really any YUV 4:2:0 input format should work */
+ switch (fmt) {
+ case IMGFMT_YV12:
+ case IMGFMT_IYUV:
+ case IMGFMT_I420:
+ return vf_next_query_format(vf, IMGFMT_YV12);
+ }
+ return 0;
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config=config;
+ vf->query_format=query_format;
+ vf->put_image=put_image;
+ vf->uninit=uninit;
+
+ vf->priv = calloc(1, sizeof (struct vf_priv_s));
+ vf->priv->skipline = 0;
+ vf->priv->scalew = 1;
+ vf->priv->scaleh = 2;
+ if (args) sscanf(args, "%d:%d:%d", &vf->priv->skipline, &vf->priv->scalew, &vf->priv->scaleh);
+
+ return 1;
+}
+
+const vf_info_t vf_info_down3dright = {
+ "convert stereo movie from top-bottom to left-right field",
+ "down3dright",
+ "Zdenek Kabelac",
+ "",
+ vf_open,
+ NULL
+};
diff --git a/video/filter/vf_dsize.c b/video/filter/vf_dsize.c
new file mode 100644
index 0000000000..d46d22ebb2
--- /dev/null
+++ b/video/filter/vf_dsize.c
@@ -0,0 +1,125 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+struct vf_priv_s {
+ int w, h;
+ int method; // aspect method, 0 -> downscale, 1-> upscale. +2 -> original aspect.
+ int round;
+ float aspect;
+};
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ int w = vf->priv->w;
+ int h = vf->priv->h;
+ if (vf->priv->aspect < 0.001) { // did the user input aspect or w,h params
+ if (w == 0) w = d_width;
+ if (h == 0) h = d_height;
+ if (w == -1) w = width;
+ if (h == -1) h = height;
+ if (w == -2) w = h * (double)d_width / d_height;
+ if (w == -3) w = h * (double)width / height;
+ if (h == -2) h = w * (double)d_height / d_width;
+ if (h == -3) h = w * (double)height / width;
+ if (vf->priv->method > -1) {
+ double aspect = (vf->priv->method & 2) ? ((double)height / width) : ((double)d_height / d_width);
+ if ((h > w * aspect) ^ (vf->priv->method & 1)) {
+ h = w * aspect;
+ } else {
+ w = h / aspect;
+ }
+ }
+ if (vf->priv->round > 1) { // round up
+ w += (vf->priv->round - 1 - (w - 1) % vf->priv->round);
+ h += (vf->priv->round - 1 - (h - 1) % vf->priv->round);
+ }
+ d_width = w;
+ d_height = h;
+ } else {
+ if (vf->priv->aspect * height > width) {
+ d_width = height * vf->priv->aspect + .5;
+ d_height = height;
+ } else {
+ d_height = width / vf->priv->aspect + .5;
+ d_width = width;
+ }
+ }
+ return vf_next_config(vf, width, height, d_width, d_height, flags, outfmt);
+}
+
+static void uninit(vf_instance_t *vf) {
+ free(vf->priv);
+ vf->priv = NULL;
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config = config;
+ vf->draw_slice = vf_next_draw_slice;
+ vf->uninit = uninit;
+ //vf->default_caps = 0;
+ vf->priv = calloc(sizeof(struct vf_priv_s), 1);
+ vf->priv->aspect = 0.;
+ vf->priv->w = -1;
+ vf->priv->h = -1;
+ vf->priv->method = -1;
+ vf->priv->round = 1;
+ if (args) {
+ if (strchr(args, '/')) {
+ int w, h;
+ sscanf(args, "%d/%d", &w, &h);
+ vf->priv->aspect = (float)w/h;
+ } else if (strchr(args, '.')) {
+ sscanf(args, "%f", &vf->priv->aspect);
+ } else {
+ sscanf(args, "%d:%d:%d:%d", &vf->priv->w, &vf->priv->h, &vf->priv->method, &vf->priv->round);
+ }
+ }
+ if ((vf->priv->aspect < 0.) || (vf->priv->w < -3) || (vf->priv->h < -3) ||
+ ((vf->priv->w < -1) && (vf->priv->h < -1)) ||
+ (vf->priv->method < -1) || (vf->priv->method > 3) ||
+ (vf->priv->round < 0)) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "[dsize] Illegal value(s): aspect: %f w: %d h: %d aspect_method: %d round: %d\n", vf->priv->aspect, vf->priv->w, vf->priv->h, vf->priv->method, vf->priv->round);
+ free(vf->priv); vf->priv = NULL;
+ return -1;
+ }
+ return 1;
+}
+
+const vf_info_t vf_info_dsize = {
+ "reset displaysize/aspect",
+ "dsize",
+ "Rich Felker",
+ "",
+ vf_open,
+ NULL
+};
diff --git a/video/filter/vf_eq2.c b/video/filter/vf_eq2.c
new file mode 100644
index 0000000000..fe4a89fb13
--- /dev/null
+++ b/video/filter/vf_eq2.c
@@ -0,0 +1,519 @@
+/*
+ * Software equalizer (brightness, contrast, gamma, saturation)
+ *
+ * Hampa Hug <hampa@hampa.ch> (original LUT gamma/contrast/brightness filter)
+ * Daniel Moreno <comac@comac.darktech.org> (saturation, R/G/B gamma support)
+ * Richard Felker (original MMX contrast/brightness code (vf_eq.c))
+ * Michael Niedermayer <michalni@gmx.at> (LUT16)
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#define LUT16
+
+/* Per channel parameters */
+typedef struct eq2_param_t {
+ unsigned char lut[256];
+#ifdef LUT16
+ uint16_t lut16[256*256];
+#endif
+ int lut_clean;
+
+ void (*adjust) (struct eq2_param_t *par, unsigned char *dst, unsigned char *src,
+ unsigned w, unsigned h, unsigned dstride, unsigned sstride);
+
+ double c;
+ double b;
+ double g;
+ double w;
+} eq2_param_t;
+
+typedef struct vf_priv_s {
+ eq2_param_t param[3];
+
+ double contrast;
+ double brightness;
+ double saturation;
+
+ double gamma;
+ double gamma_weight;
+ double rgamma;
+ double ggamma;
+ double bgamma;
+
+ unsigned buf_w[3];
+ unsigned buf_h[3];
+ unsigned char *buf[3];
+} vf_eq2_t;
+
+
+static
+void create_lut (eq2_param_t *par)
+{
+ unsigned i;
+ double g, v;
+ double lw, gw;
+
+ g = par->g;
+ gw = par->w;
+ lw = 1.0 - gw;
+
+ if ((g < 0.001) || (g > 1000.0)) {
+ g = 1.0;
+ }
+
+ g = 1.0 / g;
+
+ for (i = 0; i < 256; i++) {
+ v = (double) i / 255.0;
+ v = par->c * (v - 0.5) + 0.5 + par->b;
+
+ if (v <= 0.0) {
+ par->lut[i] = 0;
+ }
+ else {
+ v = v*lw + pow(v, g)*gw;
+
+ if (v >= 1.0) {
+ par->lut[i] = 255;
+ }
+ else {
+ par->lut[i] = (unsigned char) (256.0 * v);
+ }
+ }
+ }
+
+#ifdef LUT16
+ for(i=0; i<256*256; i++){
+ par->lut16[i]= par->lut[i&0xFF] + (par->lut[i>>8]<<8);
+ }
+#endif
+
+ par->lut_clean = 1;
+}
+
+#if HAVE_MMX
+static
+void affine_1d_MMX (eq2_param_t *par, unsigned char *dst, unsigned char *src,
+ unsigned w, unsigned h, unsigned dstride, unsigned sstride)
+{
+ unsigned i;
+ int contrast, brightness;
+ unsigned dstep, sstep;
+ int pel;
+ short brvec[4];
+ short contvec[4];
+
+// printf("\nmmx: src=%p dst=%p w=%d h=%d ds=%d ss=%d\n",src,dst,w,h,dstride,sstride);
+
+ contrast = (int) (par->c * 256 * 16);
+ brightness = ((int) (100.0 * par->b + 100.0) * 511) / 200 - 128 - contrast / 32;
+
+ brvec[0] = brvec[1] = brvec[2] = brvec[3] = brightness;
+ contvec[0] = contvec[1] = contvec[2] = contvec[3] = contrast;
+
+ sstep = sstride - w;
+ dstep = dstride - w;
+
+ while (h-- > 0) {
+ __asm__ volatile (
+ "movq (%5), %%mm3 \n\t"
+ "movq (%6), %%mm4 \n\t"
+ "pxor %%mm0, %%mm0 \n\t"
+ "movl %4, %%eax\n\t"
+ ASMALIGN(4)
+ "1: \n\t"
+ "movq (%0), %%mm1 \n\t"
+ "movq (%0), %%mm2 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ "punpckhbw %%mm0, %%mm2 \n\t"
+ "psllw $4, %%mm1 \n\t"
+ "psllw $4, %%mm2 \n\t"
+ "pmulhw %%mm4, %%mm1 \n\t"
+ "pmulhw %%mm4, %%mm2 \n\t"
+ "paddw %%mm3, %%mm1 \n\t"
+ "paddw %%mm3, %%mm2 \n\t"
+ "packuswb %%mm2, %%mm1 \n\t"
+ "add $8, %0 \n\t"
+ "movq %%mm1, (%1) \n\t"
+ "add $8, %1 \n\t"
+ "decl %%eax \n\t"
+ "jnz 1b \n\t"
+ : "=r" (src), "=r" (dst)
+ : "0" (src), "1" (dst), "r" (w >> 3), "r" (brvec), "r" (contvec)
+ : "%eax"
+ );
+
+ for (i = w & 7; i > 0; i--) {
+ pel = ((*src++ * contrast) >> 12) + brightness;
+ if (pel & 768) {
+ pel = (-pel) >> 31;
+ }
+ *dst++ = pel;
+ }
+
+ src += sstep;
+ dst += dstep;
+ }
+
+ __asm__ volatile ( "emms \n\t" ::: "memory" );
+}
+#endif
+
+static
+void apply_lut (eq2_param_t *par, unsigned char *dst, unsigned char *src,
+ unsigned w, unsigned h, unsigned dstride, unsigned sstride)
+{
+ unsigned i, j, w2;
+ unsigned char *lut;
+ uint16_t *lut16;
+
+ if (!par->lut_clean) {
+ create_lut (par);
+ }
+
+ lut = par->lut;
+#ifdef LUT16
+ lut16 = par->lut16;
+ w2= (w>>3)<<2;
+ for (j = 0; j < h; j++) {
+ uint16_t *src16= (uint16_t*)src;
+ uint16_t *dst16= (uint16_t*)dst;
+ for (i = 0; i < w2; i+=4) {
+ dst16[i+0] = lut16[src16[i+0]];
+ dst16[i+1] = lut16[src16[i+1]];
+ dst16[i+2] = lut16[src16[i+2]];
+ dst16[i+3] = lut16[src16[i+3]];
+ }
+ i <<= 1;
+#else
+ w2= (w>>3)<<3;
+ for (j = 0; j < h; j++) {
+ for (i = 0; i < w2; i+=8) {
+ dst[i+0] = lut[src[i+0]];
+ dst[i+1] = lut[src[i+1]];
+ dst[i+2] = lut[src[i+2]];
+ dst[i+3] = lut[src[i+3]];
+ dst[i+4] = lut[src[i+4]];
+ dst[i+5] = lut[src[i+5]];
+ dst[i+6] = lut[src[i+6]];
+ dst[i+7] = lut[src[i+7]];
+ }
+#endif
+ for (; i < w; i++) {
+ dst[i] = lut[src[i]];
+ }
+
+ src += sstride;
+ dst += dstride;
+ }
+}
+
+static
+int put_image (vf_instance_t *vf, mp_image_t *src, double pts)
+{
+ unsigned i;
+ vf_eq2_t *eq2;
+ mp_image_t *dst;
+ unsigned long img_n,img_c;
+
+ eq2 = vf->priv;
+
+ if ((eq2->buf_w[0] != src->w) || (eq2->buf_h[0] != src->h)) {
+ eq2->buf_w[0] = src->w;
+ eq2->buf_h[0] = src->h;
+ eq2->buf_w[1] = eq2->buf_w[2] = src->w >> src->chroma_x_shift;
+ eq2->buf_h[1] = eq2->buf_h[2] = src->h >> src->chroma_y_shift;
+ img_n = eq2->buf_w[0]*eq2->buf_h[0];
+ if(src->num_planes>1){
+ img_c = eq2->buf_w[1]*eq2->buf_h[1];
+ eq2->buf[0] = realloc (eq2->buf[0], img_n + 2*img_c);
+ eq2->buf[1] = eq2->buf[0] + img_n;
+ eq2->buf[2] = eq2->buf[1] + img_c;
+ } else
+ eq2->buf[0] = realloc (eq2->buf[0], img_n);
+ }
+
+ dst = vf_get_image (vf->next, src->imgfmt, MP_IMGTYPE_EXPORT, 0, src->w, src->h);
+
+ for (i = 0; i < ((src->num_planes>1)?3:1); i++) {
+ if (eq2->param[i].adjust != NULL) {
+ dst->planes[i] = eq2->buf[i];
+ dst->stride[i] = eq2->buf_w[i];
+
+ eq2->param[i].adjust (&eq2->param[i], dst->planes[i], src->planes[i],
+ eq2->buf_w[i], eq2->buf_h[i], dst->stride[i], src->stride[i]);
+ }
+ else {
+ dst->planes[i] = src->planes[i];
+ dst->stride[i] = src->stride[i];
+ }
+ }
+
+ return vf_next_put_image (vf, dst, pts);
+}
+
+static
+void check_values (eq2_param_t *par)
+{
+ /* yuck! floating point comparisons... */
+
+ if ((par->c == 1.0) && (par->b == 0.0) && (par->g == 1.0)) {
+ par->adjust = NULL;
+ }
+#if HAVE_MMX
+ else if (par->g == 1.0 && gCpuCaps.hasMMX) {
+ par->adjust = &affine_1d_MMX;
+ }
+#endif
+ else {
+ par->adjust = &apply_lut;
+ }
+}
+
+static
+void print_values (vf_eq2_t *eq2)
+{
+ mp_msg (MSGT_VFILTER, MSGL_V, "vf_eq2: c=%.2f b=%.2f g=%.4f s=%.2f \n",
+ eq2->contrast, eq2->brightness, eq2->gamma, eq2->saturation
+ );
+}
+
+static
+void set_contrast (vf_eq2_t *eq2, double c)
+{
+ eq2->contrast = c;
+ eq2->param[0].c = c;
+ eq2->param[0].lut_clean = 0;
+ check_values (&eq2->param[0]);
+ print_values (eq2);
+}
+
+static
+void set_brightness (vf_eq2_t *eq2, double b)
+{
+ eq2->brightness = b;
+ eq2->param[0].b = b;
+ eq2->param[0].lut_clean = 0;
+ check_values (&eq2->param[0]);
+ print_values (eq2);
+}
+
+static
+void set_gamma (vf_eq2_t *eq2, double g)
+{
+ eq2->gamma = g;
+
+ eq2->param[0].g = eq2->gamma * eq2->ggamma;
+ eq2->param[1].g = sqrt (eq2->bgamma / eq2->ggamma);
+ eq2->param[2].g = sqrt (eq2->rgamma / eq2->ggamma);
+ eq2->param[0].w = eq2->param[1].w = eq2->param[2].w = eq2->gamma_weight;
+
+ eq2->param[0].lut_clean = 0;
+ eq2->param[1].lut_clean = 0;
+ eq2->param[2].lut_clean = 0;
+
+ check_values (&eq2->param[0]);
+ check_values (&eq2->param[1]);
+ check_values (&eq2->param[2]);
+
+ print_values (eq2);
+}
+
+static
+void set_saturation (vf_eq2_t *eq2, double s)
+{
+ eq2->saturation = s;
+
+ eq2->param[1].c = s;
+ eq2->param[2].c = s;
+
+ eq2->param[1].lut_clean = 0;
+ eq2->param[2].lut_clean = 0;
+
+ check_values (&eq2->param[1]);
+ check_values (&eq2->param[2]);
+
+ print_values (eq2);
+}
+
+static
+int control (vf_instance_t *vf, int request, void *data)
+{
+ vf_equalizer_t *eq;
+
+ switch (request) {
+ case VFCTRL_SET_EQUALIZER:
+ eq = (vf_equalizer_t *) data;
+
+ if (strcmp (eq->item, "gamma") == 0) {
+ set_gamma (vf->priv, exp (log (8.0) * eq->value / 100.0));
+ return CONTROL_TRUE;
+ }
+ else if (strcmp (eq->item, "contrast") == 0) {
+ set_contrast (vf->priv, (1.0 / 100.0) * (eq->value + 100));
+ return CONTROL_TRUE;
+ }
+ else if (strcmp (eq->item, "brightness") == 0) {
+ set_brightness (vf->priv, (1.0 / 100.0) * eq->value);
+ return CONTROL_TRUE;
+ }
+ else if (strcmp (eq->item, "saturation") == 0) {
+ set_saturation (vf->priv, (double) (eq->value + 100) / 100.0);
+ return CONTROL_TRUE;
+ }
+ break;
+
+ case VFCTRL_GET_EQUALIZER:
+ eq = (vf_equalizer_t *) data;
+ if (strcmp (eq->item, "gamma") == 0) {
+ eq->value = (int) (100.0 * log (vf->priv->gamma) / log (8.0));
+ return CONTROL_TRUE;
+ }
+ else if (strcmp (eq->item, "contrast") == 0) {
+ eq->value = (int) (100.0 * vf->priv->contrast) - 100;
+ return CONTROL_TRUE;
+ }
+ else if (strcmp (eq->item, "brightness") == 0) {
+ eq->value = (int) (100.0 * vf->priv->brightness);
+ return CONTROL_TRUE;
+ }
+ else if (strcmp (eq->item, "saturation") == 0) {
+ eq->value = (int) (100.0 * vf->priv->saturation) - 100;
+ return CONTROL_TRUE;
+ }
+ break;
+ }
+
+ return vf_next_control (vf, request, data);
+}
+
+static
+int query_format (vf_instance_t *vf, unsigned fmt)
+{
+ switch (fmt) {
+ case IMGFMT_YVU9:
+ case IMGFMT_IF09:
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_Y800:
+ case IMGFMT_Y8:
+ case IMGFMT_444P:
+ case IMGFMT_422P:
+ case IMGFMT_411P:
+ return vf_next_query_format (vf, fmt);
+ }
+
+ return 0;
+}
+
+static
+void uninit (vf_instance_t *vf)
+{
+ if (vf->priv != NULL) {
+ free (vf->priv->buf[0]);
+ free (vf->priv);
+ }
+}
+
+static
+int vf_open(vf_instance_t *vf, char *args)
+{
+ unsigned i;
+ vf_eq2_t *eq2;
+ double par[8];
+
+ vf->control = control;
+ vf->query_format = query_format;
+ vf->put_image = put_image;
+ vf->uninit = uninit;
+
+ vf->priv = malloc (sizeof (vf_eq2_t));
+ eq2 = vf->priv;
+
+ for (i = 0; i < 3; i++) {
+ eq2->buf[i] = NULL;
+ eq2->buf_w[i] = 0;
+ eq2->buf_h[i] = 0;
+
+ eq2->param[i].adjust = NULL;
+ eq2->param[i].c = 1.0;
+ eq2->param[i].b = 0.0;
+ eq2->param[i].g = 1.0;
+ eq2->param[i].lut_clean = 0;
+ }
+
+ eq2->contrast = 1.0;
+ eq2->brightness = 0.0;
+ eq2->saturation = 1.0;
+
+ eq2->gamma = 1.0;
+ eq2->gamma_weight = 1.0;
+ eq2->rgamma = 1.0;
+ eq2->ggamma = 1.0;
+ eq2->bgamma = 1.0;
+
+ if (args != NULL) {
+ par[0] = 1.0;
+ par[1] = 1.0;
+ par[2] = 0.0;
+ par[3] = 1.0;
+ par[4] = 1.0;
+ par[5] = 1.0;
+ par[6] = 1.0;
+ par[7] = 1.0;
+ sscanf (args, "%lf:%lf:%lf:%lf:%lf:%lf:%lf:%lf",
+ par, par + 1, par + 2, par + 3, par + 4, par + 5, par + 6, par + 7
+ );
+
+ eq2->rgamma = par[4];
+ eq2->ggamma = par[5];
+ eq2->bgamma = par[6];
+ eq2->gamma_weight = par[7];
+
+ set_gamma (eq2, par[0]);
+ set_contrast (eq2, par[1]);
+ set_brightness (eq2, par[2]);
+ set_saturation (eq2, par[3]);
+ }
+
+ return 1;
+}
+
+const vf_info_t vf_info_eq2 = {
+ "Software equalizer",
+ "eq2",
+ "Hampa Hug, Daniel Moreno, Richard Felker",
+ "",
+ &vf_open,
+ NULL
+};
diff --git a/video/filter/vf_expand.c b/video/filter/vf_expand.c
new file mode 100644
index 0000000000..839820510d
--- /dev/null
+++ b/video/filter/vf_expand.c
@@ -0,0 +1,350 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <libavutil/common.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+static struct vf_priv_s {
+ // These four values are a backup of the values parsed from the command line.
+ // This is necessary so that we do not get a mess upon filter reinit due to
+ // e.g. aspect changes and with only aspect specified on the command line,
+ // where we would otherwise use the values calculated for a different aspect
+ // instead of recalculating them again.
+ int cfg_exp_w, cfg_exp_h;
+ int cfg_exp_x, cfg_exp_y;
+ int exp_w,exp_h;
+ int exp_x,exp_y;
+ double aspect;
+ int round;
+ int first_slice;
+} const vf_priv_dflt = {
+ -1,-1,
+ -1,-1,
+ -1,-1,
+ -1,-1,
+ 0.,
+ 1,
+ 0
+};
+
+//===========================================================================//
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ struct MPOpts *opts = vf->opts;
+ mp_image_t test_mpi;
+ mp_image_setfmt(&test_mpi, outfmt);
+ if (outfmt == IMGFMT_IF09 || !test_mpi.bpp) return 0;
+ vf->priv->exp_x = vf->priv->cfg_exp_x;
+ vf->priv->exp_y = vf->priv->cfg_exp_y;
+ vf->priv->exp_w = vf->priv->cfg_exp_w;
+ vf->priv->exp_h = vf->priv->cfg_exp_h;
+ // calculate the missing parameters:
+#if 0
+ if(vf->priv->exp_w<width) vf->priv->exp_w=width;
+ if(vf->priv->exp_h<height) vf->priv->exp_h=height;
+#else
+ if ( vf->priv->exp_w == -1 ) vf->priv->exp_w=width;
+ else if (vf->priv->exp_w < -1 ) vf->priv->exp_w=width - vf->priv->exp_w;
+ else if ( vf->priv->exp_w<width ) vf->priv->exp_w=width;
+ if ( vf->priv->exp_h == -1 ) vf->priv->exp_h=height;
+ else if ( vf->priv->exp_h < -1 ) vf->priv->exp_h=height - vf->priv->exp_h;
+ else if( vf->priv->exp_h<height ) vf->priv->exp_h=height;
+#endif
+ if (vf->priv->aspect) {
+ float adjusted_aspect = vf->priv->aspect;
+ adjusted_aspect *= ((double)width/height) / ((double)d_width/d_height);
+ if (vf->priv->exp_h < vf->priv->exp_w / adjusted_aspect) {
+ vf->priv->exp_h = vf->priv->exp_w / adjusted_aspect + 0.5;
+ } else {
+ vf->priv->exp_w = vf->priv->exp_h * adjusted_aspect + 0.5;
+ }
+ }
+ if (vf->priv->round > 1) { // round up.
+ vf->priv->exp_w = (1 + (vf->priv->exp_w - 1) / vf->priv->round) * vf->priv->round;
+ vf->priv->exp_h = (1 + (vf->priv->exp_h - 1) / vf->priv->round) * vf->priv->round;
+ }
+
+ if(vf->priv->exp_x<0 || vf->priv->exp_x+width>vf->priv->exp_w) vf->priv->exp_x=(vf->priv->exp_w-width)/2;
+ if(vf->priv->exp_y<0 || vf->priv->exp_y+height>vf->priv->exp_h) vf->priv->exp_y=(vf->priv->exp_h-height)/2;
+ if(test_mpi.flags & MP_IMGFLAG_YUV) {
+ int x_align_mask = (1 << test_mpi.chroma_x_shift) - 1;
+ int y_align_mask = (1 << test_mpi.chroma_y_shift) - 1;
+ // For 1-plane format non-aligned offsets will completely
+ // destroy the colours, for planar it will break the chroma
+ // sampling position.
+ if (vf->priv->exp_x & x_align_mask) {
+ vf->priv->exp_x &= ~x_align_mask;
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "Specified x offset not supported "
+ "for YUV, reduced to %i.\n", vf->priv->exp_x);
+ }
+ if (vf->priv->exp_y & y_align_mask) {
+ vf->priv->exp_y &= ~y_align_mask;
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "Specified y offset not supported "
+ "for YUV, reduced to %i.\n", vf->priv->exp_y);
+ }
+ }
+
+ if(!opts->screen_size_x && !opts->screen_size_y){
+ d_width=d_width*vf->priv->exp_w/width;
+ d_height=d_height*vf->priv->exp_h/height;
+ }
+ return vf_next_config(vf,vf->priv->exp_w,vf->priv->exp_h,d_width,d_height,flags,outfmt);
+}
+
+// there are 4 cases:
+// codec --DR--> expand --DR--> vo
+// codec --DR--> expand -copy-> vo
+// codec -copy-> expand --DR--> vo
+// codec -copy-> expand -copy-> vo (worst case)
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi){
+// if(mpi->type==MP_IMGTYPE_IPB) return; // not yet working
+ if(vf->priv->exp_w==mpi->width ||
+ (mpi->flags&(MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_ACCEPT_WIDTH)) ){
+ // try full DR !
+ mpi->priv=vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ mpi->type, mpi->flags,
+ FFMAX(vf->priv->exp_w, mpi->width +vf->priv->exp_x),
+ FFMAX(vf->priv->exp_h, mpi->height+vf->priv->exp_y));
+ if((vf->dmpi->flags & MP_IMGFLAG_DRAW_CALLBACK) &&
+ !(vf->dmpi->flags & MP_IMGFLAG_DIRECT)){
+ mp_tmsg(MSGT_VFILTER, MSGL_INFO, "Full DR not possible, trying SLICES instead!\n");
+ return;
+ }
+ // set up mpi as a cropped-down image of dmpi:
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ mpi->planes[0]=vf->dmpi->planes[0]+
+ vf->priv->exp_y*vf->dmpi->stride[0]+vf->priv->exp_x;
+ mpi->planes[1]=vf->dmpi->planes[1]+
+ (vf->priv->exp_y>>mpi->chroma_y_shift)*vf->dmpi->stride[1]+(vf->priv->exp_x>>mpi->chroma_x_shift);
+ mpi->planes[2]=vf->dmpi->planes[2]+
+ (vf->priv->exp_y>>mpi->chroma_y_shift)*vf->dmpi->stride[2]+(vf->priv->exp_x>>mpi->chroma_x_shift);
+ mpi->stride[1]=vf->dmpi->stride[1];
+ mpi->stride[2]=vf->dmpi->stride[2];
+ } else {
+ mpi->planes[0]=vf->dmpi->planes[0]+
+ vf->priv->exp_y*vf->dmpi->stride[0]+
+ vf->priv->exp_x*(vf->dmpi->bpp/8);
+ }
+ mpi->stride[0]=vf->dmpi->stride[0];
+ mpi->width=vf->dmpi->width;
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+ mpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
+// vf->dmpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
+ }
+}
+
+static void start_slice(struct vf_instance *vf, mp_image_t *mpi){
+// printf("start_slice called! flag=%d\n",mpi->flags&MP_IMGFLAG_DRAW_CALLBACK);
+ // they want slices!!! allocate the buffer.
+ if(!mpi->priv)
+ mpi->priv=vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+// MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
+ MP_IMGTYPE_TEMP, mpi->flags,
+ FFMAX(vf->priv->exp_w, mpi->width +vf->priv->exp_x),
+ FFMAX(vf->priv->exp_h, mpi->height+vf->priv->exp_y));
+ vf->priv->first_slice = 1;
+}
+
+static void draw_top_blackbar_slice(struct vf_instance *vf,
+ unsigned char** src, int* stride, int w,int h, int x, int y){
+ if(vf->priv->exp_y>0 && y == 0) {
+ vf_next_draw_slice(vf, vf->dmpi->planes, vf->dmpi->stride,
+ vf->dmpi->w,vf->priv->exp_y,0,0);
+ }
+
+}
+
+static void draw_bottom_blackbar_slice(struct vf_instance *vf,
+ unsigned char** src, int* stride, int w,int h, int x, int y){
+ if(vf->priv->exp_y+vf->h<vf->dmpi->h && y+h == vf->h) {
+ unsigned char *src2[MP_MAX_PLANES];
+ src2[0] = vf->dmpi->planes[0]
+ + (vf->priv->exp_y+vf->h)*vf->dmpi->stride[0];
+ if(vf->dmpi->flags&MP_IMGFLAG_PLANAR){
+ src2[1] = vf->dmpi->planes[1]
+ + ((vf->priv->exp_y+vf->h)>>vf->dmpi->chroma_y_shift)*vf->dmpi->stride[1];
+ src2[2] = vf->dmpi->planes[2]
+ + ((vf->priv->exp_y+vf->h)>>vf->dmpi->chroma_y_shift)*vf->dmpi->stride[2];
+ } else {
+ src2[1] = vf->dmpi->planes[1]; // passthrough rgb8 palette
+ }
+ vf_next_draw_slice(vf, src2, vf->dmpi->stride,
+ vf->dmpi->w,vf->dmpi->h-(vf->priv->exp_y+vf->h),
+ 0,vf->priv->exp_y+vf->h);
+ }
+}
+
+static void draw_slice(struct vf_instance *vf,
+ unsigned char** src, int* stride, int w,int h, int x, int y){
+// printf("draw_slice() called %d at %d\n",h,y);
+
+ if (y == 0 && y+h == vf->h) {
+ // special case - only one slice
+ draw_top_blackbar_slice(vf, src, stride, w, h, x, y);
+ vf_next_draw_slice(vf,src,stride,w,h,x+vf->priv->exp_x,y+vf->priv->exp_y);
+ draw_bottom_blackbar_slice(vf, src, stride, w, h, x, y);
+ return;
+ }
+ if (vf->priv->first_slice) {
+ draw_top_blackbar_slice(vf, src, stride, w, h, x, y);
+ draw_bottom_blackbar_slice(vf, src, stride, w, h, x, y);
+ }
+ vf_next_draw_slice(vf,src,stride,w,h,x+vf->priv->exp_x,y+vf->priv->exp_y);
+ if (!vf->priv->first_slice) {
+ draw_top_blackbar_slice(vf, src, stride, w, h, x, y);
+ draw_bottom_blackbar_slice(vf, src, stride, w, h, x, y);
+ }
+ vf->priv->first_slice = 0;
+}
+
+// w, h = width and height of the actual video frame (located at exp_x/exp_y)
+static void clear_borders(struct vf_instance *vf, int w, int h)
+{
+ // upper border (over the full width)
+ vf_mpi_clear(vf->dmpi, 0, 0, vf->priv->exp_w, vf->priv->exp_y);
+ // lower border
+ vf_mpi_clear(vf->dmpi, 0, vf->priv->exp_y + h, vf->priv->exp_w,
+ vf->priv->exp_h - (vf->priv->exp_y + h));
+ // left
+ vf_mpi_clear(vf->dmpi, 0, vf->priv->exp_y, vf->priv->exp_x, h);
+ // right
+ vf_mpi_clear(vf->dmpi, vf->priv->exp_x + w, vf->priv->exp_y,
+ vf->priv->exp_w - (vf->priv->exp_x + w), h);
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ if(mpi->flags&MP_IMGFLAG_DIRECT || mpi->flags&MP_IMGFLAG_DRAW_CALLBACK){
+ vf->dmpi=mpi->priv;
+ if(!vf->dmpi) { mp_tmsg(MSGT_VFILTER, MSGL_WARN, "Why do we get NULL??\n"); return 0; }
+ mpi->priv=NULL;
+ clear_borders(vf,mpi->w,mpi->h);
+ // we've used DR, so we're ready...
+ if(!(mpi->flags&MP_IMGFLAG_PLANAR))
+ vf->dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette
+ return vf_next_put_image(vf,vf->dmpi, pts);
+ }
+
+ // hope we'll get DR buffer:
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ vf->priv->exp_w, vf->priv->exp_h);
+
+ // copy mpi->dmpi...
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ memcpy_pic(vf->dmpi->planes[0]+
+ vf->priv->exp_y*vf->dmpi->stride[0]+vf->priv->exp_x,
+ mpi->planes[0], mpi->w, mpi->h,
+ vf->dmpi->stride[0],mpi->stride[0]);
+ memcpy_pic(vf->dmpi->planes[1]+
+ (vf->priv->exp_y>>mpi->chroma_y_shift)*vf->dmpi->stride[1]+(vf->priv->exp_x>>mpi->chroma_x_shift),
+ mpi->planes[1], (mpi->w>>mpi->chroma_x_shift), (mpi->h>>mpi->chroma_y_shift),
+ vf->dmpi->stride[1],mpi->stride[1]);
+ memcpy_pic(vf->dmpi->planes[2]+
+ (vf->priv->exp_y>>mpi->chroma_y_shift)*vf->dmpi->stride[2]+(vf->priv->exp_x>>mpi->chroma_x_shift),
+ mpi->planes[2], (mpi->w>>mpi->chroma_x_shift), (mpi->h>>mpi->chroma_y_shift),
+ vf->dmpi->stride[2],mpi->stride[2]);
+ } else {
+ memcpy_pic(vf->dmpi->planes[0]+
+ vf->priv->exp_y*vf->dmpi->stride[0]+vf->priv->exp_x*(vf->dmpi->bpp/8),
+ mpi->planes[0], mpi->w*(vf->dmpi->bpp/8), mpi->h,
+ vf->dmpi->stride[0],mpi->stride[0]);
+ vf->dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette
+ }
+ clear_borders(vf,mpi->w,mpi->h);
+ return vf_next_put_image(vf,vf->dmpi, pts);
+}
+
+//===========================================================================//
+
+static int control(struct vf_instance *vf, int request, void* data){
+ return vf_next_control(vf,request,data);
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ return vf_next_query_format(vf,fmt);
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->control=control;
+ vf->query_format=query_format;
+ vf->start_slice=start_slice;
+ vf->draw_slice=draw_slice;
+ vf->get_image=get_image;
+ vf->put_image=put_image;
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "Expand: %d x %d, %d ; %d, aspect: %f, round: %d\n",
+ vf->priv->cfg_exp_w,
+ vf->priv->cfg_exp_h,
+ vf->priv->cfg_exp_x,
+ vf->priv->cfg_exp_y,
+ vf->priv->aspect,
+ vf->priv->round);
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static m_option_t vf_opts_fields[] = {
+ {"w", ST_OFF(cfg_exp_w), CONF_TYPE_INT, 0, 0 ,0, NULL},
+ {"h", ST_OFF(cfg_exp_h), CONF_TYPE_INT, 0, 0 ,0, NULL},
+ {"x", ST_OFF(cfg_exp_x), CONF_TYPE_INT, M_OPT_MIN, -1, 0, NULL},
+ {"y", ST_OFF(cfg_exp_y), CONF_TYPE_INT, M_OPT_MIN, -1, 0, NULL},
+ {"aspect", ST_OFF(aspect), CONF_TYPE_DOUBLE, M_OPT_MIN, 0, 0, NULL},
+ {"round", ST_OFF(round), CONF_TYPE_INT, M_OPT_MIN, 1, 0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "expand",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+
+
+const vf_info_t vf_info_expand = {
+ "expanding",
+ "expand",
+ "A'rpi",
+ "",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_flip.c b/video/filter/vf_flip.c
new file mode 100644
index 0000000000..e8660ceb51
--- /dev/null
+++ b/video/filter/vf_flip.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/video_out.h"
+
+//===========================================================================//
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+ flags&=~VOFLAG_FLIPPING; // remove the FLIP flag
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi){
+ if(mpi->flags&MP_IMGFLAG_ACCEPT_STRIDE){
+ // try full DR !
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ mpi->type, mpi->flags, mpi->width, mpi->height);
+ // set up mpi as a upside-down image of dmpi:
+ mpi->planes[0]=vf->dmpi->planes[0]+
+ vf->dmpi->stride[0]*(vf->dmpi->height-1);
+ mpi->stride[0]=-vf->dmpi->stride[0];
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ mpi->planes[1]=vf->dmpi->planes[1]+
+ vf->dmpi->stride[1]*((vf->dmpi->height>>mpi->chroma_y_shift)-1);
+ mpi->stride[1]=-vf->dmpi->stride[1];
+ mpi->planes[2]=vf->dmpi->planes[2]+
+ vf->dmpi->stride[2]*((vf->dmpi->height>>mpi->chroma_y_shift)-1);
+ mpi->stride[2]=-vf->dmpi->stride[2];
+ }
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+ mpi->priv=(void*)vf->dmpi;
+ }
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ if(mpi->flags&MP_IMGFLAG_DIRECT){
+ // we've used DR, so we're ready...
+ if(!(mpi->flags&MP_IMGFLAG_PLANAR))
+ ((mp_image_t*)mpi->priv)->planes[1] = mpi->planes[1]; // passthrough rgb8 palette
+ return vf_next_put_image(vf,(mp_image_t*)mpi->priv, pts);
+ }
+
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_EXPORT, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->width, mpi->height);
+
+ // set up mpi as a upside-down image of dmpi:
+ vf->dmpi->planes[0]=mpi->planes[0]+
+ mpi->stride[0]*(mpi->height-1);
+ vf->dmpi->stride[0]=-mpi->stride[0];
+ if(vf->dmpi->flags&MP_IMGFLAG_PLANAR){
+ vf->dmpi->planes[1]=mpi->planes[1]+
+ mpi->stride[1]*((mpi->height>>mpi->chroma_y_shift)-1);
+ vf->dmpi->stride[1]=-mpi->stride[1];
+ vf->dmpi->planes[2]=mpi->planes[2]+
+ mpi->stride[2]*((mpi->height>>mpi->chroma_y_shift)-1);
+ vf->dmpi->stride[2]=-mpi->stride[2];
+ } else
+ vf->dmpi->planes[1]=mpi->planes[1]; // passthru bgr8 palette!!!
+
+ return vf_next_put_image(vf,vf->dmpi, pts);
+}
+
+//===========================================================================//
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->get_image=get_image;
+ vf->put_image=put_image;
+ vf->default_reqs=VFCAP_ACCEPT_STRIDE;
+ return 1;
+}
+
+const vf_info_t vf_info_flip = {
+ "flip image upside-down",
+ "flip",
+ "A'rpi",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c
new file mode 100644
index 0000000000..422956539b
--- /dev/null
+++ b/video/filter/vf_format.c
@@ -0,0 +1,91 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+static struct vf_priv_s {
+ unsigned int fmt;
+ unsigned int outfmt;
+} const vf_priv_dflt = {
+ IMGFMT_YUY2,
+ 0
+};
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ if(fmt==vf->priv->fmt) {
+ if (vf->priv->outfmt)
+ fmt = vf->priv->outfmt;
+ return vf_next_query_format(vf,fmt);
+ }
+ return 0;
+}
+
+static int config(struct vf_instance *vf, int width, int height,
+ int d_width, int d_height,
+ unsigned flags, unsigned outfmt){
+ return vf_next_config(vf, width, height, d_width, d_height, flags, vf->priv->outfmt);
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->query_format=query_format;
+ vf->draw_slice=vf_next_draw_slice;
+ vf->default_caps=0;
+ if (vf->priv->outfmt)
+ vf->config=config;
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"fmt", ST_OFF(fmt), CONF_TYPE_IMGFMT, 0,0 ,0, NULL},
+ {"outfmt", ST_OFF(outfmt), CONF_TYPE_IMGFMT, 0,0 ,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "format",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_format = {
+ "force output format",
+ "format",
+ "A'rpi",
+ "FIXME! get_image()/put_image()",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_gradfun.c b/video/filter/vf_gradfun.c
new file mode 100644
index 0000000000..eb73cfa2a4
--- /dev/null
+++ b/video/filter/vf_gradfun.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2009 Loren Merritt <lorenm@u.washignton.edu>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Debanding algorithm (from gradfun2db by prunedtree):
+ * Boxblur.
+ * Foreach pixel, if it's within threshold of the blurred value, make it closer.
+ * So now we have a smoothed and higher bitdepth version of all the shallow
+ * gradients, while leaving detailed areas untouched.
+ * Dither it back to 8bit.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include <libavutil/common.h>
+
+#include "config.h"
+#include "cpudetect.h"
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libvo/fastmemcpy.h"
+#include "ffmpeg_files/x86_cpu.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+struct vf_priv_s {
+ float cfg_thresh;
+ int cfg_radius;
+ float cfg_size;
+ int thresh;
+ int radius;
+ uint16_t *buf;
+ void (*filter_line)(uint8_t *dst, uint8_t *src, uint16_t *dc,
+ int width, int thresh, const uint16_t *dithers);
+ void (*blur_line)(uint16_t *dc, uint16_t *buf, uint16_t *buf1,
+ uint8_t *src, int sstride, int width);
+} const vf_priv_dflt = {
+ .cfg_thresh = 1.5,
+ .cfg_radius = -1,
+ .cfg_size = -1,
+};
+
+static const uint16_t __attribute__((aligned(16))) pw_7f[8] = {127,127,127,127,127,127,127,127};
+static const uint16_t __attribute__((aligned(16))) pw_ff[8] = {255,255,255,255,255,255,255,255};
+static const uint16_t __attribute__((aligned(16))) dither[8][8] = {
+ { 0, 96, 24,120, 6,102, 30,126 },
+ { 64, 32, 88, 56, 70, 38, 94, 62 },
+ { 16,112, 8,104, 22,118, 14,110 },
+ { 80, 48, 72, 40, 86, 54, 78, 46 },
+ { 4,100, 28,124, 2, 98, 26,122 },
+ { 68, 36, 92, 60, 66, 34, 90, 58 },
+ { 20,116, 12,108, 18,114, 10,106 },
+ { 84, 52, 76, 44, 82, 50, 74, 42 },
+};
+
+static void filter_line_c(uint8_t *dst, uint8_t *src, uint16_t *dc,
+ int width, int thresh, const uint16_t *dithers)
+{
+ int x;
+ for (x=0; x<width; x++, dc+=x&1) {
+ int pix = src[x]<<7;
+ int delta = dc[0] - pix;
+ int m = abs(delta) * thresh >> 16;
+ m = FFMAX(0, 127-m);
+ m = m*m*delta >> 14;
+ pix += m + dithers[x&7];
+ dst[x] = av_clip_uint8(pix>>7);
+ }
+}
+
+static void blur_line_c(uint16_t *dc, uint16_t *buf, uint16_t *buf1,
+ uint8_t *src, int sstride, int width)
+{
+ int x, v, old;
+ for (x=0; x<width; x++) {
+ v = buf1[x] + src[2*x] + src[2*x+1] + src[2*x+sstride] + src[2*x+1+sstride];
+ old = buf[x];
+ buf[x] = v;
+ dc[x] = v - old;
+ }
+}
+
+#if HAVE_MMX2
+static void filter_line_mmx2(uint8_t *dst, uint8_t *src, uint16_t *dc,
+ int width, int thresh, const uint16_t *dithers)
+{
+ intptr_t x;
+ if (width&3) {
+ x = width&~3;
+ filter_line_c(dst+x, src+x, dc+x/2, width-x, thresh, dithers);
+ width = x;
+ }
+ x = -width;
+ __asm__ volatile(
+ "movd %4, %%mm5 \n"
+ "pxor %%mm7, %%mm7 \n"
+ "pshufw $0, %%mm5, %%mm5 \n"
+ "movq %6, %%mm6 \n"
+ "movq %5, %%mm4 \n"
+ "1: \n"
+ "movd (%2,%0), %%mm0 \n"
+ "movd (%3,%0), %%mm1 \n"
+ "punpcklbw %%mm7, %%mm0 \n"
+ "punpcklwd %%mm1, %%mm1 \n"
+ "psllw $7, %%mm0 \n"
+ "pxor %%mm2, %%mm2 \n"
+ "psubw %%mm0, %%mm1 \n" // delta = dc - pix
+ "psubw %%mm1, %%mm2 \n"
+ "pmaxsw %%mm1, %%mm2 \n"
+ "pmulhuw %%mm5, %%mm2 \n" // m = abs(delta) * thresh >> 16
+ "psubw %%mm6, %%mm2 \n"
+ "pminsw %%mm7, %%mm2 \n" // m = -max(0, 127-m)
+ "pmullw %%mm2, %%mm2 \n"
+ "paddw %%mm4, %%mm0 \n" // pix += dither
+ "pmulhw %%mm2, %%mm1 \n"
+ "psllw $2, %%mm1 \n" // m = m*m*delta >> 14
+ "paddw %%mm1, %%mm0 \n" // pix += m
+ "psraw $7, %%mm0 \n"
+ "packuswb %%mm0, %%mm0 \n"
+ "movd %%mm0, (%1,%0) \n" // dst = clip(pix>>7)
+ "add $4, %0 \n"
+ "jl 1b \n"
+ "emms \n"
+ :"+r"(x)
+ :"r"(dst+width), "r"(src+width), "r"(dc+width/2),
+ "rm"(thresh), "m"(*dithers), "m"(*pw_7f)
+ :"memory"
+ );
+}
+#endif
+
+#if HAVE_SSSE3
+static void filter_line_ssse3(uint8_t *dst, uint8_t *src, uint16_t *dc,
+ int width, int thresh, const uint16_t *dithers)
+{
+ intptr_t x;
+ if (width&7) {
+ // could be 10% faster if I somehow eliminated this
+ x = width&~7;
+ filter_line_c(dst+x, src+x, dc+x/2, width-x, thresh, dithers);
+ width = x;
+ }
+ x = -width;
+ __asm__ volatile(
+ "movd %4, %%xmm5 \n"
+ "pxor %%xmm7, %%xmm7 \n"
+ "pshuflw $0,%%xmm5, %%xmm5 \n"
+ "movdqa %6, %%xmm6 \n"
+ "punpcklqdq %%xmm5, %%xmm5 \n"
+ "movdqa %5, %%xmm4 \n"
+ "1: \n"
+ "movq (%2,%0), %%xmm0 \n"
+ "movq (%3,%0), %%xmm1 \n"
+ "punpcklbw %%xmm7, %%xmm0 \n"
+ "punpcklwd %%xmm1, %%xmm1 \n"
+ "psllw $7, %%xmm0 \n"
+ "psubw %%xmm0, %%xmm1 \n" // delta = dc - pix
+ "pabsw %%xmm1, %%xmm2 \n"
+ "pmulhuw %%xmm5, %%xmm2 \n" // m = abs(delta) * thresh >> 16
+ "psubw %%xmm6, %%xmm2 \n"
+ "pminsw %%xmm7, %%xmm2 \n" // m = -max(0, 127-m)
+ "pmullw %%xmm2, %%xmm2 \n"
+ "psllw $1, %%xmm2 \n"
+ "paddw %%xmm4, %%xmm0 \n" // pix += dither
+ "pmulhrsw %%xmm2, %%xmm1 \n" // m = m*m*delta >> 14
+ "paddw %%xmm1, %%xmm0 \n" // pix += m
+ "psraw $7, %%xmm0 \n"
+ "packuswb %%xmm0, %%xmm0 \n"
+ "movq %%xmm0, (%1,%0) \n" // dst = clip(pix>>7)
+ "add $8, %0 \n"
+ "jl 1b \n"
+ :"+&r"(x)
+ :"r"(dst+width), "r"(src+width), "r"(dc+width/2),
+ "rm"(thresh), "m"(*dithers), "m"(*pw_7f)
+ :"memory"
+ );
+}
+#endif // HAVE_SSSE3
+
+#if HAVE_SSE2 && HAVE_6REGS
+#define BLURV(load)\
+ intptr_t x = -2*width;\
+ __asm__ volatile(\
+ "movdqa %6, %%xmm7 \n"\
+ "1: \n"\
+ load" (%4,%0), %%xmm0 \n"\
+ load" (%5,%0), %%xmm1 \n"\
+ "movdqa %%xmm0, %%xmm2 \n"\
+ "movdqa %%xmm1, %%xmm3 \n"\
+ "psrlw $8, %%xmm0 \n"\
+ "psrlw $8, %%xmm1 \n"\
+ "pand %%xmm7, %%xmm2 \n"\
+ "pand %%xmm7, %%xmm3 \n"\
+ "paddw %%xmm1, %%xmm0 \n"\
+ "paddw %%xmm3, %%xmm2 \n"\
+ "paddw %%xmm2, %%xmm0 \n"\
+ "paddw (%2,%0), %%xmm0 \n"\
+ "movdqa (%1,%0), %%xmm1 \n"\
+ "movdqa %%xmm0, (%1,%0) \n"\
+ "psubw %%xmm1, %%xmm0 \n"\
+ "movdqa %%xmm0, (%3,%0) \n"\
+ "add $16, %0 \n"\
+ "jl 1b \n"\
+ :"+&r"(x)\
+ :"r"(buf+width),\
+ "r"(buf1+width),\
+ "r"(dc+width),\
+ "r"(src+width*2),\
+ "r"(src+width*2+sstride),\
+ "m"(*pw_ff)\
+ :"memory"\
+ );
+
+static void blur_line_sse2(uint16_t *dc, uint16_t *buf, uint16_t *buf1,
+ uint8_t *src, int sstride, int width)
+{
+ if (((intptr_t)src|sstride)&15) {
+ BLURV("movdqu");
+ } else {
+ BLURV("movdqa");
+ }
+}
+#endif // HAVE_6REGS && HAVE_SSE2
+
+static void filter(struct vf_priv_s *ctx, uint8_t *dst, uint8_t *src,
+ int width, int height, int dstride, int sstride, int r)
+{
+ int bstride = ((width+15)&~15)/2;
+ int y;
+ uint32_t dc_factor = (1<<21)/(r*r);
+ uint16_t *dc = ctx->buf+16;
+ uint16_t *buf = ctx->buf+bstride+32;
+ int thresh = ctx->thresh;
+
+ memset(dc, 0, (bstride+16)*sizeof(*buf));
+ for (y=0; y<r; y++)
+ ctx->blur_line(dc, buf+y*bstride, buf+(y-1)*bstride, src+2*y*sstride, sstride, width/2);
+ for (;;) {
+ if (y < height-r) {
+ int mod = ((y+r)/2)%r;
+ uint16_t *buf0 = buf+mod*bstride;
+ uint16_t *buf1 = buf+(mod?mod-1:r-1)*bstride;
+ int x, v;
+ ctx->blur_line(dc, buf0, buf1, src+(y+r)*sstride, sstride, width/2);
+ for (x=v=0; x<r; x++)
+ v += dc[x];
+ for (; x<width/2; x++) {
+ v += dc[x] - dc[x-r];
+ dc[x-r] = v * dc_factor >> 16;
+ }
+ for (; x<(width+r+1)/2; x++)
+ dc[x-r] = v * dc_factor >> 16;
+ for (x=-r/2; x<0; x++)
+ dc[x] = dc[0];
+ }
+ if (y == r) {
+ for (y=0; y<r; y++)
+ ctx->filter_line(dst+y*dstride, src+y*sstride, dc-r/2, width, thresh, dither[y&7]);
+ }
+ ctx->filter_line(dst+y*dstride, src+y*sstride, dc-r/2, width, thresh, dither[y&7]);
+ if (++y >= height) break;
+ ctx->filter_line(dst+y*dstride, src+y*sstride, dc-r/2, width, thresh, dither[y&7]);
+ if (++y >= height) break;
+ }
+}
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi)
+{
+ if (mpi->flags&MP_IMGFLAG_PRESERVE) return; // don't change
+ // ok, we can do pp in-place:
+ vf->dmpi = vf_get_image(vf->next, mpi->imgfmt,
+ mpi->type, mpi->flags, mpi->width, mpi->height);
+ mpi->planes[0] = vf->dmpi->planes[0];
+ mpi->stride[0] = vf->dmpi->stride[0];
+ mpi->width = vf->dmpi->width;
+ if (mpi->flags&MP_IMGFLAG_PLANAR){
+ mpi->planes[1] = vf->dmpi->planes[1];
+ mpi->planes[2] = vf->dmpi->planes[2];
+ mpi->stride[1] = vf->dmpi->stride[1];
+ mpi->stride[2] = vf->dmpi->stride[2];
+ }
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ mp_image_t *dmpi = vf->dmpi;
+ int p;
+
+ if (!(mpi->flags&MP_IMGFLAG_DIRECT)) {
+ // no DR, so get a new image. hope we'll get DR buffer:
+ dmpi = vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_TEMP,
+ MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
+ mpi->w, mpi->h);
+ }
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+ for (p=0; p<mpi->num_planes; p++) {
+ int w = mpi->w;
+ int h = mpi->h;
+ int r = vf->priv->radius;
+ if (p) {
+ w >>= mpi->chroma_x_shift;
+ h >>= mpi->chroma_y_shift;
+ r = ((r>>mpi->chroma_x_shift) + (r>>mpi->chroma_y_shift)) / 2;
+ r = av_clip((r+1)&~1,4,32);
+ }
+ if (FFMIN(w,h) > 2*r)
+ filter(vf->priv, dmpi->planes[p], mpi->planes[p], w, h,
+ dmpi->stride[p], mpi->stride[p], r);
+ else if (dmpi->planes[p] != mpi->planes[p])
+ memcpy_pic(dmpi->planes[p], mpi->planes[p], w, h,
+ dmpi->stride[p], mpi->stride[p]);
+ }
+
+ return vf_next_put_image(vf, dmpi, pts);
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ switch (fmt){
+ case IMGFMT_YVU9:
+ case IMGFMT_IF09:
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_CLPL:
+ case IMGFMT_Y800:
+ case IMGFMT_Y8:
+ case IMGFMT_NV12:
+ case IMGFMT_NV21:
+ case IMGFMT_444P:
+ case IMGFMT_422P:
+ case IMGFMT_411P:
+ case IMGFMT_HM12:
+ return vf_next_query_format(vf,fmt);
+ }
+ return 0;
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ free(vf->priv->buf);
+ vf->priv->radius = vf->priv->cfg_radius;
+ if (vf->priv->cfg_size > -1) {
+ vf->priv->radius = (vf->priv->cfg_size / 100.0f)
+ * sqrtf(width * width + height * height);
+ }
+ vf->priv->radius = av_clip((vf->priv->radius+1)&~1, 4, 32);
+ vf->priv->buf = av_mallocz((((width+15)&~15)*(vf->priv->radius+1)/2+32)*sizeof(uint16_t));
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ if (!vf->priv) return;
+ av_free(vf->priv->buf);
+ free(vf->priv);
+ vf->priv = NULL;
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->get_image=get_image;
+ vf->put_image=put_image;
+ vf->query_format=query_format;
+ vf->config=config;
+ vf->uninit=uninit;
+
+ bool have_radius = vf->priv->cfg_radius > -1;
+ bool have_size = vf->priv->cfg_size > -1;
+
+ if (have_radius && have_size) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "scale: gradfun: only one of "
+ "radius/size parameters allowed at the same time!\n");
+ return 0;
+ }
+
+ if (!have_radius && !have_size)
+ vf->priv->cfg_size = 1.0;
+
+ vf->priv->thresh = (1<<15)/av_clipf(vf->priv->cfg_thresh,0.51,255);
+
+ vf->priv->blur_line = blur_line_c;
+ vf->priv->filter_line = filter_line_c;
+#if HAVE_SSE2 && HAVE_6REGS
+ if (gCpuCaps.hasSSE2)
+ vf->priv->blur_line = blur_line_sse2;
+#endif
+#if HAVE_MMX2
+ if (gCpuCaps.hasMMX2)
+ vf->priv->filter_line = filter_line_mmx2;
+#endif
+#if HAVE_SSSE3
+ if (gCpuCaps.hasSSSE3)
+ vf->priv->filter_line = filter_line_ssse3;
+#endif
+
+ return 1;
+}
+
+#undef ST_OFF
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"strength", ST_OFF(cfg_thresh), CONF_TYPE_FLOAT, M_OPT_RANGE, 0.51, 255, NULL},
+ {"radius", ST_OFF(cfg_radius), CONF_TYPE_INT, M_OPT_RANGE, 4, 32, NULL},
+ {"size", ST_OFF(cfg_size), CONF_TYPE_FLOAT, M_OPT_RANGE, 0.1, 5.0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "gradfun",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_gradfun = {
+ "gradient deband",
+ "gradfun",
+ "Loren Merritt",
+ "",
+ vf_open,
+ &vf_opts
+};
diff --git a/video/filter/vf_hqdn3d.c b/video/filter/vf_hqdn3d.c
new file mode 100644
index 0000000000..95cdba1ef5
--- /dev/null
+++ b/video/filter/vf_hqdn3d.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2003 Daniel Moreno <comac@comac.darktech.org>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "mp_msg.h"
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#define PARAM1_DEFAULT 4.0
+#define PARAM2_DEFAULT 3.0
+#define PARAM3_DEFAULT 6.0
+
+//===========================================================================//
+
+struct vf_priv_s {
+ int Coefs[4][512*16];
+ unsigned int *Line;
+ unsigned short *Frame[3];
+};
+
+
+/***************************************************************************/
+
+static void uninit(struct vf_instance *vf)
+{
+ free(vf->priv->Line);
+ free(vf->priv->Frame[0]);
+ free(vf->priv->Frame[1]);
+ free(vf->priv->Frame[2]);
+
+ vf->priv->Line = NULL;
+ vf->priv->Frame[0] = NULL;
+ vf->priv->Frame[1] = NULL;
+ vf->priv->Frame[2] = NULL;
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+
+ uninit(vf);
+ vf->priv->Line = malloc(width*sizeof(int));
+
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static inline unsigned int LowPassMul(unsigned int PrevMul, unsigned int CurrMul, int* Coef){
+// int dMul= (PrevMul&0xFFFFFF)-(CurrMul&0xFFFFFF);
+ int dMul= PrevMul-CurrMul;
+ unsigned int d=((dMul+0x10007FF)>>12);
+ return CurrMul + Coef[d];
+}
+
+static void deNoiseTemporal(
+ unsigned char *Frame, // mpi->planes[x]
+ unsigned char *FrameDest, // dmpi->planes[x]
+ unsigned short *FrameAnt,
+ int W, int H, int sStride, int dStride,
+ int *Temporal)
+{
+ long X, Y;
+ unsigned int PixelDst;
+
+ for (Y = 0; Y < H; Y++){
+ for (X = 0; X < W; X++){
+ PixelDst = LowPassMul(FrameAnt[X]<<8, Frame[X]<<16, Temporal);
+ FrameAnt[X] = ((PixelDst+0x1000007F)>>8);
+ FrameDest[X]= ((PixelDst+0x10007FFF)>>16);
+ }
+ Frame += sStride;
+ FrameDest += dStride;
+ FrameAnt += W;
+ }
+}
+
+static void deNoiseSpacial(
+ unsigned char *Frame, // mpi->planes[x]
+ unsigned char *FrameDest, // dmpi->planes[x]
+ unsigned int *LineAnt, // vf->priv->Line (width bytes)
+ int W, int H, int sStride, int dStride,
+ int *Horizontal, int *Vertical)
+{
+ long X, Y;
+ long sLineOffs = 0, dLineOffs = 0;
+ unsigned int PixelAnt;
+ unsigned int PixelDst;
+
+ /* First pixel has no left nor top neighbor. */
+ PixelDst = LineAnt[0] = PixelAnt = Frame[0]<<16;
+ FrameDest[0]= ((PixelDst+0x10007FFF)>>16);
+
+ /* First line has no top neighbor, only left. */
+ for (X = 1; X < W; X++){
+ PixelDst = LineAnt[X] = LowPassMul(PixelAnt, Frame[X]<<16, Horizontal);
+ FrameDest[X]= ((PixelDst+0x10007FFF)>>16);
+ }
+
+ for (Y = 1; Y < H; Y++){
+ unsigned int PixelAnt;
+ sLineOffs += sStride, dLineOffs += dStride;
+ /* First pixel on each line doesn't have previous pixel */
+ PixelAnt = Frame[sLineOffs]<<16;
+ PixelDst = LineAnt[0] = LowPassMul(LineAnt[0], PixelAnt, Vertical);
+ FrameDest[dLineOffs]= ((PixelDst+0x10007FFF)>>16);
+
+ for (X = 1; X < W; X++){
+ unsigned int PixelDst;
+ /* The rest are normal */
+ PixelAnt = LowPassMul(PixelAnt, Frame[sLineOffs+X]<<16, Horizontal);
+ PixelDst = LineAnt[X] = LowPassMul(LineAnt[X], PixelAnt, Vertical);
+ FrameDest[dLineOffs+X]= ((PixelDst+0x10007FFF)>>16);
+ }
+ }
+}
+
+static void deNoise(unsigned char *Frame, // mpi->planes[x]
+ unsigned char *FrameDest, // dmpi->planes[x]
+ unsigned int *LineAnt, // vf->priv->Line (width bytes)
+ unsigned short **FrameAntPtr,
+ int W, int H, int sStride, int dStride,
+ int *Horizontal, int *Vertical, int *Temporal)
+{
+ long X, Y;
+ long sLineOffs = 0, dLineOffs = 0;
+ unsigned int PixelAnt;
+ unsigned int PixelDst;
+ unsigned short* FrameAnt=(*FrameAntPtr);
+
+ if(!FrameAnt){
+ (*FrameAntPtr)=FrameAnt=malloc(W*H*sizeof(unsigned short));
+ for (Y = 0; Y < H; Y++){
+ unsigned short* dst=&FrameAnt[Y*W];
+ unsigned char* src=Frame+Y*sStride;
+ for (X = 0; X < W; X++) dst[X]=src[X]<<8;
+ }
+ }
+
+ if(!Horizontal[0] && !Vertical[0]){
+ deNoiseTemporal(Frame, FrameDest, FrameAnt,
+ W, H, sStride, dStride, Temporal);
+ return;
+ }
+ if(!Temporal[0]){
+ deNoiseSpacial(Frame, FrameDest, LineAnt,
+ W, H, sStride, dStride, Horizontal, Vertical);
+ return;
+ }
+
+ /* First pixel has no left nor top neighbor. Only previous frame */
+ LineAnt[0] = PixelAnt = Frame[0]<<16;
+ PixelDst = LowPassMul(FrameAnt[0]<<8, PixelAnt, Temporal);
+ FrameAnt[0] = ((PixelDst+0x1000007F)>>8);
+ FrameDest[0]= ((PixelDst+0x10007FFF)>>16);
+
+ /* First line has no top neighbor. Only left one for each pixel and
+ * last frame */
+ for (X = 1; X < W; X++){
+ LineAnt[X] = PixelAnt = LowPassMul(PixelAnt, Frame[X]<<16, Horizontal);
+ PixelDst = LowPassMul(FrameAnt[X]<<8, PixelAnt, Temporal);
+ FrameAnt[X] = ((PixelDst+0x1000007F)>>8);
+ FrameDest[X]= ((PixelDst+0x10007FFF)>>16);
+ }
+
+ for (Y = 1; Y < H; Y++){
+ unsigned int PixelAnt;
+ unsigned short* LinePrev=&FrameAnt[Y*W];
+ sLineOffs += sStride, dLineOffs += dStride;
+ /* First pixel on each line doesn't have previous pixel */
+ PixelAnt = Frame[sLineOffs]<<16;
+ LineAnt[0] = LowPassMul(LineAnt[0], PixelAnt, Vertical);
+ PixelDst = LowPassMul(LinePrev[0]<<8, LineAnt[0], Temporal);
+ LinePrev[0] = ((PixelDst+0x1000007F)>>8);
+ FrameDest[dLineOffs]= ((PixelDst+0x10007FFF)>>16);
+
+ for (X = 1; X < W; X++){
+ unsigned int PixelDst;
+ /* The rest are normal */
+ PixelAnt = LowPassMul(PixelAnt, Frame[sLineOffs+X]<<16, Horizontal);
+ LineAnt[X] = LowPassMul(LineAnt[X], PixelAnt, Vertical);
+ PixelDst = LowPassMul(LinePrev[X]<<8, LineAnt[X], Temporal);
+ LinePrev[X] = ((PixelDst+0x1000007F)>>8);
+ FrameDest[dLineOffs+X]= ((PixelDst+0x10007FFF)>>16);
+ }
+ }
+}
+
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ int cw= mpi->w >> mpi->chroma_x_shift;
+ int ch= mpi->h >> mpi->chroma_y_shift;
+ int W = mpi->w, H = mpi->h;
+
+ mp_image_t *dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->w,mpi->h);
+
+ if(!dmpi) return 0;
+
+ deNoise(mpi->planes[0], dmpi->planes[0],
+ vf->priv->Line, &vf->priv->Frame[0], W, H,
+ mpi->stride[0], dmpi->stride[0],
+ vf->priv->Coefs[0],
+ vf->priv->Coefs[0],
+ vf->priv->Coefs[1]);
+ deNoise(mpi->planes[1], dmpi->planes[1],
+ vf->priv->Line, &vf->priv->Frame[1], cw, ch,
+ mpi->stride[1], dmpi->stride[1],
+ vf->priv->Coefs[2],
+ vf->priv->Coefs[2],
+ vf->priv->Coefs[3]);
+ deNoise(mpi->planes[2], dmpi->planes[2],
+ vf->priv->Line, &vf->priv->Frame[2], cw, ch,
+ mpi->stride[2], dmpi->stride[2],
+ vf->priv->Coefs[2],
+ vf->priv->Coefs[2],
+ vf->priv->Coefs[3]);
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ switch(fmt)
+ {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_YVU9:
+ case IMGFMT_444P:
+ case IMGFMT_422P:
+ case IMGFMT_411P:
+ return vf_next_query_format(vf, fmt);
+ }
+ return 0;
+}
+
+
+#define ABS(A) ( (A) > 0 ? (A) : -(A) )
+
+static void PrecalcCoefs(int *Ct, double Dist25)
+{
+ int i;
+ double Gamma, Simil, C;
+
+ Gamma = log(0.25) / log(1.0 - Dist25/255.0 - 0.00001);
+
+ for (i = -255*16; i <= 255*16; i++)
+ {
+ Simil = 1.0 - ABS(i) / (16*255.0);
+ C = pow(Simil, Gamma) * 65536.0 * (double)i / 16.0;
+ Ct[16*256+i] = (C<0) ? (C-0.5) : (C+0.5);
+ }
+
+ Ct[0] = (Dist25 != 0);
+}
+
+
+static int vf_open(vf_instance_t *vf, char *args){
+ double LumSpac, LumTmp, ChromSpac, ChromTmp;
+ double Param1, Param2, Param3, Param4;
+
+ vf->config=config;
+ vf->put_image=put_image;
+ vf->query_format=query_format;
+ vf->uninit=uninit;
+ vf->priv=malloc(sizeof(struct vf_priv_s));
+ memset(vf->priv, 0, sizeof(struct vf_priv_s));
+
+ if (args)
+ {
+ switch(sscanf(args, "%lf:%lf:%lf:%lf",
+ &Param1, &Param2, &Param3, &Param4
+ ))
+ {
+ case 0:
+ LumSpac = PARAM1_DEFAULT;
+ LumTmp = PARAM3_DEFAULT;
+
+ ChromSpac = PARAM2_DEFAULT;
+ ChromTmp = LumTmp * ChromSpac / LumSpac;
+ break;
+
+ case 1:
+ LumSpac = Param1;
+ LumTmp = PARAM3_DEFAULT * Param1 / PARAM1_DEFAULT;
+
+ ChromSpac = PARAM2_DEFAULT * Param1 / PARAM1_DEFAULT;
+ ChromTmp = LumTmp * ChromSpac / LumSpac;
+ break;
+
+ case 2:
+ LumSpac = Param1;
+ LumTmp = PARAM3_DEFAULT * Param1 / PARAM1_DEFAULT;
+
+ ChromSpac = Param2;
+ ChromTmp = LumTmp * ChromSpac / LumSpac;
+ break;
+
+ case 3:
+ LumSpac = Param1;
+ LumTmp = Param3;
+
+ ChromSpac = Param2;
+ ChromTmp = LumTmp * ChromSpac / LumSpac;
+ break;
+
+ case 4:
+ LumSpac = Param1;
+ LumTmp = Param3;
+
+ ChromSpac = Param2;
+ ChromTmp = Param4;
+ break;
+
+ default:
+ LumSpac = PARAM1_DEFAULT;
+ LumTmp = PARAM3_DEFAULT;
+
+ ChromSpac = PARAM2_DEFAULT;
+ ChromTmp = LumTmp * ChromSpac / LumSpac;
+ }
+ }
+ else
+ {
+ LumSpac = PARAM1_DEFAULT;
+ LumTmp = PARAM3_DEFAULT;
+
+ ChromSpac = PARAM2_DEFAULT;
+ ChromTmp = LumTmp * ChromSpac / LumSpac;
+ }
+
+ PrecalcCoefs(vf->priv->Coefs[0], LumSpac);
+ PrecalcCoefs(vf->priv->Coefs[1], LumTmp);
+ PrecalcCoefs(vf->priv->Coefs[2], ChromSpac);
+ PrecalcCoefs(vf->priv->Coefs[3], ChromTmp);
+
+ return 1;
+}
+
+const vf_info_t vf_info_hqdn3d = {
+ "High Quality 3D Denoiser",
+ "hqdn3d",
+ "Daniel Moreno & A'rpi",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_ilpack.c b/video/filter/vf_ilpack.c
new file mode 100644
index 0000000000..e98d70d85d
--- /dev/null
+++ b/video/filter/vf_ilpack.c
@@ -0,0 +1,457 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libavutil/attributes.h"
+
+typedef void (pack_func_t)(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w, int us, int vs);
+
+struct vf_priv_s {
+ int mode;
+ pack_func_t *pack[2];
+};
+
+static void pack_nn_C(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w,
+ int av_unused us, int av_unused vs)
+{
+ int j;
+ for (j = w/2; j; j--) {
+ *dst++ = *y++;
+ *dst++ = *u++;
+ *dst++ = *y++;
+ *dst++ = *v++;
+ }
+}
+
+static void pack_li_0_C(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w, int us, int vs)
+{
+ int j;
+ for (j = w/2; j; j--) {
+ *dst++ = *y++;
+ *dst++ = (u[us+us] + 7*u[0])>>3;
+ *dst++ = *y++;
+ *dst++ = (v[vs+vs] + 7*v[0])>>3;
+ u++; v++;
+ }
+}
+
+static void pack_li_1_C(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w, int us, int vs)
+{
+ int j;
+ for (j = w/2; j; j--) {
+ *dst++ = *y++;
+ *dst++ = (3*u[us+us] + 5*u[0])>>3;
+ *dst++ = *y++;
+ *dst++ = (3*v[vs+vs] + 5*v[0])>>3;
+ u++; v++;
+ }
+}
+
+#if HAVE_MMX
+static void pack_nn_MMX(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w,
+ int av_unused us, int av_unused vs)
+{
+ __asm__ volatile (""
+ ASMALIGN(4)
+ "1: \n\t"
+ "movq (%0), %%mm1 \n\t"
+ "movq (%0), %%mm2 \n\t"
+ "movq (%1), %%mm4 \n\t"
+ "movq (%2), %%mm6 \n\t"
+ "punpcklbw %%mm6, %%mm4 \n\t"
+ "punpcklbw %%mm4, %%mm1 \n\t"
+ "punpckhbw %%mm4, %%mm2 \n\t"
+
+ "add $8, %0 \n\t"
+ "add $4, %1 \n\t"
+ "add $4, %2 \n\t"
+ "movq %%mm1, (%3) \n\t"
+ "movq %%mm2, 8(%3) \n\t"
+ "add $16, %3 \n\t"
+ "decl %4 \n\t"
+ "jnz 1b \n\t"
+ "emms \n\t"
+ :
+ : "r" (y), "r" (u), "r" (v), "r" (dst), "r" (w/8)
+ : "memory"
+ );
+ pack_nn_C(dst, y, u, v, (w&7), 0, 0);
+}
+
+#if HAVE_EBX_AVAILABLE
+static void pack_li_0_MMX(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w, int us, int vs)
+{
+ __asm__ volatile (""
+ "push %%"REG_BP" \n\t"
+#if ARCH_X86_64
+ "mov %6, %%"REG_BP" \n\t"
+#else
+ "movl 4(%%"REG_d"), %%"REG_BP" \n\t"
+ "movl (%%"REG_d"), %%"REG_d" \n\t"
+#endif
+ "pxor %%mm0, %%mm0 \n\t"
+
+ ASMALIGN(4)
+ ".Lli0: \n\t"
+ "movq (%%"REG_S"), %%mm1 \n\t"
+ "movq (%%"REG_S"), %%mm2 \n\t"
+
+ "movq (%%"REG_a",%%"REG_d",2), %%mm4 \n\t"
+ "movq (%%"REG_b",%%"REG_BP",2), %%mm6 \n\t"
+ "punpcklbw %%mm0, %%mm4 \n\t"
+ "punpcklbw %%mm0, %%mm6 \n\t"
+ "movq (%%"REG_a"), %%mm3 \n\t"
+ "movq (%%"REG_b"), %%mm5 \n\t"
+ "punpcklbw %%mm0, %%mm3 \n\t"
+ "punpcklbw %%mm0, %%mm5 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "psrlw $3, %%mm4 \n\t"
+ "psrlw $3, %%mm6 \n\t"
+ "packuswb %%mm4, %%mm4 \n\t"
+ "packuswb %%mm6, %%mm6 \n\t"
+ "punpcklbw %%mm6, %%mm4 \n\t"
+ "punpcklbw %%mm4, %%mm1 \n\t"
+ "punpckhbw %%mm4, %%mm2 \n\t"
+
+ "movq %%mm1, (%%"REG_D") \n\t"
+ "movq %%mm2, 8(%%"REG_D") \n\t"
+
+ "movq 8(%%"REG_S"), %%mm1 \n\t"
+ "movq 8(%%"REG_S"), %%mm2 \n\t"
+
+ "movq (%%"REG_a",%%"REG_d",2), %%mm4 \n\t"
+ "movq (%%"REG_b",%%"REG_BP",2), %%mm6 \n\t"
+ "punpckhbw %%mm0, %%mm4 \n\t"
+ "punpckhbw %%mm0, %%mm6 \n\t"
+ "movq (%%"REG_a"), %%mm3 \n\t"
+ "movq (%%"REG_b"), %%mm5 \n\t"
+ "punpckhbw %%mm0, %%mm3 \n\t"
+ "punpckhbw %%mm0, %%mm5 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "psrlw $3, %%mm4 \n\t"
+ "psrlw $3, %%mm6 \n\t"
+ "packuswb %%mm4, %%mm4 \n\t"
+ "packuswb %%mm6, %%mm6 \n\t"
+ "punpcklbw %%mm6, %%mm4 \n\t"
+ "punpcklbw %%mm4, %%mm1 \n\t"
+ "punpckhbw %%mm4, %%mm2 \n\t"
+
+ "add $16, %%"REG_S" \n\t"
+ "add $8, %%"REG_a" \n\t"
+ "add $8, %%"REG_b" \n\t"
+
+ "movq %%mm1, 16(%%"REG_D") \n\t"
+ "movq %%mm2, 24(%%"REG_D") \n\t"
+ "add $32, %%"REG_D" \n\t"
+
+ "decl %%ecx \n\t"
+ "jnz .Lli0 \n\t"
+ "emms \n\t"
+ "pop %%"REG_BP" \n\t"
+ :
+ : "S" (y), "D" (dst), "a" (u), "b" (v), "c" (w/16),
+#if ARCH_X86_64
+ "d" ((x86_reg)us), "r" ((x86_reg)vs)
+#else
+ "d" (&us)
+#endif
+ : "memory"
+ );
+ pack_li_0_C(dst, y, u, v, (w&15), us, vs);
+}
+
+static void pack_li_1_MMX(unsigned char *dst, unsigned char *y,
+ unsigned char *u, unsigned char *v, int w, int us, int vs)
+{
+ __asm__ volatile (""
+ "push %%"REG_BP" \n\t"
+#if ARCH_X86_64
+ "mov %6, %%"REG_BP" \n\t"
+#else
+ "movl 4(%%"REG_d"), %%"REG_BP" \n\t"
+ "movl (%%"REG_d"), %%"REG_d" \n\t"
+#endif
+ "pxor %%mm0, %%mm0 \n\t"
+
+ ASMALIGN(4)
+ ".Lli1: \n\t"
+ "movq (%%"REG_S"), %%mm1 \n\t"
+ "movq (%%"REG_S"), %%mm2 \n\t"
+
+ "movq (%%"REG_a",%%"REG_d",2), %%mm4 \n\t"
+ "movq (%%"REG_b",%%"REG_BP",2), %%mm6 \n\t"
+ "punpcklbw %%mm0, %%mm4 \n\t"
+ "punpcklbw %%mm0, %%mm6 \n\t"
+ "movq (%%"REG_a"), %%mm3 \n\t"
+ "movq (%%"REG_b"), %%mm5 \n\t"
+ "punpcklbw %%mm0, %%mm3 \n\t"
+ "punpcklbw %%mm0, %%mm5 \n\t"
+ "movq %%mm4, %%mm7 \n\t"
+ "paddw %%mm4, %%mm4 \n\t"
+ "paddw %%mm7, %%mm4 \n\t"
+ "movq %%mm6, %%mm7 \n\t"
+ "paddw %%mm6, %%mm6 \n\t"
+ "paddw %%mm7, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "psrlw $3, %%mm4 \n\t"
+ "psrlw $3, %%mm6 \n\t"
+ "packuswb %%mm4, %%mm4 \n\t"
+ "packuswb %%mm6, %%mm6 \n\t"
+ "punpcklbw %%mm6, %%mm4 \n\t"
+ "punpcklbw %%mm4, %%mm1 \n\t"
+ "punpckhbw %%mm4, %%mm2 \n\t"
+
+ "movq %%mm1, (%%"REG_D") \n\t"
+ "movq %%mm2, 8(%%"REG_D") \n\t"
+
+ "movq 8(%%"REG_S"), %%mm1 \n\t"
+ "movq 8(%%"REG_S"), %%mm2 \n\t"
+
+ "movq (%%"REG_a",%%"REG_d",2), %%mm4 \n\t"
+ "movq (%%"REG_b",%%"REG_BP",2), %%mm6 \n\t"
+ "punpckhbw %%mm0, %%mm4 \n\t"
+ "punpckhbw %%mm0, %%mm6 \n\t"
+ "movq (%%"REG_a"), %%mm3 \n\t"
+ "movq (%%"REG_b"), %%mm5 \n\t"
+ "punpckhbw %%mm0, %%mm3 \n\t"
+ "punpckhbw %%mm0, %%mm5 \n\t"
+ "movq %%mm4, %%mm7 \n\t"
+ "paddw %%mm4, %%mm4 \n\t"
+ "paddw %%mm7, %%mm4 \n\t"
+ "movq %%mm6, %%mm7 \n\t"
+ "paddw %%mm6, %%mm6 \n\t"
+ "paddw %%mm7, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "paddw %%mm3, %%mm4 \n\t"
+ "paddw %%mm5, %%mm6 \n\t"
+ "psrlw $3, %%mm4 \n\t"
+ "psrlw $3, %%mm6 \n\t"
+ "packuswb %%mm4, %%mm4 \n\t"
+ "packuswb %%mm6, %%mm6 \n\t"
+ "punpcklbw %%mm6, %%mm4 \n\t"
+ "punpcklbw %%mm4, %%mm1 \n\t"
+ "punpckhbw %%mm4, %%mm2 \n\t"
+
+ "add $16, %%"REG_S" \n\t"
+ "add $8, %%"REG_a" \n\t"
+ "add $8, %%"REG_b" \n\t"
+
+ "movq %%mm1, 16(%%"REG_D") \n\t"
+ "movq %%mm2, 24(%%"REG_D") \n\t"
+ "add $32, %%"REG_D" \n\t"
+
+ "decl %%ecx \n\t"
+ "jnz .Lli1 \n\t"
+ "emms \n\t"
+ "pop %%"REG_BP" \n\t"
+ :
+ : "S" (y), "D" (dst), "a" (u), "b" (v), "c" (w/16),
+#if ARCH_X86_64
+ "d" ((x86_reg)us), "r" ((x86_reg)vs)
+#else
+ "d" (&us)
+#endif
+ : "memory"
+ );
+ pack_li_1_C(dst, y, u, v, (w&15), us, vs);
+}
+#endif /* HAVE_EBX_AVAILABLE */
+#endif
+
+static pack_func_t *pack_nn;
+static pack_func_t *pack_li_0;
+static pack_func_t *pack_li_1;
+
+static void ilpack(unsigned char *dst, unsigned char *src[3],
+ int dststride, int srcstride[3], int w, int h, pack_func_t *pack[2])
+{
+ int i;
+ unsigned char *y, *u, *v;
+ int ys = srcstride[0], us = srcstride[1], vs = srcstride[2];
+ int a, b;
+
+ y = src[0];
+ u = src[1];
+ v = src[2];
+
+ pack_nn(dst, y, u, v, w, 0, 0);
+ y += ys; dst += dststride;
+ pack_nn(dst, y, u+us, v+vs, w, 0, 0);
+ y += ys; dst += dststride;
+ for (i=2; i<h-2; i++) {
+ a = (i&2) ? 1 : -1;
+ b = (i&1) ^ ((i&2)>>1);
+ pack[b](dst, y, u, v, w, us*a, vs*a);
+ y += ys;
+ if ((i&3) == 1) {
+ u -= us;
+ v -= vs;
+ } else {
+ u += us;
+ v += vs;
+ }
+ dst += dststride;
+ }
+ pack_nn(dst, y, u, v, w, 0, 0);
+ y += ys; dst += dststride; u += us; v += vs;
+ pack_nn(dst, y, u, v, w, 0, 0);
+}
+
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ mp_image_t *dmpi;
+
+ // hope we'll get DR buffer:
+ dmpi=vf_get_image(vf->next, IMGFMT_YUY2,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->w, mpi->h);
+
+ ilpack(dmpi->planes[0], mpi->planes, dmpi->stride[0], mpi->stride, mpi->w, mpi->h, vf->priv->pack);
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ /* FIXME - also support UYVY output? */
+ return vf_next_config(vf, width, height, d_width, d_height, flags, IMGFMT_YUY2);
+}
+
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ /* FIXME - really any YUV 4:2:0 input format should work */
+ switch (fmt) {
+ case IMGFMT_YV12:
+ case IMGFMT_IYUV:
+ case IMGFMT_I420:
+ return vf_next_query_format(vf,IMGFMT_YUY2);
+ }
+ return 0;
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config=config;
+ vf->query_format=query_format;
+ vf->put_image=put_image;
+ vf->priv = calloc(1, sizeof(struct vf_priv_s));
+ vf->priv->mode = 1;
+ if (args) sscanf(args, "%d", &vf->priv->mode);
+
+ pack_nn = pack_nn_C;
+ pack_li_0 = pack_li_0_C;
+ pack_li_1 = pack_li_1_C;
+#if HAVE_MMX
+ if(gCpuCaps.hasMMX) {
+ pack_nn = pack_nn_MMX;
+#if HAVE_EBX_AVAILABLE
+ pack_li_0 = pack_li_0_MMX;
+ pack_li_1 = pack_li_1_MMX;
+#endif
+ }
+#endif
+
+ switch(vf->priv->mode) {
+ case 0:
+ vf->priv->pack[0] = vf->priv->pack[1] = pack_nn;
+ break;
+ default:
+ mp_msg(MSGT_VFILTER, MSGL_WARN,
+ "ilpack: unknown mode %d (fallback to linear)\n",
+ vf->priv->mode);
+ /* Fallthrough */
+ case 1:
+ vf->priv->pack[0] = pack_li_0;
+ vf->priv->pack[1] = pack_li_1;
+ break;
+ }
+
+ return 1;
+}
+
+const vf_info_t vf_info_ilpack = {
+ "4:2:0 planar -> 4:2:2 packed reinterlacer",
+ "ilpack",
+ "Richard Felker",
+ "",
+ vf_open,
+ NULL
+};
diff --git a/video/filter/vf_mirror.c b/video/filter/vf_mirror.c
new file mode 100644
index 0000000000..b48d9a2ef6
--- /dev/null
+++ b/video/filter/vf_mirror.c
@@ -0,0 +1,131 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+
+static void mirror(unsigned char* dst,unsigned char* src,int dststride,int srcstride,int w,int h,int bpp,unsigned int fmt){
+ int y;
+ for(y=0;y<h;y++){
+ int x;
+ switch(bpp){
+ case 1:
+ for(x=0;x<w;x++) dst[x]=src[w-x-1];
+ break;
+ case 2:
+ switch(fmt){
+ case IMGFMT_UYVY: {
+ // packed YUV is tricky. U,V are 32bpp while Y is 16bpp:
+ int w2=w>>1;
+ for(x=0;x<w2;x++){
+ // TODO: optimize this...
+ dst[x*4+0]=src[0+(w2-x-1)*4];
+ dst[x*4+1]=src[3+(w2-x-1)*4];
+ dst[x*4+2]=src[2+(w2-x-1)*4];
+ dst[x*4+3]=src[1+(w2-x-1)*4];
+ }
+ break; }
+ case IMGFMT_YUY2:
+ case IMGFMT_YVYU: {
+ // packed YUV is tricky. U,V are 32bpp while Y is 16bpp:
+ int w2=w>>1;
+ for(x=0;x<w2;x++){
+ // TODO: optimize this...
+ dst[x*4+0]=src[2+(w2-x-1)*4];
+ dst[x*4+1]=src[1+(w2-x-1)*4];
+ dst[x*4+2]=src[0+(w2-x-1)*4];
+ dst[x*4+3]=src[3+(w2-x-1)*4];
+ }
+ break; }
+ default:
+ for(x=0;x<w;x++) *((short*)(dst+x*2))=*((short*)(src+(w-x-1)*2));
+ }
+ break;
+ case 3:
+ for(x=0;x<w;x++){
+ dst[x*3+0]=src[0+(w-x-1)*3];
+ dst[x*3+1]=src[1+(w-x-1)*3];
+ dst[x*3+2]=src[2+(w-x-1)*3];
+ }
+ break;
+ case 4:
+ for(x=0;x<w;x++) *((int*)(dst+x*4))=*((int*)(src+(w-x-1)*4));
+ }
+ src+=srcstride;
+ dst+=dststride;
+ }
+}
+
+//===========================================================================//
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi;
+
+ // hope we'll get DR buffer:
+ dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->w, mpi->h);
+
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ mirror(dmpi->planes[0],mpi->planes[0],
+ dmpi->stride[0],mpi->stride[0],
+ dmpi->w,dmpi->h,1,mpi->imgfmt);
+ mirror(dmpi->planes[1],mpi->planes[1],
+ dmpi->stride[1],mpi->stride[1],
+ dmpi->w>>mpi->chroma_x_shift,dmpi->h>>mpi->chroma_y_shift,1,mpi->imgfmt);
+ mirror(dmpi->planes[2],mpi->planes[2],
+ dmpi->stride[2],mpi->stride[2],
+ dmpi->w>>mpi->chroma_x_shift,dmpi->h>>mpi->chroma_y_shift,1,mpi->imgfmt);
+ } else {
+ mirror(dmpi->planes[0],mpi->planes[0],
+ dmpi->stride[0],mpi->stride[0],
+ dmpi->w,dmpi->h,dmpi->bpp>>3,mpi->imgfmt);
+ dmpi->planes[1]=mpi->planes[1]; // passthrough rgb8 palette
+ }
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+//===========================================================================//
+
+static int vf_open(vf_instance_t *vf, char *args){
+ //vf->config=config;
+ vf->put_image=put_image;
+ return 1;
+}
+
+const vf_info_t vf_info_mirror = {
+ "horizontal mirror",
+ "mirror",
+ "Eyck",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_noformat.c b/video/filter/vf_noformat.c
new file mode 100644
index 0000000000..b143ac0005
--- /dev/null
+++ b/video/filter/vf_noformat.c
@@ -0,0 +1,77 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+static struct vf_priv_s {
+ unsigned int fmt;
+} const vf_priv_dflt = {
+ IMGFMT_YV12
+};
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ if(fmt!=vf->priv->fmt)
+ return vf_next_query_format(vf,fmt);
+ return 0;
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->query_format=query_format;
+ vf->draw_slice=vf_next_draw_slice;
+ vf->default_caps=0;
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"fmt", ST_OFF(fmt), CONF_TYPE_IMGFMT, 0,0 ,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "noformat",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_noformat = {
+ "disallow one output format",
+ "noformat",
+ "Joey",
+ "",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_noise.c b/video/filter/vf_noise.c
new file mode 100644
index 0000000000..87a480d655
--- /dev/null
+++ b/video/filter/vf_noise.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2002 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libvo/fastmemcpy.h"
+#include "libavutil/mem.h"
+
+#define MAX_NOISE 4096
+#define MAX_SHIFT 1024
+#define MAX_RES (MAX_NOISE-MAX_SHIFT)
+
+//===========================================================================//
+
+static inline void lineNoise_C(uint8_t *dst, uint8_t *src, int8_t *noise, int len, int shift);
+static inline void lineNoiseAvg_C(uint8_t *dst, uint8_t *src, int len, int8_t **shift);
+
+static void (*lineNoise)(uint8_t *dst, uint8_t *src, int8_t *noise, int len, int shift)= lineNoise_C;
+static void (*lineNoiseAvg)(uint8_t *dst, uint8_t *src, int len, int8_t **shift)= lineNoiseAvg_C;
+
+typedef struct FilterParam{
+ int strength;
+ int uniform;
+ int temporal;
+ int quality;
+ int averaged;
+ int pattern;
+ int shiftptr;
+ int8_t *noise;
+ int8_t *prev_shift[MAX_RES][3];
+}FilterParam;
+
+struct vf_priv_s {
+ FilterParam lumaParam;
+ FilterParam chromaParam;
+ unsigned int outfmt;
+};
+
+static int nonTempRandShift_init;
+static int nonTempRandShift[MAX_RES];
+
+static int patt[4] = {
+ -1,0,1,0
+};
+
+#define RAND_N(range) ((int) ((double)range*rand()/(RAND_MAX+1.0)))
+static int8_t *initNoise(FilterParam *fp){
+ int strength= fp->strength;
+ int uniform= fp->uniform;
+ int averaged= fp->averaged;
+ int pattern= fp->pattern;
+ int8_t *noise= av_malloc(MAX_NOISE*sizeof(int8_t));
+ int i, j;
+
+ srand(123457);
+
+ for(i=0,j=0; i<MAX_NOISE; i++,j++)
+ {
+ if(uniform) {
+ if (averaged) {
+ if (pattern) {
+ noise[i]= (RAND_N(strength) - strength/2)/6
+ +patt[j%4]*strength*0.25/3;
+ } else {
+ noise[i]= (RAND_N(strength) - strength/2)/3;
+ }
+ } else {
+ if (pattern) {
+ noise[i]= (RAND_N(strength) - strength/2)/2
+ + patt[j%4]*strength*0.25;
+ } else {
+ noise[i]= RAND_N(strength) - strength/2;
+ }
+ }
+ } else {
+ double x1, x2, w, y1;
+ do {
+ x1 = 2.0 * rand()/(float)RAND_MAX - 1.0;
+ x2 = 2.0 * rand()/(float)RAND_MAX - 1.0;
+ w = x1 * x1 + x2 * x2;
+ } while ( w >= 1.0 );
+
+ w = sqrt( (-2.0 * log( w ) ) / w );
+ y1= x1 * w;
+ y1*= strength / sqrt(3.0);
+ if (pattern) {
+ y1 /= 2;
+ y1 += patt[j%4]*strength*0.35;
+ }
+ if (y1<-128) y1=-128;
+ else if(y1> 127) y1= 127;
+ if (averaged) y1 /= 3.0;
+ noise[i]= (int)y1;
+ }
+ if (RAND_N(6) == 0) j--;
+ }
+
+
+ for (i = 0; i < MAX_RES; i++)
+ for (j = 0; j < 3; j++)
+ fp->prev_shift[i][j] = noise + (rand()&(MAX_SHIFT-1));
+
+ if(!nonTempRandShift_init){
+ for(i=0; i<MAX_RES; i++){
+ nonTempRandShift[i]= rand()&(MAX_SHIFT-1);
+ }
+ nonTempRandShift_init = 1;
+ }
+
+ fp->noise= noise;
+ fp->shiftptr= 0;
+ return noise;
+}
+
+/***************************************************************************/
+
+#if HAVE_MMX
+static inline void lineNoise_MMX(uint8_t *dst, uint8_t *src, int8_t *noise, int len, int shift){
+ x86_reg mmx_len= len&(~7);
+ noise+=shift;
+
+ __asm__ volatile(
+ "mov %3, %%"REG_a" \n\t"
+ "pcmpeqb %%mm7, %%mm7 \n\t"
+ "psllw $15, %%mm7 \n\t"
+ "packsswb %%mm7, %%mm7 \n\t"
+ ASMALIGN(4)
+ "1: \n\t"
+ "movq (%0, %%"REG_a"), %%mm0 \n\t"
+ "movq (%1, %%"REG_a"), %%mm1 \n\t"
+ "pxor %%mm7, %%mm0 \n\t"
+ "paddsb %%mm1, %%mm0 \n\t"
+ "pxor %%mm7, %%mm0 \n\t"
+ "movq %%mm0, (%2, %%"REG_a") \n\t"
+ "add $8, %%"REG_a" \n\t"
+ " js 1b \n\t"
+ :: "r" (src+mmx_len), "r" (noise+mmx_len), "r" (dst+mmx_len), "g" (-mmx_len)
+ : "%"REG_a
+ );
+ if(mmx_len!=len)
+ lineNoise_C(dst+mmx_len, src+mmx_len, noise+mmx_len, len-mmx_len, 0);
+}
+#endif
+
+//duplicate of previous except movntq
+#if HAVE_MMX2
+static inline void lineNoise_MMX2(uint8_t *dst, uint8_t *src, int8_t *noise, int len, int shift){
+ x86_reg mmx_len= len&(~7);
+ noise+=shift;
+
+ __asm__ volatile(
+ "mov %3, %%"REG_a" \n\t"
+ "pcmpeqb %%mm7, %%mm7 \n\t"
+ "psllw $15, %%mm7 \n\t"
+ "packsswb %%mm7, %%mm7 \n\t"
+ ASMALIGN(4)
+ "1: \n\t"
+ "movq (%0, %%"REG_a"), %%mm0 \n\t"
+ "movq (%1, %%"REG_a"), %%mm1 \n\t"
+ "pxor %%mm7, %%mm0 \n\t"
+ "paddsb %%mm1, %%mm0 \n\t"
+ "pxor %%mm7, %%mm0 \n\t"
+ "movntq %%mm0, (%2, %%"REG_a") \n\t"
+ "add $8, %%"REG_a" \n\t"
+ " js 1b \n\t"
+ :: "r" (src+mmx_len), "r" (noise+mmx_len), "r" (dst+mmx_len), "g" (-mmx_len)
+ : "%"REG_a
+ );
+ if(mmx_len!=len)
+ lineNoise_C(dst+mmx_len, src+mmx_len, noise+mmx_len, len-mmx_len, 0);
+}
+#endif
+
+static inline void lineNoise_C(uint8_t *dst, uint8_t *src, int8_t *noise, int len, int shift){
+ int i;
+ noise+= shift;
+ for(i=0; i<len; i++)
+ {
+ int v= src[i]+ noise[i];
+ if(v>255) dst[i]=255; //FIXME optimize
+ else if(v<0) dst[i]=0;
+ else dst[i]=v;
+ }
+}
+
+/***************************************************************************/
+
+#if HAVE_MMX
+static inline void lineNoiseAvg_MMX(uint8_t *dst, uint8_t *src, int len, int8_t **shift){
+ x86_reg mmx_len= len&(~7);
+
+ __asm__ volatile(
+ "mov %5, %%"REG_a" \n\t"
+ ASMALIGN(4)
+ "1: \n\t"
+ "movq (%1, %%"REG_a"), %%mm1 \n\t"
+ "movq (%0, %%"REG_a"), %%mm0 \n\t"
+ "paddb (%2, %%"REG_a"), %%mm1 \n\t"
+ "paddb (%3, %%"REG_a"), %%mm1 \n\t"
+ "movq %%mm0, %%mm2 \n\t"
+ "movq %%mm1, %%mm3 \n\t"
+ "punpcklbw %%mm0, %%mm0 \n\t"
+ "punpckhbw %%mm2, %%mm2 \n\t"
+ "punpcklbw %%mm1, %%mm1 \n\t"
+ "punpckhbw %%mm3, %%mm3 \n\t"
+ "pmulhw %%mm0, %%mm1 \n\t"
+ "pmulhw %%mm2, %%mm3 \n\t"
+ "paddw %%mm1, %%mm1 \n\t"
+ "paddw %%mm3, %%mm3 \n\t"
+ "paddw %%mm0, %%mm1 \n\t"
+ "paddw %%mm2, %%mm3 \n\t"
+ "psrlw $8, %%mm1 \n\t"
+ "psrlw $8, %%mm3 \n\t"
+ "packuswb %%mm3, %%mm1 \n\t"
+ "movq %%mm1, (%4, %%"REG_a") \n\t"
+ "add $8, %%"REG_a" \n\t"
+ " js 1b \n\t"
+ :: "r" (src+mmx_len), "r" (shift[0]+mmx_len), "r" (shift[1]+mmx_len), "r" (shift[2]+mmx_len),
+ "r" (dst+mmx_len), "g" (-mmx_len)
+ : "%"REG_a
+ );
+
+ if(mmx_len!=len){
+ int8_t *shift2[3]={shift[0]+mmx_len, shift[1]+mmx_len, shift[2]+mmx_len};
+ lineNoiseAvg_C(dst+mmx_len, src+mmx_len, len-mmx_len, shift2);
+ }
+}
+#endif
+
+static inline void lineNoiseAvg_C(uint8_t *dst, uint8_t *src, int len, int8_t **shift){
+ int i;
+ int8_t *src2= (int8_t*)src;
+
+ for(i=0; i<len; i++)
+ {
+ const int n= shift[0][i] + shift[1][i] + shift[2][i];
+ dst[i]= src2[i]+((n*src2[i])>>7);
+ }
+}
+
+/***************************************************************************/
+
+static void noise(uint8_t *dst, uint8_t *src, int dstStride, int srcStride, int width, int height, FilterParam *fp){
+ int8_t *noise= fp->noise;
+ int y;
+ int shift=0;
+
+ if(!noise)
+ {
+ if(src==dst) return;
+
+ if(dstStride==srcStride) memcpy(dst, src, srcStride*height);
+ else
+ {
+ for(y=0; y<height; y++)
+ {
+ memcpy(dst, src, width);
+ dst+= dstStride;
+ src+= srcStride;
+ }
+ }
+ return;
+ }
+
+ for(y=0; y<height; y++)
+ {
+ if(fp->temporal) shift= rand()&(MAX_SHIFT -1);
+ else shift= nonTempRandShift[y];
+
+ if(fp->quality==0) shift&= ~7;
+ if (fp->averaged) {
+ lineNoiseAvg(dst, src, width, fp->prev_shift[y]);
+ fp->prev_shift[y][fp->shiftptr] = noise + shift;
+ } else {
+ lineNoise(dst, src, noise, width, shift);
+ }
+ dst+= dstStride;
+ src+= srcStride;
+ }
+ fp->shiftptr++;
+ if (fp->shiftptr == 3) fp->shiftptr = 0;
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi){
+ if(mpi->flags&MP_IMGFLAG_PRESERVE) return; // don't change
+ if(mpi->imgfmt!=vf->priv->outfmt) return; // colorspace differ
+ // ok, we can do pp in-place (or pp disabled):
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ mpi->type, mpi->flags, mpi->w, mpi->h);
+ mpi->planes[0]=vf->dmpi->planes[0];
+ mpi->stride[0]=vf->dmpi->stride[0];
+ mpi->width=vf->dmpi->width;
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ mpi->planes[1]=vf->dmpi->planes[1];
+ mpi->planes[2]=vf->dmpi->planes[2];
+ mpi->stride[1]=vf->dmpi->stride[1];
+ mpi->stride[2]=vf->dmpi->stride[2];
+ }
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi;
+
+ if(!(mpi->flags&MP_IMGFLAG_DIRECT)){
+ // no DR, so get a new image! hope we'll get DR buffer:
+ vf->dmpi=vf_get_image(vf->next,vf->priv->outfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->w,mpi->h);
+//printf("nodr\n");
+ }
+//else printf("dr\n");
+ dmpi= vf->dmpi;
+
+ noise(dmpi->planes[0], mpi->planes[0], dmpi->stride[0], mpi->stride[0], mpi->w, mpi->h, &vf->priv->lumaParam);
+ noise(dmpi->planes[1], mpi->planes[1], dmpi->stride[1], mpi->stride[1], mpi->w/2, mpi->h/2, &vf->priv->chromaParam);
+ noise(dmpi->planes[2], mpi->planes[2], dmpi->stride[2], mpi->stride[2], mpi->w/2, mpi->h/2, &vf->priv->chromaParam);
+
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+#if HAVE_MMX
+ if(gCpuCaps.hasMMX) __asm__ volatile ("emms\n\t");
+#endif
+#if HAVE_MMX2
+ if(gCpuCaps.hasMMX2) __asm__ volatile ("sfence\n\t");
+#endif
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+static void uninit(struct vf_instance *vf){
+ if(!vf->priv) return;
+
+ av_free(vf->priv->chromaParam.noise);
+ vf->priv->chromaParam.noise= NULL;
+
+ av_free(vf->priv->lumaParam.noise);
+ vf->priv->lumaParam.noise= NULL;
+
+ free(vf->priv);
+ vf->priv=NULL;
+}
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ switch(fmt)
+ {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ return vf_next_query_format(vf,vf->priv->outfmt);
+ }
+ return 0;
+}
+
+static void parse(FilterParam *fp, char* args){
+ char *pos;
+ char *max= strchr(args, ':');
+
+ if(!max) max= args + strlen(args);
+
+ fp->strength= atoi(args);
+ pos= strchr(args, 'u');
+ if(pos && pos<max) fp->uniform=1;
+ pos= strchr(args, 't');
+ if(pos && pos<max) fp->temporal=1;
+ pos= strchr(args, 'h');
+ if(pos && pos<max) fp->quality=1;
+ pos= strchr(args, 'p');
+ if(pos && pos<max) fp->pattern=1;
+ pos= strchr(args, 'a');
+ if(pos && pos<max) {
+ fp->temporal=1;
+ fp->averaged=1;
+ }
+
+ if(fp->strength) initNoise(fp);
+}
+
+static const unsigned int fmt_list[]={
+ IMGFMT_YV12,
+ IMGFMT_I420,
+ IMGFMT_IYUV,
+ 0
+};
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->put_image=put_image;
+ vf->get_image=get_image;
+ vf->query_format=query_format;
+ vf->uninit=uninit;
+ vf->priv=malloc(sizeof(struct vf_priv_s));
+ memset(vf->priv, 0, sizeof(struct vf_priv_s));
+ if(args)
+ {
+ char *arg2= strchr(args,':');
+ if(arg2) parse(&vf->priv->chromaParam, arg2+1);
+ parse(&vf->priv->lumaParam, args);
+ }
+
+ // check csp:
+ vf->priv->outfmt=vf_match_csp(&vf->next,fmt_list,IMGFMT_YV12);
+ if(!vf->priv->outfmt)
+ {
+ uninit(vf);
+ return 0; // no csp match :(
+ }
+
+
+#if HAVE_MMX
+ if(gCpuCaps.hasMMX){
+ lineNoise= lineNoise_MMX;
+ lineNoiseAvg= lineNoiseAvg_MMX;
+ }
+#endif
+#if HAVE_MMX2
+ if(gCpuCaps.hasMMX2) lineNoise= lineNoise_MMX2;
+// if(gCpuCaps.hasMMX) lineNoiseAvg= lineNoiseAvg_MMX2;
+#endif
+
+ return 1;
+}
+
+const vf_info_t vf_info_noise = {
+ "noise generator",
+ "noise",
+ "Michael Niedermayer",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_phase.c b/video/filter/vf_phase.c
new file mode 100644
index 0000000000..568c8f1f45
--- /dev/null
+++ b/video/filter/vf_phase.c
@@ -0,0 +1,303 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+enum mode { PROGRESSIVE, TOP_FIRST, BOTTOM_FIRST,
+ TOP_FIRST_ANALYZE, BOTTOM_FIRST_ANALYZE,
+ ANALYZE, FULL_ANALYZE, AUTO, AUTO_ANALYZE };
+
+#define fixed_mode(p) ((p)<=BOTTOM_FIRST)
+
+struct vf_priv_s
+ {
+ enum mode mode;
+ int verbose;
+ unsigned char *buf[3];
+ };
+
+/*
+ * Copy fields from either current or buffered previous frame to the
+ * output and store the current frame unmodified to the buffer.
+ */
+
+static void do_plane(unsigned char *to, unsigned char *from,
+ int w, int h, int ts, int fs,
+ unsigned char **bufp, enum mode mode)
+ {
+ unsigned char *buf, *end;
+ int top;
+
+ if(!*bufp)
+ {
+ mode=PROGRESSIVE;
+ if(!(*bufp=malloc(h*w))) return;
+ }
+
+ for(end=to+h*ts, buf=*bufp, top=1; to<end; from+=fs, to+=ts, buf+=w, top^=1)
+ {
+ memcpy(to, mode==(top?BOTTOM_FIRST:TOP_FIRST)?buf:from, w);
+ memcpy(buf, from, w);
+ }
+ }
+
+/*
+ * This macro interpolates the value of both fields at a point halfway
+ * between lines and takes the squared difference. In field resolution
+ * the point is a quarter pixel below a line in one field and a quarter
+ * pixel above a line in other.
+ *
+ * (the result is actually multiplied by 25)
+ */
+
+#define diff(a, as, b, bs) (t=((*a-b[bs])<<2)+a[as<<1]-b[-bs], t*t)
+
+/*
+ * Find which field combination has the smallest average squared difference
+ * between the fields.
+ */
+
+static enum mode analyze_plane(unsigned char *old, unsigned char *new,
+ int w, int h, int os, int ns, enum mode mode,
+ int verbose, int fields)
+ {
+ double bdiff, pdiff, tdiff, scale;
+ int bdif, tdif, pdif;
+ int top, t;
+ unsigned char *end, *rend;
+
+ if(mode==AUTO)
+ mode=fields&MP_IMGFIELD_ORDERED?fields&MP_IMGFIELD_TOP_FIRST?
+ TOP_FIRST:BOTTOM_FIRST:PROGRESSIVE;
+ else if(mode==AUTO_ANALYZE)
+ mode=fields&MP_IMGFIELD_ORDERED?fields&MP_IMGFIELD_TOP_FIRST?
+ TOP_FIRST_ANALYZE:BOTTOM_FIRST_ANALYZE:FULL_ANALYZE;
+
+ if(fixed_mode(mode))
+ bdiff=pdiff=tdiff=65536.0;
+ else
+ {
+ bdiff=pdiff=tdiff=0.0;
+
+ for(end=new+(h-2)*ns, new+=ns, old+=os, top=0;
+ new<end; new+=ns-w, old+=os-w, top^=1)
+ {
+ pdif=tdif=bdif=0;
+
+ switch(mode)
+ {
+ case TOP_FIRST_ANALYZE:
+ if(top)
+ for(rend=new+w; new<rend; new++, old++)
+ pdif+=diff(new, ns, new, ns),
+ tdif+=diff(new, ns, old, os);
+ else
+ for(rend=new+w; new<rend; new++, old++)
+ pdif+=diff(new, ns, new, ns),
+ tdif+=diff(old, os, new, ns);
+ break;
+
+ case BOTTOM_FIRST_ANALYZE:
+ if(top)
+ for(rend=new+w; new<rend; new++, old++)
+ pdif+=diff(new, ns, new, ns),
+ bdif+=diff(old, os, new, ns);
+ else
+ for(rend=new+w; new<rend; new++, old++)
+ pdif+=diff(new, ns, new, ns),
+ bdif+=diff(new, ns, old, os);
+ break;
+
+ case ANALYZE:
+ if(top)
+ for(rend=new+w; new<rend; new++, old++)
+ tdif+=diff(new, ns, old, os),
+ bdif+=diff(old, os, new, ns);
+ else
+ for(rend=new+w; new<rend; new++, old++)
+ bdif+=diff(new, ns, old, os),
+ tdif+=diff(old, os, new, ns);
+ break;
+
+ default: /* FULL_ANALYZE */
+ if(top)
+ for(rend=new+w; new<rend; new++, old++)
+ pdif+=diff(new, ns, new, ns),
+ tdif+=diff(new, ns, old, os),
+ bdif+=diff(old, os, new, ns);
+ else
+ for(rend=new+w; new<rend; new++, old++)
+ pdif+=diff(new, ns, new, ns),
+ bdif+=diff(new, ns, old, os),
+ tdif+=diff(old, os, new, ns);
+ }
+
+ pdiff+=(double)pdif;
+ tdiff+=(double)tdif;
+ bdiff+=(double)bdif;
+ }
+
+ scale=1.0/(w*(h-3))/25.0;
+ pdiff*=scale;
+ tdiff*=scale;
+ bdiff*=scale;
+
+ if(mode==TOP_FIRST_ANALYZE)
+ bdiff=65536.0;
+ else if(mode==BOTTOM_FIRST_ANALYZE)
+ tdiff=65536.0;
+ else if(mode==ANALYZE)
+ pdiff=65536.0;
+
+ if(bdiff<pdiff && bdiff<tdiff)
+ mode=BOTTOM_FIRST;
+ else if(tdiff<pdiff && tdiff<bdiff)
+ mode=TOP_FIRST;
+ else
+ mode=PROGRESSIVE;
+ }
+
+ if( mp_msg_test(MSGT_VFILTER,MSGL_V) )
+ {
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "%c", mode==BOTTOM_FIRST?'b':mode==TOP_FIRST?'t':'p');
+ if(tdiff==65536.0) mp_msg(MSGT_VFILTER, MSGL_INFO," N/A "); else mp_msg(MSGT_VFILTER, MSGL_INFO," %8.2f", tdiff);
+ if(bdiff==65536.0) mp_msg(MSGT_VFILTER, MSGL_INFO," N/A "); else mp_msg(MSGT_VFILTER, MSGL_INFO," %8.2f", bdiff);
+ if(pdiff==65536.0) mp_msg(MSGT_VFILTER, MSGL_INFO," N/A "); else mp_msg(MSGT_VFILTER, MSGL_INFO," %8.2f", pdiff);
+ mp_msg(MSGT_VFILTER, MSGL_INFO," \n");
+ }
+
+ return mode;
+ }
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+ {
+ mp_image_t *dmpi;
+ int w;
+ enum mode mode;
+
+ if(!(dmpi=vf_get_image(vf->next, mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->w, mpi->h)))
+ return 0;
+
+ w=dmpi->w;
+ if(!(dmpi->flags&MP_IMGFLAG_PLANAR))
+ w*=dmpi->bpp/8;
+
+ mode=vf->priv->mode;
+
+ if(!vf->priv->buf[0])
+ mode=PROGRESSIVE;
+ else
+ mode=analyze_plane(vf->priv->buf[0], mpi->planes[0],
+ w, dmpi->h, w, mpi->stride[0], mode,
+ vf->priv->verbose, mpi->fields);
+
+ do_plane(dmpi->planes[0], mpi->planes[0],
+ w, dmpi->h,
+ dmpi->stride[0], mpi->stride[0],
+ &vf->priv->buf[0], mode);
+
+ if(dmpi->flags&MP_IMGFLAG_PLANAR)
+ {
+ do_plane(dmpi->planes[1], mpi->planes[1],
+ dmpi->chroma_width, dmpi->chroma_height,
+ dmpi->stride[1], mpi->stride[1],
+ &vf->priv->buf[1], mode);
+ do_plane(dmpi->planes[2], mpi->planes[2],
+ dmpi->chroma_width, dmpi->chroma_height,
+ dmpi->stride[2], mpi->stride[2],
+ &vf->priv->buf[2], mode);
+ }
+
+ return vf_next_put_image(vf, dmpi, pts);
+ }
+
+static void uninit(struct vf_instance *vf)
+ {
+ if (!vf->priv)
+ return;
+ free(vf->priv->buf[0]);
+ free(vf->priv->buf[1]);
+ free(vf->priv->buf[2]);
+ free(vf->priv);
+ }
+
+static int vf_open(vf_instance_t *vf, char *args)
+ {
+ vf->put_image = put_image;
+ vf->uninit = uninit;
+ vf->default_reqs = VFCAP_ACCEPT_STRIDE;
+
+ if(!(vf->priv = calloc(1, sizeof(struct vf_priv_s))))
+ {
+ uninit(vf);
+ return 0;
+ }
+
+ vf->priv->mode=AUTO_ANALYZE;
+ vf->priv->verbose=0;
+
+ while(args && *args)
+ {
+ switch(*args)
+ {
+ case 't': vf->priv->mode=TOP_FIRST; break;
+ case 'a': vf->priv->mode=AUTO; break;
+ case 'b': vf->priv->mode=BOTTOM_FIRST; break;
+ case 'u': vf->priv->mode=ANALYZE; break;
+ case 'T': vf->priv->mode=TOP_FIRST_ANALYZE; break;
+ case 'A': vf->priv->mode=AUTO_ANALYZE; break;
+ case 'B': vf->priv->mode=BOTTOM_FIRST_ANALYZE; break;
+ case 'U': vf->priv->mode=FULL_ANALYZE; break;
+ case 'p': vf->priv->mode=PROGRESSIVE; break;
+ case 'v': vf->priv->verbose=1; break;
+ case ':': break;
+
+ default:
+ uninit(vf);
+ return 0; /* bad args */
+ }
+
+ if( (args=strchr(args, ':')) ) args++;
+ }
+
+ return 1;
+ }
+
+const vf_info_t vf_info_phase =
+ {
+ "phase shift fields",
+ "phase",
+ "Ville Saari",
+ "",
+ vf_open,
+ NULL
+ };
diff --git a/video/filter/vf_pp.c b/video/filter/vf_pp.c
new file mode 100644
index 0000000000..9647032002
--- /dev/null
+++ b/video/filter/vf_pp.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libpostproc/postprocess.h"
+
+struct vf_priv_s {
+ int pp;
+ pp_mode *ppMode[PP_QUALITY_MAX+1];
+ void *context;
+ unsigned int outfmt;
+};
+
+//===========================================================================//
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int voflags, unsigned int outfmt){
+ int flags=
+ (gCpuCaps.hasMMX ? PP_CPU_CAPS_MMX : 0)
+ | (gCpuCaps.hasMMX2 ? PP_CPU_CAPS_MMX2 : 0);
+
+ switch(outfmt){
+ case IMGFMT_444P: flags|= PP_FORMAT_444; break;
+ case IMGFMT_422P: flags|= PP_FORMAT_422; break;
+ case IMGFMT_411P: flags|= PP_FORMAT_411; break;
+ default: flags|= PP_FORMAT_420; break;
+ }
+
+ if(vf->priv->context) pp_free_context(vf->priv->context);
+ vf->priv->context= pp_get_context(width, height, flags);
+
+ return vf_next_config(vf,width,height,d_width,d_height,voflags,outfmt);
+}
+
+static void uninit(struct vf_instance *vf){
+ int i;
+ for(i=0; i<=PP_QUALITY_MAX; i++){
+ if(vf->priv->ppMode[i])
+ pp_free_mode(vf->priv->ppMode[i]);
+ }
+ if(vf->priv->context) pp_free_context(vf->priv->context);
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ switch(fmt){
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_444P:
+ case IMGFMT_422P:
+ case IMGFMT_411P:
+ return vf_next_query_format(vf,fmt);
+ }
+ return 0;
+}
+
+static int control(struct vf_instance *vf, int request, void* data){
+ switch(request){
+ case VFCTRL_QUERY_MAX_PP_LEVEL:
+ return PP_QUALITY_MAX;
+ case VFCTRL_SET_PP_LEVEL:
+ vf->priv->pp= *((unsigned int*)data);
+ return CONTROL_TRUE;
+ }
+ return vf_next_control(vf,request,data);
+}
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi){
+ if(vf->priv->pp&0xFFFF) return; // non-local filters enabled
+ if((mpi->type==MP_IMGTYPE_IPB || vf->priv->pp) &&
+ mpi->flags&MP_IMGFLAG_PRESERVE) return; // don't change
+ if(!(mpi->flags&MP_IMGFLAG_ACCEPT_STRIDE) && mpi->imgfmt!=vf->priv->outfmt)
+ return; // colorspace differ
+ // ok, we can do pp in-place (or pp disabled):
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ mpi->type, mpi->flags | MP_IMGFLAG_READABLE, mpi->width, mpi->height);
+ mpi->planes[0]=vf->dmpi->planes[0];
+ mpi->stride[0]=vf->dmpi->stride[0];
+ mpi->width=vf->dmpi->width;
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ mpi->planes[1]=vf->dmpi->planes[1];
+ mpi->planes[2]=vf->dmpi->planes[2];
+ mpi->stride[1]=vf->dmpi->stride[1];
+ mpi->stride[2]=vf->dmpi->stride[2];
+ }
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ if(!(mpi->flags&MP_IMGFLAG_DIRECT)){
+ // no DR, so get a new image! hope we'll get DR buffer:
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE |
+ MP_IMGFLAG_PREFER_ALIGNED_STRIDE | MP_IMGFLAG_READABLE,
+// MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+// mpi->w,mpi->h);
+ (mpi->width+7)&(~7),(mpi->height+7)&(~7));
+ vf->dmpi->w=mpi->w; vf->dmpi->h=mpi->h; // display w;h
+ }
+
+ if(vf->priv->pp || !(mpi->flags&MP_IMGFLAG_DIRECT)){
+ // do the postprocessing! (or copy if no DR)
+ pp_postprocess((const uint8_t **)mpi->planes, mpi->stride,
+ vf->dmpi->planes,vf->dmpi->stride,
+ (mpi->w+7)&(~7),mpi->h,
+ mpi->qscale, mpi->qstride,
+ vf->priv->ppMode[ vf->priv->pp ], vf->priv->context,
+#ifdef PP_PICT_TYPE_QP2
+ mpi->pict_type | (mpi->qscale_type ? PP_PICT_TYPE_QP2 : 0));
+#else
+ mpi->pict_type);
+#endif
+ }
+ return vf_next_put_image(vf,vf->dmpi, pts);
+}
+
+//===========================================================================//
+
+extern int divx_quality;
+
+static const unsigned int fmt_list[]={
+ IMGFMT_YV12,
+ IMGFMT_I420,
+ IMGFMT_IYUV,
+ IMGFMT_444P,
+ IMGFMT_422P,
+ IMGFMT_411P,
+ 0
+};
+
+static int vf_open(vf_instance_t *vf, char *args){
+ int i;
+
+ vf->query_format=query_format;
+ vf->control=control;
+ vf->config=config;
+ vf->get_image=get_image;
+ vf->put_image=put_image;
+ vf->uninit=uninit;
+ vf->default_caps=VFCAP_ACCEPT_STRIDE|VFCAP_POSTPROC;
+ vf->priv=malloc(sizeof(struct vf_priv_s));
+ vf->priv->context=NULL;
+
+ // check csp:
+ vf->priv->outfmt=vf_match_csp(&vf->next,fmt_list,IMGFMT_YV12);
+ if(!vf->priv->outfmt) return 0; // no csp match :(
+
+ char *name = args ? args : "de";
+
+ for(i=0; i<=PP_QUALITY_MAX; i++){
+ vf->priv->ppMode[i]= pp_get_mode_by_name_and_quality(name, i);
+ if(vf->priv->ppMode[i]==NULL) return -1;
+ }
+
+ vf->priv->pp=PP_QUALITY_MAX;
+ return 1;
+}
+
+const vf_info_t vf_info_pp = {
+ "postprocessing",
+ "pp",
+ "A'rpi",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_pullup.c b/video/filter/vf_pullup.c
new file mode 100644
index 0000000000..97f851f348
--- /dev/null
+++ b/video/filter/vf_pullup.c
@@ -0,0 +1,333 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+#include "pullup.h"
+
+#undef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+struct vf_priv_s {
+ struct pullup_context *ctx;
+ int init;
+ int fakecount;
+ char *qbuf;
+ double lastpts;
+};
+
+static void init_pullup(struct vf_instance *vf, mp_image_t *mpi)
+{
+ struct pullup_context *c = vf->priv->ctx;
+
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ c->format = PULLUP_FMT_Y;
+ c->nplanes = 4;
+ pullup_preinit_context(c);
+ c->bpp[0] = c->bpp[1] = c->bpp[2] = 8;
+ c->w[0] = mpi->w;
+ c->h[0] = mpi->h;
+ c->w[1] = c->w[2] = mpi->chroma_width;
+ c->h[1] = c->h[2] = mpi->chroma_height;
+ c->w[3] = ((mpi->w+15)/16) * ((mpi->h+15)/16);
+ c->h[3] = 2;
+ c->stride[0] = mpi->width;
+ c->stride[1] = c->stride[2] = mpi->chroma_width;
+ c->stride[3] = c->w[3];
+ c->background[1] = c->background[2] = 128;
+ }
+
+ if (gCpuCaps.hasMMX) c->cpu |= PULLUP_CPU_MMX;
+ if (gCpuCaps.hasMMX2) c->cpu |= PULLUP_CPU_MMX2;
+ if (gCpuCaps.hasSSE) c->cpu |= PULLUP_CPU_SSE;
+ if (gCpuCaps.hasSSE2) c->cpu |= PULLUP_CPU_SSE2;
+
+ pullup_init_context(c);
+
+ vf->priv->init = 1;
+ vf->priv->qbuf = malloc(c->w[3]);
+}
+
+
+#if 0
+static void get_image(struct vf_instance *vf, mp_image_t *mpi)
+{
+ struct pullup_context *c = vf->priv->ctx;
+ struct pullup_buffer *b;
+
+ if (mpi->type == MP_IMGTYPE_STATIC) return;
+
+ if (!vf->priv->init) init_pullup(vf, mpi);
+
+ b = pullup_get_buffer(c, 2);
+ if (!b) return; /* shouldn't happen... */
+
+ mpi->priv = b;
+
+ mpi->planes[0] = b->planes[0];
+ mpi->planes[1] = b->planes[1];
+ mpi->planes[2] = b->planes[2];
+ mpi->stride[0] = c->stride[0];
+ mpi->stride[1] = c->stride[1];
+ mpi->stride[2] = c->stride[2];
+
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+ mpi->flags &= ~MP_IMGFLAG_DRAW_CALLBACK;
+}
+#endif
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ struct pullup_context *c = vf->priv->ctx;
+ struct pullup_buffer *b;
+ struct pullup_frame *f;
+ mp_image_t *dmpi;
+ int ret;
+ int p;
+ int i;
+
+ if (!vf->priv->init) init_pullup(vf, mpi);
+
+ if (mpi->flags & MP_IMGFLAG_DIRECT) {
+ b = mpi->priv;
+ mpi->priv = 0;
+ } else {
+ b = pullup_get_buffer(c, 2);
+ if (!b) {
+ mp_msg(MSGT_VFILTER,MSGL_ERR,"Could not get buffer from pullup!\n");
+ f = pullup_get_frame(c);
+ pullup_release_frame(f);
+ return 0;
+ }
+ memcpy_pic(b->planes[0], mpi->planes[0], mpi->w, mpi->h,
+ c->stride[0], mpi->stride[0]);
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ memcpy_pic(b->planes[1], mpi->planes[1],
+ mpi->chroma_width, mpi->chroma_height,
+ c->stride[1], mpi->stride[1]);
+ memcpy_pic(b->planes[2], mpi->planes[2],
+ mpi->chroma_width, mpi->chroma_height,
+ c->stride[2], mpi->stride[2]);
+ }
+ }
+ if (mpi->qscale) {
+ memcpy(b->planes[3], mpi->qscale, c->w[3]);
+ memcpy(b->planes[3]+c->w[3], mpi->qscale, c->w[3]);
+ }
+
+ p = mpi->fields & MP_IMGFIELD_TOP_FIRST ? 0 :
+ (mpi->fields & MP_IMGFIELD_ORDERED ? 1 : 0);
+
+ if (pts == MP_NOPTS_VALUE) {
+ pullup_submit_field(c, b, p, MP_NOPTS_VALUE);
+ pullup_submit_field(c, b, p^1, MP_NOPTS_VALUE);
+ if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST)
+ pullup_submit_field(c, b, p, MP_NOPTS_VALUE);
+ } else {
+ double delta;
+ if (vf->priv->lastpts == MP_NOPTS_VALUE)
+ delta = 1001.0/60000.0; // delta = field time distance
+ else
+ delta = (pts - vf->priv->lastpts) / 2;
+ if (delta <= 0.0 || delta >= 0.5)
+ delta = 0.0;
+ vf->priv->lastpts = pts;
+ if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) {
+ pullup_submit_field(c, b, p, pts - delta);
+ pullup_submit_field(c, b, p^1, pts);
+ pullup_submit_field(c, b, p, pts + delta);
+ } else {
+ pullup_submit_field(c, b, p, pts - delta * 0.5);
+ pullup_submit_field(c, b, p^1, pts + delta * 0.5);
+ }
+ }
+
+ pullup_release_buffer(b, 2);
+
+ f = pullup_get_frame(c);
+
+ /* Fake yes for first few frames (buffer depth) to keep from
+ * breaking A/V sync with G1's bad architecture... */
+ if (!f) return vf->priv->fakecount ? (--vf->priv->fakecount,1) : 0;
+
+ if (f->length < 2) {
+ pullup_release_frame(f);
+ f = pullup_get_frame(c);
+ if (!f) return 0;
+ if (f->length < 2) {
+ pullup_release_frame(f);
+ if (!(mpi->fields & MP_IMGFIELD_REPEAT_FIRST))
+ return 0;
+ f = pullup_get_frame(c);
+ if (!f) return 0;
+ if (f->length < 2) {
+ pullup_release_frame(f);
+ return 0;
+ }
+ }
+ }
+
+#if 0
+ /* Average qscale tables from both frames. */
+ if (mpi->qscale) {
+ for (i=0; i<c->w[3]; i++) {
+ vf->priv->qbuf[i] = (f->ofields[0]->planes[3][i]
+ + f->ofields[1]->planes[3][i+c->w[3]])>>1;
+ }
+ }
+#else
+ /* Take worst of qscale tables from both frames. */
+ if (mpi->qscale) {
+ for (i=0; i<c->w[3]; i++) {
+ vf->priv->qbuf[i] = MAX(f->ofields[0]->planes[3][i], f->ofields[1]->planes[3][i+c->w[3]]);
+ }
+ }
+#endif
+
+ /* If the frame isn't already exportable... */
+ while (!f->buffer) {
+ dmpi = vf_get_image(vf->next, mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->width, mpi->height);
+ /* FIXME: Is it ok to discard dmpi if it's not direct? */
+ if (!(dmpi->flags & MP_IMGFLAG_DIRECT)) {
+ pullup_pack_frame(c, f);
+ break;
+ }
+ /* Direct render fields into output buffer */
+ my_memcpy_pic(dmpi->planes[0], f->ofields[0]->planes[0],
+ mpi->w, mpi->h/2, dmpi->stride[0]*2, c->stride[0]*2);
+ my_memcpy_pic(dmpi->planes[0] + dmpi->stride[0],
+ f->ofields[1]->planes[0] + c->stride[0],
+ mpi->w, mpi->h/2, dmpi->stride[0]*2, c->stride[0]*2);
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ my_memcpy_pic(dmpi->planes[1], f->ofields[0]->planes[1],
+ mpi->chroma_width, mpi->chroma_height/2,
+ dmpi->stride[1]*2, c->stride[1]*2);
+ my_memcpy_pic(dmpi->planes[1] + dmpi->stride[1],
+ f->ofields[1]->planes[1] + c->stride[1],
+ mpi->chroma_width, mpi->chroma_height/2,
+ dmpi->stride[1]*2, c->stride[1]*2);
+ my_memcpy_pic(dmpi->planes[2], f->ofields[0]->planes[2],
+ mpi->chroma_width, mpi->chroma_height/2,
+ dmpi->stride[2]*2, c->stride[2]*2);
+ my_memcpy_pic(dmpi->planes[2] + dmpi->stride[2],
+ f->ofields[1]->planes[2] + c->stride[2],
+ mpi->chroma_width, mpi->chroma_height/2,
+ dmpi->stride[2]*2, c->stride[2]*2);
+ }
+ pullup_release_frame(f);
+ if (mpi->qscale) {
+ dmpi->qscale = vf->priv->qbuf;
+ dmpi->qstride = mpi->qstride;
+ dmpi->qscale_type = mpi->qscale_type;
+ }
+ return vf_next_put_image(vf, dmpi, f->pts);
+ }
+ dmpi = vf_get_image(vf->next, mpi->imgfmt,
+ MP_IMGTYPE_EXPORT, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->width, mpi->height);
+
+ dmpi->planes[0] = f->buffer->planes[0];
+ dmpi->planes[1] = f->buffer->planes[1];
+ dmpi->planes[2] = f->buffer->planes[2];
+
+ dmpi->stride[0] = c->stride[0];
+ dmpi->stride[1] = c->stride[1];
+ dmpi->stride[2] = c->stride[2];
+
+ if (mpi->qscale) {
+ dmpi->qscale = vf->priv->qbuf;
+ dmpi->qstride = mpi->qstride;
+ dmpi->qscale_type = mpi->qscale_type;
+ }
+ ret = vf_next_put_image(vf, dmpi, f->pts);
+ pullup_release_frame(f);
+ return ret;
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ /* FIXME - support more formats */
+ switch (fmt) {
+ case IMGFMT_YV12:
+ case IMGFMT_IYUV:
+ case IMGFMT_I420:
+ return vf_next_query_format(vf, fmt);
+ }
+ return 0;
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ if (height&3) return 0;
+ return vf_next_config(vf, width, height, d_width, d_height, flags, outfmt);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ pullup_free_context(vf->priv->ctx);
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ struct vf_priv_s *p;
+ struct pullup_context *c;
+ //vf->get_image = get_image;
+ vf->put_image = put_image;
+ vf->config = config;
+ vf->query_format = query_format;
+ vf->uninit = uninit;
+ vf->default_reqs = VFCAP_ACCEPT_STRIDE;
+ vf->priv = p = calloc(1, sizeof(struct vf_priv_s));
+ p->ctx = c = pullup_alloc_context();
+ p->fakecount = 1;
+ c->verbose = verbose>0;
+ c->junk_left = c->junk_right = 1;
+ c->junk_top = c->junk_bottom = 4;
+ c->strict_breaks = 0;
+ c->metric_plane = 0;
+ if (args) {
+ sscanf(args, "%d:%d:%d:%d:%d:%d", &c->junk_left, &c->junk_right, &c->junk_top, &c->junk_bottom, &c->strict_breaks, &c->metric_plane);
+ }
+ return 1;
+}
+
+const vf_info_t vf_info_pullup = {
+ "pullup (from field sequence to frames)",
+ "pullup",
+ "Rich Felker",
+ "",
+ vf_open,
+ NULL
+};
diff --git a/video/filter/vf_rotate.c b/video/filter/vf_rotate.c
new file mode 100644
index 0000000000..19eeae6d35
--- /dev/null
+++ b/video/filter/vf_rotate.c
@@ -0,0 +1,152 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+struct vf_priv_s {
+ int direction;
+};
+
+static void rotate(unsigned char* dst,unsigned char* src,int dststride,int srcstride,int w,int h,int bpp,int dir){
+ int y;
+ if(dir&1){
+ src+=srcstride*(w-1);
+ srcstride*=-1;
+ }
+ if(dir&2){
+ dst+=dststride*(h-1);
+ dststride*=-1;
+ }
+
+ for(y=0;y<h;y++){
+ int x;
+ switch(bpp){
+ case 1:
+ for(x=0;x<w;x++) dst[x]=src[y+x*srcstride];
+ break;
+ case 2:
+ for(x=0;x<w;x++) *((short*)(dst+x*2))=*((short*)(src+y*2+x*srcstride));
+ break;
+ case 3:
+ for(x=0;x<w;x++){
+ dst[x*3+0]=src[0+y*3+x*srcstride];
+ dst[x*3+1]=src[1+y*3+x*srcstride];
+ dst[x*3+2]=src[2+y*3+x*srcstride];
+ }
+ break;
+ case 4:
+ for(x=0;x<w;x++) *((int*)(dst+x*4))=*((int*)(src+y*4+x*srcstride));
+ }
+ dst+=dststride;
+ }
+}
+
+//===========================================================================//
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+ if (vf->priv->direction & 4) {
+ if (width<height) vf->priv->direction&=3;
+ }
+ if (vf->priv->direction & 4){
+ vf->put_image=vf_next_put_image; // passthru mode!
+ if (vf->next->draw_slice) vf->draw_slice=vf_next_draw_slice;
+/* FIXME: this should be in an other procedure in vf.c; that should always check
+ whether the filter after the passthrough one still (not)supports slices */
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+ }
+ return vf_next_config(vf,height,width,d_height,d_width,flags,outfmt);
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi;
+
+ // hope we'll get DR buffer:
+ dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
+ mpi->h, mpi->w);
+
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ rotate(dmpi->planes[0],mpi->planes[0],
+ dmpi->stride[0],mpi->stride[0],
+ dmpi->w,dmpi->h,1,vf->priv->direction);
+ rotate(dmpi->planes[1],mpi->planes[1],
+ dmpi->stride[1],mpi->stride[1],
+ dmpi->w>>mpi->chroma_x_shift,dmpi->h>>mpi->chroma_y_shift,1,vf->priv->direction);
+ rotate(dmpi->planes[2],mpi->planes[2],
+ dmpi->stride[2],mpi->stride[2],
+ dmpi->w>>mpi->chroma_x_shift,dmpi->h>>mpi->chroma_y_shift,1,vf->priv->direction);
+ } else {
+ rotate(dmpi->planes[0],mpi->planes[0],
+ dmpi->stride[0],mpi->stride[0],
+ dmpi->w,dmpi->h,dmpi->bpp>>3,vf->priv->direction);
+ dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette
+ }
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ if(IMGFMT_IS_RGB(fmt) || IMGFMT_IS_BGR(fmt)) return vf_next_query_format(vf, fmt);
+ // we can support only symmetric (chroma_x_shift==chroma_y_shift) YUV formats:
+ switch(fmt) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_YVU9:
+// case IMGFMT_IF09:
+ case IMGFMT_Y8:
+ case IMGFMT_Y800:
+ case IMGFMT_444P:
+ return vf_next_query_format(vf, fmt);
+ }
+ return 0;
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->put_image=put_image;
+ vf->query_format=query_format;
+ vf->priv=malloc(sizeof(struct vf_priv_s));
+ vf->priv->direction=args?atoi(args):0;
+ return 1;
+}
+
+const vf_info_t vf_info_rotate = {
+ "rotate",
+ "rotate",
+ "A'rpi",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_scale.c b/video/filter/vf_scale.c
new file mode 100644
index 0000000000..5ea62bacbd
--- /dev/null
+++ b/video/filter/vf_scale.c
@@ -0,0 +1,718 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "fmt-conversion.h"
+#include "mpbswap.h"
+
+#include "libmpcodecs/sws_utils.h"
+
+#include "libvo/csputils.h"
+// VOFLAG_SWSCALE
+#include "libvo/video_out.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+static struct vf_priv_s {
+ int w,h;
+ int cfg_w, cfg_h;
+ int v_chr_drop;
+ double param[2];
+ unsigned int fmt;
+ struct SwsContext *ctx;
+ struct SwsContext *ctx2; //for interlaced slices only
+ unsigned char* palette;
+ int interlaced;
+ int noup;
+ int accurate_rnd;
+ struct mp_csp_details colorspace;
+} const vf_priv_dflt = {
+ 0, 0,
+ -1,-1,
+ 0,
+ {SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT},
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+//===========================================================================//
+
+static const unsigned int outfmt_list[]={
+// YUV:
+ IMGFMT_444P,
+ IMGFMT_444P16_LE,
+ IMGFMT_444P16_BE,
+ IMGFMT_444P10_LE,
+ IMGFMT_444P10_BE,
+ IMGFMT_444P9_LE,
+ IMGFMT_444P9_BE,
+ IMGFMT_422P,
+ IMGFMT_422P16_LE,
+ IMGFMT_422P16_BE,
+ IMGFMT_422P10_LE,
+ IMGFMT_422P10_BE,
+ IMGFMT_422P9_LE,
+ IMGFMT_422P9_BE,
+ IMGFMT_YV12,
+ IMGFMT_I420,
+ IMGFMT_420P16_LE,
+ IMGFMT_420P16_BE,
+ IMGFMT_420P10_LE,
+ IMGFMT_420P10_BE,
+ IMGFMT_420P9_LE,
+ IMGFMT_420P9_BE,
+ IMGFMT_420A,
+ IMGFMT_IYUV,
+ IMGFMT_YVU9,
+ IMGFMT_IF09,
+ IMGFMT_411P,
+ IMGFMT_NV12,
+ IMGFMT_NV21,
+ IMGFMT_YUY2,
+ IMGFMT_UYVY,
+ IMGFMT_440P,
+// RGB and grayscale (Y8 and Y800):
+ IMGFMT_BGR32,
+ IMGFMT_RGB32,
+ IMGFMT_BGR24,
+ IMGFMT_RGB24,
+ IMGFMT_GBRP,
+ IMGFMT_RGB48LE,
+ IMGFMT_RGB48BE,
+ IMGFMT_BGR16,
+ IMGFMT_RGB16,
+ IMGFMT_BGR15,
+ IMGFMT_RGB15,
+ IMGFMT_BGR12,
+ IMGFMT_RGB12,
+ IMGFMT_Y800,
+ IMGFMT_Y8,
+ IMGFMT_BGR8,
+ IMGFMT_RGB8,
+ IMGFMT_BGR4,
+ IMGFMT_RGB4,
+ IMGFMT_BG4B,
+ IMGFMT_RG4B,
+ IMGFMT_BGR1,
+ IMGFMT_RGB1,
+ 0
+};
+
+/**
+ * A list of preferred conversions, in order of preference.
+ * This should be used for conversions that e.g. involve no scaling
+ * or to stop vf_scale from choosing a conversion that has no
+ * fast assembler implementation.
+ */
+static int preferred_conversions[][2] = {
+ {IMGFMT_YUY2, IMGFMT_UYVY},
+ {IMGFMT_YUY2, IMGFMT_422P},
+ {IMGFMT_UYVY, IMGFMT_YUY2},
+ {IMGFMT_UYVY, IMGFMT_422P},
+ {IMGFMT_422P, IMGFMT_YUY2},
+ {IMGFMT_422P, IMGFMT_UYVY},
+ {IMGFMT_GBRP, IMGFMT_BGR24},
+ {IMGFMT_GBRP, IMGFMT_RGB24},
+ {IMGFMT_GBRP, IMGFMT_BGR32},
+ {IMGFMT_GBRP, IMGFMT_RGB32},
+ {0, 0}
+};
+
+static unsigned int find_best_out(vf_instance_t *vf, int in_format){
+ unsigned int best=0;
+ int i = -1;
+ int j = -1;
+ int format = 0;
+
+ // find the best outfmt:
+ while (1) {
+ int ret;
+ if (j < 0) {
+ format = in_format;
+ j = 0;
+ } else if (i < 0) {
+ while (preferred_conversions[j][0] &&
+ preferred_conversions[j][0] != in_format)
+ j++;
+ format = preferred_conversions[j++][1];
+ // switch to standard list
+ if (!format)
+ i = 0;
+ }
+ if (i >= 0)
+ format = outfmt_list[i++];
+ if (!format)
+ break;
+ ret = vf_next_query_format(vf, format);
+
+ mp_msg(MSGT_VFILTER,MSGL_DBG2,"scale: query(%s) -> %d\n",vo_format_name(format),ret&3);
+ if(ret&VFCAP_CSP_SUPPORTED_BY_HW){
+ best=format; // no conversion -> bingo!
+ break;
+ }
+ if(ret&VFCAP_CSP_SUPPORTED && !best)
+ best=format; // best with conversion
+ }
+ return best;
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+ struct MPOpts *opts = vf->opts;
+ unsigned int best=find_best_out(vf, outfmt);
+ int vo_flags;
+ int int_sws_flags=0;
+ int round_w=0, round_h=0;
+ int i;
+ SwsFilter *srcFilter, *dstFilter;
+ enum PixelFormat dfmt, sfmt;
+
+ vf->priv->colorspace = (struct mp_csp_details) {0};
+
+ if(!best){
+ mp_msg(MSGT_VFILTER,MSGL_WARN,"SwScale: no supported outfmt found :(\n");
+ return 0;
+ }
+ sfmt = imgfmt2pixfmt(outfmt);
+ if (outfmt == IMGFMT_RGB8 || outfmt == IMGFMT_BGR8) sfmt = PIX_FMT_PAL8;
+ dfmt = imgfmt2pixfmt(best);
+
+ vo_flags=vf->next->query_format(vf->next,best);
+
+ vf->priv->w = vf->priv->cfg_w;
+ vf->priv->h = vf->priv->cfg_h;
+
+ // scaling to dwidth*d_height, if all these TRUE:
+ // - option -zoom
+ // - no other sw/hw up/down scaling avail.
+ // - we're after postproc
+ // - user didn't set w:h
+ if(!(vo_flags&VFCAP_POSTPROC) && (flags&VOFLAG_SWSCALE) &&
+ vf->priv->w<0 && vf->priv->h<0){ // -zoom
+ int x=(vo_flags&VFCAP_SWSCALE) ? 0 : 1;
+ if(d_width<width || d_height<height){
+ // downscale!
+ if(vo_flags&VFCAP_HWSCALE_DOWN) x=0;
+ } else {
+ // upscale:
+ if(vo_flags&VFCAP_HWSCALE_UP) x=0;
+ }
+ if(x){
+ // user wants sw scaling! (-zoom)
+ vf->priv->w=d_width;
+ vf->priv->h=d_height;
+ }
+ }
+
+ if (vf->priv->w <= -8) {
+ vf->priv->w += 8;
+ round_w = 1;
+ }
+ if (vf->priv->h <= -8) {
+ vf->priv->h += 8;
+ round_h = 1;
+ }
+
+ if (vf->priv->w < -3 || vf->priv->h < -3 ||
+ (vf->priv->w < -1 && vf->priv->h < -1)) {
+ // TODO: establish a direct connection to the user's brain
+ // and find out what the heck he thinks MPlayer should do
+ // with this nonsense.
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "SwScale: EUSERBROKEN Check your parameters, they make no sense!\n");
+ return 0;
+ }
+
+ if (vf->priv->w == -1)
+ vf->priv->w = width;
+ if (vf->priv->w == 0)
+ vf->priv->w = d_width;
+
+ if (vf->priv->h == -1)
+ vf->priv->h = height;
+ if (vf->priv->h == 0)
+ vf->priv->h = d_height;
+
+ if (vf->priv->w == -3)
+ vf->priv->w = vf->priv->h * width / height;
+ if (vf->priv->w == -2)
+ vf->priv->w = vf->priv->h * d_width / d_height;
+
+ if (vf->priv->h == -3)
+ vf->priv->h = vf->priv->w * height / width;
+ if (vf->priv->h == -2)
+ vf->priv->h = vf->priv->w * d_height / d_width;
+
+ if (round_w)
+ vf->priv->w = ((vf->priv->w + 8) / 16) * 16;
+ if (round_h)
+ vf->priv->h = ((vf->priv->h + 8) / 16) * 16;
+
+ // check for upscaling, now that all parameters had been applied
+ if(vf->priv->noup){
+ if((vf->priv->w > width) + (vf->priv->h > height) >= vf->priv->noup){
+ vf->priv->w= width;
+ vf->priv->h= height;
+ }
+ }
+
+ // calculate the missing parameters:
+ switch(best) {
+ case IMGFMT_YV12: /* YV12 needs w & h rounded to 2 */
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_NV12:
+ case IMGFMT_NV21:
+ vf->priv->h = (vf->priv->h + 1) & ~1;
+ case IMGFMT_YUY2: /* YUY2 needs w rounded to 2 */
+ case IMGFMT_UYVY:
+ vf->priv->w = (vf->priv->w + 1) & ~1;
+ }
+
+ mp_msg(MSGT_VFILTER,MSGL_DBG2,"SwScale: scaling %dx%d %s to %dx%d %s \n",
+ width,height,vo_format_name(outfmt),
+ vf->priv->w,vf->priv->h,vo_format_name(best));
+
+ // free old ctx:
+ if(vf->priv->ctx) sws_freeContext(vf->priv->ctx);
+ if(vf->priv->ctx2)sws_freeContext(vf->priv->ctx2);
+
+ // new swscaler:
+ sws_getFlagsAndFilterFromCmdLine(&int_sws_flags, &srcFilter, &dstFilter);
+ int_sws_flags|= vf->priv->v_chr_drop << SWS_SRC_V_CHR_DROP_SHIFT;
+ int_sws_flags|= vf->priv->accurate_rnd * SWS_ACCURATE_RND;
+ vf->priv->ctx=sws_getContext(width, height >> vf->priv->interlaced,
+ sfmt,
+ vf->priv->w, vf->priv->h >> vf->priv->interlaced,
+ dfmt,
+ int_sws_flags, srcFilter, dstFilter, vf->priv->param);
+ if(vf->priv->interlaced){
+ vf->priv->ctx2=sws_getContext(width, height >> 1,
+ sfmt,
+ vf->priv->w, vf->priv->h >> 1,
+ dfmt,
+ int_sws_flags, srcFilter, dstFilter, vf->priv->param);
+ }
+ if(!vf->priv->ctx){
+ // error...
+ mp_msg(MSGT_VFILTER,MSGL_WARN,"Couldn't init SwScaler for this setup\n");
+ return 0;
+ }
+ vf->priv->fmt=best;
+
+ free(vf->priv->palette);
+ vf->priv->palette=NULL;
+ switch(best){
+ case IMGFMT_RGB8: {
+ /* set 332 palette for 8 bpp */
+ vf->priv->palette=malloc(4*256);
+ for(i=0; i<256; i++){
+ vf->priv->palette[4*i+0]=4*(i>>6)*21;
+ vf->priv->palette[4*i+1]=4*((i>>3)&7)*9;
+ vf->priv->palette[4*i+2]=4*((i&7)&7)*9;
+ vf->priv->palette[4*i+3]=0;
+ }
+ break; }
+ case IMGFMT_BGR8: {
+ /* set 332 palette for 8 bpp */
+ vf->priv->palette=malloc(4*256);
+ for(i=0; i<256; i++){
+ vf->priv->palette[4*i+0]=4*(i&3)*21;
+ vf->priv->palette[4*i+1]=4*((i>>2)&7)*9;
+ vf->priv->palette[4*i+2]=4*((i>>5)&7)*9;
+ vf->priv->palette[4*i+3]=0;
+ }
+ break; }
+ case IMGFMT_BGR4:
+ case IMGFMT_BG4B: {
+ vf->priv->palette=malloc(4*16);
+ for(i=0; i<16; i++){
+ vf->priv->palette[4*i+0]=4*(i&1)*63;
+ vf->priv->palette[4*i+1]=4*((i>>1)&3)*21;
+ vf->priv->palette[4*i+2]=4*((i>>3)&1)*63;
+ vf->priv->palette[4*i+3]=0;
+ }
+ break; }
+ case IMGFMT_RGB4:
+ case IMGFMT_RG4B: {
+ vf->priv->palette=malloc(4*16);
+ for(i=0; i<16; i++){
+ vf->priv->palette[4*i+0]=4*(i>>3)*63;
+ vf->priv->palette[4*i+1]=4*((i>>1)&3)*21;
+ vf->priv->palette[4*i+2]=4*((i&1)&1)*63;
+ vf->priv->palette[4*i+3]=0;
+ }
+ break; }
+ }
+
+ if (!opts->screen_size_x && !opts->screen_size_y
+ && !(opts->screen_size_xy >= 0.001)) {
+ // Compute new d_width and d_height, preserving aspect
+ // while ensuring that both are >= output size in pixels.
+ if (vf->priv->h * d_width > vf->priv->w * d_height) {
+ d_width = vf->priv->h * d_width / d_height;
+ d_height = vf->priv->h;
+ } else {
+ d_height = vf->priv->w * d_height / d_width;
+ d_width = vf->priv->w;
+ }
+ //d_width=d_width*vf->priv->w/width;
+ //d_height=d_height*vf->priv->h/height;
+ }
+ return vf_next_config(vf,vf->priv->w,vf->priv->h,d_width,d_height,flags,best);
+}
+
+static void start_slice(struct vf_instance *vf, mp_image_t *mpi){
+// printf("start_slice called! flag=%d\n",mpi->flags&MP_IMGFLAG_DRAW_CALLBACK);
+ if(!(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)) return; // shouldn't happen
+ // they want slices!!! allocate the buffer.
+ mpi->priv=vf->dmpi=vf_get_image(vf->next,vf->priv->fmt,
+// mpi->type, mpi->flags & (~MP_IMGFLAG_DRAW_CALLBACK),
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
+ vf->priv->w, vf->priv->h);
+}
+
+static void scale(struct SwsContext *sws1, struct SwsContext *sws2, uint8_t *src[MP_MAX_PLANES], int src_stride[MP_MAX_PLANES],
+ int y, int h, uint8_t *dst[MP_MAX_PLANES], int dst_stride[MP_MAX_PLANES], int interlaced){
+ const uint8_t *src2[MP_MAX_PLANES]={src[0], src[1], src[2], src[3]};
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t pal2[256];
+ if (src[1] && !src[2]){
+ int i;
+ for(i=0; i<256; i++)
+ pal2[i]= bswap_32(((uint32_t*)src[1])[i]);
+ src2[1]= pal2;
+ }
+#endif
+
+ if(interlaced){
+ int i;
+ uint8_t *dst2[MP_MAX_PLANES]={dst[0], dst[1], dst[2], dst[3]};
+ int src_stride2[MP_MAX_PLANES]={2*src_stride[0], 2*src_stride[1], 2*src_stride[2], 2*src_stride[3]};
+ int dst_stride2[MP_MAX_PLANES]={2*dst_stride[0], 2*dst_stride[1], 2*dst_stride[2], 2*dst_stride[3]};
+
+ sws_scale(sws1, src2, src_stride2, y>>1, h>>1, dst2, dst_stride2);
+ for(i=0; i<MP_MAX_PLANES; i++){
+ src2[i] += src_stride[i];
+ dst2[i] += dst_stride[i];
+ }
+ sws_scale(sws2, src2, src_stride2, y>>1, h>>1, dst2, dst_stride2);
+ }else{
+ sws_scale(sws1, src2, src_stride, y, h, dst, dst_stride);
+ }
+}
+
+static void draw_slice(struct vf_instance *vf,
+ unsigned char** src, int* stride, int w,int h, int x, int y){
+ mp_image_t *dmpi=vf->dmpi;
+ if(!dmpi){
+ mp_msg(MSGT_VFILTER,MSGL_FATAL,"vf_scale: draw_slice() called with dmpi=NULL (no get_image?)\n");
+ return;
+ }
+// printf("vf_scale::draw_slice() y=%d h=%d\n",y,h);
+ scale(vf->priv->ctx, vf->priv->ctx2, src, stride, y, h, dmpi->planes, dmpi->stride, vf->priv->interlaced);
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi=mpi->priv;
+
+// printf("vf_scale::put_image(): processing whole frame! dmpi=%p flag=%d\n",
+// dmpi, (mpi->flags&MP_IMGFLAG_DRAW_CALLBACK));
+
+ if(!(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK && dmpi)){
+
+ // hope we'll get DR buffer:
+ dmpi=vf_get_image(vf->next,vf->priv->fmt,
+ MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
+ vf->priv->w, vf->priv->h);
+
+ scale(vf->priv->ctx, vf->priv->ctx, mpi->planes,mpi->stride,0,mpi->h,dmpi->planes,dmpi->stride, vf->priv->interlaced);
+ }
+
+ if(vf->priv->w==mpi->w && vf->priv->h==mpi->h){
+ // just conversion, no scaling -> keep postprocessing data
+ // this way we can apply pp filter to non-yv12 source using scaler
+ vf_clone_mpi_attributes(dmpi, mpi);
+ }
+
+ if(vf->priv->palette) dmpi->planes[1]=vf->priv->palette; // export palette!
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+static int control(struct vf_instance *vf, int request, void* data){
+ int *table;
+ int *inv_table;
+ int r;
+ int brightness, contrast, saturation, srcRange, dstRange;
+ vf_equalizer_t *eq;
+
+ if(vf->priv->ctx)
+ switch(request){
+ case VFCTRL_GET_EQUALIZER:
+ r= sws_getColorspaceDetails(vf->priv->ctx, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation);
+ if(r<0) break;
+
+ eq = data;
+ if (!strcmp(eq->item,"brightness")) {
+ eq->value = ((brightness*100) + (1<<15))>>16;
+ }
+ else if (!strcmp(eq->item,"contrast")) {
+ eq->value = (((contrast *100) + (1<<15))>>16) - 100;
+ }
+ else if (!strcmp(eq->item,"saturation")) {
+ eq->value = (((saturation*100) + (1<<15))>>16) - 100;
+ }
+ else
+ break;
+ return CONTROL_TRUE;
+ case VFCTRL_SET_EQUALIZER:
+ r= sws_getColorspaceDetails(vf->priv->ctx, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation);
+ if(r<0) break;
+//printf("set %f %f %f\n", brightness/(float)(1<<16), contrast/(float)(1<<16), saturation/(float)(1<<16));
+ eq = data;
+
+ if (!strcmp(eq->item,"brightness")) {
+ brightness = (( eq->value <<16) + 50)/100;
+ }
+ else if (!strcmp(eq->item,"contrast")) {
+ contrast = (((eq->value+100)<<16) + 50)/100;
+ }
+ else if (!strcmp(eq->item,"saturation")) {
+ saturation = (((eq->value+100)<<16) + 50)/100;
+ }
+ else
+ break;
+
+ r= sws_setColorspaceDetails(vf->priv->ctx, inv_table, srcRange, table, dstRange, brightness, contrast, saturation);
+ if(r<0) break;
+ if(vf->priv->ctx2){
+ r= sws_setColorspaceDetails(vf->priv->ctx2, inv_table, srcRange, table, dstRange, brightness, contrast, saturation);
+ if(r<0) break;
+ }
+
+ return CONTROL_TRUE;
+ case VFCTRL_SET_YUV_COLORSPACE: {
+ struct mp_csp_details colorspace = *(struct mp_csp_details *)data;
+ if (mp_sws_set_colorspace(vf->priv->ctx, &colorspace) >= 0) {
+ if (vf->priv->ctx2)
+ mp_sws_set_colorspace(vf->priv->ctx2, &colorspace);
+ vf->priv->colorspace = colorspace;
+ return 1;
+ }
+ break;
+ }
+ case VFCTRL_GET_YUV_COLORSPACE: {
+ /* This scale filter should never react to colorspace commands if it
+ * doesn't do YUV->RGB conversion. But because finding out whether this
+ * is really YUV->RGB (and not YUV->YUV or anything else) is hard,
+ * react only if the colorspace has been set explicitly before. The
+ * trick is that mp_sws_set_colorspace does not succeed for YUV->YUV
+ * and RGB->YUV conversions, which makes this code correct in "most"
+ * cases. (This would be trivial to do correctly if libswscale exposed
+ * functionality like isYUV()).
+ */
+ if (vf->priv->colorspace.format) {
+ *(struct mp_csp_details *)data = vf->priv->colorspace;
+ return CONTROL_TRUE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return vf_next_control(vf,request,data);
+}
+
+static const int mp_csp_to_swscale[MP_CSP_COUNT] = {
+ [MP_CSP_BT_601] = SWS_CS_ITU601,
+ [MP_CSP_BT_709] = SWS_CS_ITU709,
+ [MP_CSP_SMPTE_240M] = SWS_CS_SMPTE240M,
+};
+
+// Adjust the colorspace used for YUV->RGB conversion. On other conversions,
+// do nothing or return an error.
+// The csp argument is set to the supported values.
+// Return 0 on success and -1 on error.
+int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp)
+{
+ int *table, *inv_table;
+ int brightness, contrast, saturation, srcRange, dstRange;
+
+ csp->levels_out = MP_CSP_LEVELS_PC;
+
+ // NOTE: returns an error if the destination format is YUV
+ if (sws_getColorspaceDetails(sws, &inv_table, &srcRange, &table, &dstRange,
+ &brightness, &contrast, &saturation) == -1)
+ goto error_out;
+
+ int sws_csp = mp_csp_to_swscale[csp->format];
+ if (sws_csp == 0) {
+ // colorspace not supported, go with a reasonable default
+ csp->format = SWS_CS_ITU601;
+ sws_csp = MP_CSP_BT_601;
+ }
+
+ /* The swscale API for these is hardly documented.
+ * Apparently table/range only apply to YUV. Thus dstRange has no effect
+ * for YUV->RGB conversions, and conversions to limited-range RGB are
+ * not supported.
+ */
+ srcRange = csp->levels_in == MP_CSP_LEVELS_PC;
+ const int *new_inv_table = sws_getCoefficients(sws_csp);
+
+ if (sws_setColorspaceDetails(sws, new_inv_table, srcRange, table, dstRange,
+ brightness, contrast, saturation) == -1)
+ goto error_out;
+
+ return 0;
+
+error_out:
+ *csp = (struct mp_csp_details){0};
+ return -1;
+}
+
+//===========================================================================//
+
+// supported Input formats: YV12, I420, IYUV, YUY2, UYVY, BGR32, BGR24, BGR16, BGR15, RGB32, RGB24, Y8, Y800
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ if (!IMGFMT_IS_HWACCEL(fmt) && imgfmt2pixfmt(fmt) != PIX_FMT_NONE) {
+ unsigned int best=find_best_out(vf, fmt);
+ int flags;
+ if(!best) return 0; // no matching out-fmt
+ flags=vf_next_query_format(vf,best);
+ if(!(flags&(VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW))) return 0; // huh?
+ if(fmt!=best) flags&=~VFCAP_CSP_SUPPORTED_BY_HW;
+ // do not allow scaling, if we are before the PP fliter!
+ if(!(flags&VFCAP_POSTPROC)) flags|=VFCAP_SWSCALE;
+ return flags;
+ }
+ return 0; // nomatching in-fmt
+}
+
+static void uninit(struct vf_instance *vf){
+ if(vf->priv->ctx) sws_freeContext(vf->priv->ctx);
+ if(vf->priv->ctx2) sws_freeContext(vf->priv->ctx2);
+ free(vf->priv->palette);
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->config=config;
+ vf->start_slice=start_slice;
+ vf->draw_slice=draw_slice;
+ vf->put_image=put_image;
+ vf->query_format=query_format;
+ vf->control= control;
+ vf->uninit=uninit;
+ mp_msg(MSGT_VFILTER,MSGL_V,"SwScale params: %d x %d (-1=no scaling)\n",
+ vf->priv->cfg_w,
+ vf->priv->cfg_h);
+
+ return 1;
+}
+
+/// An example of presets usage
+static const struct size_preset {
+ char* name;
+ int w, h;
+} vf_size_presets_defs[] = {
+ // TODO add more 'standard' resolutions
+ { "qntsc", 352, 240 },
+ { "qpal", 352, 288 },
+ { "ntsc", 720, 480 },
+ { "pal", 720, 576 },
+ { "sntsc", 640, 480 },
+ { "spal", 768, 576 },
+ { NULL, 0, 0}
+};
+
+#define ST_OFF(f) M_ST_OFF(struct size_preset,f)
+static const m_option_t vf_size_preset_fields[] = {
+ {"w", ST_OFF(w), CONF_TYPE_INT, M_OPT_MIN,1 ,0, NULL},
+ {"h", ST_OFF(h), CONF_TYPE_INT, M_OPT_MIN,1 ,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_size_preset = {
+ "scale_size_preset",
+ sizeof(struct size_preset),
+ NULL,
+ vf_size_preset_fields
+};
+
+static const m_struct_t vf_opts;
+static const m_obj_presets_t size_preset = {
+ &vf_size_preset, // Input struct desc
+ &vf_opts, // Output struct desc
+ vf_size_presets_defs, // The list of presets
+ ST_OFF(name) // At wich offset is the name field in the preset struct
+};
+
+/// Now the options
+#undef ST_OFF
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"w", ST_OFF(cfg_w), CONF_TYPE_INT, M_OPT_MIN,-11,0, NULL},
+ {"h", ST_OFF(cfg_h), CONF_TYPE_INT, M_OPT_MIN,-11,0, NULL},
+ {"interlaced", ST_OFF(interlaced), CONF_TYPE_INT, M_OPT_RANGE, 0, 1, NULL},
+ {"chr-drop", ST_OFF(v_chr_drop), CONF_TYPE_INT, M_OPT_RANGE, 0, 3, NULL},
+ {"param" , ST_OFF(param[0]), CONF_TYPE_DOUBLE, M_OPT_RANGE, 0.0, 100.0, NULL},
+ {"param2", ST_OFF(param[1]), CONF_TYPE_DOUBLE, M_OPT_RANGE, 0.0, 100.0, NULL},
+ // Note that here the 2 field is NULL (ie 0)
+ // As we want this option to act on the option struct itself
+ {"presize", 0, CONF_TYPE_OBJ_PRESETS, 0, 0, 0, (void *)&size_preset},
+ {"noup", ST_OFF(noup), CONF_TYPE_INT, M_OPT_RANGE, 0, 2, NULL},
+ {"arnd", ST_OFF(accurate_rnd), CONF_TYPE_FLAG, 0, 0, 1, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "scale",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_scale = {
+ "software scaling",
+ "scale",
+ "A'rpi",
+ "",
+ vf_open,
+ &vf_opts
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_screenshot.c b/video/filter/vf_screenshot.c
new file mode 100644
index 0000000000..693d871e5f
--- /dev/null
+++ b/video/filter/vf_screenshot.c
@@ -0,0 +1,219 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "mp_msg.h"
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libmpcodecs/sws_utils.h"
+#include "fmt-conversion.h"
+#include "libvo/fastmemcpy.h"
+
+#include <libswscale/swscale.h>
+
+struct vf_priv_s {
+ mp_image_t *image;
+ void (*image_callback)(void *, mp_image_t *);
+ void *image_callback_ctx;
+ int shot, store_slices;
+};
+
+//===========================================================================//
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ free_mp_image(vf->priv->image);
+ vf->priv->image = new_mp_image(width, height);
+ mp_image_setfmt(vf->priv->image, outfmt);
+ vf->priv->image->w = d_width;
+ vf->priv->image->h = d_height;
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static void start_slice(struct vf_instance *vf, mp_image_t *mpi)
+{
+ mpi->priv=
+ vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ mpi->type, mpi->flags, mpi->width, mpi->height);
+ if (vf->priv->shot) {
+ vf->priv->store_slices = 1;
+ if (!(vf->priv->image->flags & MP_IMGFLAG_ALLOCATED))
+ mp_image_alloc_planes(vf->priv->image);
+ }
+
+}
+
+static void memcpy_pic_slice(unsigned char *dst, unsigned char *src,
+ int bytesPerLine, int y, int h,
+ int dstStride, int srcStride)
+{
+ memcpy_pic(dst + h * dstStride, src + h * srcStride, bytesPerLine,
+ h, dstStride, srcStride);
+}
+
+static void draw_slice(struct vf_instance *vf, unsigned char** src,
+ int* stride, int w,int h, int x, int y)
+{
+ if (vf->priv->store_slices) {
+ mp_image_t *dst = vf->priv->image;
+ int bp = (dst->bpp + 7) / 8;
+
+ if (dst->flags & MP_IMGFLAG_PLANAR) {
+ int bytes_per_line[3] = { w * bp, dst->chroma_width, dst->chroma_width };
+ for (int n = 0; n < 3; n++) {
+ memcpy_pic_slice(dst->planes[n], src[n], bytes_per_line[n],
+ y, h, dst->stride[n], stride[n]);
+ }
+ } else {
+ memcpy_pic_slice(dst->planes[0], src[0], dst->w*bp, y, dst->h,
+ dst->stride[0], stride[0]);
+ }
+ }
+ vf_next_draw_slice(vf,src,stride,w,h,x,y);
+}
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi)
+{
+ // FIXME: should vf.c really call get_image when using slices??
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ return;
+ vf->dmpi= vf_get_image(vf->next, mpi->imgfmt,
+ mpi->type, mpi->flags/* | MP_IMGFLAG_READABLE*/, mpi->width, mpi->height);
+
+ for (int i = 0; i < MP_MAX_PLANES; i++) {
+ mpi->planes[i]=vf->dmpi->planes[i];
+ mpi->stride[i]=vf->dmpi->stride[i];
+ }
+ mpi->width=vf->dmpi->width;
+
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+
+ mpi->priv=(void*)vf->dmpi;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ mp_image_t *dmpi = (mp_image_t *)mpi->priv;
+
+ if(!(mpi->flags&(MP_IMGFLAG_DIRECT|MP_IMGFLAG_DRAW_CALLBACK))){
+ dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_EXPORT, 0,
+ mpi->width, mpi->height);
+ vf_clone_mpi_attributes(dmpi, mpi);
+ for (int i = 0; i < MP_MAX_PLANES; i++) {
+ dmpi->planes[i]=mpi->planes[i];
+ dmpi->stride[i]=mpi->stride[i];
+ }
+ dmpi->width=mpi->width;
+ dmpi->height=mpi->height;
+ }
+
+ if(vf->priv->shot) {
+ vf->priv->shot=0;
+ mp_image_t image;
+ if (!vf->priv->store_slices)
+ image = *dmpi;
+ else
+ image = *vf->priv->image;
+ image.w = vf->priv->image->w;
+ image.h = vf->priv->image->h;
+ vf_clone_mpi_attributes(&image, mpi);
+ vf->priv->image_callback(vf->priv->image_callback_ctx, &image);
+ vf->priv->store_slices = 0;
+ }
+
+ return vf_next_put_image(vf, dmpi, pts);
+}
+
+static int control (vf_instance_t *vf, int request, void *data)
+{
+ if(request==VFCTRL_SCREENSHOT) {
+ struct vf_ctrl_screenshot *cmd = (struct vf_ctrl_screenshot *)data;
+ vf->priv->image_callback = cmd->image_callback;
+ vf->priv->image_callback_ctx = cmd->image_callback_ctx;
+ vf->priv->shot=1;
+ return CONTROL_TRUE;
+ }
+ return vf_next_control (vf, request, data);
+}
+
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ enum PixelFormat av_format = imgfmt2pixfmt(fmt);
+
+ if (av_format != PIX_FMT_NONE && sws_isSupportedInput(av_format))
+ return vf_next_query_format(vf, fmt);
+ return 0;
+}
+
+static void uninit(vf_instance_t *vf)
+{
+ free_mp_image(vf->priv->image);
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config=config;
+ vf->control=control;
+ vf->put_image=put_image;
+ vf->query_format=query_format;
+ vf->start_slice=start_slice;
+ vf->draw_slice=draw_slice;
+ vf->get_image=get_image;
+ vf->uninit=uninit;
+ vf->priv=malloc(sizeof(struct vf_priv_s));
+ vf->priv->shot=0;
+ vf->priv->store_slices=0;
+ vf->priv->image=NULL;
+ return 1;
+}
+
+const vf_info_t vf_info_screenshot = {
+ "screenshot to file",
+ "screenshot",
+ "A'rpi, Jindrich Makovicka",
+ "",
+ vf_open,
+ NULL
+};
+
+// screenshot.c will look for a filter named "screenshot_force", and not use
+// the VO based screenshot code if it's in the filter chain.
+const vf_info_t vf_info_screenshot_force = {
+ "screenshot to file (override VO based screenshot code)",
+ "screenshot_force",
+ "A'rpi, Jindrich Makovicka",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_softpulldown.c b/video/filter/vf_softpulldown.c
new file mode 100644
index 0000000000..d07f9d6e26
--- /dev/null
+++ b/video/filter/vf_softpulldown.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/fastmemcpy.h"
+
+struct vf_priv_s {
+ int state;
+ long long in;
+ long long out;
+ struct vf_detc_pts_buf ptsbuf;
+ int last_frame_duration;
+ double buffered_pts;
+ mp_image_t *buffered_mpi;
+ int buffered_last_frame_duration;
+};
+
+static int continue_buffered_image(struct vf_instance *vf)
+{
+ double pts = vf->priv->buffered_pts;
+ mp_image_t *mpi = vf->priv->buffered_mpi;
+ vf->priv->out++;
+ vf->priv->state=0;
+ return vf_next_put_image(vf, mpi, vf_softpulldown_adjust_pts(&vf->priv->ptsbuf, pts, 0, 0, vf->priv->buffered_last_frame_duration));
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ mp_image_t *dmpi;
+ int ret = 0;
+ int flags = mpi->fields;
+ int state = vf->priv->state;
+
+ dmpi = vf_get_image(vf->next, mpi->imgfmt,
+ MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE |
+ MP_IMGFLAG_PRESERVE, mpi->width, mpi->height);
+
+ vf->priv->in++;
+
+ if ((state == 0 &&
+ !(flags & MP_IMGFIELD_TOP_FIRST)) ||
+ (state == 1 &&
+ flags & MP_IMGFIELD_TOP_FIRST)) {
+ mp_msg(MSGT_VFILTER, MSGL_WARN,
+ "softpulldown: Unexpected field flags: state=%d top_field_first=%d repeat_first_field=%d\n",
+ state,
+ (flags & MP_IMGFIELD_TOP_FIRST) != 0,
+ (flags & MP_IMGFIELD_REPEAT_FIRST) != 0);
+ state ^= 1;
+ }
+
+ if (state == 0) {
+ ret = vf_next_put_image(vf, mpi, vf_softpulldown_adjust_pts(&vf->priv->ptsbuf, pts, 0, 0, vf->priv->last_frame_duration));
+ vf->priv->out++;
+ if (flags & MP_IMGFIELD_REPEAT_FIRST) {
+ my_memcpy_pic(dmpi->planes[0],
+ mpi->planes[0], mpi->w, mpi->h/2,
+ dmpi->stride[0]*2, mpi->stride[0]*2);
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ my_memcpy_pic(dmpi->planes[1],
+ mpi->planes[1],
+ mpi->chroma_width,
+ mpi->chroma_height/2,
+ dmpi->stride[1]*2,
+ mpi->stride[1]*2);
+ my_memcpy_pic(dmpi->planes[2],
+ mpi->planes[2],
+ mpi->chroma_width,
+ mpi->chroma_height/2,
+ dmpi->stride[2]*2,
+ mpi->stride[2]*2);
+ }
+ state=1;
+ }
+ } else {
+ my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0],
+ mpi->planes[0]+mpi->stride[0], mpi->w, mpi->h/2,
+ dmpi->stride[0]*2, mpi->stride[0]*2);
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1],
+ mpi->planes[1]+mpi->stride[1],
+ mpi->chroma_width, mpi->chroma_height/2,
+ dmpi->stride[1]*2, mpi->stride[1]*2);
+ my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2],
+ mpi->planes[2]+mpi->stride[2],
+ mpi->chroma_width, mpi->chroma_height/2,
+ dmpi->stride[2]*2, mpi->stride[2]*2);
+ }
+ ret = vf_next_put_image(vf, dmpi, vf_softpulldown_adjust_pts(&vf->priv->ptsbuf, pts, 0, 0, vf->priv->last_frame_duration));
+ vf->priv->out++;
+ if (flags & MP_IMGFIELD_REPEAT_FIRST) {
+ vf->priv->buffered_mpi = mpi;
+ vf->priv->buffered_pts = pts;
+ vf->priv->buffered_last_frame_duration = vf->priv->last_frame_duration;
+ vf_queue_frame(vf, continue_buffered_image);
+ } else {
+ my_memcpy_pic(dmpi->planes[0],
+ mpi->planes[0], mpi->w, mpi->h/2,
+ dmpi->stride[0]*2, mpi->stride[0]*2);
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ my_memcpy_pic(dmpi->planes[1],
+ mpi->planes[1],
+ mpi->chroma_width,
+ mpi->chroma_height/2,
+ dmpi->stride[1]*2,
+ mpi->stride[1]*2);
+ my_memcpy_pic(dmpi->planes[2],
+ mpi->planes[2],
+ mpi->chroma_width,
+ mpi->chroma_height/2,
+ dmpi->stride[2]*2,
+ mpi->stride[2]*2);
+ }
+ }
+ }
+
+ vf->priv->state = state;
+ if (flags & MP_IMGFIELD_REPEAT_FIRST)
+ vf->priv->last_frame_duration = 3;
+ else
+ vf->priv->last_frame_duration = 2;
+
+ return ret;
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ mp_msg(MSGT_VFILTER, MSGL_INFO, "softpulldown: %lld frames in, %lld frames out\n", vf->priv->in, vf->priv->out);
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config = config;
+ vf->put_image = put_image;
+ vf->uninit = uninit;
+ vf->default_reqs = VFCAP_ACCEPT_STRIDE;
+ vf->priv = calloc(1, sizeof(struct vf_priv_s));
+ vf->priv->state = 0;
+ return 1;
+}
+
+const vf_info_t vf_info_softpulldown = {
+ "mpeg2 soft 3:2 pulldown",
+ "softpulldown",
+ "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
+ "",
+ vf_open,
+ NULL
+};
diff --git a/video/filter/vf_stereo3d.c b/video/filter/vf_stereo3d.c
new file mode 100644
index 0000000000..60208cb3c6
--- /dev/null
+++ b/video/filter/vf_stereo3d.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2010 Gordon Schmidt <gordon.schmidt <at> s2000.tu-chemnitz.de>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+//==includes==//
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "m_struct.h"
+#include "m_option.h"
+
+#include "libavutil/common.h"
+#include "libvo/fastmemcpy.h"
+
+//==types==//
+typedef enum stereo_code {
+ ANAGLYPH_RC_GRAY, //anaglyph red/cyan gray
+ ANAGLYPH_RC_HALF, //anaglyph red/cyan half colored
+ ANAGLYPH_RC_COLOR, //anaglyph red/cyan colored
+ ANAGLYPH_RC_DUBOIS, //anaglyph red/cyan dubois
+ ANAGLYPH_GM_GRAY, //anaglyph green/magenta gray
+ ANAGLYPH_GM_HALF, //anaglyph green/magenta half colored
+ ANAGLYPH_GM_COLOR, //anaglyph green/magenta colored
+ ANAGLYPH_YB_GRAY, //anaglyph yellow/blue gray
+ ANAGLYPH_YB_HALF, //anaglyph yellow/blue half colored
+ ANAGLYPH_YB_COLOR, //anaglyph yellow/blue colored
+ ANAGLYPH_YB_DUBOIS, //anaglyph yellow/blue dubois
+ MONO_L, //mono output for debugging (left eye only)
+ MONO_R, //mono output for debugging (right eye only)
+ SIDE_BY_SIDE_LR, //side by side parallel (left eye left, right eye right)
+ SIDE_BY_SIDE_RL, //side by side crosseye (right eye left, left eye right)
+ SIDE_BY_SIDE_2_LR, //side by side parallel with half width resolution
+ SIDE_BY_SIDE_2_RL, //side by side crosseye with half width resolution
+ ABOVE_BELOW_LR, //above-below (left eye above, right eye below)
+ ABOVE_BELOW_RL, //above-below (right eye above, left eye below)
+ ABOVE_BELOW_2_LR, //above-below with half height resolution
+ ABOVE_BELOW_2_RL, //above-below with half height resolution
+ INTERLEAVE_ROWS_LR, //row-interleave (left eye has top row)
+ INTERLEAVE_ROWS_RL, //row-interleave (right eye has top row)
+ STEREO_CODE_COUNT //no value set - TODO: needs autodetection
+} stereo_code;
+
+typedef struct component {
+ stereo_code fmt;
+ unsigned int width;
+ unsigned int height;
+ unsigned int off_left;
+ unsigned int off_right;
+ unsigned int row_left;
+ unsigned int row_right;
+} component;
+
+//==global variables==//
+static const int ana_coeff[][3][6] = {
+ [ANAGLYPH_RC_GRAY] =
+ {{19595, 38470, 7471, 0, 0, 0},
+ { 0, 0, 0, 19595, 38470, 7471},
+ { 0, 0, 0, 19595, 38470, 7471}},
+ [ANAGLYPH_RC_HALF] =
+ {{19595, 38470, 7471, 0, 0, 0},
+ { 0, 0, 0, 0, 65536, 0},
+ { 0, 0, 0, 0, 0, 65536}},
+ [ANAGLYPH_RC_COLOR] =
+ {{65536, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 65536, 0},
+ { 0, 0, 0, 0, 0, 65536}},
+ [ANAGLYPH_RC_DUBOIS] =
+ {{29891, 32800, 11559, -2849, -5763, -102},
+ {-2627, -2479, -1033, 24804, 48080, -1209},
+ { -997, -1350, -358, -4729, -7403, 80373}},
+ [ANAGLYPH_GM_GRAY] =
+ {{ 0, 0, 0, 19595, 38470, 7471},
+ {19595, 38470, 7471, 0, 0, 0},
+ { 0, 0, 0, 19595, 38470, 7471}},
+ [ANAGLYPH_GM_HALF] =
+ {{ 0, 0, 0, 65536, 0, 0},
+ {19595, 38470, 7471, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 65536}},
+ [ANAGLYPH_GM_COLOR] =
+ {{ 0, 0, 0, 65536, 0, 0},
+ { 0, 65536, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 65536}},
+ [ANAGLYPH_YB_GRAY] =
+ {{ 0, 0, 0, 19595, 38470, 7471},
+ { 0, 0, 0, 19595, 38470, 7471},
+ {19595, 38470, 7471, 0, 0, 0}},
+ [ANAGLYPH_YB_HALF] =
+ {{ 0, 0, 0, 65536, 0, 0},
+ { 0, 0, 0, 0, 65536, 0},
+ {19595, 38470, 7471, 0, 0, 0}},
+ [ANAGLYPH_YB_COLOR] =
+ {{ 0, 0, 0, 65536, 0, 0},
+ { 0, 0, 0, 0, 65536, 0},
+ { 0, 0, 65536, 0, 0, 0}},
+ [ANAGLYPH_YB_DUBOIS] =
+ {{65535,-12650,18451, -987, -7590, -1049},
+ {-1604, 56032, 4196, 370, 3826, -1049},
+ {-2345,-10676, 1358, 5801, 11416, 56217}},
+};
+
+struct vf_priv_s {
+ component in;
+ component out;
+ int ana_matrix[3][6];
+ unsigned int width;
+ unsigned int height;
+ unsigned int row_step;
+} const vf_priv_default = {
+ {SIDE_BY_SIDE_LR},
+ {ANAGLYPH_RC_DUBOIS}
+};
+
+//==functions==//
+static inline uint8_t ana_convert(int coeff[6], uint8_t left[3], uint8_t right[3])
+{
+ int sum;
+
+ sum = coeff[0] * left[0] + coeff[3] * right[0]; //red in
+ sum += coeff[1] * left[1] + coeff[4] * right[1]; //green in
+ sum += coeff[2] * left[2] + coeff[5] * right[2]; //blue in
+ return av_clip_uint8(sum >> 16);
+}
+
+static int config(struct vf_instance *vf, int width, int height, int d_width,
+ int d_height, unsigned int flags, unsigned int outfmt)
+{
+ struct MPOpts *opts = vf->opts;
+
+ if ((width & 1) || (height & 1)) {
+ mp_msg(MSGT_VFILTER, MSGL_WARN, "[stereo3d] invalid height or width\n");
+ return 0;
+ }
+ //default input values
+ vf->priv->width = width;
+ vf->priv->height = height;
+ vf->priv->row_step = 1;
+ vf->priv->in.width = width;
+ vf->priv->in.height = height;
+ vf->priv->in.off_left = 0;
+ vf->priv->in.off_right = 0;
+ vf->priv->in.row_left = 0;
+ vf->priv->in.row_right = 0;
+
+ //check input format
+ switch (vf->priv->in.fmt) {
+ case SIDE_BY_SIDE_2_LR:
+ d_width *= 2;
+ case SIDE_BY_SIDE_LR:
+ vf->priv->width = width / 2;
+ vf->priv->in.off_right = vf->priv->width * 3;
+ break;
+ case SIDE_BY_SIDE_2_RL:
+ d_width *= 2;
+ case SIDE_BY_SIDE_RL:
+ vf->priv->width = width / 2;
+ vf->priv->in.off_left = vf->priv->width * 3;
+ break;
+ case ABOVE_BELOW_2_LR:
+ d_height *= 2;
+ case ABOVE_BELOW_LR:
+ vf->priv->height = height / 2;
+ vf->priv->in.row_right = vf->priv->height;
+ break;
+ case ABOVE_BELOW_2_RL:
+ d_height *= 2;
+ case ABOVE_BELOW_RL:
+ vf->priv->height = height / 2;
+ vf->priv->in.row_left = vf->priv->height;
+ break;
+ default:
+ mp_msg(MSGT_VFILTER, MSGL_WARN,
+ "[stereo3d] stereo format of input is not supported\n");
+ return 0;
+ break;
+ }
+ //default output values
+ vf->priv->out.width = vf->priv->width;
+ vf->priv->out.height = vf->priv->height;
+ vf->priv->out.off_left = 0;
+ vf->priv->out.off_right = 0;
+ vf->priv->out.row_left = 0;
+ vf->priv->out.row_right = 0;
+
+ //check output format
+ switch (vf->priv->out.fmt) {
+ case ANAGLYPH_RC_GRAY:
+ case ANAGLYPH_RC_HALF:
+ case ANAGLYPH_RC_COLOR:
+ case ANAGLYPH_RC_DUBOIS:
+ case ANAGLYPH_GM_GRAY:
+ case ANAGLYPH_GM_HALF:
+ case ANAGLYPH_GM_COLOR:
+ case ANAGLYPH_YB_GRAY:
+ case ANAGLYPH_YB_HALF:
+ case ANAGLYPH_YB_COLOR:
+ case ANAGLYPH_YB_DUBOIS:
+ memcpy(vf->priv->ana_matrix, ana_coeff[vf->priv->out.fmt],
+ sizeof(vf->priv->ana_matrix));
+ break;
+ case SIDE_BY_SIDE_2_LR:
+ d_width /= 2;
+ case SIDE_BY_SIDE_LR:
+ vf->priv->out.width = vf->priv->width * 2;
+ vf->priv->out.off_right = vf->priv->width * 3;
+ break;
+ case SIDE_BY_SIDE_2_RL:
+ d_width /= 2;
+ case SIDE_BY_SIDE_RL:
+ vf->priv->out.width = vf->priv->width * 2;
+ vf->priv->out.off_left = vf->priv->width * 3;
+ break;
+ case ABOVE_BELOW_2_LR:
+ d_height /= 2;
+ case ABOVE_BELOW_LR:
+ vf->priv->out.height = vf->priv->height * 2;
+ vf->priv->out.row_right = vf->priv->height;
+ break;
+ case ABOVE_BELOW_2_RL:
+ d_height /= 2;
+ case ABOVE_BELOW_RL:
+ vf->priv->out.height = vf->priv->height * 2;
+ vf->priv->out.row_left = vf->priv->height;
+ break;
+ case INTERLEAVE_ROWS_LR:
+ vf->priv->row_step = 2;
+ vf->priv->height = vf->priv->height / 2;
+ vf->priv->out.off_right = vf->priv->width * 3;
+ vf->priv->in.off_right += vf->priv->in.width * 3;
+ break;
+ case INTERLEAVE_ROWS_RL:
+ vf->priv->row_step = 2;
+ vf->priv->height = vf->priv->height / 2;
+ vf->priv->out.off_left = vf->priv->width * 3;
+ vf->priv->in.off_left += vf->priv->in.width * 3;
+ break;
+ case MONO_R:
+ //same as MONO_L only needs switching of input offsets
+ vf->priv->in.off_left = vf->priv->in.off_right;
+ vf->priv->in.row_left = vf->priv->in.row_right;
+ //nobreak;
+ case MONO_L:
+ //use default settings
+ break;
+ default:
+ mp_msg(MSGT_VFILTER, MSGL_WARN,
+ "[stereo3d] stereo format of output is not supported\n");
+ return 0;
+ break;
+ }
+ if (!opts->screen_size_x && !opts->screen_size_y) {
+ d_width = d_width * vf->priv->out.width / width;
+ d_height = d_height * vf->priv->out.height / height;
+ }
+ return vf_next_config(vf, vf->priv->out.width, vf->priv->out.height,
+ d_width, d_height, flags, outfmt);
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ mp_image_t *dmpi;
+ if (vf->priv->in.fmt == vf->priv->out.fmt) { //nothing to do
+ dmpi = mpi;
+ } else {
+ int out_off_left, out_off_right;
+ int in_off_left = vf->priv->in.row_left * mpi->stride[0] +
+ vf->priv->in.off_left;
+ int in_off_right = vf->priv->in.row_right * mpi->stride[0] +
+ vf->priv->in.off_right;
+
+ dmpi = vf_get_image(vf->next, IMGFMT_RGB24, MP_IMGTYPE_TEMP,
+ MP_IMGFLAG_ACCEPT_STRIDE,
+ vf->priv->out.width, vf->priv->out.height);
+ out_off_left = vf->priv->out.row_left * dmpi->stride[0] +
+ vf->priv->out.off_left;
+ out_off_right = vf->priv->out.row_right * dmpi->stride[0] +
+ vf->priv->out.off_right;
+
+ switch (vf->priv->out.fmt) {
+ case SIDE_BY_SIDE_LR:
+ case SIDE_BY_SIDE_RL:
+ case SIDE_BY_SIDE_2_LR:
+ case SIDE_BY_SIDE_2_RL:
+ case ABOVE_BELOW_LR:
+ case ABOVE_BELOW_RL:
+ case ABOVE_BELOW_2_LR:
+ case ABOVE_BELOW_2_RL:
+ case INTERLEAVE_ROWS_LR:
+ case INTERLEAVE_ROWS_RL:
+ memcpy_pic2(dmpi->planes[0] + out_off_left,
+ mpi->planes[0] + in_off_left,
+ 3 * vf->priv->width,
+ vf->priv->height,
+ dmpi->stride[0] * vf->priv->row_step,
+ mpi->stride[0] * vf->priv->row_step,
+ vf->priv->row_step != 1);
+ memcpy_pic2(dmpi->planes[0] + out_off_right,
+ mpi->planes[0] + in_off_right,
+ 3 * vf->priv->width,
+ vf->priv->height,
+ dmpi->stride[0] * vf->priv->row_step,
+ mpi->stride[0] * vf->priv->row_step,
+ vf->priv->row_step != 1);
+ break;
+ case MONO_L:
+ case MONO_R:
+ memcpy_pic(dmpi->planes[0],
+ mpi->planes[0] + in_off_left,
+ 3 * vf->priv->width,
+ vf->priv->height,
+ dmpi->stride[0],
+ mpi->stride[0]);
+ break;
+ case ANAGLYPH_RC_GRAY:
+ case ANAGLYPH_RC_HALF:
+ case ANAGLYPH_RC_COLOR:
+ case ANAGLYPH_RC_DUBOIS:
+ case ANAGLYPH_GM_GRAY:
+ case ANAGLYPH_GM_HALF:
+ case ANAGLYPH_GM_COLOR:
+ case ANAGLYPH_YB_GRAY:
+ case ANAGLYPH_YB_HALF:
+ case ANAGLYPH_YB_COLOR: {
+ int x,y,il,ir,o;
+ unsigned char *source = mpi->planes[0];
+ unsigned char *dest = dmpi->planes[0];
+ unsigned int out_width = vf->priv->out.width;
+ int *ana_matrix[3];
+
+ for(int i = 0; i < 3; i++)
+ ana_matrix[i] = vf->priv->ana_matrix[i];
+
+ for (y = 0; y < vf->priv->out.height; y++) {
+ o = dmpi->stride[0] * y;
+ il = in_off_left + y * mpi->stride[0];
+ ir = in_off_right + y * mpi->stride[0];
+ for (x = 0; x < out_width; x++) {
+ dest[o ] = ana_convert(
+ ana_matrix[0], source + il, source + ir); //red out
+ dest[o + 1] = ana_convert(
+ ana_matrix[1], source + il, source + ir); //green out
+ dest[o + 2] = ana_convert(
+ ana_matrix[2], source + il, source + ir); //blue out
+ il += 3;
+ ir += 3;
+ o += 3;
+ }
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_VFILTER, MSGL_WARN,
+ "[stereo3d] stereo format of output is not supported\n");
+ return 0;
+ break;
+ }
+ }
+ return vf_next_put_image(vf, dmpi, pts);
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ switch (fmt)
+ case IMGFMT_RGB24:
+ return vf_next_query_format(vf, fmt);
+ return 0;
+}
+
+static void uninit(vf_instance_t *vf)
+{
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config = config;
+ vf->uninit = uninit;
+ vf->put_image = put_image;
+ vf->query_format = query_format;
+
+ return 1;
+}
+
+///Presets usage
+static const struct format_preset {
+ char* name;
+ stereo_code scode;
+} vf_format_presets_defs[] = {
+ {"arcg", ANAGLYPH_RC_GRAY},
+ {"anaglyph_red_cyan_gray", ANAGLYPH_RC_GRAY},
+ {"arch", ANAGLYPH_RC_HALF},
+ {"anaglyph_red_cyan_half_color", ANAGLYPH_RC_HALF},
+ {"arcc", ANAGLYPH_RC_COLOR},
+ {"anaglyph_red_cyan_color", ANAGLYPH_RC_COLOR},
+ {"arcd", ANAGLYPH_RC_DUBOIS},
+ {"anaglyph_red_cyan_dubios", ANAGLYPH_RC_DUBOIS},
+ {"agmg", ANAGLYPH_GM_GRAY},
+ {"anaglyph_green_magenta_gray", ANAGLYPH_GM_GRAY},
+ {"agmh", ANAGLYPH_GM_HALF},
+ {"anaglyph_green_magenta_half_color",ANAGLYPH_GM_HALF},
+ {"agmc", ANAGLYPH_GM_COLOR},
+ {"anaglyph_green_magenta_color", ANAGLYPH_GM_COLOR},
+ {"aybg", ANAGLYPH_YB_GRAY},
+ {"anaglyph_yellow_blue_gray", ANAGLYPH_YB_GRAY},
+ {"aybh", ANAGLYPH_YB_HALF},
+ {"anaglyph_yellow_blue_half_color", ANAGLYPH_YB_HALF},
+ {"aybc", ANAGLYPH_YB_COLOR},
+ {"anaglyph_yellow_blue_color", ANAGLYPH_YB_COLOR},
+ {"aybd", ANAGLYPH_YB_DUBOIS},
+ {"anaglyph_yellow_blue_dubois", ANAGLYPH_YB_DUBOIS},
+ {"ml", MONO_L},
+ {"mono_left", MONO_L},
+ {"mr", MONO_R},
+ {"mono_right", MONO_R},
+ {"sbsl", SIDE_BY_SIDE_LR},
+ {"side_by_side_left_first", SIDE_BY_SIDE_LR},
+ {"sbsr", SIDE_BY_SIDE_RL},
+ {"side_by_side_right_first", SIDE_BY_SIDE_RL},
+ {"sbs2l", SIDE_BY_SIDE_2_LR},
+ {"side_by_side_half_width_left_first", SIDE_BY_SIDE_2_LR},
+ {"sbs2r", SIDE_BY_SIDE_2_RL},
+ {"side_by_side_half_width_right_first",SIDE_BY_SIDE_2_RL},
+ {"abl", ABOVE_BELOW_LR},
+ {"above_below_left_first", ABOVE_BELOW_LR},
+ {"abr", ABOVE_BELOW_RL},
+ {"above_below_right_first", ABOVE_BELOW_RL},
+ {"ab2l", ABOVE_BELOW_2_LR},
+ {"above_below_half_height_left_first", ABOVE_BELOW_2_LR},
+ {"ab2r", ABOVE_BELOW_2_RL},
+ {"above_below_half_height_right_first",ABOVE_BELOW_2_RL},
+ {"irl", INTERLEAVE_ROWS_LR},
+ {"interleave_rows_left_first", INTERLEAVE_ROWS_LR},
+ {"irr", INTERLEAVE_ROWS_RL},
+ {"interleave_rows_right_first", INTERLEAVE_ROWS_RL},
+ { NULL, 0}
+};
+
+#define ST_OFF(f) M_ST_OFF(struct format_preset,f)
+static const m_option_t vf_format_preset_fields_in[] = {
+ {"in", ST_OFF(scode), CONF_TYPE_INT, 0,0,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+static const m_option_t vf_format_preset_fields_out[] = {
+ {"out", ST_OFF(scode), CONF_TYPE_INT, 0,0,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_format_preset_in = {
+ "stereo_format_preset_in",
+ sizeof(struct format_preset),
+ NULL,
+ vf_format_preset_fields_in
+};
+static const m_struct_t vf_format_preset_out = {
+ "stereo_format_preset_out",
+ sizeof(struct format_preset),
+ NULL,
+ vf_format_preset_fields_out
+};
+
+static const m_struct_t vf_opts;
+static const m_obj_presets_t format_preset_in = {
+ (struct m_struct_st*)&vf_format_preset_in,
+ (struct m_struct_st*)&vf_opts,
+ (struct format_preset*)vf_format_presets_defs,
+ ST_OFF(name)
+};
+static const m_obj_presets_t format_preset_out = {
+ (struct m_struct_st*)&vf_format_preset_out,
+ (struct m_struct_st*)&vf_opts,
+ (struct format_preset*)vf_format_presets_defs,
+ ST_OFF(name)
+};
+
+/// Now the options
+#undef ST_OFF
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"stereo_in", 0, CONF_TYPE_OBJ_PRESETS, 0, 0, 0,
+ (m_obj_presets_t*)&format_preset_in},
+ {"stereo_out", 0, CONF_TYPE_OBJ_PRESETS, 0, 0, 0,
+ (m_obj_presets_t*)&format_preset_out},
+ {"in", ST_OFF(in.fmt), CONF_TYPE_INT, 0,0,0, NULL},
+ {"out", ST_OFF(out.fmt), CONF_TYPE_INT, 0,0,0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "stereo3d",
+ sizeof(struct vf_priv_s),
+ &vf_priv_default,
+ vf_opts_fields
+};
+
+
+//==info struct==//
+const vf_info_t vf_info_stereo3d = {
+ "stereoscopic 3d view",
+ "stereo3d",
+ "Gordon Schmidt",
+ "view stereoscopic videos",
+ vf_open,
+ &vf_opts
+};
diff --git a/video/filter/vf_sub.c b/video/filter/vf_sub.c
new file mode 100644
index 0000000000..2d5de3a7ba
--- /dev/null
+++ b/video/filter/vf_sub.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <libavutil/common.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "sub/sub.h"
+#include "sub/dec_sub.h"
+
+#include "libvo/fastmemcpy.h"
+#include "libvo/csputils.h"
+
+#include "m_option.h"
+#include "m_struct.h"
+
+static const struct vf_priv_s {
+ int outh, outw;
+
+ unsigned int outfmt;
+ struct mp_csp_details csp;
+
+ struct osd_state *osd;
+ struct mp_osd_res dim;
+} vf_priv_dflt = {
+ .csp = MP_CSP_DETAILS_DEFAULTS,
+};
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+ struct MPOpts *opts = vf->opts;
+ if (outfmt == IMGFMT_IF09)
+ return 0;
+
+ vf->priv->outh = height + opts->ass_top_margin + opts->ass_bottom_margin;
+ vf->priv->outw = width;
+
+ if (!opts->screen_size_x && !opts->screen_size_y) {
+ d_width = d_width * vf->priv->outw / width;
+ d_height = d_height * vf->priv->outh / height;
+ }
+
+ double dar = (double)d_width / d_height;
+ double sar = (double)width / height;
+
+ vf->priv->dim = (struct mp_osd_res) {
+ .w = vf->priv->outw,
+ .h = vf->priv->outh,
+ .mt = opts->ass_top_margin,
+ .mb = opts->ass_bottom_margin,
+ .display_par = sar / dar,
+ .video_par = dar / sar,
+ };
+
+ return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width,
+ d_height, flags, outfmt);
+}
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi)
+{
+ if (mpi->type == MP_IMGTYPE_IPB)
+ return;
+ if (mpi->flags & MP_IMGFLAG_PRESERVE)
+ return;
+ if (mpi->imgfmt != vf->priv->outfmt)
+ return; // colorspace differ
+
+ // width never changes, always try full DR
+ mpi->priv = vf->dmpi = vf_get_image(vf->next, mpi->imgfmt, mpi->type,
+ mpi->flags | MP_IMGFLAG_READABLE,
+ FFMAX(mpi->width, vf->priv->outw),
+ FFMAX(mpi->height, vf->priv->outh));
+
+ if ((vf->dmpi->flags & MP_IMGFLAG_DRAW_CALLBACK) &&
+ !(vf->dmpi->flags & MP_IMGFLAG_DIRECT)) {
+ mp_tmsg(MSGT_ASS, MSGL_INFO, "Full DR not possible, trying SLICES instead!\n");
+ return;
+ }
+
+ int tmargin = vf->opts->ass_top_margin;
+ // set up mpi as a cropped-down image of dmpi:
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ mpi->planes[0] = vf->dmpi->planes[0] + tmargin * vf->dmpi->stride[0];
+ mpi->planes[1] = vf->dmpi->planes[1] + (tmargin >> mpi->chroma_y_shift) * vf->dmpi->stride[1];
+ mpi->planes[2] = vf->dmpi->planes[2] + (tmargin >> mpi->chroma_y_shift) * vf->dmpi->stride[2];
+ mpi->stride[1] = vf->dmpi->stride[1];
+ mpi->stride[2] = vf->dmpi->stride[2];
+ } else {
+ mpi->planes[0] = vf->dmpi->planes[0] + tmargin * vf->dmpi->stride[0];
+ }
+ mpi->stride[0] = vf->dmpi->stride[0];
+ mpi->width = vf->dmpi->width;
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+ mpi->flags &= ~MP_IMGFLAG_DRAW_CALLBACK;
+// vf->dmpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
+}
+
+static void blank(mp_image_t *mpi, int y1, int y2)
+{
+ int color[3] = {16, 128, 128}; // black (YUV)
+ int y;
+ unsigned char *dst;
+ int chroma_rows = (y2 - y1) >> mpi->chroma_y_shift;
+
+ dst = mpi->planes[0] + y1 * mpi->stride[0];
+ for (y = 0; y < y2 - y1; ++y) {
+ memset(dst, color[0], mpi->w);
+ dst += mpi->stride[0];
+ }
+ dst = mpi->planes[1] + (y1 >> mpi->chroma_y_shift) * mpi->stride[1];
+ for (y = 0; y < chroma_rows; ++y) {
+ memset(dst, color[1], mpi->chroma_width);
+ dst += mpi->stride[1];
+ }
+ dst = mpi->planes[2] + (y1 >> mpi->chroma_y_shift) * mpi->stride[2];
+ for (y = 0; y < chroma_rows; ++y) {
+ memset(dst, color[2], mpi->chroma_width);
+ dst += mpi->stride[2];
+ }
+}
+
+static int prepare_image(struct vf_instance *vf, mp_image_t *mpi)
+{
+ struct MPOpts *opts = vf->opts;
+ int tmargin = opts->ass_top_margin;
+ if (mpi->flags & MP_IMGFLAG_DIRECT
+ || mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) {
+ vf->dmpi = mpi->priv;
+ if (!vf->dmpi) {
+ mp_tmsg(MSGT_ASS, MSGL_WARN, "Why do we get NULL??\n");
+ return 0;
+ }
+ mpi->priv = NULL;
+ // we've used DR, so we're ready...
+ if (tmargin)
+ blank(vf->dmpi, 0, tmargin);
+ if (opts->ass_bottom_margin)
+ blank(vf->dmpi, vf->priv->outh - opts->ass_bottom_margin,
+ vf->priv->outh);
+ if (!(mpi->flags & MP_IMGFLAG_PLANAR))
+ vf->dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette
+ return 0;
+ }
+
+ // hope we'll get DR buffer:
+ vf->dmpi = vf_get_image(vf->next, vf->priv->outfmt, MP_IMGTYPE_TEMP,
+ MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_READABLE,
+ vf->priv->outw, vf->priv->outh);
+
+ // copy mpi->dmpi...
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ memcpy_pic(vf->dmpi->planes[0] + tmargin * vf->dmpi->stride[0],
+ mpi->planes[0],
+ mpi->w,
+ mpi->h,
+ vf->dmpi->stride[0],
+ mpi->stride[0]);
+ memcpy_pic(vf->dmpi->planes[1] + (tmargin >> mpi->chroma_y_shift) * vf->dmpi->stride[1],
+ mpi->planes[1],
+ mpi->w >> mpi->chroma_x_shift,
+ mpi->h >> mpi->chroma_y_shift,
+ vf->dmpi->stride[1],
+ mpi->stride[1]);
+ memcpy_pic(vf->dmpi->planes[2] + (tmargin >> mpi->chroma_y_shift) * vf->dmpi->stride[2],
+ mpi->planes[2],
+ mpi->w >> mpi->chroma_x_shift,
+ mpi->h >> mpi->chroma_y_shift,
+ vf->dmpi->stride[2],
+ mpi->stride[2]);
+ } else {
+ memcpy_pic(vf->dmpi->planes[0] + tmargin * vf->dmpi->stride[0],
+ mpi->planes[0],
+ mpi->w * (vf->dmpi->bpp / 8),
+ mpi->h,
+ vf->dmpi->stride[0],
+ mpi->stride[0]);
+ vf->dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette
+ }
+ if (tmargin)
+ blank(vf->dmpi, 0, tmargin);
+ if (opts->ass_bottom_margin)
+ blank(vf->dmpi, vf->priv->outh - opts->ass_bottom_margin,
+ vf->priv->outh);
+ return 0;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ struct vf_priv_s *priv = vf->priv;
+ struct osd_state *osd = priv->osd;
+
+ prepare_image(vf, mpi);
+ mp_image_set_colorspace_details(mpi, &priv->csp);
+
+ if (pts != MP_NOPTS_VALUE)
+ osd_draw_on_image(osd, priv->dim, pts, OSD_DRAW_SUB_FILTER, vf->dmpi);
+
+ return vf_next_put_image(vf, vf->dmpi, pts);
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ switch (fmt) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ return vf_next_query_format(vf, vf->priv->outfmt);
+ }
+ return 0;
+}
+
+static int control(vf_instance_t *vf, int request, void *data)
+{
+ switch (request) {
+ case VFCTRL_SET_OSD_OBJ:
+ vf->priv->osd = data;
+ break;
+ case VFCTRL_INIT_OSD:
+ return CONTROL_TRUE;
+ case VFCTRL_SET_YUV_COLORSPACE: {
+ struct mp_csp_details colorspace = *(struct mp_csp_details *)data;
+ vf->priv->csp = colorspace;
+ break;
+ }
+ }
+ return vf_next_control(vf, request, data);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ free(vf->priv);
+}
+
+static const unsigned int fmt_list[] = {
+ IMGFMT_YV12,
+ IMGFMT_I420,
+ IMGFMT_IYUV,
+ 0
+};
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->priv->outfmt = vf_match_csp(&vf->next, fmt_list, IMGFMT_YV12);
+ if (!vf->priv->outfmt) {
+ uninit(vf);
+ return 0;
+ }
+
+ vf->config = config;
+ vf->query_format = query_format;
+ vf->uninit = uninit;
+ vf->control = control;
+ vf->get_image = get_image;
+ vf->put_image = put_image;
+ vf->default_caps = VFCAP_OSD;
+ return 1;
+}
+
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s, f)
+static const m_option_t vf_opts_fields[] = {
+ {NULL, NULL, 0, 0, 0, 0, NULL}
+};
+
+static const m_struct_t vf_opts = {
+ "sub",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_sub = {
+ "Render subtitles",
+ "sub",
+ "Evgeniy Stepanov",
+ "",
+ vf_open,
+ &vf_opts
+};
diff --git a/video/filter/vf_swapuv.c b/video/filter/vf_swapuv.c
new file mode 100644
index 0000000000..6edb256759
--- /dev/null
+++ b/video/filter/vf_swapuv.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2002 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "mp_msg.h"
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+
+//===========================================================================//
+
+static void get_image(struct vf_instance *vf, mp_image_t *mpi){
+ mp_image_t *dmpi= vf_get_image(vf->next, mpi->imgfmt,
+ mpi->type, mpi->flags, mpi->w, mpi->h);
+
+ mpi->planes[0]=dmpi->planes[0];
+ mpi->planes[1]=dmpi->planes[2];
+ mpi->planes[2]=dmpi->planes[1];
+ mpi->stride[0]=dmpi->stride[0];
+ mpi->stride[1]=dmpi->stride[2];
+ mpi->stride[2]=dmpi->stride[1];
+ mpi->width=dmpi->width;
+
+ mpi->flags|=MP_IMGFLAG_DIRECT;
+ mpi->priv=(void*)dmpi;
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ mp_image_t *dmpi;
+
+ if(mpi->flags&MP_IMGFLAG_DIRECT){
+ dmpi=(mp_image_t*)mpi->priv;
+ } else {
+ dmpi=vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_EXPORT, 0, mpi->w, mpi->h);
+ assert(mpi->flags&MP_IMGFLAG_PLANAR);
+ dmpi->planes[0]=mpi->planes[0];
+ dmpi->planes[1]=mpi->planes[2];
+ dmpi->planes[2]=mpi->planes[1];
+ dmpi->stride[0]=mpi->stride[0];
+ dmpi->stride[1]=mpi->stride[2];
+ dmpi->stride[2]=mpi->stride[1];
+ dmpi->width=mpi->width;
+ }
+
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+ return vf_next_put_image(vf,dmpi, pts);
+}
+
+//===========================================================================//
+
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ switch(fmt)
+ {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_YVU9:
+ case IMGFMT_444P:
+ case IMGFMT_422P:
+ case IMGFMT_411P:
+ return vf_next_query_format(vf, fmt);
+ }
+ return 0;
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+ vf->put_image=put_image;
+ vf->get_image=get_image;
+ vf->query_format=query_format;
+ return 1;
+}
+
+const vf_info_t vf_info_swapuv = {
+ "UV swapper",
+ "swapuv",
+ "Michael Niedermayer",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_unsharp.c b/video/filter/vf_unsharp.c
new file mode 100644
index 0000000000..69368d6bf5
--- /dev/null
+++ b/video/filter/vf_unsharp.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2002 Remi Guyomarch <rguyom@pobox.com>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "cpudetect.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libvo/fastmemcpy.h"
+#include "libavutil/common.h"
+
+//===========================================================================//
+
+#define MIN_MATRIX_SIZE 3
+#define MAX_MATRIX_SIZE 63
+
+typedef struct FilterParam {
+ int msizeX, msizeY;
+ double amount;
+ uint32_t *SC[MAX_MATRIX_SIZE-1];
+} FilterParam;
+
+struct vf_priv_s {
+ FilterParam lumaParam;
+ FilterParam chromaParam;
+ unsigned int outfmt;
+};
+
+
+//===========================================================================//
+
+/* This code is based on :
+
+An Efficient algorithm for Gaussian blur using finite-state machines
+Frederick M. Waltz and John W. V. Miller
+
+SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII
+Originally published Boston, Nov 98
+
+*/
+
+static void unsharp( uint8_t *dst, uint8_t *src, int dstStride, int srcStride, int width, int height, FilterParam *fp ) {
+
+ uint32_t **SC = fp->SC;
+ uint32_t SR[MAX_MATRIX_SIZE-1], Tmp1, Tmp2;
+ uint8_t* src2 = src; // avoid gcc warning
+
+ int32_t res;
+ int x, y, z;
+ int amount = fp->amount * 65536.0;
+ int stepsX = fp->msizeX/2;
+ int stepsY = fp->msizeY/2;
+ int scalebits = (stepsX+stepsY)*2;
+ int32_t halfscale = 1 << ((stepsX+stepsY)*2-1);
+
+ if( !fp->amount ) {
+ if( src == dst )
+ return;
+ if( dstStride == srcStride )
+ memcpy( dst, src, srcStride*height );
+ else
+ for( y=0; y<height; y++, dst+=dstStride, src+=srcStride )
+ memcpy( dst, src, width );
+ return;
+ }
+
+ for( y=0; y<2*stepsY; y++ )
+ memset( SC[y], 0, sizeof(SC[y][0]) * (width+2*stepsX) );
+
+ for( y=-stepsY; y<height+stepsY; y++ ) {
+ if( y < height ) src2 = src;
+ memset( SR, 0, sizeof(SR[0]) * (2*stepsX-1) );
+ for( x=-stepsX; x<width+stepsX; x++ ) {
+ Tmp1 = x<=0 ? src2[0] : x>=width ? src2[width-1] : src2[x];
+ for( z=0; z<stepsX*2; z+=2 ) {
+ Tmp2 = SR[z+0] + Tmp1; SR[z+0] = Tmp1;
+ Tmp1 = SR[z+1] + Tmp2; SR[z+1] = Tmp2;
+ }
+ for( z=0; z<stepsY*2; z+=2 ) {
+ Tmp2 = SC[z+0][x+stepsX] + Tmp1; SC[z+0][x+stepsX] = Tmp1;
+ Tmp1 = SC[z+1][x+stepsX] + Tmp2; SC[z+1][x+stepsX] = Tmp2;
+ }
+ if( x>=stepsX && y>=stepsY ) {
+ uint8_t* srx = src - stepsY*srcStride + x - stepsX;
+ uint8_t* dsx = dst - stepsY*dstStride + x - stepsX;
+
+ res = (int32_t)*srx + ( ( ( (int32_t)*srx - (int32_t)((Tmp1+halfscale) >> scalebits) ) * amount ) >> 16 );
+ *dsx = res>255 ? 255 : res<0 ? 0 : (uint8_t)res;
+ }
+ }
+ if( y >= 0 ) {
+ dst += dstStride;
+ src += srcStride;
+ }
+ }
+}
+
+//===========================================================================//
+
+static int config( struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt ) {
+
+ int z, stepsX, stepsY;
+ FilterParam *fp;
+ char *effect;
+
+ // allocate buffers
+
+ fp = &vf->priv->lumaParam;
+ effect = fp->amount == 0 ? "don't touch" : fp->amount < 0 ? "blur" : "sharpen";
+ mp_msg( MSGT_VFILTER, MSGL_INFO, "unsharp: %dx%d:%0.2f (%s luma) \n", fp->msizeX, fp->msizeY, fp->amount, effect );
+ memset( fp->SC, 0, sizeof( fp->SC ) );
+ stepsX = fp->msizeX/2;
+ stepsY = fp->msizeY/2;
+ for( z=0; z<2*stepsY; z++ )
+ fp->SC[z] = av_malloc(sizeof(*(fp->SC[z])) * (width+2*stepsX));
+
+ fp = &vf->priv->chromaParam;
+ effect = fp->amount == 0 ? "don't touch" : fp->amount < 0 ? "blur" : "sharpen";
+ mp_msg( MSGT_VFILTER, MSGL_INFO, "unsharp: %dx%d:%0.2f (%s chroma)\n", fp->msizeX, fp->msizeY, fp->amount, effect );
+ memset( fp->SC, 0, sizeof( fp->SC ) );
+ stepsX = fp->msizeX/2;
+ stepsY = fp->msizeY/2;
+ for( z=0; z<2*stepsY; z++ )
+ fp->SC[z] = av_malloc(sizeof(*(fp->SC[z])) * (width+2*stepsX));
+
+ return vf_next_config( vf, width, height, d_width, d_height, flags, outfmt );
+}
+
+//===========================================================================//
+
+static void get_image( struct vf_instance *vf, mp_image_t *mpi ) {
+ if( mpi->flags & MP_IMGFLAG_PRESERVE )
+ return; // don't change
+ if( mpi->imgfmt!=vf->priv->outfmt )
+ return; // colorspace differ
+
+ mpi->priv =
+ vf->dmpi = vf_get_image( vf->next, mpi->imgfmt, mpi->type, mpi->flags, mpi->width, mpi->height );
+ mpi->planes[0] = vf->dmpi->planes[0];
+ mpi->stride[0] = vf->dmpi->stride[0];
+ mpi->width = vf->dmpi->width;
+ if( mpi->flags & MP_IMGFLAG_PLANAR ) {
+ mpi->planes[1] = vf->dmpi->planes[1];
+ mpi->planes[2] = vf->dmpi->planes[2];
+ mpi->stride[1] = vf->dmpi->stride[1];
+ mpi->stride[2] = vf->dmpi->stride[2];
+ }
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+}
+
+static int put_image( struct vf_instance *vf, mp_image_t *mpi, double pts) {
+ mp_image_t *dmpi = mpi->priv;
+ mpi->priv = NULL;
+
+ if( !(mpi->flags & MP_IMGFLAG_DIRECT) )
+ // no DR, so get a new image! hope we'll get DR buffer:
+ dmpi = vf->dmpi = vf_get_image( vf->next,vf->priv->outfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height);
+
+ unsharp( dmpi->planes[0], mpi->planes[0], dmpi->stride[0], mpi->stride[0], mpi->w, mpi->h, &vf->priv->lumaParam );
+ unsharp( dmpi->planes[1], mpi->planes[1], dmpi->stride[1], mpi->stride[1], mpi->w/2, mpi->h/2, &vf->priv->chromaParam );
+ unsharp( dmpi->planes[2], mpi->planes[2], dmpi->stride[2], mpi->stride[2], mpi->w/2, mpi->h/2, &vf->priv->chromaParam );
+
+ vf_clone_mpi_attributes(dmpi, mpi);
+
+#if HAVE_MMX
+ if(gCpuCaps.hasMMX)
+ __asm__ volatile ("emms\n\t");
+#endif
+#if HAVE_MMX2
+ if(gCpuCaps.hasMMX2)
+ __asm__ volatile ("sfence\n\t");
+#endif
+
+ return vf_next_put_image( vf, dmpi, pts);
+}
+
+static void uninit( struct vf_instance *vf ) {
+ unsigned int z;
+ FilterParam *fp;
+
+ if( !vf->priv ) return;
+
+ fp = &vf->priv->lumaParam;
+ for( z=0; z<sizeof(fp->SC)/sizeof(fp->SC[0]); z++ ) {
+ av_free( fp->SC[z] );
+ fp->SC[z] = NULL;
+ }
+ fp = &vf->priv->chromaParam;
+ for( z=0; z<sizeof(fp->SC)/sizeof(fp->SC[0]); z++ ) {
+ av_free( fp->SC[z] );
+ fp->SC[z] = NULL;
+ }
+
+ free( vf->priv );
+ vf->priv = NULL;
+}
+
+//===========================================================================//
+
+static int query_format( struct vf_instance *vf, unsigned int fmt ) {
+ switch(fmt) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ return vf_next_query_format( vf, vf->priv->outfmt );
+ }
+ return 0;
+}
+
+//===========================================================================//
+
+static void parse( FilterParam *fp, char* args ) {
+
+ // l7x5:0.8:c3x3:-0.2
+
+ char *z;
+ char *pos = args;
+ char *max = args + strlen(args);
+
+ // parse matrix sizes
+ fp->msizeX = ( pos && pos+1<max ) ? atoi( pos+1 ) : 0;
+ z = strchr( pos+1, 'x' );
+ fp->msizeY = ( z && z+1<max ) ? atoi( pos=z+1 ) : fp->msizeX;
+
+ // min/max & odd
+ fp->msizeX = 1 | av_clip(fp->msizeX, MIN_MATRIX_SIZE, MAX_MATRIX_SIZE);
+ fp->msizeY = 1 | av_clip(fp->msizeY, MIN_MATRIX_SIZE, MAX_MATRIX_SIZE);
+
+ // parse amount
+ pos = strchr( pos+1, ':' );
+ fp->amount = ( pos && pos+1<max ) ? atof( pos+1 ) : 0;
+}
+
+//===========================================================================//
+
+static const unsigned int fmt_list[] = {
+ IMGFMT_YV12,
+ IMGFMT_I420,
+ IMGFMT_IYUV,
+ 0
+};
+
+static int vf_open( vf_instance_t *vf, char *args ) {
+ vf->config = config;
+ vf->put_image = put_image;
+ vf->get_image = get_image;
+ vf->query_format = query_format;
+ vf->uninit = uninit;
+ vf->priv = malloc( sizeof(struct vf_priv_s) );
+ memset( vf->priv, 0, sizeof(struct vf_priv_s) );
+
+ if( args ) {
+ char *args2 = strchr( args, 'l' );
+ if( args2 )
+ parse( &vf->priv->lumaParam, args2 );
+ else {
+ vf->priv->lumaParam.amount =
+ vf->priv->lumaParam.msizeX =
+ vf->priv->lumaParam.msizeY = 0;
+ }
+
+ args2 = strchr( args, 'c' );
+ if( args2 )
+ parse( &vf->priv->chromaParam, args2 );
+ else {
+ vf->priv->chromaParam.amount =
+ vf->priv->chromaParam.msizeX =
+ vf->priv->chromaParam.msizeY = 0;
+ }
+
+ if( !vf->priv->lumaParam.msizeX && !vf->priv->chromaParam.msizeX )
+ return 0; // nothing to do
+ }
+
+ // check csp:
+ vf->priv->outfmt = vf_match_csp( &vf->next, fmt_list, IMGFMT_YV12 );
+ if( !vf->priv->outfmt ) {
+ uninit( vf );
+ return 0; // no csp match :(
+ }
+
+ return 1;
+}
+
+const vf_info_t vf_info_unsharp = {
+ "unsharp mask & gaussian blur",
+ "unsharp",
+ "Remi Guyomarch",
+ "",
+ vf_open,
+ NULL
+};
+
+//===========================================================================//
diff --git a/video/filter/vf_vo.c b/video/filter/vf_vo.c
new file mode 100644
index 0000000000..d11724f881
--- /dev/null
+++ b/video/filter/vf_vo.c
@@ -0,0 +1,202 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "mp_image.h"
+#include "vf.h"
+
+#include "libvo/video_out.h"
+
+#include "sub/sub.h"
+
+struct vf_priv_s {
+ struct vo *vo;
+};
+#define video_out (vf->priv->vo)
+
+static void draw_slice(struct vf_instance *vf, unsigned char **src,
+ int *stride, int w, int h, int x, int y);
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt)
+{
+
+ if ((width <= 0) || (height <= 0) || (d_width <= 0) || (d_height <= 0)) {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "VO: invalid dimensions!\n");
+ return 0;
+ }
+
+ const vo_info_t *info = video_out->driver->info;
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "VO: [%s] %dx%d => %dx%d %s %s%s%s%s\n",
+ info->short_name,
+ width, height,
+ d_width, d_height,
+ vo_format_name(outfmt),
+ (flags & VOFLAG_FULLSCREEN) ? " [fs]" : "",
+ (flags & VOFLAG_MODESWITCHING) ? " [vm]" : "",
+ (flags & VOFLAG_SWSCALE) ? " [zoom]" : "",
+ (flags & VOFLAG_FLIPPING) ? " [flip]" : "");
+ mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Description: %s\n", info->name);
+ mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Author: %s\n", info->author);
+ if (info->comment && strlen(info->comment) > 0)
+ mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Comment: %s\n", info->comment);
+
+ if (vo_config(video_out, width, height, d_width, d_height, flags, outfmt))
+ return 0;
+
+ // save vo's stride capability for the wanted colorspace:
+ vf->default_caps = video_out->default_caps;
+ vf->draw_slice = (vf->default_caps & VOCAP_NOSLICES) ? NULL : draw_slice;
+
+ return 1;
+}
+
+static int control(struct vf_instance *vf, int request, void *data)
+{
+ switch (request) {
+ case VFCTRL_GET_DEINTERLACE:
+ if (!video_out)
+ return CONTROL_FALSE; // vo not configured?
+ return vo_control(video_out, VOCTRL_GET_DEINTERLACE, data) == VO_TRUE;
+ case VFCTRL_SET_DEINTERLACE:
+ if (!video_out)
+ return CONTROL_FALSE; // vo not configured?
+ return vo_control(video_out, VOCTRL_SET_DEINTERLACE, data) == VO_TRUE;
+ case VFCTRL_GET_YUV_COLORSPACE:
+ return vo_control(video_out, VOCTRL_GET_YUV_COLORSPACE, data) == true;
+ case VFCTRL_SET_YUV_COLORSPACE:
+ return vo_control(video_out, VOCTRL_SET_YUV_COLORSPACE, data) == true;
+ case VFCTRL_SET_EQUALIZER: {
+ vf_equalizer_t *eq = data;
+ if (!video_out->config_ok)
+ return CONTROL_FALSE; // vo not configured?
+ struct voctrl_set_equalizer_args param = {
+ eq->item, eq->value
+ };
+ return vo_control(video_out, VOCTRL_SET_EQUALIZER, &param) == VO_TRUE;
+ }
+ case VFCTRL_GET_EQUALIZER: {
+ vf_equalizer_t *eq = data;
+ if (!video_out->config_ok)
+ return CONTROL_FALSE; // vo not configured?
+ struct voctrl_get_equalizer_args param = {
+ eq->item, &eq->value
+ };
+ return vo_control(video_out, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
+ }
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ int flags = vo_control(video_out, VOCTRL_QUERY_FORMAT, &fmt);
+ // draw_slice() accepts stride, draw_frame() doesn't:
+ if (flags)
+ if (fmt == IMGFMT_YV12 || fmt == IMGFMT_I420 || fmt == IMGFMT_IYUV)
+ flags |= VFCAP_ACCEPT_STRIDE;
+ return flags;
+}
+
+static void get_image(struct vf_instance *vf,
+ mp_image_t *mpi)
+{
+ if (!video_out->config_ok)
+ return;
+ // GET_IMAGE is required for hardware-accelerated formats
+ if (IMGFMT_IS_HWACCEL(mpi->imgfmt))
+ vo_control(video_out, VOCTRL_GET_IMAGE, mpi);
+}
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
+{
+ if (!video_out->config_ok)
+ return 0;
+ // first check, maybe the vo/vf plugin implements draw_image using mpi:
+ if (vo_draw_image(video_out, mpi, pts) >= 0)
+ return 1;
+ // nope, fallback to old draw_frame/draw_slice:
+ if (!(mpi->flags & (MP_IMGFLAG_DIRECT | MP_IMGFLAG_DRAW_CALLBACK))) {
+ // blit frame:
+ if (vf->default_caps & VFCAP_ACCEPT_STRIDE)
+ vo_draw_slice(video_out, mpi->planes, mpi->stride, mpi->w, mpi->h,
+ 0, 0);
+ // else: out of luck
+ }
+ return 1;
+}
+
+static void start_slice(struct vf_instance *vf, mp_image_t *mpi)
+{
+ if (!video_out->config_ok)
+ return;
+ vo_control(video_out, VOCTRL_START_SLICE, mpi);
+}
+
+static void draw_slice(struct vf_instance *vf, unsigned char **src,
+ int *stride, int w, int h, int x, int y)
+{
+ if (!video_out->config_ok)
+ return;
+ vo_draw_slice(video_out, src, stride, w, h, x, y);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ if (vf->priv) {
+ /* Allow VO (which may live on to work with another instance of vf_vo)
+ * to get rid of numbered-mpi references that will now be invalid. */
+ vo_seek_reset(video_out);
+ free(vf->priv);
+ }
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->config = config;
+ vf->control = control;
+ vf->query_format = query_format;
+ vf->get_image = get_image;
+ vf->put_image = put_image;
+ vf->draw_slice = draw_slice;
+ vf->start_slice = start_slice;
+ vf->uninit = uninit;
+ vf->priv = calloc(1, sizeof(struct vf_priv_s));
+ vf->priv->vo = (struct vo *)args;
+ if (!video_out)
+ return 0;
+ return 1;
+}
+
+const vf_info_t vf_info_vo = {
+ "libvo wrapper",
+ "vo",
+ "A'rpi",
+ "for internal use",
+ vf_open,
+ NULL
+};
diff --git a/video/filter/vf_yadif.c b/video/filter/vf_yadif.c
new file mode 100644
index 0000000000..bb6595cdcd
--- /dev/null
+++ b/video/filter/vf_yadif.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "config.h"
+#include "cpudetect.h"
+#include "options.h"
+
+#include "mp_msg.h"
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "libvo/fastmemcpy.h"
+#include "libavutil/common.h"
+
+//===========================================================================//
+
+struct vf_priv_s {
+ int mode;
+ int parity;
+ int buffered_i;
+ int buffered_tff;
+ double buffered_pts;
+ double buffered_pts_delta;
+ mp_image_t *buffered_mpi;
+ int stride[3];
+ uint8_t *ref[4][3];
+ int do_deinterlace;
+};
+
+static void (*filter_line)(struct vf_priv_s *p, uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int refs, int parity);
+
+static void store_ref(struct vf_priv_s *p, uint8_t *src[3], int src_stride[3], int width, int height){
+ int i;
+
+ memcpy (p->ref[3], p->ref[0], sizeof(uint8_t *)*3);
+ memmove(p->ref[0], p->ref[1], sizeof(uint8_t *)*3*3);
+
+ for(i=0; i<3; i++){
+ int is_chroma= !!i;
+ int pn_width = width >>is_chroma;
+ int pn_height = height>>is_chroma;
+
+
+ memcpy_pic(p->ref[2][i], src[i], pn_width, pn_height, p->stride[i], src_stride[i]);
+
+ memcpy(p->ref[2][i] + pn_height * p->stride[i],
+ src[i] + (pn_height-1)*src_stride[i], pn_width);
+ memcpy(p->ref[2][i] + (pn_height+1)* p->stride[i],
+ src[i] + (pn_height-1)*src_stride[i], pn_width);
+
+ memcpy(p->ref[2][i] - p->stride[i], src[i], pn_width);
+ memcpy(p->ref[2][i] - 2*p->stride[i], src[i], pn_width);
+ }
+}
+
+#if HAVE_MMX
+
+#define LOAD4(mem,dst) \
+ "movd "mem", "#dst" \n\t"\
+ "punpcklbw %%mm7, "#dst" \n\t"
+
+#define PABS(tmp,dst) \
+ "pxor "#tmp", "#tmp" \n\t"\
+ "psubw "#dst", "#tmp" \n\t"\
+ "pmaxsw "#tmp", "#dst" \n\t"
+
+#define CHECK(pj,mj) \
+ "movq "#pj"(%[cur],%[mrefs]), %%mm2 \n\t" /* cur[x-refs-1+j] */\
+ "movq "#mj"(%[cur],%[prefs]), %%mm3 \n\t" /* cur[x+refs-1-j] */\
+ "movq %%mm2, %%mm4 \n\t"\
+ "movq %%mm2, %%mm5 \n\t"\
+ "pxor %%mm3, %%mm4 \n\t"\
+ "pavgb %%mm3, %%mm5 \n\t"\
+ "pand %[pb1], %%mm4 \n\t"\
+ "psubusb %%mm4, %%mm5 \n\t"\
+ "psrlq $8, %%mm5 \n\t"\
+ "punpcklbw %%mm7, %%mm5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\
+ "movq %%mm2, %%mm4 \n\t"\
+ "psubusb %%mm3, %%mm2 \n\t"\
+ "psubusb %%mm4, %%mm3 \n\t"\
+ "pmaxub %%mm3, %%mm2 \n\t"\
+ "movq %%mm2, %%mm3 \n\t"\
+ "movq %%mm2, %%mm4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\
+ "psrlq $8, %%mm3 \n\t" /* ABS(cur[x-refs +j] - cur[x+refs -j]) */\
+ "psrlq $16, %%mm4 \n\t" /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\
+ "punpcklbw %%mm7, %%mm2 \n\t"\
+ "punpcklbw %%mm7, %%mm3 \n\t"\
+ "punpcklbw %%mm7, %%mm4 \n\t"\
+ "paddw %%mm3, %%mm2 \n\t"\
+ "paddw %%mm4, %%mm2 \n\t" /* score */
+
+#define CHECK1 \
+ "movq %%mm0, %%mm3 \n\t"\
+ "pcmpgtw %%mm2, %%mm3 \n\t" /* if(score < spatial_score) */\
+ "pminsw %%mm2, %%mm0 \n\t" /* spatial_score= score; */\
+ "movq %%mm3, %%mm6 \n\t"\
+ "pand %%mm3, %%mm5 \n\t"\
+ "pandn %%mm1, %%mm3 \n\t"\
+ "por %%mm5, %%mm3 \n\t"\
+ "movq %%mm3, %%mm1 \n\t" /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */
+
+#define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\
+ hurts both quality and speed, but matches the C version. */\
+ "paddw %[pw1], %%mm6 \n\t"\
+ "psllw $14, %%mm6 \n\t"\
+ "paddsw %%mm6, %%mm2 \n\t"\
+ "movq %%mm0, %%mm3 \n\t"\
+ "pcmpgtw %%mm2, %%mm3 \n\t"\
+ "pminsw %%mm2, %%mm0 \n\t"\
+ "pand %%mm3, %%mm5 \n\t"\
+ "pandn %%mm1, %%mm3 \n\t"\
+ "por %%mm5, %%mm3 \n\t"\
+ "movq %%mm3, %%mm1 \n\t"
+
+static void filter_line_mmx2(struct vf_priv_s *p, uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int refs, int parity){
+ static const uint64_t pw_1 = 0x0001000100010001ULL;
+ static const uint64_t pb_1 = 0x0101010101010101ULL;
+ const int mode = p->mode;
+ uint64_t tmp0, tmp1, tmp2, tmp3;
+ int x;
+
+#define FILTER\
+ for(x=0; x<w; x+=4){\
+ __asm__ volatile(\
+ "pxor %%mm7, %%mm7 \n\t"\
+ LOAD4("(%[cur],%[mrefs])", %%mm0) /* c = cur[x-refs] */\
+ LOAD4("(%[cur],%[prefs])", %%mm1) /* e = cur[x+refs] */\
+ LOAD4("(%["prev2"])", %%mm2) /* prev2[x] */\
+ LOAD4("(%["next2"])", %%mm3) /* next2[x] */\
+ "movq %%mm3, %%mm4 \n\t"\
+ "paddw %%mm2, %%mm3 \n\t"\
+ "psraw $1, %%mm3 \n\t" /* d = (prev2[x] + next2[x])>>1 */\
+ "movq %%mm0, %[tmp0] \n\t" /* c */\
+ "movq %%mm3, %[tmp1] \n\t" /* d */\
+ "movq %%mm1, %[tmp2] \n\t" /* e */\
+ "psubw %%mm4, %%mm2 \n\t"\
+ PABS( %%mm4, %%mm2) /* temporal_diff0 */\
+ LOAD4("(%[prev],%[mrefs])", %%mm3) /* prev[x-refs] */\
+ LOAD4("(%[prev],%[prefs])", %%mm4) /* prev[x+refs] */\
+ "psubw %%mm0, %%mm3 \n\t"\
+ "psubw %%mm1, %%mm4 \n\t"\
+ PABS( %%mm5, %%mm3)\
+ PABS( %%mm5, %%mm4)\
+ "paddw %%mm4, %%mm3 \n\t" /* temporal_diff1 */\
+ "psrlw $1, %%mm2 \n\t"\
+ "psrlw $1, %%mm3 \n\t"\
+ "pmaxsw %%mm3, %%mm2 \n\t"\
+ LOAD4("(%[next],%[mrefs])", %%mm3) /* next[x-refs] */\
+ LOAD4("(%[next],%[prefs])", %%mm4) /* next[x+refs] */\
+ "psubw %%mm0, %%mm3 \n\t"\
+ "psubw %%mm1, %%mm4 \n\t"\
+ PABS( %%mm5, %%mm3)\
+ PABS( %%mm5, %%mm4)\
+ "paddw %%mm4, %%mm3 \n\t" /* temporal_diff2 */\
+ "psrlw $1, %%mm3 \n\t"\
+ "pmaxsw %%mm3, %%mm2 \n\t"\
+ "movq %%mm2, %[tmp3] \n\t" /* diff */\
+\
+ "paddw %%mm0, %%mm1 \n\t"\
+ "paddw %%mm0, %%mm0 \n\t"\
+ "psubw %%mm1, %%mm0 \n\t"\
+ "psrlw $1, %%mm1 \n\t" /* spatial_pred */\
+ PABS( %%mm2, %%mm0) /* ABS(c-e) */\
+\
+ "movq -1(%[cur],%[mrefs]), %%mm2 \n\t" /* cur[x-refs-1] */\
+ "movq -1(%[cur],%[prefs]), %%mm3 \n\t" /* cur[x+refs-1] */\
+ "movq %%mm2, %%mm4 \n\t"\
+ "psubusb %%mm3, %%mm2 \n\t"\
+ "psubusb %%mm4, %%mm3 \n\t"\
+ "pmaxub %%mm3, %%mm2 \n\t"\
+ "pshufw $9,%%mm2, %%mm3 \n\t"\
+ "punpcklbw %%mm7, %%mm2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\
+ "punpcklbw %%mm7, %%mm3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\
+ "paddw %%mm2, %%mm0 \n\t"\
+ "paddw %%mm3, %%mm0 \n\t"\
+ "psubw %[pw1], %%mm0 \n\t" /* spatial_score */\
+\
+ CHECK(-2,0)\
+ CHECK1\
+ CHECK(-3,1)\
+ CHECK2\
+ CHECK(0,-2)\
+ CHECK1\
+ CHECK(1,-3)\
+ CHECK2\
+\
+ /* if(p->mode<2) ... */\
+ "movq %[tmp3], %%mm6 \n\t" /* diff */\
+ "cmpl $2, %[mode] \n\t"\
+ "jge 1f \n\t"\
+ LOAD4("(%["prev2"],%[mrefs],2)", %%mm2) /* prev2[x-2*refs] */\
+ LOAD4("(%["next2"],%[mrefs],2)", %%mm4) /* next2[x-2*refs] */\
+ LOAD4("(%["prev2"],%[prefs],2)", %%mm3) /* prev2[x+2*refs] */\
+ LOAD4("(%["next2"],%[prefs],2)", %%mm5) /* next2[x+2*refs] */\
+ "paddw %%mm4, %%mm2 \n\t"\
+ "paddw %%mm5, %%mm3 \n\t"\
+ "psrlw $1, %%mm2 \n\t" /* b */\
+ "psrlw $1, %%mm3 \n\t" /* f */\
+ "movq %[tmp0], %%mm4 \n\t" /* c */\
+ "movq %[tmp1], %%mm5 \n\t" /* d */\
+ "movq %[tmp2], %%mm7 \n\t" /* e */\
+ "psubw %%mm4, %%mm2 \n\t" /* b-c */\
+ "psubw %%mm7, %%mm3 \n\t" /* f-e */\
+ "movq %%mm5, %%mm0 \n\t"\
+ "psubw %%mm4, %%mm5 \n\t" /* d-c */\
+ "psubw %%mm7, %%mm0 \n\t" /* d-e */\
+ "movq %%mm2, %%mm4 \n\t"\
+ "pminsw %%mm3, %%mm2 \n\t"\
+ "pmaxsw %%mm4, %%mm3 \n\t"\
+ "pmaxsw %%mm5, %%mm2 \n\t"\
+ "pminsw %%mm5, %%mm3 \n\t"\
+ "pmaxsw %%mm0, %%mm2 \n\t" /* max */\
+ "pminsw %%mm0, %%mm3 \n\t" /* min */\
+ "pxor %%mm4, %%mm4 \n\t"\
+ "pmaxsw %%mm3, %%mm6 \n\t"\
+ "psubw %%mm2, %%mm4 \n\t" /* -max */\
+ "pmaxsw %%mm4, %%mm6 \n\t" /* diff= MAX3(diff, min, -max); */\
+ "1: \n\t"\
+\
+ "movq %[tmp1], %%mm2 \n\t" /* d */\
+ "movq %%mm2, %%mm3 \n\t"\
+ "psubw %%mm6, %%mm2 \n\t" /* d-diff */\
+ "paddw %%mm6, %%mm3 \n\t" /* d+diff */\
+ "pmaxsw %%mm2, %%mm1 \n\t"\
+ "pminsw %%mm3, %%mm1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\
+ "packuswb %%mm1, %%mm1 \n\t"\
+\
+ :[tmp0]"=m"(tmp0),\
+ [tmp1]"=m"(tmp1),\
+ [tmp2]"=m"(tmp2),\
+ [tmp3]"=m"(tmp3)\
+ :[prev] "r"(prev),\
+ [cur] "r"(cur),\
+ [next] "r"(next),\
+ [prefs]"r"((x86_reg)refs),\
+ [mrefs]"r"((x86_reg)-refs),\
+ [pw1] "m"(pw_1),\
+ [pb1] "m"(pb_1),\
+ [mode] "g"(mode)\
+ );\
+ __asm__ volatile("movd %%mm1, %0" :"=m"(*dst));\
+ dst += 4;\
+ prev+= 4;\
+ cur += 4;\
+ next+= 4;\
+ }
+
+ if(parity){
+#define prev2 "prev"
+#define next2 "cur"
+ FILTER
+#undef prev2
+#undef next2
+ }else{
+#define prev2 "cur"
+#define next2 "next"
+ FILTER
+#undef prev2
+#undef next2
+ }
+}
+#undef LOAD4
+#undef PABS
+#undef CHECK
+#undef CHECK1
+#undef CHECK2
+#undef FILTER
+
+#endif /* HAVE_MMX */
+
+static void filter_line_c(struct vf_priv_s *p, uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int refs, int parity){
+ int x;
+ uint8_t *prev2= parity ? prev : cur ;
+ uint8_t *next2= parity ? cur : next;
+ for(x=0; x<w; x++){
+ int c= cur[-refs];
+ int d= (prev2[0] + next2[0])>>1;
+ int e= cur[+refs];
+ int temporal_diff0= FFABS(prev2[0] - next2[0]);
+ int temporal_diff1=( FFABS(prev[-refs] - c) + FFABS(prev[+refs] - e) )>>1;
+ int temporal_diff2=( FFABS(next[-refs] - c) + FFABS(next[+refs] - e) )>>1;
+ int diff= FFMAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2);
+ int spatial_pred= (c+e)>>1;
+ int spatial_score= FFABS(cur[-refs-1] - cur[+refs-1]) + FFABS(c-e)
+ + FFABS(cur[-refs+1] - cur[+refs+1]) - 1;
+
+#define CHECK(j)\
+ { int score= FFABS(cur[-refs-1+j] - cur[+refs-1-j])\
+ + FFABS(cur[-refs +j] - cur[+refs -j])\
+ + FFABS(cur[-refs+1+j] - cur[+refs+1-j]);\
+ if(score < spatial_score){\
+ spatial_score= score;\
+ spatial_pred= (cur[-refs +j] + cur[+refs -j])>>1;\
+
+ CHECK(-1) CHECK(-2) }} }}
+ CHECK( 1) CHECK( 2) }} }}
+
+ if(p->mode<2){
+ int b= (prev2[-2*refs] + next2[-2*refs])>>1;
+ int f= (prev2[+2*refs] + next2[+2*refs])>>1;
+#if 0
+ int a= cur[-3*refs];
+ int g= cur[+3*refs];
+ int max= FFMAX3(d-e, d-c, FFMIN3(FFMAX(b-c,f-e),FFMAX(b-c,b-a),FFMAX(f-g,f-e)) );
+ int min= FFMIN3(d-e, d-c, FFMAX3(FFMIN(b-c,f-e),FFMIN(b-c,b-a),FFMIN(f-g,f-e)) );
+#else
+ int max= FFMAX3(d-e, d-c, FFMIN(b-c, f-e));
+ int min= FFMIN3(d-e, d-c, FFMAX(b-c, f-e));
+#endif
+
+ diff= FFMAX3(diff, min, -max);
+ }
+
+ if(spatial_pred > d + diff)
+ spatial_pred = d + diff;
+ else if(spatial_pred < d - diff)
+ spatial_pred = d - diff;
+
+ dst[0] = spatial_pred;
+
+ dst++;
+ cur++;
+ prev++;
+ next++;
+ prev2++;
+ next2++;
+ }
+}
+
+static void filter(struct vf_priv_s *p, uint8_t *dst[3], int dst_stride[3], int width, int height, int parity, int tff){
+ int y, i;
+
+ for(i=0; i<3; i++){
+ int is_chroma= !!i;
+ int w= width >>is_chroma;
+ int h= height>>is_chroma;
+ int refs= p->stride[i];
+
+ for(y=0; y<h; y++){
+ if((y ^ parity) & 1){
+ uint8_t *prev= &p->ref[0][i][y*refs];
+ uint8_t *cur = &p->ref[1][i][y*refs];
+ uint8_t *next= &p->ref[2][i][y*refs];
+ uint8_t *dst2= &dst[i][y*dst_stride[i]];
+ filter_line(p, dst2, prev, cur, next, w, refs, parity ^ tff);
+ }else{
+ memcpy(&dst[i][y*dst_stride[i]], &p->ref[1][i][y*refs], w);
+ }
+ }
+ }
+#if HAVE_MMX
+ if(gCpuCaps.hasMMX2) __asm__ volatile("emms \n\t" : : : "memory");
+#endif
+}
+
+static int config(struct vf_instance *vf,
+ int width, int height, int d_width, int d_height,
+ unsigned int flags, unsigned int outfmt){
+ int i, j;
+
+ for(i=0; i<3; i++){
+ int is_chroma= !!i;
+ int w= ((width + 31) & (~31))>>is_chroma;
+ int h=(((height + 1) & ( ~1))>>is_chroma) + 6;
+
+ vf->priv->stride[i]= w;
+ for(j=0; j<3; j++)
+ vf->priv->ref[j][i]= (char *)malloc(w*h*sizeof(uint8_t))+3*w;
+ }
+
+ return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
+}
+
+static int continue_buffered_image(struct vf_instance *vf);
+
+static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){
+ int tff;
+
+ if(vf->priv->parity < 0) {
+ if (mpi->fields & MP_IMGFIELD_ORDERED)
+ tff = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
+ else
+ tff = 1;
+ }
+ else tff = (vf->priv->parity&1)^1;
+
+ store_ref(vf->priv, mpi->planes, mpi->stride, mpi->w, mpi->h);
+
+ {
+ double delta;
+ if (vf->priv->buffered_pts == MP_NOPTS_VALUE)
+ delta = 1001.0/60000.0; // delta = field time distance
+ else
+ delta = (pts - vf->priv->buffered_pts) / 2;
+ if (delta <= 0.0 || delta >= 0.5)
+ delta = 0.0;
+ vf->priv->buffered_pts_delta = delta;
+ }
+
+ vf->priv->buffered_mpi = mpi;
+ vf->priv->buffered_tff = tff;
+ vf->priv->buffered_i = 0;
+ vf->priv->buffered_pts = pts;
+
+ if(vf->priv->do_deinterlace == 0)
+ return vf_next_put_image(vf, mpi, pts);
+ else if(vf->priv->do_deinterlace == 1){
+ vf->priv->do_deinterlace= 2;
+ return 0;
+ }else
+ return continue_buffered_image(vf);
+}
+
+static int continue_buffered_image(struct vf_instance *vf)
+{
+ mp_image_t *mpi = vf->priv->buffered_mpi;
+ int tff = vf->priv->buffered_tff;
+ double pts = vf->priv->buffered_pts;
+ int i;
+ int ret=0;
+ mp_image_t *dmpi;
+
+ pts += (vf->priv->buffered_i - 0.5 * (vf->priv->mode&1)) * vf->priv->buffered_pts_delta;
+
+ for(i = vf->priv->buffered_i; i<=(vf->priv->mode&1); i++){
+ dmpi=vf_get_image(vf->next,mpi->imgfmt,
+ MP_IMGTYPE_TEMP,
+ MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
+ mpi->width,mpi->height);
+ vf_clone_mpi_attributes(dmpi, mpi);
+ filter(vf->priv, dmpi->planes, dmpi->stride, mpi->w, mpi->h, i ^ tff ^ 1, tff);
+ if (i < (vf->priv->mode & 1))
+ vf_queue_frame(vf, continue_buffered_image);
+ ret |= vf_next_put_image(vf, dmpi, pts);
+ break;
+ }
+ vf->priv->buffered_i = 1;
+ return ret;
+}
+
+static void uninit(struct vf_instance *vf){
+ int i;
+ if(!vf->priv) return;
+
+ for(i=0; i<3*3; i++){
+ uint8_t **p= &vf->priv->ref[i%3][i/3];
+ if(*p) free(*p - 3*vf->priv->stride[i/3]);
+ *p= NULL;
+ }
+ free(vf->priv);
+ vf->priv=NULL;
+}
+
+//===========================================================================//
+static int query_format(struct vf_instance *vf, unsigned int fmt){
+ switch(fmt){
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_Y800:
+ case IMGFMT_Y8:
+ return vf_next_query_format(vf,fmt);
+ }
+ return 0;
+}
+
+static int control(struct vf_instance *vf, int request, void* data){
+ switch (request){
+ case VFCTRL_GET_DEINTERLACE:
+ *(int*)data = vf->priv->do_deinterlace;
+ return CONTROL_OK;
+ case VFCTRL_SET_DEINTERLACE:
+ vf->priv->do_deinterlace = 2*!!*(int*)data;
+ return CONTROL_OK;
+ }
+ return vf_next_control (vf, request, data);
+}
+
+static int vf_open(vf_instance_t *vf, char *args){
+
+ vf->config=config;
+ vf->put_image=put_image;
+ vf->query_format=query_format;
+ vf->uninit=uninit;
+ vf->priv=malloc(sizeof(struct vf_priv_s));
+ vf->control=control;
+ memset(vf->priv, 0, sizeof(struct vf_priv_s));
+
+ vf->priv->mode=0;
+ vf->priv->parity= -1;
+ vf->priv->do_deinterlace=1;
+
+ if (args) sscanf(args, "%d:%d", &vf->priv->mode, &vf->priv->parity);
+
+ filter_line = filter_line_c;
+#if HAVE_MMX
+ if(gCpuCaps.hasMMX2) filter_line = filter_line_mmx2;
+#endif
+
+ return 1;
+}
+
+const vf_info_t vf_info_yadif = {
+ "Yet Another DeInterlacing Filter",
+ "yadif",
+ "Michael Niedermayer",
+ "",
+ vf_open,
+ NULL
+};
diff --git a/video/fmt-conversion.c b/video/fmt-conversion.c
new file mode 100644
index 0000000000..81ab7a45fb
--- /dev/null
+++ b/video/fmt-conversion.c
@@ -0,0 +1,144 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "mp_msg.h"
+#include "libavutil/avutil.h"
+#include <libavutil/pixdesc.h>
+#include "libmpcodecs/img_format.h"
+#include "fmt-conversion.h"
+
+static const struct {
+ int fmt;
+ enum PixelFormat pix_fmt;
+} conversion_map[] = {
+ {IMGFMT_ARGB, PIX_FMT_ARGB},
+ {IMGFMT_BGRA, PIX_FMT_BGRA},
+ {IMGFMT_BGR24, PIX_FMT_BGR24},
+ {IMGFMT_BGR16BE, PIX_FMT_RGB565BE},
+ {IMGFMT_BGR16LE, PIX_FMT_RGB565LE},
+ {IMGFMT_BGR15BE, PIX_FMT_RGB555BE},
+ {IMGFMT_BGR15LE, PIX_FMT_RGB555LE},
+ {IMGFMT_BGR12BE, PIX_FMT_RGB444BE},
+ {IMGFMT_BGR12LE, PIX_FMT_RGB444LE},
+ {IMGFMT_BGR8, PIX_FMT_RGB8},
+ {IMGFMT_BGR4, PIX_FMT_RGB4},
+ {IMGFMT_BGR1, PIX_FMT_MONOBLACK},
+ {IMGFMT_RGB1, PIX_FMT_MONOBLACK},
+ {IMGFMT_RG4B, PIX_FMT_BGR4_BYTE},
+ {IMGFMT_BG4B, PIX_FMT_RGB4_BYTE},
+ {IMGFMT_RGB48LE, PIX_FMT_RGB48LE},
+ {IMGFMT_RGB48BE, PIX_FMT_RGB48BE},
+ {IMGFMT_ABGR, PIX_FMT_ABGR},
+ {IMGFMT_RGBA, PIX_FMT_RGBA},
+ {IMGFMT_RGB24, PIX_FMT_RGB24},
+ {IMGFMT_RGB16BE, PIX_FMT_BGR565BE},
+ {IMGFMT_RGB16LE, PIX_FMT_BGR565LE},
+ {IMGFMT_RGB15BE, PIX_FMT_BGR555BE},
+ {IMGFMT_RGB15LE, PIX_FMT_BGR555LE},
+ {IMGFMT_RGB12BE, PIX_FMT_BGR444BE},
+ {IMGFMT_RGB12LE, PIX_FMT_BGR444LE},
+ {IMGFMT_RGB8, PIX_FMT_BGR8},
+ {IMGFMT_RGB4, PIX_FMT_BGR4},
+ {IMGFMT_BGR8, PIX_FMT_PAL8},
+ {IMGFMT_GBRP, PIX_FMT_GBRP},
+ {IMGFMT_YUY2, PIX_FMT_YUYV422},
+ {IMGFMT_UYVY, PIX_FMT_UYVY422},
+ {IMGFMT_NV12, PIX_FMT_NV12},
+ {IMGFMT_NV21, PIX_FMT_NV21},
+ {IMGFMT_Y800, PIX_FMT_GRAY8},
+ {IMGFMT_Y8, PIX_FMT_GRAY8},
+ {IMGFMT_YVU9, PIX_FMT_YUV410P},
+ {IMGFMT_IF09, PIX_FMT_YUV410P},
+ {IMGFMT_YV12, PIX_FMT_YUV420P},
+ {IMGFMT_I420, PIX_FMT_YUV420P},
+ {IMGFMT_IYUV, PIX_FMT_YUV420P},
+ {IMGFMT_411P, PIX_FMT_YUV411P},
+ {IMGFMT_422P, PIX_FMT_YUV422P},
+ {IMGFMT_444P, PIX_FMT_YUV444P},
+ {IMGFMT_440P, PIX_FMT_YUV440P},
+
+ {IMGFMT_420A, PIX_FMT_YUVA420P},
+
+ {IMGFMT_420P16_LE, PIX_FMT_YUV420P16LE},
+ {IMGFMT_420P16_BE, PIX_FMT_YUV420P16BE},
+ {IMGFMT_420P9_LE, PIX_FMT_YUV420P9LE},
+ {IMGFMT_420P9_BE, PIX_FMT_YUV420P9BE},
+ {IMGFMT_420P10_LE, PIX_FMT_YUV420P10LE},
+ {IMGFMT_420P10_BE, PIX_FMT_YUV420P10BE},
+ {IMGFMT_422P10_LE, PIX_FMT_YUV422P10LE},
+ {IMGFMT_422P10_BE, PIX_FMT_YUV422P10BE},
+ {IMGFMT_444P9_BE , PIX_FMT_YUV444P9BE},
+ {IMGFMT_444P9_LE , PIX_FMT_YUV444P9LE},
+ {IMGFMT_444P10_BE, PIX_FMT_YUV444P10BE},
+ {IMGFMT_444P10_LE, PIX_FMT_YUV444P10LE},
+ {IMGFMT_422P16_LE, PIX_FMT_YUV422P16LE},
+ {IMGFMT_422P16_BE, PIX_FMT_YUV422P16BE},
+ {IMGFMT_422P9_LE, PIX_FMT_YUV422P9LE},
+ {IMGFMT_422P9_BE, PIX_FMT_YUV422P9BE},
+ {IMGFMT_444P16_LE, PIX_FMT_YUV444P16LE},
+ {IMGFMT_444P16_BE, PIX_FMT_YUV444P16BE},
+
+ // YUVJ are YUV formats that use the full Y range and not just
+ // 16 - 235 (see colorspaces.txt).
+ // Currently they are all treated the same way.
+ {IMGFMT_YV12, PIX_FMT_YUVJ420P},
+ {IMGFMT_422P, PIX_FMT_YUVJ422P},
+ {IMGFMT_444P, PIX_FMT_YUVJ444P},
+ {IMGFMT_440P, PIX_FMT_YUVJ440P},
+
+ // ffmpeg only
+#if LIBAVUTIL_VERSION_MICRO >= 100
+ {IMGFMT_BGR0, PIX_FMT_BGR0},
+#endif
+
+ {IMGFMT_VDPAU_MPEG1, PIX_FMT_VDPAU_MPEG1},
+ {IMGFMT_VDPAU_MPEG2, PIX_FMT_VDPAU_MPEG2},
+ {IMGFMT_VDPAU_H264, PIX_FMT_VDPAU_H264},
+ {IMGFMT_VDPAU_WMV3, PIX_FMT_VDPAU_WMV3},
+ {IMGFMT_VDPAU_VC1, PIX_FMT_VDPAU_VC1},
+ {IMGFMT_VDPAU_MPEG4, PIX_FMT_VDPAU_MPEG4},
+ {0, PIX_FMT_NONE}
+};
+
+enum PixelFormat imgfmt2pixfmt(int fmt)
+{
+ int i;
+ enum PixelFormat pix_fmt;
+ for (i = 0; conversion_map[i].fmt; i++)
+ if (conversion_map[i].fmt == fmt)
+ break;
+ pix_fmt = conversion_map[i].pix_fmt;
+ if (pix_fmt == PIX_FMT_NONE)
+ mp_msg(MSGT_GLOBAL, MSGL_ERR, "Unsupported format %s\n", vo_format_name(fmt));
+ return pix_fmt;
+}
+
+int pixfmt2imgfmt(enum PixelFormat pix_fmt)
+{
+ int i;
+ for (i = 0; conversion_map[i].pix_fmt != PIX_FMT_NONE; i++)
+ if (conversion_map[i].pix_fmt == pix_fmt)
+ break;
+ int fmt = conversion_map[i].fmt;
+ if (!fmt) {
+ const char *fmtname = av_get_pix_fmt_name(pix_fmt);
+ mp_msg(MSGT_GLOBAL, MSGL_ERR, "Unsupported PixelFormat %s (%d)\n",
+ fmtname ? fmtname : "INVALID", pix_fmt);
+ }
+ return fmt;
+}
diff --git a/video/fmt-conversion.h b/video/fmt-conversion.h
new file mode 100644
index 0000000000..f7114b0aef
--- /dev/null
+++ b/video/fmt-conversion.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_FMT_CONVERSION_H
+#define MPLAYER_FMT_CONVERSION_H
+
+#include <libavutil/pixfmt.h>
+
+enum PixelFormat imgfmt2pixfmt(int fmt);
+int pixfmt2imgfmt(enum PixelFormat pix_fmt);
+
+#endif /* MPLAYER_FMT_CONVERSION_H */
diff --git a/video/image_writer.c b/video/image_writer.c
new file mode 100644
index 0000000000..877c89e700
--- /dev/null
+++ b/video/image_writer.c
@@ -0,0 +1,327 @@
+/*
+ * This file is part of mplayer.
+ *
+ * mplayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#include <libswscale/swscale.h>
+#include <libavcodec/avcodec.h>
+
+#include "config.h"
+
+#ifdef CONFIG_JPEG
+#include <jpeglib.h>
+#endif
+
+#include "osdep/io.h"
+
+#include "image_writer.h"
+#include "talloc.h"
+#include "libmpcodecs/img_format.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/dec_video.h"
+#include "libmpcodecs/vf.h"
+#include "fmt-conversion.h"
+
+#include "libmpcodecs/sws_utils.h"
+#include "libmpcodecs/vf.h"
+
+#include "m_option.h"
+
+const struct image_writer_opts image_writer_opts_defaults = {
+ .format = "jpg",
+ .png_compression = 7,
+ .jpeg_quality = 90,
+ .jpeg_optimize = 100,
+ .jpeg_smooth = 0,
+ .jpeg_dpi = 72,
+ .jpeg_progressive = 0,
+ .jpeg_baseline = 1,
+};
+
+#undef OPT_BASE_STRUCT
+#define OPT_BASE_STRUCT struct image_writer_opts
+
+const struct m_sub_options image_writer_conf = {
+ .opts = (m_option_t[]) {
+ OPT_INTRANGE("jpeg-quality", jpeg_quality, 0, 0, 100),
+ OPT_INTRANGE("jpeg-optimize", jpeg_optimize, 0, 0, 100),
+ OPT_INTRANGE("jpeg-smooth", jpeg_smooth, 0, 0, 100),
+ OPT_INTRANGE("jpeg-dpi", jpeg_dpi, M_OPT_MIN, 1, 99999),
+ OPT_MAKE_FLAGS("jpeg-progressive", jpeg_progressive, 0),
+ OPT_MAKE_FLAGS("jpeg-baseline", jpeg_baseline, 0),
+ OPT_INTRANGE("png-compression", png_compression, 0, 0, 9),
+ OPT_STRING("format", format, 0),
+ {0},
+ },
+ .size = sizeof(struct image_writer_opts),
+ .defaults = &image_writer_opts_defaults,
+};
+
+struct image_writer_ctx {
+ const struct image_writer_opts *opts;
+ const struct img_writer *writer;
+};
+
+struct img_writer {
+ const char *file_ext;
+ int (*write)(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp);
+ int *pixfmts;
+ int lavc_codec;
+};
+
+static int write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
+{
+ void *outbuffer = NULL;
+ int success = 0;
+ AVFrame *pic = NULL;
+
+ struct AVCodec *codec = avcodec_find_encoder(ctx->writer->lavc_codec);
+ AVCodecContext *avctx = NULL;
+ if (!codec)
+ goto print_open_fail;
+ avctx = avcodec_alloc_context3(codec);
+ if (!avctx)
+ goto print_open_fail;
+
+ avctx->time_base = AV_TIME_BASE_Q;
+ avctx->width = image->width;
+ avctx->height = image->height;
+ avctx->pix_fmt = imgfmt2pixfmt(image->imgfmt);
+ if (ctx->writer->lavc_codec == CODEC_ID_PNG)
+ avctx->compression_level = ctx->opts->png_compression;
+
+ if (avcodec_open2(avctx, codec, NULL) < 0) {
+ print_open_fail:
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "Could not open libavcodec encoder"
+ " for saving images\n");
+ goto error_exit;
+ }
+
+ size_t outbuffer_size = image->width * image->height * 3 * 2;
+ outbuffer = malloc(outbuffer_size);
+ if (!outbuffer)
+ goto error_exit;
+
+ pic = avcodec_alloc_frame();
+ if (!pic)
+ goto error_exit;
+ avcodec_get_frame_defaults(pic);
+ for (int n = 0; n < 4; n++) {
+ pic->data[n] = image->planes[n];
+ pic->linesize[n] = image->stride[n];
+ }
+ int size = avcodec_encode_video(avctx, outbuffer, outbuffer_size, pic);
+ if (size < 1)
+ goto error_exit;
+
+ fwrite(outbuffer, size, 1, fp);
+
+ success = 1;
+error_exit:
+ if (avctx)
+ avcodec_close(avctx);
+ av_free(avctx);
+ avcodec_free_frame(&pic);
+ free(outbuffer);
+ return success;
+}
+
+#ifdef CONFIG_JPEG
+
+static void write_jpeg_error_exit(j_common_ptr cinfo)
+{
+ // NOTE: do not write error message, too much effort to connect the libjpeg
+ // log callbacks with mplayer's log function mp_msp()
+
+ // Return control to the setjmp point
+ longjmp(*(jmp_buf*)cinfo->client_data, 1);
+}
+
+static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jerr.error_exit = write_jpeg_error_exit;
+
+ jmp_buf error_return_jmpbuf;
+ cinfo.client_data = &error_return_jmpbuf;
+ if (setjmp(cinfo.client_data)) {
+ jpeg_destroy_compress(&cinfo);
+ return 0;
+ }
+
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, fp);
+
+ cinfo.image_width = image->width;
+ cinfo.image_height = image->height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ cinfo.write_JFIF_header = TRUE;
+ cinfo.JFIF_major_version = 1;
+ cinfo.JFIF_minor_version = 2;
+ cinfo.density_unit = 1; /* 0=unknown, 1=dpi, 2=dpcm */
+ /* Image DPI is determined by Y_density, so we leave that at
+ jpeg_dpi if possible and crunch X_density instead (PAR > 1) */
+ // NOTE: write_image never passes anamorphic images currently
+ cinfo.X_density = ctx->opts->jpeg_dpi*image->width/image->w;
+ cinfo.Y_density = ctx->opts->jpeg_dpi*image->height/image->h;
+ cinfo.write_Adobe_marker = TRUE;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, ctx->opts->jpeg_quality, ctx->opts->jpeg_baseline);
+ cinfo.optimize_coding = ctx->opts->jpeg_optimize;
+ cinfo.smoothing_factor = ctx->opts->jpeg_smooth;
+
+ if (ctx->opts->jpeg_progressive)
+ jpeg_simple_progression(&cinfo);
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ JSAMPROW row_pointer[1];
+ row_pointer[0] = image->planes[0] +
+ cinfo.next_scanline * image->stride[0];
+ jpeg_write_scanlines(&cinfo, row_pointer,1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+
+ jpeg_destroy_compress(&cinfo);
+
+ return 1;
+}
+
+#endif
+
+static const struct img_writer img_writers[] = {
+ { "png", write_lavc, .lavc_codec = CODEC_ID_PNG },
+ { "ppm", write_lavc, .lavc_codec = CODEC_ID_PPM },
+ { "pgm", write_lavc,
+ .lavc_codec = CODEC_ID_PGM,
+ .pixfmts = (int[]) { IMGFMT_Y800, 0 },
+ },
+ { "pgmyuv", write_lavc,
+ .lavc_codec = CODEC_ID_PGMYUV,
+ .pixfmts = (int[]) { IMGFMT_YV12, 0 },
+ },
+ { "tga", write_lavc,
+ .lavc_codec = CODEC_ID_TARGA,
+ .pixfmts = (int[]) { IMGFMT_BGR24, IMGFMT_BGRA, IMGFMT_BGR15LE,
+ IMGFMT_Y800, 0},
+ },
+#ifdef CONFIG_JPEG
+ { "jpg", write_jpeg },
+ { "jpeg", write_jpeg },
+#endif
+};
+
+static const struct img_writer *get_writer(const struct image_writer_opts *opts)
+{
+ const char *type = opts->format;
+
+ for (size_t n = 0; n < sizeof(img_writers) / sizeof(img_writers[0]); n++) {
+ const struct img_writer *writer = &img_writers[n];
+ if (type && strcmp(type, writer->file_ext) == 0)
+ return writer;
+ }
+
+ return &img_writers[0];
+}
+
+const char *image_writer_file_ext(const struct image_writer_opts *opts)
+{
+ struct image_writer_opts defs = image_writer_opts_defaults;
+
+ if (!opts)
+ opts = &defs;
+
+ return get_writer(opts)->file_ext;
+}
+
+int write_image(struct mp_image *image, const struct image_writer_opts *opts,
+ const char *filename)
+{
+ struct mp_image *allocated_image = NULL;
+ struct image_writer_opts defs = image_writer_opts_defaults;
+ int d_w = image->display_w ? image->display_w : image->w;
+ int d_h = image->display_h ? image->display_h : image->h;
+ bool is_anamorphic = image->w != d_w || image->h != d_h;
+
+ if (!opts)
+ opts = &defs;
+
+ const struct img_writer *writer = get_writer(opts);
+ struct image_writer_ctx ctx = { opts, writer };
+ int destfmt = IMGFMT_RGB24;
+
+ if (writer->pixfmts) {
+ destfmt = writer->pixfmts[0]; // default to first pixel format
+ for (int *fmt = writer->pixfmts; *fmt; fmt++) {
+ if (*fmt == image->imgfmt) {
+ destfmt = *fmt;
+ break;
+ }
+ }
+ }
+
+ // Caveat: - no colorspace/levels conversion done if pixel formats equal
+ // - RGB->YUV assumes BT.601
+ // - color levels broken in various ways thanks to libswscale
+ if (image->imgfmt != destfmt || is_anamorphic) {
+ struct mp_image *dst = alloc_mpi(d_w, d_h, destfmt);
+ vf_clone_mpi_attributes(dst, image);
+
+ int flags = SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
+ SWS_ACCURATE_RND | SWS_BITEXACT;
+
+ mp_image_swscale(dst, image, flags);
+
+ allocated_image = dst;
+ image = dst;
+ }
+
+ FILE *fp = fopen(filename, "wb");
+ int success = 0;
+ if (fp == NULL) {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR,
+ "Error opening '%s' for writing!\n", filename);
+ } else {
+ success = writer->write(&ctx, image, fp);
+ success = !fclose(fp) && success;
+ if (!success)
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error writing file '%s'!\n",
+ filename);
+ }
+
+ free_mp_image(allocated_image);
+
+ return success;
+}
+
+void dump_png(struct mp_image *image, const char *filename)
+{
+ struct image_writer_opts opts = image_writer_opts_defaults;
+ opts.format = "png";
+ write_image(image, &opts, filename);
+}
diff --git a/video/image_writer.h b/video/image_writer.h
new file mode 100644
index 0000000000..e73b526c7e
--- /dev/null
+++ b/video/image_writer.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of mplayer.
+ *
+ * mplayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+struct mp_image;
+struct mp_csp_details;
+
+struct image_writer_opts {
+ char *format;
+ int png_compression;
+ int jpeg_quality;
+ int jpeg_optimize;
+ int jpeg_smooth;
+ int jpeg_dpi;
+ int jpeg_progressive;
+ int jpeg_baseline;
+};
+
+extern const struct image_writer_opts image_writer_opts_defaults;
+
+extern const struct m_sub_options image_writer_conf;
+
+// Return the file extension that will be used, e.g. "png".
+const char *image_writer_file_ext(const struct image_writer_opts *opts);
+
+/*
+ * Save the given image under the given filename. The parameters csp and opts
+ * are optional. All pixel formats supported by swscale are supported.
+ *
+ * File format and compression settings are controlled via the opts parameter.
+ *
+ * NOTE: The fields w/h/width/height of the passed mp_image must be all set
+ * accordingly. Setting w and width or h and height to different values
+ * can be used to store snapshots of anamorphic video.
+ */
+int write_image(struct mp_image *image, const struct image_writer_opts *opts,
+ const char *filename);
+
+// Debugging helper.
+void dump_png(struct mp_image *image, const char *filename);
diff --git a/video/img_format.c b/video/img_format.c
new file mode 100644
index 0000000000..1084a8f9a1
--- /dev/null
+++ b/video/img_format.c
@@ -0,0 +1,233 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "img_format.h"
+#include "stdio.h"
+#include "mpbswap.h"
+
+#include <string.h>
+
+const char *vo_format_name(int format)
+{
+ const char *name = mp_imgfmt_to_name(format);
+ if (name)
+ return name;
+ static char unknown_format[20];
+ snprintf(unknown_format, 20, "Unknown 0x%04x", format);
+ return unknown_format;
+}
+
+int mp_get_chroma_shift(int format, int *x_shift, int *y_shift,
+ int *component_bits)
+{
+ int xs = 0, ys = 0;
+ int bpp;
+ int err = 0;
+ int bits = 8;
+ if ((format & 0xff0000f0) == 0x34000050)
+ format = bswap_32(format);
+ if ((format & 0xf00000ff) == 0x50000034) {
+ switch (format >> 24) {
+ case 0x50:
+ break;
+ case 0x51:
+ bits = 16;
+ break;
+ case 0x52:
+ bits = 10;
+ break;
+ case 0x53:
+ bits = 9;
+ break;
+ default:
+ err = 1;
+ break;
+ }
+ switch (format & 0x00ffffff) {
+ case 0x00343434: // 444
+ xs = 0;
+ ys = 0;
+ break;
+ case 0x00323234: // 422
+ xs = 1;
+ ys = 0;
+ break;
+ case 0x00303234: // 420
+ xs = 1;
+ ys = 1;
+ break;
+ case 0x00313134: // 411
+ xs = 2;
+ ys = 0;
+ break;
+ case 0x00303434: // 440
+ xs = 0;
+ ys = 1;
+ break;
+ default:
+ err = 1;
+ break;
+ }
+ } else
+ switch (format) {
+ case IMGFMT_420A:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_YV12:
+ xs = 1;
+ ys = 1;
+ break;
+ case IMGFMT_IF09:
+ case IMGFMT_YVU9:
+ xs = 2;
+ ys = 2;
+ break;
+ case IMGFMT_Y8:
+ case IMGFMT_Y800:
+ xs = 31;
+ ys = 31;
+ break;
+ default:
+ err = 1;
+ break;
+ }
+ if (x_shift)
+ *x_shift = xs;
+ if (y_shift)
+ *y_shift = ys;
+ if (component_bits)
+ *component_bits = bits;
+ bpp = 8 + ((16 >> xs) >> ys);
+ if (format == IMGFMT_420A)
+ bpp += 8;
+ bpp *= (bits + 7) >> 3;
+ return err ? 0 : bpp;
+}
+
+struct mp_imgfmt_entry mp_imgfmt_list[] = {
+ {"444p16le", IMGFMT_444P16_LE},
+ {"444p16be", IMGFMT_444P16_BE},
+ {"444p10le", IMGFMT_444P10_LE},
+ {"444p10be", IMGFMT_444P10_BE},
+ {"444p9le", IMGFMT_444P9_LE},
+ {"444p9be", IMGFMT_444P9_BE},
+ {"422p16le", IMGFMT_422P16_LE},
+ {"422p16be", IMGFMT_422P16_BE},
+ {"422p10le", IMGFMT_422P10_LE},
+ {"422p10be", IMGFMT_422P10_BE},
+ {"422p9le", IMGFMT_422P9_LE},
+ {"422p9be", IMGFMT_422P9_BE},
+ {"420p16le", IMGFMT_420P16_LE},
+ {"420p16be", IMGFMT_420P16_BE},
+ {"420p10le", IMGFMT_420P10_LE},
+ {"420p10be", IMGFMT_420P10_BE},
+ {"420p9le", IMGFMT_420P9_LE},
+ {"420p9be", IMGFMT_420P9_BE},
+ {"444p16", IMGFMT_444P16},
+ {"444p10", IMGFMT_444P10},
+ {"444p9", IMGFMT_444P9},
+ {"422p16", IMGFMT_422P16},
+ {"422p10", IMGFMT_422P10},
+ {"420p10", IMGFMT_420P10},
+ {"420p9", IMGFMT_420P9},
+ {"420p16", IMGFMT_420P16},
+ {"420a", IMGFMT_420A},
+ {"444p", IMGFMT_444P},
+ {"422p", IMGFMT_422P},
+ {"411p", IMGFMT_411P},
+ {"440p", IMGFMT_440P},
+ {"yuy2", IMGFMT_YUY2},
+ {"yvyu", IMGFMT_YVYU},
+ {"uyvy", IMGFMT_UYVY},
+ {"yvu9", IMGFMT_YVU9},
+ {"if09", IMGFMT_IF09},
+ {"yv12", IMGFMT_YV12},
+ {"i420", IMGFMT_I420},
+ {"iyuv", IMGFMT_IYUV},
+ {"clpl", IMGFMT_CLPL},
+ {"hm12", IMGFMT_HM12},
+ {"y800", IMGFMT_Y800},
+ {"y8", IMGFMT_Y8},
+ {"nv12", IMGFMT_NV12},
+ {"nv21", IMGFMT_NV21},
+ {"bgr24", IMGFMT_BGR24},
+ {"bgr32", IMGFMT_BGR32},
+ {"bgr16", IMGFMT_BGR16},
+ {"bgr15", IMGFMT_BGR15},
+ {"bgr12", IMGFMT_BGR12},
+ {"bgr8", IMGFMT_BGR8},
+ {"bgr4", IMGFMT_BGR4},
+ {"bg4b", IMGFMT_BG4B},
+ {"bgr1", IMGFMT_BGR1},
+ {"rgb48be", IMGFMT_RGB48BE},
+ {"rgb48le", IMGFMT_RGB48LE},
+ {"rgb48ne", IMGFMT_RGB48NE},
+ {"rgb24", IMGFMT_RGB24},
+ {"rgb32", IMGFMT_RGB32},
+ {"rgb16", IMGFMT_RGB16},
+ {"rgb15", IMGFMT_RGB15},
+ {"rgb12", IMGFMT_RGB12},
+ {"rgb8", IMGFMT_RGB8},
+ {"rgb4", IMGFMT_RGB4},
+ {"rg4b", IMGFMT_RG4B},
+ {"rgb1", IMGFMT_RGB1},
+ {"rgba", IMGFMT_RGBA},
+ {"argb", IMGFMT_ARGB},
+ {"bgra", IMGFMT_BGRA},
+ {"abgr", IMGFMT_ABGR},
+ {"bgr0", IMGFMT_BGR0},
+ {"gbrp", IMGFMT_GBRP},
+ {"mjpeg", IMGFMT_MJPEG},
+ {"mjpg", IMGFMT_MJPEG},
+ {"vdpau_h264", IMGFMT_VDPAU_H264},
+ {"vdpau_mpeg1", IMGFMT_VDPAU_MPEG1},
+ {"vdpau_mpeg2", IMGFMT_VDPAU_MPEG2},
+ {"vdpau_mpeg4", IMGFMT_VDPAU_MPEG4},
+ {"vdpau_wmv3", IMGFMT_VDPAU_WMV3},
+ {"vdpau_vc1", IMGFMT_VDPAU_VC1},
+ {0}
+};
+
+unsigned int mp_imgfmt_from_name(bstr name, bool allow_hwaccel)
+{
+ if (bstr_startswith0(name, "0x")) {
+ bstr rest;
+ unsigned int fmt = bstrtoll(name, &rest, 16);
+ if (rest.len == 0)
+ return fmt;
+ }
+ for(struct mp_imgfmt_entry *p = mp_imgfmt_list; p->name; ++p) {
+ if(!bstrcasecmp0(name, p->name)) {
+ if (!allow_hwaccel && IMGFMT_IS_HWACCEL(p->fmt))
+ return 0;
+ return p->fmt;
+ }
+ }
+ return 0;
+}
+
+const char *mp_imgfmt_to_name(unsigned int fmt)
+{
+ struct mp_imgfmt_entry *p = mp_imgfmt_list;
+ for(; p->name; ++p) {
+ if(p->fmt == fmt)
+ return p->name;
+ }
+ return NULL;
+}
diff --git a/video/img_format.h b/video/img_format.h
new file mode 100644
index 0000000000..b488734f02
--- /dev/null
+++ b/video/img_format.h
@@ -0,0 +1,243 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_IMG_FORMAT_H
+#define MPLAYER_IMG_FORMAT_H
+
+#include <sys/types.h>
+#include "config.h"
+#include "bstr.h"
+
+/* RGB/BGR Formats */
+
+#define IMGFMT_RGB_MASK 0xFFFFFF00
+#define IMGFMT_RGB (('R'<<24)|('G'<<16)|('B'<<8))
+#define IMGFMT_RGB1 (IMGFMT_RGB|1)
+#define IMGFMT_RGB4 (IMGFMT_RGB|4)
+#define IMGFMT_RGB4_CHAR (IMGFMT_RGB|4|128) // RGB4 with 1 pixel per byte
+#define IMGFMT_RGB8 (IMGFMT_RGB|8)
+#define IMGFMT_RGB12 (IMGFMT_RGB|12)
+#define IMGFMT_RGB15 (IMGFMT_RGB|15)
+#define IMGFMT_RGB16 (IMGFMT_RGB|16)
+#define IMGFMT_RGB24 (IMGFMT_RGB|24)
+#define IMGFMT_RGB32 (IMGFMT_RGB|32)
+#define IMGFMT_RGB48LE (IMGFMT_RGB|48)
+#define IMGFMT_RGB48BE (IMGFMT_RGB|48|128)
+
+#define IMGFMT_BGR_MASK 0xFFFFFF00
+#define IMGFMT_BGR (('B'<<24)|('G'<<16)|('R'<<8))
+#define IMGFMT_BGR1 (IMGFMT_BGR|1)
+#define IMGFMT_BGR4 (IMGFMT_BGR|4)
+#define IMGFMT_BGR4_CHAR (IMGFMT_BGR|4|128) // BGR4 with 1 pixel per byte
+#define IMGFMT_BGR8 (IMGFMT_BGR|8)
+#define IMGFMT_BGR12 (IMGFMT_BGR|12)
+#define IMGFMT_BGR15 (IMGFMT_BGR|15)
+#define IMGFMT_BGR16 (IMGFMT_BGR|16)
+#define IMGFMT_BGR24 (IMGFMT_BGR|24)
+#define IMGFMT_BGR32 (IMGFMT_BGR|32)
+
+#define IMGFMT_GBRP (('G'<<24)|('B'<<16)|('R'<<8)|24)
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define IMGFMT_ABGR IMGFMT_RGB32
+#define IMGFMT_BGRA (IMGFMT_RGB32|128)
+#define IMGFMT_ARGB IMGFMT_BGR32
+#define IMGFMT_RGBA (IMGFMT_BGR32|128)
+#define IMGFMT_RGB48NE IMGFMT_RGB48BE
+#define IMGFMT_RGB12BE IMGFMT_RGB12
+#define IMGFMT_RGB12LE (IMGFMT_RGB12|128)
+#define IMGFMT_RGB15BE IMGFMT_RGB15
+#define IMGFMT_RGB15LE (IMGFMT_RGB15|128)
+#define IMGFMT_RGB16BE IMGFMT_RGB16
+#define IMGFMT_RGB16LE (IMGFMT_RGB16|128)
+#define IMGFMT_BGR12BE IMGFMT_BGR12
+#define IMGFMT_BGR12LE (IMGFMT_BGR12|128)
+#define IMGFMT_BGR15BE IMGFMT_BGR15
+#define IMGFMT_BGR15LE (IMGFMT_BGR15|128)
+#define IMGFMT_BGR16BE IMGFMT_BGR16
+#define IMGFMT_BGR16LE (IMGFMT_BGR16|128)
+#else
+#define IMGFMT_ABGR (IMGFMT_BGR32|128)
+#define IMGFMT_BGRA IMGFMT_BGR32
+#define IMGFMT_ARGB (IMGFMT_RGB32|128)
+#define IMGFMT_RGBA IMGFMT_RGB32
+#define IMGFMT_RGB48NE IMGFMT_RGB48LE
+#define IMGFMT_RGB12BE (IMGFMT_RGB12|128)
+#define IMGFMT_RGB12LE IMGFMT_RGB12
+#define IMGFMT_RGB15BE (IMGFMT_RGB15|128)
+#define IMGFMT_RGB15LE IMGFMT_RGB15
+#define IMGFMT_RGB16BE (IMGFMT_RGB16|128)
+#define IMGFMT_RGB16LE IMGFMT_RGB16
+#define IMGFMT_BGR12BE (IMGFMT_BGR12|128)
+#define IMGFMT_BGR12LE IMGFMT_BGR12
+#define IMGFMT_BGR15BE (IMGFMT_BGR15|128)
+#define IMGFMT_BGR15LE IMGFMT_BGR15
+#define IMGFMT_BGR16BE (IMGFMT_BGR16|128)
+#define IMGFMT_BGR16LE IMGFMT_BGR16
+#endif
+
+/* old names for compatibility */
+#define IMGFMT_RG4B IMGFMT_RGB4_CHAR
+#define IMGFMT_BG4B IMGFMT_BGR4_CHAR
+
+#define IMGFMT_IS_RGB(fmt) (((fmt)&IMGFMT_RGB_MASK)==IMGFMT_RGB)
+#define IMGFMT_IS_BGR(fmt) (((fmt)&IMGFMT_BGR_MASK)==IMGFMT_BGR)
+
+#define IMGFMT_RGB_DEPTH(fmt) ((fmt)&0x3F)
+#define IMGFMT_BGR_DEPTH(fmt) ((fmt)&0x3F)
+
+// AV_PIX_FMT_BGR0
+#define IMGFMT_BGR0 0x1DC70000
+
+/* Planar YUV Formats */
+
+#define IMGFMT_YVU9 0x39555659
+#define IMGFMT_IF09 0x39304649
+#define IMGFMT_YV12 0x32315659
+#define IMGFMT_I420 0x30323449
+#define IMGFMT_IYUV 0x56555949
+#define IMGFMT_CLPL 0x4C504C43
+#define IMGFMT_Y800 0x30303859
+#define IMGFMT_Y8 0x20203859
+#define IMGFMT_NV12 0x3231564E
+#define IMGFMT_NV21 0x3132564E
+
+/* unofficial Planar Formats, FIXME if official 4CC exists */
+#define IMGFMT_444P 0x50343434
+#define IMGFMT_422P 0x50323234
+#define IMGFMT_411P 0x50313134
+#define IMGFMT_440P 0x50303434
+#define IMGFMT_HM12 0x32314D48
+
+// 4:2:0 planar with alpha
+#define IMGFMT_420A 0x41303234
+
+#define IMGFMT_444P16_LE 0x51343434
+#define IMGFMT_444P16_BE 0x34343451
+#define IMGFMT_444P10_LE 0x52343434
+#define IMGFMT_444P10_BE 0x34343452
+#define IMGFMT_444P9_LE 0x53343434
+#define IMGFMT_444P9_BE 0x34343453
+#define IMGFMT_422P16_LE 0x51323234
+#define IMGFMT_422P16_BE 0x34323251
+#define IMGFMT_422P10_LE 0x52323234
+#define IMGFMT_422P10_BE 0x34323252
+#define IMGFMT_422P9_LE 0x53323234
+#define IMGFMT_422P9_BE 0x34323253
+#define IMGFMT_420P16_LE 0x51303234
+#define IMGFMT_420P16_BE 0x34323051
+#define IMGFMT_420P10_LE 0x52303234
+#define IMGFMT_420P10_BE 0x34323052
+#define IMGFMT_420P9_LE 0x53303234
+#define IMGFMT_420P9_BE 0x34323053
+#if BYTE_ORDER == BIG_ENDIAN
+#define IMGFMT_444P16 IMGFMT_444P16_BE
+#define IMGFMT_444P10 IMGFMT_444P10_BE
+#define IMGFMT_444P9 IMGFMT_444P9_BE
+#define IMGFMT_422P16 IMGFMT_422P16_BE
+#define IMGFMT_422P10 IMGFMT_422P10_BE
+#define IMGFMT_422P9 IMGFMT_422P9_BE
+#define IMGFMT_420P16 IMGFMT_420P16_BE
+#define IMGFMT_420P10 IMGFMT_420P10_BE
+#define IMGFMT_420P9 IMGFMT_420P9_BE
+#define IMGFMT_IS_YUVP16_NE(fmt) IMGFMT_IS_YUVP16_BE(fmt)
+#else
+#define IMGFMT_444P16 IMGFMT_444P16_LE
+#define IMGFMT_444P10 IMGFMT_444P10_LE
+#define IMGFMT_444P9 IMGFMT_444P9_LE
+#define IMGFMT_422P16 IMGFMT_422P16_LE
+#define IMGFMT_422P10 IMGFMT_422P10_LE
+#define IMGFMT_422P9 IMGFMT_422P9_LE
+#define IMGFMT_420P16 IMGFMT_420P16_LE
+#define IMGFMT_420P10 IMGFMT_420P10_LE
+#define IMGFMT_420P9 IMGFMT_420P9_LE
+#define IMGFMT_IS_YUVP16_NE(fmt) IMGFMT_IS_YUVP16_LE(fmt)
+#endif
+
+// These macros are misnamed - they actually match 9, 10 or 16 bits
+#define IMGFMT_IS_YUVP16_LE(fmt) (((fmt - 0x51000034) & 0xfc0000ff) == 0)
+#define IMGFMT_IS_YUVP16_BE(fmt) (((fmt - 0x34000051) & 0xff0000fc) == 0)
+#define IMGFMT_IS_YUVP16(fmt) (IMGFMT_IS_YUVP16_LE(fmt) || IMGFMT_IS_YUVP16_BE(fmt))
+
+/* Packed YUV Formats */
+
+#define IMGFMT_IUYV 0x56595549 // Interlaced UYVY
+#define IMGFMT_IY41 0x31435949 // Interlaced Y41P
+#define IMGFMT_IYU1 0x31555949
+#define IMGFMT_IYU2 0x32555949
+#define IMGFMT_UYVY 0x59565955
+#define IMGFMT_UYNV 0x564E5955 // Exactly same as UYVY
+#define IMGFMT_cyuv 0x76757963 // upside-down UYVY
+#define IMGFMT_Y422 0x32323459 // Exactly same as UYVY
+#define IMGFMT_YUY2 0x32595559
+#define IMGFMT_YUNV 0x564E5559 // Exactly same as YUY2
+#define IMGFMT_YVYU 0x55595659
+#define IMGFMT_Y41P 0x50313459
+#define IMGFMT_Y211 0x31313259
+#define IMGFMT_Y41T 0x54313459 // Y41P, Y lsb = transparency
+#define IMGFMT_Y42T 0x54323459 // UYVY, Y lsb = transparency
+#define IMGFMT_V422 0x32323456 // upside-down UYVY?
+#define IMGFMT_V655 0x35353656
+#define IMGFMT_CLJR 0x524A4C43
+#define IMGFMT_YUVP 0x50565559 // 10-bit YUYV
+#define IMGFMT_UYVP 0x50565955 // 10-bit UYVY
+
+/* Compressed Formats */
+#define IMGFMT_MJPEG (('M')|('J'<<8)|('P'<<16)|('G'<<24))
+
+// VDPAU specific format.
+#define IMGFMT_VDPAU 0x1DC80000
+#define IMGFMT_VDPAU_MASK 0xFFFF0000
+#define IMGFMT_IS_VDPAU(fmt) (((fmt)&IMGFMT_VDPAU_MASK)==IMGFMT_VDPAU)
+#define IMGFMT_VDPAU_MPEG1 (IMGFMT_VDPAU|0x01)
+#define IMGFMT_VDPAU_MPEG2 (IMGFMT_VDPAU|0x02)
+#define IMGFMT_VDPAU_H264 (IMGFMT_VDPAU|0x03)
+#define IMGFMT_VDPAU_WMV3 (IMGFMT_VDPAU|0x04)
+#define IMGFMT_VDPAU_VC1 (IMGFMT_VDPAU|0x05)
+#define IMGFMT_VDPAU_MPEG4 (IMGFMT_VDPAU|0x06)
+
+#define IMGFMT_IS_HWACCEL(fmt) IMGFMT_IS_VDPAU(fmt)
+
+typedef struct {
+ void* data;
+ int size;
+ int id; // stream id. usually 0x1E0
+ int timestamp; // pts, 90000 Hz counter based
+} vo_mpegpes_t;
+
+const char *vo_format_name(int format);
+
+/**
+ * Calculates the scale shifts for the chroma planes for planar YUV
+ *
+ * \param component_bits bits per component
+ * \return bits-per-pixel for format if successful (i.e. format is 3 or 4-planes planar YUV), 0 otherwise
+ */
+int mp_get_chroma_shift(int format, int *x_shift, int *y_shift, int *component_bits);
+
+struct mp_imgfmt_entry {
+ const char *name;
+ unsigned int fmt;
+};
+
+extern struct mp_imgfmt_entry mp_imgfmt_list[];
+
+unsigned int mp_imgfmt_from_name(bstr name, bool allow_hwaccel);
+const char *mp_imgfmt_to_name(unsigned int fmt);
+
+#endif /* MPLAYER_IMG_FORMAT_H */
diff --git a/video/memcpy_pic.h b/video/memcpy_pic.h
new file mode 100644
index 0000000000..c2cd79314f
--- /dev/null
+++ b/video/memcpy_pic.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MPLAYER_FASTMEMCPY_H
+#define MPLAYER_FASTMEMCPY_H
+
+#include "config.h"
+#include <inttypes.h>
+#include <string.h>
+#include <stddef.h>
+
+#define memcpy_pic(d, s, b, h, ds, ss) memcpy_pic2(d, s, b, h, ds, ss, 0)
+#define my_memcpy_pic(d, s, b, h, ds, ss) memcpy_pic2(d, s, b, h, ds, ss, 1)
+
+/**
+ * \param limit2width always skip data between end of line and start of next
+ * instead of copying the full block when strides are the same
+ */
+static inline void * memcpy_pic2(void * dst, const void * src,
+ int bytesPerLine, int height,
+ int dstStride, int srcStride, int limit2width)
+{
+ int i;
+ void *retval=dst;
+
+ if(!limit2width && dstStride == srcStride)
+ {
+ if (srcStride < 0) {
+ src = (uint8_t*)src + (height-1)*srcStride;
+ dst = (uint8_t*)dst + (height-1)*dstStride;
+ srcStride = -srcStride;
+ }
+
+ memcpy(dst, src, srcStride*height);
+ }
+ else
+ {
+ for(i=0; i<height; i++)
+ {
+ memcpy(dst, src, bytesPerLine);
+ src = (uint8_t*)src + srcStride;
+ dst = (uint8_t*)dst + dstStride;
+ }
+ }
+
+ return retval;
+}
+
+static inline void memset_pic(void *dst, int fill, int bytesPerLine, int height,
+ int stride)
+{
+ if (bytesPerLine == stride) {
+ memset(dst, fill, stride * height);
+ } else {
+ for (int i = 0; i < height; i++) {
+ memset(dst, fill, bytesPerLine);
+ dst = (uint8_t *)dst + stride;
+ }
+ }
+}
+
+#endif /* MPLAYER_FASTMEMCPY_H */
diff --git a/video/mp_image.c b/video/mp_image.c
new file mode 100644
index 0000000000..c0227e4b1d
--- /dev/null
+++ b/video/mp_image.c
@@ -0,0 +1,280 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "talloc.h"
+
+#include "libmpcodecs/img_format.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/sws_utils.h"
+
+#include "libvo/fastmemcpy.h"
+#include "libavutil/mem.h"
+#include "libavutil/common.h"
+
+void mp_image_alloc_planes(mp_image_t *mpi) {
+ if (mpi->imgfmt == IMGFMT_BGRA) {
+ mpi->stride[0]=FFALIGN(mpi->width*4,SWS_MIN_BYTE_ALIGN);
+ mpi->planes[0]=av_malloc(mpi->stride[0]*mpi->height);
+ mpi->flags|=MP_IMGFLAG_ALLOCATED;
+ return;
+ }
+ if (mpi->imgfmt == IMGFMT_444P16 || mpi->imgfmt == IMGFMT_444P) {
+ int bp = mpi->imgfmt == IMGFMT_444P16 ? 2 : 1;
+ mpi->stride[0]=FFALIGN(mpi->width*bp,SWS_MIN_BYTE_ALIGN);
+ mpi->stride[1]=mpi->stride[2]=mpi->stride[0];
+ int imgsize = mpi->stride[0] * mpi->height;
+ mpi->planes[0]=av_malloc(imgsize*3);
+ mpi->planes[1]=mpi->planes[0]+imgsize;
+ mpi->planes[2]=mpi->planes[1]+imgsize;
+ mpi->flags|=MP_IMGFLAG_ALLOCATED;
+ return;
+ }
+ // IF09 - allocate space for 4. plane delta info - unused
+ if (mpi->imgfmt == IMGFMT_IF09) {
+ mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8+
+ mpi->chroma_width*mpi->chroma_height);
+ } else
+ mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8);
+ if (!mpi->planes[0])
+ abort(); //out of memory
+ if (mpi->flags&MP_IMGFLAG_PLANAR) {
+ // FIXME this code only supports same bpp for all planes, and bpp divisible
+ // by 8. Currently the case for all planar formats.
+ int bpp = MP_IMAGE_PLANAR_BITS_PER_PIXEL_ON_PLANE(mpi, 0) / 8;
+ // YV12/I420/YVU9/IF09. feel free to add other planar formats here...
+ mpi->stride[0]=mpi->stride[3]=bpp*mpi->width;
+ if(mpi->num_planes > 2){
+ mpi->stride[1]=mpi->stride[2]=bpp*mpi->chroma_width;
+ if(mpi->flags&MP_IMGFLAG_SWAPPED){
+ // I420/IYUV (Y,U,V)
+ mpi->planes[1]=mpi->planes[0]+mpi->stride[0]*mpi->height;
+ mpi->planes[2]=mpi->planes[1]+mpi->stride[1]*mpi->chroma_height;
+ if (mpi->num_planes > 3)
+ mpi->planes[3]=mpi->planes[2]+mpi->stride[2]*mpi->chroma_height;
+ } else {
+ // YV12,YVU9,IF09 (Y,V,U)
+ mpi->planes[2]=mpi->planes[0]+mpi->stride[0]*mpi->height;
+ mpi->planes[1]=mpi->planes[2]+mpi->stride[1]*mpi->chroma_height;
+ if (mpi->num_planes > 3)
+ mpi->planes[3]=mpi->planes[1]+mpi->stride[1]*mpi->chroma_height;
+ }
+ } else {
+ // NV12/NV21
+ mpi->stride[1]=mpi->chroma_width;
+ mpi->planes[1]=mpi->planes[0]+mpi->stride[0]*mpi->height;
+ }
+ } else {
+ mpi->stride[0]=mpi->width*mpi->bpp/8;
+ if (mpi->flags & MP_IMGFLAG_RGB_PALETTE)
+ mpi->planes[1] = av_malloc(1024);
+ }
+ mpi->flags|=MP_IMGFLAG_ALLOCATED;
+}
+
+mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt) {
+ mp_image_t* mpi = new_mp_image(w,h);
+
+ mp_image_setfmt(mpi,fmt);
+ mp_image_alloc_planes(mpi);
+
+ return mpi;
+}
+
+void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) {
+ if(mpi->flags&MP_IMGFLAG_PLANAR){
+ memcpy_pic(dmpi->planes[0],mpi->planes[0], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 0), mpi->h,
+ dmpi->stride[0],mpi->stride[0]);
+ memcpy_pic(dmpi->planes[1],mpi->planes[1], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 1), mpi->chroma_height,
+ dmpi->stride[1],mpi->stride[1]);
+ memcpy_pic(dmpi->planes[2], mpi->planes[2], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 2), mpi->chroma_height,
+ dmpi->stride[2],mpi->stride[2]);
+ } else {
+ memcpy_pic(dmpi->planes[0],mpi->planes[0],
+ MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 0), mpi->h,
+ dmpi->stride[0],mpi->stride[0]);
+ }
+}
+
+void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){
+ mpi->flags&=~(MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV|MP_IMGFLAG_SWAPPED);
+ mpi->imgfmt=out_fmt;
+ // compressed formats
+ if(IMGFMT_IS_HWACCEL(out_fmt)){
+ mpi->bpp=0;
+ return;
+ }
+ mpi->num_planes=1;
+ if (IMGFMT_IS_RGB(out_fmt)) {
+ if (IMGFMT_RGB_DEPTH(out_fmt) < 8 && !(out_fmt&128))
+ mpi->bpp = IMGFMT_RGB_DEPTH(out_fmt);
+ else
+ mpi->bpp=(IMGFMT_RGB_DEPTH(out_fmt)+7)&(~7);
+ return;
+ }
+ if (IMGFMT_IS_BGR(out_fmt)) {
+ if (IMGFMT_BGR_DEPTH(out_fmt) < 8 && !(out_fmt&128))
+ mpi->bpp = IMGFMT_BGR_DEPTH(out_fmt);
+ else
+ mpi->bpp=(IMGFMT_BGR_DEPTH(out_fmt)+7)&(~7);
+ mpi->flags|=MP_IMGFLAG_SWAPPED;
+ return;
+ }
+ switch (out_fmt) {
+ case IMGFMT_BGR0:
+ mpi->bpp = 32;
+ return;
+ }
+ mpi->num_planes=3;
+ if (out_fmt == IMGFMT_GBRP) {
+ mpi->bpp=24;
+ mpi->flags|=MP_IMGFLAG_PLANAR;
+ return;
+ }
+ mpi->flags|=MP_IMGFLAG_YUV;
+ if (mp_get_chroma_shift(out_fmt, NULL, NULL, NULL)) {
+ mpi->flags|=MP_IMGFLAG_PLANAR;
+ mpi->bpp = mp_get_chroma_shift(out_fmt, &mpi->chroma_x_shift, &mpi->chroma_y_shift, NULL);
+ mpi->chroma_width = mpi->width >> mpi->chroma_x_shift;
+ mpi->chroma_height = mpi->height >> mpi->chroma_y_shift;
+ }
+ switch(out_fmt){
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ mpi->flags|=MP_IMGFLAG_SWAPPED;
+ case IMGFMT_YV12:
+ return;
+ case IMGFMT_420A:
+ case IMGFMT_IF09:
+ mpi->num_planes=4;
+ case IMGFMT_YVU9:
+ case IMGFMT_444P:
+ case IMGFMT_422P:
+ case IMGFMT_411P:
+ case IMGFMT_440P:
+ case IMGFMT_444P16_LE:
+ case IMGFMT_444P16_BE:
+ case IMGFMT_444P10_LE:
+ case IMGFMT_444P10_BE:
+ case IMGFMT_444P9_LE:
+ case IMGFMT_444P9_BE:
+ case IMGFMT_422P16_LE:
+ case IMGFMT_422P16_BE:
+ case IMGFMT_422P10_LE:
+ case IMGFMT_422P10_BE:
+ case IMGFMT_422P9_LE:
+ case IMGFMT_422P9_BE:
+ case IMGFMT_420P16_LE:
+ case IMGFMT_420P16_BE:
+ case IMGFMT_420P10_LE:
+ case IMGFMT_420P10_BE:
+ case IMGFMT_420P9_LE:
+ case IMGFMT_420P9_BE:
+ return;
+ case IMGFMT_Y800:
+ case IMGFMT_Y8:
+ /* they're planar ones, but for easier handling use them as packed */
+ mpi->flags&=~MP_IMGFLAG_PLANAR;
+ mpi->num_planes=1;
+ return;
+ case IMGFMT_UYVY:
+ mpi->flags|=MP_IMGFLAG_SWAPPED;
+ case IMGFMT_YUY2:
+ mpi->chroma_x_shift = 1;
+ mpi->chroma_y_shift = 1;
+ mpi->chroma_width=(mpi->width>>1);
+ mpi->chroma_height=(mpi->height>>1);
+ mpi->bpp=16;
+ mpi->num_planes=1;
+ return;
+ case IMGFMT_NV12:
+ mpi->flags|=MP_IMGFLAG_SWAPPED;
+ case IMGFMT_NV21:
+ mpi->flags|=MP_IMGFLAG_PLANAR;
+ mpi->bpp=12;
+ mpi->num_planes=2;
+ mpi->chroma_width=(mpi->width>>0);
+ mpi->chroma_height=(mpi->height>>1);
+ mpi->chroma_x_shift=0;
+ mpi->chroma_y_shift=1;
+ return;
+ }
+ mp_msg(MSGT_DECVIDEO,MSGL_WARN,"mp_image: unknown out_fmt: 0x%X\n",out_fmt);
+ mpi->bpp=0;
+}
+
+static int mp_image_destructor(void *ptr)
+{
+ mp_image_t *mpi = ptr;
+
+ if(mpi->flags&MP_IMGFLAG_ALLOCATED){
+ /* because we allocate the whole image at once */
+ av_free(mpi->planes[0]);
+ if (mpi->flags & MP_IMGFLAG_RGB_PALETTE)
+ av_free(mpi->planes[1]);
+ }
+
+ return 0;
+}
+
+mp_image_t* new_mp_image(int w,int h){
+ mp_image_t* mpi = talloc_zero(NULL, mp_image_t);
+ talloc_set_destructor(mpi, mp_image_destructor);
+ mpi->width=mpi->w=w;
+ mpi->height=mpi->h=h;
+ return mpi;
+}
+
+void free_mp_image(mp_image_t* mpi){
+ talloc_free(mpi);
+}
+
+enum mp_csp mp_image_csp(struct mp_image *img)
+{
+ if (img->colorspace != MP_CSP_AUTO)
+ return img->colorspace;
+ return (img->flags & MP_IMGFLAG_YUV) ? MP_CSP_BT_601 : MP_CSP_RGB;
+}
+
+enum mp_csp_levels mp_image_levels(struct mp_image *img)
+{
+ if (img->levels != MP_CSP_LEVELS_AUTO)
+ return img->levels;
+ return (img->flags & MP_IMGFLAG_YUV) ? MP_CSP_LEVELS_TV : MP_CSP_LEVELS_PC;
+}
+
+void mp_image_set_colorspace_details(struct mp_image *image,
+ struct mp_csp_details *csp)
+{
+ if (image->flags & MP_IMGFLAG_YUV) {
+ image->colorspace = csp->format;
+ if (image->colorspace == MP_CSP_AUTO)
+ image->colorspace = MP_CSP_BT_601;
+ image->levels = csp->levels_in;
+ if (image->levels == MP_CSP_LEVELS_AUTO)
+ image->levels = MP_CSP_LEVELS_TV;
+ } else {
+ image->colorspace = MP_CSP_RGB;
+ image->levels = MP_CSP_LEVELS_PC;
+ }
+}
diff --git a/video/mp_image.h b/video/mp_image.h
new file mode 100644
index 0000000000..f2d149bc9d
--- /dev/null
+++ b/video/mp_image.h
@@ -0,0 +1,162 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_MP_IMAGE_H
+#define MPLAYER_MP_IMAGE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "mp_msg.h"
+#include "libvo/csputils.h"
+
+//--------- codec's requirements (filled by the codec/vf) ---------
+
+//--- buffer content restrictions:
+// set if buffer content shouldn't be modified:
+#define MP_IMGFLAG_PRESERVE 0x01
+// set if buffer content will be READ.
+// This can be e.g. for next frame's MC: (I/P mpeg frames) -
+// then in combination with MP_IMGFLAG_PRESERVE - or it
+// can be because a video filter or codec will read a significant
+// amount of data while processing that frame (e.g. blending something
+// onto the frame, MV based intra prediction).
+// A frame marked like this should not be placed in to uncachable
+// video RAM for example.
+#define MP_IMGFLAG_READABLE 0x02
+
+//--- buffer width/stride/plane restrictions: (used for direct rendering)
+// stride _have_to_ be aligned to MB boundary: [for DR restrictions]
+#define MP_IMGFLAG_ACCEPT_ALIGNED_STRIDE 0x4
+// stride should be aligned to MB boundary: [for buffer allocation]
+#define MP_IMGFLAG_PREFER_ALIGNED_STRIDE 0x8
+// codec accept any stride (>=width):
+#define MP_IMGFLAG_ACCEPT_STRIDE 0x10
+// codec accept any width (width*bpp=stride -> stride%bpp==0) (>=width):
+#define MP_IMGFLAG_ACCEPT_WIDTH 0x20
+//--- for planar formats only:
+// uses only stride[0], and stride[1]=stride[2]=stride[0]>>mpi->chroma_x_shift
+#define MP_IMGFLAG_COMMON_STRIDE 0x40
+// uses only planes[0], and calculates planes[1,2] from width,height,imgfmt
+#define MP_IMGFLAG_COMMON_PLANE 0x80
+
+#define MP_IMGFLAGMASK_RESTRICTIONS 0xFF
+
+//--------- color info (filled by mp_image_setfmt() ) -----------
+// set if number of planes > 1
+#define MP_IMGFLAG_PLANAR 0x100
+// set if it's YUV colorspace
+#define MP_IMGFLAG_YUV 0x200
+// set if it's swapped (BGR or YVU) plane/byteorder
+#define MP_IMGFLAG_SWAPPED 0x400
+// set if you want memory for palette allocated and managed by vf_get_image etc.
+#define MP_IMGFLAG_RGB_PALETTE 0x800
+
+#define MP_IMGFLAGMASK_COLORS 0xF00
+
+// codec uses drawing/rendering callbacks (draw_slice()-like thing, DR method 2)
+// [the codec will set this flag if it supports callbacks, and the vo _may_
+// clear it in get_image() if draw_slice() not implemented]
+#define MP_IMGFLAG_DRAW_CALLBACK 0x1000
+// set if it's in video buffer/memory: [set by vo/vf's get_image() !!!]
+#define MP_IMGFLAG_DIRECT 0x2000
+// set if buffer is allocated (used in destination images):
+#define MP_IMGFLAG_ALLOCATED 0x4000
+
+// buffer type was printed (do NOT set this flag - it's for INTERNAL USE!!!)
+#define MP_IMGFLAG_TYPE_DISPLAYED 0x8000
+
+// codec doesn't support any form of direct rendering - it has own buffer
+// allocation. so we just export its buffer pointers:
+#define MP_IMGTYPE_EXPORT 0
+// codec requires a static WO buffer, but it does only partial updates later:
+#define MP_IMGTYPE_STATIC 1
+// codec just needs some WO memory, where it writes/copies the whole frame to:
+#define MP_IMGTYPE_TEMP 2
+// I+P type, requires 2+ independent static R/W buffers
+#define MP_IMGTYPE_IP 3
+// I+P+B type, requires 2+ independent static R/W and 1+ temp WO buffers
+#define MP_IMGTYPE_IPB 4
+// Upper 16 bits give desired buffer number, -1 means get next available
+#define MP_IMGTYPE_NUMBERED 5
+
+#define MP_MAX_PLANES 4
+
+#define MP_IMGFIELD_ORDERED 0x01
+#define MP_IMGFIELD_TOP_FIRST 0x02
+#define MP_IMGFIELD_REPEAT_FIRST 0x04
+#define MP_IMGFIELD_TOP 0x08
+#define MP_IMGFIELD_BOTTOM 0x10
+#define MP_IMGFIELD_INTERLACED 0x20
+
+typedef struct mp_image {
+ unsigned int flags;
+ unsigned char type;
+ int number;
+ unsigned char bpp; // bits/pixel. NOT depth! for RGB it will be n*8
+ unsigned int imgfmt;
+ int width,height; // internal to vf.c, do not use (stored dimensions)
+ int w,h; // visible dimensions
+ int display_w,display_h; // if set (!= 0), anamorphic size
+ uint8_t *planes[MP_MAX_PLANES];
+ int stride[MP_MAX_PLANES];
+ char * qscale;
+ int qstride;
+ int pict_type; // 0->unknown, 1->I, 2->P, 3->B
+ int fields;
+ int qscale_type; // 0->mpeg1/4/h263, 1->mpeg2
+ int num_planes;
+ /* these are only used by planar formats Y,U(Cb),V(Cr) */
+ int chroma_width;
+ int chroma_height;
+ int chroma_x_shift; // horizontal
+ int chroma_y_shift; // vertical
+ enum mp_csp colorspace;
+ enum mp_csp_levels levels;
+ int usage_count;
+ /* for private use by filter or vo driver (to store buffer id or dmpi) */
+ void* priv;
+} mp_image_t;
+
+void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt);
+mp_image_t* new_mp_image(int w,int h);
+void free_mp_image(mp_image_t* mpi);
+
+mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt);
+void mp_image_alloc_planes(mp_image_t *mpi);
+void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi);
+
+enum mp_csp mp_image_csp(struct mp_image *img);
+enum mp_csp_levels mp_image_levels(struct mp_image *img);
+
+struct mp_csp_details;
+void mp_image_set_colorspace_details(struct mp_image *image,
+ struct mp_csp_details *csp);
+
+// this macro requires img_format.h to be included too:
+#define MP_IMAGE_PLANAR_BITS_PER_PIXEL_ON_PLANE(mpi, p) \
+ (IMGFMT_IS_YUVP16((mpi)->imgfmt) ? 16 : 8)
+#define MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(mpi, p) \
+ (((mpi)->flags & MP_IMGFLAG_PLANAR) \
+ ? MP_IMAGE_PLANAR_BITS_PER_PIXEL_ON_PLANE(mpi, p) \
+ : (mpi)->bpp)
+#define MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, p) \
+ ((MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(mpi, p) * ((mpi)->w >> (p ? mpi->chroma_x_shift : 0)) + 7) / 8)
+
+#endif /* MPLAYER_MP_IMAGE_H */
diff --git a/video/out/aspect.c b/video/out/aspect.c
new file mode 100644
index 0000000000..f3cd00a5e5
--- /dev/null
+++ b/video/out/aspect.c
@@ -0,0 +1,148 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Stuff for correct aspect scaling. */
+#include "aspect.h"
+#include "geometry.h"
+#include "video_out.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "video_out.h"
+
+void aspect_save_videores(struct vo *vo, int w, int h, int d_w, int d_h)
+{
+ vo->aspdat.orgw = w;
+ vo->aspdat.orgh = h;
+ vo->aspdat.prew = d_w;
+ vo->aspdat.preh = d_h;
+ vo->aspdat.par = (double)d_w / d_h * h / w;
+}
+
+void aspect_save_screenres(struct vo *vo, int scrw, int scrh)
+{
+ mp_msg(MSGT_VO, MSGL_DBG2, "aspect_save_screenres %dx%d\n", scrw, scrh);
+ struct MPOpts *opts = vo->opts;
+ if (scrw <= 0 && scrh <= 0)
+ scrw = 1024;
+ if (scrh <= 0)
+ scrh = (scrw * 3 + 3) / 4;
+ if (scrw <= 0)
+ scrw = (scrh * 4 + 2) / 3;
+ vo->aspdat.scrw = scrw;
+ vo->aspdat.scrh = scrh;
+ if (opts->force_monitor_aspect)
+ vo->monitor_par = opts->force_monitor_aspect * scrh / scrw;
+ else
+ vo->monitor_par = 1.0 / opts->monitor_pixel_aspect;
+}
+
+/* aspect is called with the source resolution and the
+ * resolution, that the scaled image should fit into
+ */
+
+void aspect_fit(struct vo *vo, int *srcw, int *srch, int fitw, int fith)
+{
+ struct aspect_data *aspdat = &vo->aspdat;
+ float pixelaspect = vo->monitor_par;
+
+ mp_msg(MSGT_VO, MSGL_DBG2, "aspect(0) fitin: %dx%d monitor_par: %.2f\n",
+ fitw, fith, vo->monitor_par);
+ *srcw = fitw;
+ *srch = (float)fitw / aspdat->prew * aspdat->preh / pixelaspect;
+ *srch += *srch % 2; // round
+ mp_msg(MSGT_VO, MSGL_DBG2, "aspect(1) wh: %dx%d (org: %dx%d)\n",
+ *srcw, *srch, aspdat->prew, aspdat->preh);
+ if (*srch > fith || *srch < aspdat->orgh) {
+ int tmpw = (float)fith / aspdat->preh * aspdat->prew * pixelaspect;
+ tmpw += tmpw % 2; // round
+ if (tmpw <= fitw) {
+ *srch = fith;
+ *srcw = tmpw;
+ } else if (*srch > fith) {
+ mp_tmsg(MSGT_VO, MSGL_WARN,
+ "[ASPECT] Warning: No suitable new res found!\n");
+ }
+ }
+ aspdat->asp = *srcw / (float)*srch;
+ mp_msg(MSGT_VO, MSGL_DBG2, "aspect(2) wh: %dx%d (org: %dx%d)\n",
+ *srcw, *srch, aspdat->prew, aspdat->preh);
+}
+
+static void get_max_dims(struct vo *vo, int *w, int *h, int zoom)
+{
+ struct aspect_data *aspdat = &vo->aspdat;
+ *w = zoom ? aspdat->scrw : aspdat->prew;
+ *h = zoom ? aspdat->scrh : aspdat->preh;
+ if (zoom && WinID >= 0)
+ zoom = A_WINZOOM;
+ if (zoom == A_WINZOOM) {
+ *w = vo->dwidth;
+ *h = vo->dheight;
+ }
+}
+
+void aspect(struct vo *vo, int *srcw, int *srch, int zoom)
+{
+ int fitw;
+ int fith;
+ get_max_dims(vo, &fitw, &fith, zoom);
+ if (!zoom && geometry_wh_changed) {
+ mp_msg(MSGT_VO, MSGL_DBG2, "aspect(0) no aspect forced!\n");
+ return; // the user doesn't want to fix aspect
+ }
+ aspect_fit(vo, srcw, srch, fitw, fith);
+}
+
+void panscan_init(struct vo *vo)
+{
+ vo->panscan_x = 0;
+ vo->panscan_y = 0;
+ vo->panscan_amount = 0.0f;
+}
+
+static void panscan_calc_internal(struct vo *vo, int zoom)
+{
+ int fwidth, fheight;
+ int vo_panscan_area;
+ int max_w, max_h;
+ get_max_dims(vo, &max_w, &max_h, zoom);
+ struct MPOpts *opts = vo->opts;
+
+ if (opts->vo_panscanrange > 0) {
+ aspect(vo, &fwidth, &fheight, zoom);
+ vo_panscan_area = max_h - fheight;
+ if (!vo_panscan_area)
+ vo_panscan_area = max_w - fwidth;
+ vo_panscan_area *= opts->vo_panscanrange;
+ } else
+ vo_panscan_area = -opts->vo_panscanrange * max_h;
+
+ vo->panscan_amount = vo_fs || zoom == A_WINZOOM ? vo_panscan : 0;
+ vo->panscan_x = vo_panscan_area * vo->panscan_amount * vo->aspdat.asp;
+ vo->panscan_y = vo_panscan_area * vo->panscan_amount;
+}
+
+/**
+ * vos that set vo_dwidth and v_dheight correctly should call this to update
+ * vo_panscan_x and vo_panscan_y
+ */
+void panscan_calc_windowed(struct vo *vo)
+{
+ panscan_calc_internal(vo, A_WINZOOM);
+}
diff --git a/video/out/aspect.h b/video/out/aspect.h
new file mode 100644
index 0000000000..c5247421d2
--- /dev/null
+++ b/video/out/aspect.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_ASPECT_H
+#define MPLAYER_ASPECT_H
+/* Stuff for correct aspect scaling. */
+
+struct vo;
+void panscan_init(struct vo *vo);
+void panscan_calc_windowed(struct vo *vo);
+
+void aspect_save_videores(struct vo *vo, int w, int h, int d_w, int d_h);
+void aspect_save_screenres(struct vo *vo, int scrw, int scrh);
+
+#define A_WINZOOM 2 ///< zoom to fill window size
+#define A_ZOOM 1
+#define A_NOZOOM 0
+
+void aspect(struct vo *vo, int *srcw, int *srch, int zoom);
+void aspect_fit(struct vo *vo, int *srcw, int *srch, int fitw, int fith);
+
+#endif /* MPLAYER_ASPECT_H */
diff --git a/video/out/bitmap_packer.c b/video/out/bitmap_packer.c
new file mode 100644
index 0000000000..603a6ce410
--- /dev/null
+++ b/video/out/bitmap_packer.c
@@ -0,0 +1,227 @@
+/*
+ * Calculate how to pack bitmap rectangles into a larger surface
+ *
+ * Copyright 2009, 2012 Uoti Urpala
+ *
+ * This file is part of mplayer2.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <libavutil/common.h>
+
+#include "talloc.h"
+#include "bitmap_packer.h"
+#include "mp_msg.h"
+#include "mpcommon.h"
+#include "sub/dec_sub.h"
+#include "fastmemcpy.h"
+
+#define IS_POWER_OF_2(x) (((x) > 0) && !(((x) - 1) & (x)))
+
+void packer_reset(struct bitmap_packer *packer)
+{
+ struct bitmap_packer old = *packer;
+ *packer = (struct bitmap_packer) {
+ .w_max = old.w_max,
+ .h_max = old.h_max,
+ };
+ talloc_free_children(packer);
+}
+
+void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2])
+{
+ out_bb[0] = (struct pos) {0};
+ out_bb[1] = (struct pos) {
+ FFMIN(packer->used_width + packer->padding, packer->w),
+ FFMIN(packer->used_height + packer->padding, packer->h),
+ };
+}
+
+#define HEIGHT_SORT_BITS 4
+static int size_index(int s)
+{
+ int n = av_log2_16bit(s);
+ return (n << HEIGHT_SORT_BITS)
+ + (- 1 - (s << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
+}
+
+/* Pack the given rectangles into an area of size w * h.
+ * The size of each rectangle is read from in[i].x / in[i].y.
+ * The height of each rectangle must be less than 65536.
+ * 'scratch' must point to work memory for num_rects+16 ints.
+ * The packed position for rectangle number i is set in out[i].
+ * Return 0 on success, -1 if the rectangles did not fit in w*h.
+ *
+ * The rectangles are placed in rows in order approximately sorted by
+ * height (the approximate sorting is simpler than a full one would be,
+ * and allows the algorithm to work in linear time). Additionally, to
+ * reduce wasted space when there are a few tall rectangles, empty
+ * lower-right parts of rows are filled recursively when the size of
+ * rectangles in the row drops past a power-of-two threshold. So if a
+ * row starts with rectangles of size 3x50, 10x40 and 5x20 then the
+ * free rectangle with corners (13, 20)-(w, 50) is filled recursively.
+ */
+static int pack_rectangles(struct pos *in, struct pos *out, int num_rects,
+ int w, int h, int *scratch, int *used_width)
+{
+ int bins[16 << HEIGHT_SORT_BITS];
+ int sizes[16 << HEIGHT_SORT_BITS] = { 0 };
+ for (int i = 0; i < num_rects; i++)
+ sizes[size_index(in[i].y)]++;
+ int idx = 0;
+ for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
+ for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
+ bins[i + j] = idx;
+ idx += sizes[i + j];
+ }
+ scratch[idx++] = -1;
+ }
+ for (int i = 0; i < num_rects; i++)
+ scratch[bins[size_index(in[i].y)]++] = i;
+ for (int i = 0; i < 16; i++)
+ bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
+ struct {
+ int size, x, bottom;
+ } stack[16] = {{15, 0, h}}, s = {};
+ int stackpos = 1;
+ int y;
+ while (stackpos) {
+ y = s.bottom;
+ s = stack[--stackpos];
+ s.size++;
+ while (s.size--) {
+ int maxy = -1;
+ int obj;
+ while ((obj = scratch[bins[s.size]]) >= 0) {
+ int bottom = y + in[obj].y;
+ if (bottom > s.bottom)
+ break;
+ int right = s.x + in[obj].x;
+ if (right > w)
+ break;
+ bins[s.size]++;
+ out[obj] = (struct pos){s.x, y};
+ num_rects--;
+ if (maxy < 0)
+ stack[stackpos++] = s;
+ s.x = right;
+ maxy = FFMAX(maxy, bottom);
+ }
+ *used_width = FFMAX(*used_width, s.x);
+ if (maxy > 0)
+ s.bottom = maxy;
+ }
+ }
+ return num_rects ? -1 : y;
+}
+
+int packer_pack(struct bitmap_packer *packer)
+{
+ if (packer->count == 0)
+ return 0;
+ int w_orig = packer->w, h_orig = packer->h;
+ struct pos *in = packer->in;
+ int xmax = 0, ymax = 0;
+ for (int i = 0; i < packer->count; i++) {
+ if (in[i].x <= packer->padding || in[i].y <= packer->padding)
+ in[i] = (struct pos){0, 0};
+ if (in[i].x < 0 || in [i].x > 65535 || in[i].y < 0 || in[i].y > 65535) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "Invalid OSD / subtitle bitmap size\n");
+ abort();
+ }
+ xmax = FFMAX(xmax, in[i].x);
+ ymax = FFMAX(ymax, in[i].y);
+ }
+ xmax = FFMAX(0, xmax - packer->padding);
+ ymax = FFMAX(0, ymax - packer->padding);
+ if (xmax > packer->w)
+ packer->w = 1 << av_log2(xmax - 1) + 1;
+ if (ymax > packer->h)
+ packer->h = 1 << av_log2(ymax - 1) + 1;
+ while (1) {
+ int used_width = 0;
+ int y = pack_rectangles(in, packer->result, packer->count,
+ packer->w + packer->padding,
+ packer->h + packer->padding,
+ packer->scratch, &used_width);
+ if (y >= 0) {
+ // No padding at edges
+ packer->used_width = FFMIN(used_width, packer->w);
+ packer->used_height = FFMIN(y, packer->h);
+ assert(packer->w == 0 || IS_POWER_OF_2(packer->w));
+ assert(packer->h == 0 || IS_POWER_OF_2(packer->h));
+ return packer->w != w_orig || packer->h != h_orig;
+ }
+ if (packer->w <= packer->h && packer->w != packer->w_max)
+ packer->w = FFMIN(packer->w * 2, packer->w_max);
+ else if (packer->h != packer->h_max)
+ packer->h = FFMIN(packer->h * 2, packer->h_max);
+ else {
+ packer->w = w_orig;
+ packer->h = h_orig;
+ return -1;
+ }
+ }
+}
+
+void packer_set_size(struct bitmap_packer *packer, int size)
+{
+ packer->count = size;
+ if (size <= packer->asize)
+ return;
+ packer->asize = FFMAX(packer->asize * 2, size);
+ talloc_free(packer->result);
+ talloc_free(packer->scratch);
+ packer->in = talloc_realloc(packer, packer->in, struct pos, packer->asize);
+ packer->result = talloc_array_ptrtype(packer, packer->result,
+ packer->asize);
+ packer->scratch = talloc_array_ptrtype(packer, packer->scratch,
+ packer->asize + 16);
+}
+
+int packer_pack_from_subbitmaps(struct bitmap_packer *packer,
+ struct sub_bitmaps *b)
+{
+ packer->count = 0;
+ if (b->format == SUBBITMAP_EMPTY)
+ return 0;
+ packer_set_size(packer, b->num_parts);
+ int a = packer->padding;
+ for (int i = 0; i < b->num_parts; i++)
+ packer->in[i] = (struct pos){b->parts[i].w + a, b->parts[i].h + a};
+ return packer_pack(packer);
+}
+
+void packer_copy_subbitmaps(struct bitmap_packer *packer, struct sub_bitmaps *b,
+ void *data, int pixel_stride, int stride)
+{
+ assert(packer->count == b->num_parts);
+ if (packer->padding) {
+ struct pos bb[2];
+ packer_get_bb(packer, bb);
+ memset_pic(data, 0, bb[1].x * pixel_stride, bb[1].y, stride);
+ }
+ for (int n = 0; n < packer->count; n++) {
+ struct sub_bitmap *s = &b->parts[n];
+ struct pos p = packer->result[n];
+
+ void *pdata = (uint8_t *)data + p.y * stride + p.x * pixel_stride;
+ memcpy_pic(pdata, s->bitmap, s->w * pixel_stride, s->h,
+ stride, s->stride);
+ }
+}
diff --git a/video/out/bitmap_packer.h b/video/out/bitmap_packer.h
new file mode 100644
index 0000000000..b86c3ec4f9
--- /dev/null
+++ b/video/out/bitmap_packer.h
@@ -0,0 +1,68 @@
+#ifndef MPLAYER_PACK_RECTANGLES_H
+#define MPLAYER_PACK_RECTANGLES_H
+
+struct pos {
+ int x;
+ int y;
+};
+
+struct bitmap_packer {
+ int w;
+ int h;
+ int w_max;
+ int h_max;
+ int padding;
+ int count;
+ struct pos *in;
+ struct pos *result;
+ int used_width;
+ int used_height;
+
+ // internal
+ int *scratch;
+ int asize;
+};
+
+struct ass_image;
+struct sub_bitmaps;
+
+// Clear all internal state. Leave the following fields: w_max, h_max
+void packer_reset(struct bitmap_packer *packer);
+
+// Get the bounding box used for bitmap data (including padding).
+// The bounding box doesn't exceed (0,0)-(packer->w,packer->h).
+void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2]);
+
+/* Reallocate packer->in for at least to desired number of items.
+ * Also sets packer->count to the same value.
+ */
+void packer_set_size(struct bitmap_packer *packer, int size);
+
+/* To use this, set packer->count to number of rectangles, w_max and h_max
+ * to maximum output rectangle size, and w and h to start size (may be 0).
+ * Write input sizes in packer->in.
+ * Resulting packing will be written in packer->result.
+ * w and h will be increased if necessary for successful packing.
+ * There is a strong guarantee that w and h will be powers of 2 (or set to 0).
+ * Return value is -1 if packing failed because w and h were set to max
+ * values but that wasn't enough, 1 if w or h was increased, and 0 otherwise.
+ */
+int packer_pack(struct bitmap_packer *packer);
+
+/* Like above, but packer->count will be automatically set and
+ * packer->in will be reallocated if needed and filled from the
+ * given image list.
+ */
+int packer_pack_from_subbitmaps(struct bitmap_packer *packer,
+ struct sub_bitmaps *b);
+
+// Copy the (already packed) sub-bitmaps from b to the image in data.
+// data must point to an image that is at least (packer->w, packer->h) big.
+// The image has the given stride (bytes between (x, y) to (x, y + 1)), and the
+// pixel format used by both the sub-bitmaps and the image uses pixel_stride
+// bytes per pixel (bytes between (x, y) to (x + 1, y)).
+// If packer->padding is set, the padding borders are cleared with 0.
+void packer_copy_subbitmaps(struct bitmap_packer *packer, struct sub_bitmaps *b,
+ void *data, int pixel_stride, int stride);
+
+#endif
diff --git a/video/out/cocoa_common.h b/video/out/cocoa_common.h
new file mode 100644
index 0000000000..079e497441
--- /dev/null
+++ b/video/out/cocoa_common.h
@@ -0,0 +1,55 @@
+/*
+ * Cocoa OpenGL Backend
+ *
+ * This file is part of mplayer2.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MPLAYER_COCOA_COMMON_H
+#define MPLAYER_COCOA_COMMON_H
+
+#include "video_out.h"
+
+struct vo_cocoa_state;
+
+bool vo_cocoa_gui_running(void);
+void *vo_cocoa_glgetaddr(const char *s);
+
+int vo_cocoa_init(struct vo *vo);
+void vo_cocoa_uninit(struct vo *vo);
+
+void vo_cocoa_update_xinerama_info(struct vo *vo);
+
+int vo_cocoa_change_attributes(struct vo *vo);
+int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
+ uint32_t d_height, uint32_t flags,
+ int gl3profile);
+
+void vo_cocoa_swap_buffers(struct vo *vo);
+int vo_cocoa_check_events(struct vo *vo);
+void vo_cocoa_fullscreen(struct vo *vo);
+void vo_cocoa_ontop(struct vo *vo);
+void vo_cocoa_pause(struct vo *vo);
+void vo_cocoa_resume(struct vo *vo);
+
+// returns an int to conform to the gl extensions from other platforms
+int vo_cocoa_swap_interval(int enabled);
+
+void *vo_cocoa_cgl_context(struct vo *vo);
+void *vo_cocoa_cgl_pixel_format(struct vo *vo);
+
+int vo_cocoa_cgl_color_size(struct vo *vo);
+
+#endif /* MPLAYER_COCOA_COMMON_H */
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m
new file mode 100644
index 0000000000..337e0a32be
--- /dev/null
+++ b/video/out/cocoa_common.m
@@ -0,0 +1,865 @@
+/*
+ * Cocoa OpenGL Backend
+ *
+ * This file is part of mplayer2.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor
+#import <IOKit/pwr_mgt/IOPMLib.h>
+#include <dlfcn.h>
+
+#include "cocoa_common.h"
+
+#include "config.h"
+
+#include "options.h"
+#include "video_out.h"
+#include "aspect.h"
+
+#include "mp_fifo.h"
+#include "talloc.h"
+
+#include "input/input.h"
+#include "input/keycodes.h"
+#include "osx_common.h"
+#include "mp_msg.h"
+
+#ifndef NSOpenGLPFAOpenGLProfile
+#define NSOpenGLPFAOpenGLProfile 99
+#endif
+
+#ifndef NSOpenGLProfileVersionLegacy
+#define NSOpenGLProfileVersionLegacy 0x1000
+#endif
+
+#ifndef NSOpenGLProfileVersion3_2Core
+#define NSOpenGLProfileVersion3_2Core 0x3200
+#endif
+
+#define NSLeftAlternateKeyMask (0x000020 | NSAlternateKeyMask)
+#define NSRightAlternateKeyMask (0x000040 | NSAlternateKeyMask)
+
+// add methods not available on OSX versions prior to 10.7
+#ifndef MAC_OS_X_VERSION_10_7
+@interface NSView (IntroducedInLion)
+- (NSRect)convertRectToBacking:(NSRect)aRect;
+- (void)setWantsBestResolutionOpenGLSurface:(BOOL)aBool;
+@end
+#endif
+
+// add power management assertion not available on OSX versions prior to 10.7
+#ifndef kIOPMAssertionTypePreventUserIdleDisplaySleep
+#define kIOPMAssertionTypePreventUserIdleDisplaySleep \
+ CFSTR("PreventUserIdleDisplaySleep")
+#endif
+
+@interface GLMPlayerWindow : NSWindow <NSWindowDelegate> {
+ struct vo *_vo;
+}
+- (void)setVideoOutput:(struct vo *)vo;
+- (BOOL)canBecomeKeyWindow;
+- (BOOL)canBecomeMainWindow;
+- (void)fullscreen;
+- (void)mouseEvent:(NSEvent *)theEvent;
+- (void)mulSize:(float)multiplier;
+- (void)setContentSize:(NSSize)newSize keepCentered:(BOOL)keepCentered;
+@end
+
+@interface GLMPlayerOpenGLView : NSView
+@end
+
+struct vo_cocoa_state {
+ NSAutoreleasePool *pool;
+ GLMPlayerWindow *window;
+ NSOpenGLContext *glContext;
+ NSOpenGLPixelFormat *pixelFormat;
+
+ NSSize current_video_size;
+ NSSize previous_video_size;
+
+ NSRect screen_frame;
+ NSScreen *screen_handle;
+ NSArray *screen_array;
+
+ NSInteger windowed_mask;
+ NSInteger fullscreen_mask;
+
+ NSRect windowed_frame;
+
+ NSString *window_title;
+
+ NSInteger window_level;
+ NSInteger fullscreen_window_level;
+
+ int display_cursor;
+ int cursor_timer;
+ int cursor_autohide_delay;
+
+ bool did_resize;
+ bool out_fs_resize;
+
+ IOPMAssertionID power_mgmt_assertion;
+};
+
+static int _instances = 0;
+
+static void create_menu(void);
+
+static struct vo_cocoa_state *vo_cocoa_init_state(struct vo *vo)
+{
+ struct vo_cocoa_state *s = talloc_ptrtype(vo, s);
+ *s = (struct vo_cocoa_state){
+ .pool = [[NSAutoreleasePool alloc] init],
+ .did_resize = NO,
+ .current_video_size = {0,0},
+ .previous_video_size = {0,0},
+ .windowed_mask = NSTitledWindowMask|NSClosableWindowMask|
+ NSMiniaturizableWindowMask|NSResizableWindowMask,
+ .fullscreen_mask = NSBorderlessWindowMask,
+ .windowed_frame = {{0,0},{0,0}},
+ .out_fs_resize = NO,
+ .display_cursor = 1,
+ .cursor_autohide_delay = vo->opts->cursor_autohide_delay,
+ .power_mgmt_assertion = kIOPMNullAssertionID,
+ };
+ return s;
+}
+
+static bool supports_hidpi(NSView *view)
+{
+ SEL hdpi_selector = @selector(setWantsBestResolutionOpenGLSurface:);
+ return is_osx_version_at_least(10, 7, 0) && view &&
+ [view respondsToSelector:hdpi_selector];
+}
+
+bool vo_cocoa_gui_running(void)
+{
+ return _instances > 0;
+}
+
+void *vo_cocoa_glgetaddr(const char *s)
+{
+ void *ret = NULL;
+ void *handle = dlopen(
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL",
+ RTLD_LAZY | RTLD_LOCAL);
+ if (!handle)
+ return NULL;
+ ret = dlsym(handle, s);
+ dlclose(handle);
+ return ret;
+}
+
+static void enable_power_management(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ if (!s->power_mgmt_assertion) return;
+ IOPMAssertionRelease(s->power_mgmt_assertion);
+ s->power_mgmt_assertion = kIOPMNullAssertionID;
+}
+
+static void disable_power_management(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ if (s->power_mgmt_assertion) return;
+
+ CFStringRef assertion_type = kIOPMAssertionTypeNoDisplaySleep;
+ if (is_osx_version_at_least(10, 7, 0))
+ assertion_type = kIOPMAssertionTypePreventUserIdleDisplaySleep;
+
+ IOPMAssertionCreateWithName(assertion_type, kIOPMAssertionLevelOn,
+ CFSTR("org.mplayer2.power_mgmt"), &s->power_mgmt_assertion);
+}
+
+int vo_cocoa_init(struct vo *vo)
+{
+ vo->cocoa = vo_cocoa_init_state(vo);
+ _instances++;
+
+ NSApplicationLoad();
+ NSApp = [NSApplication sharedApplication];
+ [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
+ disable_power_management(vo);
+
+ return 1;
+}
+
+void vo_cocoa_uninit(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ enable_power_management(vo);
+ [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+
+ [s->window release];
+ s->window = nil;
+ [s->glContext release];
+ s->glContext = nil;
+ [s->pool release];
+ s->pool = nil;
+
+ _instances--;
+}
+
+void vo_cocoa_pause(struct vo *vo)
+{
+ enable_power_management(vo);
+}
+
+void vo_cocoa_resume(struct vo *vo)
+{
+ disable_power_management(vo);
+}
+
+static int current_screen_has_dock_or_menubar(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ NSRect f = s->screen_frame;
+ NSRect vf = [s->screen_handle visibleFrame];
+ return f.size.height > vf.size.height || f.size.width > vf.size.width;
+}
+
+static void update_screen_info(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ s->screen_array = [NSScreen screens];
+ if (xinerama_screen >= (int)[s->screen_array count]) {
+ mp_msg(MSGT_VO, MSGL_INFO, "[cocoa] Device ID %d does not exist, "
+ "falling back to main device\n", xinerama_screen);
+ xinerama_screen = -1;
+ }
+
+ if (xinerama_screen < 0) { // default behaviour
+ if (! (s->screen_handle = [s->window screen]) )
+ s->screen_handle = [s->screen_array objectAtIndex:0];
+ } else {
+ s->screen_handle = [s->screen_array objectAtIndex:(xinerama_screen)];
+ }
+
+ s->screen_frame = [s->screen_handle frame];
+}
+
+void vo_cocoa_update_xinerama_info(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ struct MPOpts *opts = vo->opts;
+
+ update_screen_info(vo);
+ aspect_save_screenres(vo, s->screen_frame.size.width,
+ s->screen_frame.size.height);
+ opts->vo_screenwidth = s->screen_frame.size.width;
+ opts->vo_screenheight = s->screen_frame.size.height;
+ xinerama_x = s->screen_frame.origin.x;
+ xinerama_y = s->screen_frame.origin.y;
+}
+
+int vo_cocoa_change_attributes(struct vo *vo)
+{
+ return 0;
+}
+
+static void resize_window(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ NSView *view = [s->window contentView];
+ NSRect frame;
+
+ if (supports_hidpi(view)) {
+ frame = [view convertRectToBacking: [view frame]];
+ } else {
+ frame = [view frame];
+ }
+
+ vo->dwidth = frame.size.width;
+ vo->dheight = frame.size.height;
+ [s->glContext update];
+}
+
+static void vo_set_level(struct vo *vo, int ontop)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ if (ontop) {
+ s->window_level = NSNormalWindowLevel + 1;
+ } else {
+ s->window_level = NSNormalWindowLevel;
+ }
+
+ if (!vo_fs)
+ [s->window setLevel:s->window_level];
+}
+
+void vo_cocoa_ontop(struct vo *vo)
+{
+ struct MPOpts *opts = vo->opts;
+ opts->vo_ontop = !opts->vo_ontop;
+ vo_set_level(vo, opts->vo_ontop);
+}
+
+static void update_state_sizes(struct vo_cocoa_state *s,
+ uint32_t d_width, uint32_t d_height)
+{
+ if (s->current_video_size.width > 0 || s->current_video_size.height > 0)
+ s->previous_video_size = s->current_video_size;
+ s->current_video_size = NSMakeSize(d_width, d_height);
+}
+
+static int create_window(struct vo *vo, uint32_t d_width, uint32_t d_height,
+ uint32_t flags, int gl3profile)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ struct MPOpts *opts = vo->opts;
+
+ const NSRect window_rect = NSMakeRect(0, 0, d_width, d_height);
+ const NSRect glview_rect = NSMakeRect(0, 0, 100, 100);
+
+ s->window =
+ [[GLMPlayerWindow alloc] initWithContentRect:window_rect
+ styleMask:s->windowed_mask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+
+ GLMPlayerOpenGLView *glView =
+ [[GLMPlayerOpenGLView alloc] initWithFrame:glview_rect];
+
+ // check for HiDPI support and enable it (available on 10.7 +)
+ if (supports_hidpi(glView))
+ [glView setWantsBestResolutionOpenGLSurface:YES];
+
+ int i = 0;
+ NSOpenGLPixelFormatAttribute attr[32];
+ if (is_osx_version_at_least(10, 7, 0)) {
+ attr[i++] = NSOpenGLPFAOpenGLProfile;
+ if (gl3profile) {
+ attr[i++] = NSOpenGLProfileVersion3_2Core;
+ } else {
+ attr[i++] = NSOpenGLProfileVersionLegacy;
+ }
+ } else if(gl3profile) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[cocoa] Invalid pixel format attribute "
+ "(GL3 is not supported on OSX versions prior to 10.7)\n");
+ return -1;
+ }
+ attr[i++] = NSOpenGLPFADoubleBuffer; // double buffered
+ attr[i] = (NSOpenGLPixelFormatAttribute)0;
+
+ s->pixelFormat =
+ [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease];
+ if (!s->pixelFormat) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[cocoa] Invalid pixel format attribute "
+ "(GL3 not supported?)\n");
+ return -1;
+ }
+ s->glContext =
+ [[NSOpenGLContext alloc] initWithFormat:s->pixelFormat
+ shareContext:nil];
+
+ create_menu();
+
+ [s->window setContentView:glView];
+ [glView release];
+ [s->window setAcceptsMouseMovedEvents:YES];
+ [s->glContext setView:glView];
+ [s->glContext makeCurrentContext];
+ [s->window setVideoOutput:vo];
+
+ [NSApp setDelegate:s->window];
+ [s->window setDelegate:s->window];
+ [s->window setContentSize:s->current_video_size];
+ [s->window setContentAspectRatio:s->current_video_size];
+ [s->window setFrameOrigin:NSMakePoint(vo->dx, vo->dy)];
+
+ if (flags & VOFLAG_HIDDEN) {
+ [s->window orderOut:nil];
+ } else {
+ [s->window makeKeyAndOrderFront:nil];
+ [NSApp activateIgnoringOtherApps:YES];
+ }
+
+ if (flags & VOFLAG_FULLSCREEN)
+ vo_cocoa_fullscreen(vo);
+
+ vo_set_level(vo, opts->vo_ontop);
+
+ return 0;
+}
+
+static void update_window(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+
+ if (s->current_video_size.width != s->previous_video_size.width ||
+ s->current_video_size.height != s->previous_video_size.height) {
+ if (vo_fs) {
+ // we will resize as soon as we get out of fullscreen
+ s->out_fs_resize = YES;
+ } else {
+ // only if we are not in fullscreen and the video size did
+ // change we resize the window and set a new aspect ratio
+ [s->window setContentSize:s->current_video_size
+ keepCentered:YES];
+ [s->window setContentAspectRatio:s->current_video_size];
+ }
+ }
+}
+
+int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
+ uint32_t d_height, uint32_t flags,
+ int gl3profile)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+
+ update_state_sizes(s, d_width, d_height);
+
+ if (!(s->window || s->glContext)) {
+ if (create_window(vo, d_width, d_height, flags, gl3profile) < 0)
+ return -1;
+ } else {
+ update_window(vo);
+ }
+
+ resize_window(vo);
+
+ if (s->window_title)
+ [s->window_title release];
+
+ s->window_title =
+ [[NSString alloc] initWithUTF8String:vo_get_window_title(vo)];
+ [s->window setTitle: s->window_title];
+
+ return 0;
+}
+
+void vo_cocoa_swap_buffers(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ [s->glContext flushBuffer];
+}
+
+static void vo_cocoa_display_cursor(struct vo *vo, int requested_state)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ if (requested_state) {
+ if (!vo_fs || s->cursor_autohide_delay > -2) {
+ s->display_cursor = requested_state;
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ }
+ } else {
+ if (s->cursor_autohide_delay != -1) {
+ s->display_cursor = requested_state;
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ }
+ }
+}
+
+int vo_cocoa_check_events(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ NSEvent *event;
+ int ms_time = (int) ([[NSProcessInfo processInfo] systemUptime] * 1000);
+
+ // automatically hide mouse cursor
+ if (vo_fs && s->display_cursor &&
+ (ms_time - s->cursor_timer >= s->cursor_autohide_delay)) {
+ vo_cocoa_display_cursor(vo, 0);
+ s->cursor_timer = ms_time;
+ }
+
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil
+ inMode:NSEventTrackingRunLoopMode dequeue:YES];
+ if (event == nil)
+ return 0;
+ [NSApp sendEvent:event];
+
+ if (s->did_resize) {
+ s->did_resize = NO;
+ resize_window(vo);
+ return VO_EVENT_RESIZE;
+ }
+ // Without SDL's bootstrap code (include SDL.h in mplayer.c),
+ // on Leopard, we have trouble to get the play window automatically focused
+ // when the app is actived. The Following code fix this problem.
+ if ([event type] == NSAppKitDefined
+ && [event subtype] == NSApplicationActivatedEventType) {
+ [s->window makeMainWindow];
+ [s->window makeKeyAndOrderFront:nil];
+ }
+ return 0;
+}
+
+void vo_cocoa_fullscreen(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ [s->window fullscreen];
+ resize_window(vo);
+}
+
+int vo_cocoa_swap_interval(int enabled)
+{
+ [[NSOpenGLContext currentContext] setValues:&enabled
+ forParameter:NSOpenGLCPSwapInterval];
+ return 0;
+}
+
+void *vo_cocoa_cgl_context(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ return [s->glContext CGLContextObj];
+}
+
+void *vo_cocoa_cgl_pixel_format(struct vo *vo)
+{
+ return CGLGetPixelFormat(vo_cocoa_cgl_context(vo));
+}
+
+int vo_cocoa_cgl_color_size(struct vo *vo)
+{
+ GLint value;
+ CGLDescribePixelFormat(vo_cocoa_cgl_pixel_format(vo), 0,
+ kCGLPFAColorSize, &value);
+ switch (value) {
+ case 32:
+ case 24:
+ return 8;
+ case 16:
+ return 5;
+ }
+
+ return 8;
+}
+
+static NSMenuItem *new_menu_item(NSMenu *parent_menu, NSString *title,
+ SEL action, NSString *key_equivalent)
+{
+ NSMenuItem *new_item =
+ [[NSMenuItem alloc] initWithTitle:title action:action
+ keyEquivalent:key_equivalent];
+ [parent_menu addItem:new_item];
+ return [new_item autorelease];
+}
+
+static NSMenuItem *new_main_menu_item(NSMenu *parent_menu, NSMenu *child_menu,
+ NSString *title)
+{
+ NSMenuItem *new_item =
+ [[NSMenuItem alloc] initWithTitle:title action:nil
+ keyEquivalent:@""];
+ [new_item setSubmenu:child_menu];
+ [parent_menu addItem:new_item];
+ return [new_item autorelease];
+}
+
+void create_menu()
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+ NSMenu *main_menu, *m_menu, *w_menu;
+ NSMenuItem *app_menu_item;
+
+ main_menu = [[NSMenu new] autorelease];
+ app_menu_item = [[NSMenuItem new] autorelease];
+ [main_menu addItem:app_menu_item];
+ [NSApp setMainMenu: main_menu];
+
+ m_menu = [[[NSMenu alloc] initWithTitle:@"Movie"] autorelease];
+ new_menu_item(m_menu, @"Half Size", @selector(halfSize), @"0");
+ new_menu_item(m_menu, @"Normal Size", @selector(normalSize), @"1");
+ new_menu_item(m_menu, @"Double Size", @selector(doubleSize), @"2");
+
+ new_main_menu_item(main_menu, m_menu, @"Movie");
+
+ w_menu = [[[NSMenu alloc] initWithTitle:@"Window"] autorelease];
+ new_menu_item(w_menu, @"Minimize", @selector(performMiniaturize:), @"m");
+ new_menu_item(w_menu, @"Zoom", @selector(performZoom:), @"z");
+
+ new_main_menu_item(main_menu, w_menu, @"Window");
+ [pool release];
+}
+
+@implementation GLMPlayerWindow
+- (void)setVideoOutput:(struct vo *)vo
+{
+ _vo = vo;
+}
+
+- (void)windowDidResize:(NSNotification *) notification
+{
+ if (_vo) {
+ struct vo_cocoa_state *s = _vo->cocoa;
+ s->did_resize = YES;
+ }
+}
+
+- (void)fullscreen
+{
+ struct vo_cocoa_state *s = _vo->cocoa;
+ if (!vo_fs) {
+ update_screen_info(_vo);
+ if (current_screen_has_dock_or_menubar(_vo))
+ [NSApp setPresentationOptions:NSApplicationPresentationHideDock|
+ NSApplicationPresentationHideMenuBar];
+ s->windowed_frame = [self frame];
+ [self setHasShadow:NO];
+ [self setStyleMask:s->fullscreen_mask];
+ [self setFrame:s->screen_frame display:YES animate:NO];
+ vo_fs = VO_TRUE;
+ vo_cocoa_display_cursor(_vo, 0);
+ [self setMovableByWindowBackground: NO];
+ } else {
+ [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+ [self setHasShadow:YES];
+ [self setStyleMask:s->windowed_mask];
+ [self setTitle:s->window_title];
+ [self setFrame:s->windowed_frame display:YES animate:NO];
+ if (s->out_fs_resize) {
+ [self setContentSize:s->current_video_size keepCentered:YES];
+ s->out_fs_resize = NO;
+ }
+ [self setContentAspectRatio:s->current_video_size];
+ vo_fs = VO_FALSE;
+ vo_cocoa_display_cursor(_vo, 1);
+ [self setMovableByWindowBackground: YES];
+ }
+}
+
+- (BOOL)canBecomeMainWindow { return YES; }
+- (BOOL)canBecomeKeyWindow { return YES; }
+- (BOOL)acceptsFirstResponder { return YES; }
+- (BOOL)becomeFirstResponder { return YES; }
+- (BOOL)resignFirstResponder { return YES; }
+- (BOOL)windowShouldClose:(id)sender
+{
+ mplayer_put_key(_vo->key_fifo, KEY_CLOSE_WIN);
+ // We have to wait for MPlayer to handle this,
+ // otherwise we are in trouble if the
+ // KEY_CLOSE_WIN handler is disabled
+ return NO;
+}
+
+- (BOOL)isMovableByWindowBackground
+{
+ // this is only valid as a starting value. it will be rewritten in the
+ // -fullscreen method.
+ return !vo_fs;
+}
+
+- (void)handleQuitEvent:(NSAppleEventDescriptor*)e
+ withReplyEvent:(NSAppleEventDescriptor*)r
+{
+ mplayer_put_key(_vo->key_fifo, KEY_CLOSE_WIN);
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ unsigned char charcode;
+ if (([theEvent modifierFlags] & NSRightAlternateKeyMask) ==
+ NSRightAlternateKeyMask)
+ charcode = *[[theEvent characters] UTF8String];
+ else
+ charcode = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
+
+ int key = convert_key([theEvent keyCode], charcode);
+
+ if (key > -1) {
+ if ([theEvent modifierFlags] & NSShiftKeyMask)
+ key |= KEY_MODIFIER_SHIFT;
+ if ([theEvent modifierFlags] & NSControlKeyMask)
+ key |= KEY_MODIFIER_CTRL;
+ if (([theEvent modifierFlags] & NSLeftAlternateKeyMask) ==
+ NSLeftAlternateKeyMask)
+ key |= KEY_MODIFIER_ALT;
+ if ([theEvent modifierFlags] & NSCommandKeyMask)
+ key |= KEY_MODIFIER_META;
+ mplayer_put_key(_vo->key_fifo, key);
+ }
+}
+
+- (void)mouseMoved: (NSEvent *) theEvent
+{
+ if (vo_fs)
+ vo_cocoa_display_cursor(_vo, 1);
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)otherMouseUp:(NSEvent *)theEvent
+{
+ [self mouseEvent: theEvent];
+}
+
+- (void)scrollWheel:(NSEvent *)theEvent
+{
+ if ([theEvent deltaY] > 0)
+ mplayer_put_key(_vo->key_fifo, MOUSE_BTN3);
+ else
+ mplayer_put_key(_vo->key_fifo, MOUSE_BTN4);
+}
+
+- (void)mouseEvent:(NSEvent *)theEvent
+{
+ if ([theEvent buttonNumber] >= 0 && [theEvent buttonNumber] <= 9) {
+ int buttonNumber = [theEvent buttonNumber];
+ // Fix to mplayer defined button order: left, middle, right
+ if (buttonNumber == 1) buttonNumber = 2;
+ else if (buttonNumber == 2) buttonNumber = 1;
+ switch ([theEvent type]) {
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ mplayer_put_key(_vo->key_fifo,
+ (MOUSE_BTN0 + buttonNumber) | MP_KEY_DOWN);
+ // Looks like Cocoa doesn't create MouseUp events when we are
+ // doing the second click in a double click. Put in the key_fifo
+ // the key that would be put from the MouseUp handling code.
+ if([theEvent clickCount] == 2)
+ mplayer_put_key(_vo->key_fifo, MOUSE_BTN0 + buttonNumber);
+ break;
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ case NSOtherMouseUp:
+ mplayer_put_key(_vo->key_fifo, MOUSE_BTN0 + buttonNumber);
+ break;
+ }
+ }
+}
+
+- (void)applicationWillBecomeActive:(NSNotification *)aNotification
+{
+ if (vo_fs && current_screen_has_dock_or_menubar(_vo)) {
+ [NSApp setPresentationOptions:NSApplicationPresentationHideDock|
+ NSApplicationPresentationHideMenuBar];
+ }
+}
+
+- (void)applicationWillResignActive:(NSNotification *)aNotification
+{
+ if (vo_fs) {
+ [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+ }
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification*)notification
+{
+ // Install an event handler so the Quit menu entry works
+ // The proper way using NSApp setDelegate: and
+ // applicationShouldTerminate: does not work,
+ // probably NSApplication never installs its handler.
+ [[NSAppleEventManager sharedAppleEventManager]
+ setEventHandler:self
+ andSelector:@selector(handleQuitEvent:withReplyEvent:)
+ forEventClass:kCoreEventClass
+ andEventID:kAEQuitApplication];
+}
+
+- (void)normalSize
+{
+ struct vo_cocoa_state *s = _vo->cocoa;
+ if (!vo_fs)
+ [self setContentSize:s->current_video_size keepCentered:YES];
+}
+
+- (void)halfSize { [self mulSize:0.5f];}
+
+- (void)doubleSize { [self mulSize:2.0f];}
+
+- (void)mulSize:(float)multiplier
+{
+ if (!vo_fs) {
+ struct vo_cocoa_state *s = _vo->cocoa;
+ NSSize size = [[self contentView] frame].size;
+ size.width = s->current_video_size.width * (multiplier);
+ size.height = s->current_video_size.height * (multiplier);
+ [self setContentSize:size keepCentered:YES];
+ }
+}
+
+- (void)setCenteredContentSize:(NSSize)ns
+{
+ NSRect nf = [self frame];
+ NSRect vf = [[self screen] visibleFrame];
+ NSRect cb = [[self contentView] bounds];
+ int title_height = nf.size.height - cb.size.height;
+ double ratio = (double)ns.width / (double)ns.height;
+
+ // clip the new size to the visibleFrame's size if needed
+ if (ns.width > vf.size.width || ns.height + title_height > vf.size.height) {
+ ns = vf.size;
+ ns.height -= title_height; // make space for the title bar
+
+ if (ns.width > ns.height) {
+ ns.height = ((double)ns.width * 1/ratio + 0.5);
+ } else {
+ ns.width = ((double)ns.height * ratio + 0.5);
+ }
+ }
+
+ int dw = nf.size.width - ns.width;
+ int dh = nf.size.height - ns.height - title_height;
+
+ nf.origin.x += dw / 2;
+ nf.origin.y += dh / 2;
+
+ NSRect new_frame =
+ NSMakeRect(nf.origin.x, nf.origin.y, ns.width, ns.height + title_height);
+ [self setFrame:new_frame display:YES animate:NO];
+}
+
+- (void)setContentSize:(NSSize)ns keepCentered:(BOOL)keepCentered
+{
+ if (keepCentered) {
+ [self setCenteredContentSize:ns];
+ } else {
+ [self setContentSize:ns];
+ }
+}
+@end
+
+@implementation GLMPlayerOpenGLView
+- (void)drawRect: (NSRect)rect
+{
+ [[NSColor clearColor] set];
+ NSRectFill([self bounds]);
+}
+@end
diff --git a/video/out/d3d_shader_yuv.h b/video/out/d3d_shader_yuv.h
new file mode 100644
index 0000000000..49ef753b4c
--- /dev/null
+++ b/video/out/d3d_shader_yuv.h
@@ -0,0 +1,142 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+//
+// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv.h
+// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv
+//
+//
+// Parameters:
+//
+// float4x4 colormatrix;
+// sampler2D tex0;
+// sampler2D tex1;
+// sampler2D tex2;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// colormatrix c0 4
+// tex0 s0 1
+// tex1 s1 1
+// tex2 s2 1
+//
+
+ ps_2_0
+ def c4, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xy
+ dcl t2.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ texld r0, t0, s0
+ texld r1, t1, s1
+ texld r2, t2, s2
+ mov r0.y, r1.x
+ mov r0.z, r2.x
+ mov r0.w, c4.x
+ dp4 r1.x, r0, c0
+ dp4 r1.y, r0, c1
+ dp4 r1.z, r0, c2
+ dp4 r1.w, r0, c3
+ mov oC0, r1
+
+// approximately 11 instruction slots used (3 texture, 8 arithmetic)
+#endif
+
+const BYTE d3d_shader_yuv[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 67, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 215, 0,
+ 0, 0, 0, 2, 255, 255,
+ 4, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 208, 0, 0, 0, 108, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 2, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 2, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 160, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 6, 0, 168, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 3, 0, 2, 0,
+ 1, 0, 10, 0, 192, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 108, 111, 114, 109,
+ 97, 116, 114, 105, 120, 0,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 48, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 49, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 50, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 55, 46, 57, 53, 50,
+ 46, 51, 48, 50, 50, 0,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 2, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 2, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 176, 2, 8, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 2, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 0, 160,
+ 9, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 9, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 228, 128, 1, 0,
+ 228, 160, 9, 0, 0, 3,
+ 1, 0, 4, 128, 0, 0,
+ 228, 128, 2, 0, 228, 160,
+ 9, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 228, 128,
+ 3, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 1, 0, 228, 128, 255, 255,
+ 0, 0
+};
diff --git a/video/out/d3d_shader_yuv.hlsl b/video/out/d3d_shader_yuv.hlsl
new file mode 100644
index 0000000000..b17e257210
--- /dev/null
+++ b/video/out/d3d_shader_yuv.hlsl
@@ -0,0 +1,44 @@
+// Compile with:
+// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv
+// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv_2ch.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv_2ch /DUSE_2CH=1
+
+// Be careful with this shader. You can't use constant slots, since we don't
+// load the shader with D3DX. All uniform variables are mapped to hardcoded
+// constant slots.
+
+sampler2D tex0 : register(s0);
+sampler2D tex1 : register(s1);
+sampler2D tex2 : register(s2);
+
+uniform float4x4 colormatrix : register(c0);
+uniform float2 depth : register(c5);
+
+#ifdef USE_2CH
+
+float1 sample(sampler2D tex, float2 t)
+{
+ // Sample from A8L8 format as if we sampled a single value from L16.
+ // We compute the 2 channel values back into one.
+ return dot(tex2D(tex, t).xw, depth);
+}
+
+#else
+
+float1 sample(sampler2D tex, float2 t)
+{
+ return tex2D(tex, t).x;
+}
+
+#endif
+
+float4 main(float2 t0 : TEXCOORD0,
+ float2 t1 : TEXCOORD1,
+ float2 t2 : TEXCOORD2)
+ : COLOR
+{
+ float4 c = float4(sample(tex0, t0),
+ sample(tex1, t1),
+ sample(tex2, t2),
+ 1);
+ return mul(c, colormatrix);
+}
diff --git a/video/out/d3d_shader_yuv_2ch.h b/video/out/d3d_shader_yuv_2ch.h
new file mode 100644
index 0000000000..45dcc73992
--- /dev/null
+++ b/video/out/d3d_shader_yuv_2ch.h
@@ -0,0 +1,170 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+//
+// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv_2ch.h
+// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv_2ch
+// /DUSE_2CH=1
+//
+//
+// Parameters:
+//
+// float4x4 colormatrix;
+// float2 depth;
+// sampler2D tex0;
+// sampler2D tex1;
+// sampler2D tex2;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// colormatrix c0 4
+// depth c5 1
+// tex0 s0 1
+// tex1 s1 1
+// tex2 s2 1
+//
+
+ ps_2_0
+ def c4, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xy
+ dcl t2.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ texld r0, t0, s0
+ texld r1, t1, s1
+ texld r2, t2, s2
+ mul r0.x, r0.x, c5.x
+ mad r0.x, r0.w, c5.y, r0.x
+ mul r1.x, r1.x, c5.x
+ mad r0.y, r1.w, c5.y, r1.x
+ mul r1.x, r2.x, c5.x
+ mad r0.z, r2.w, c5.y, r1.x
+ mov r0.w, c4.x
+ dp4 r1.x, r0, c0
+ dp4 r1.y, r0, c1
+ dp4 r1.z, r0, c2
+ dp4 r1.w, r0, c3
+ mov oC0, r1
+
+// approximately 15 instruction slots used (3 texture, 12 arithmetic)
+#endif
+
+const BYTE d3d_shader_yuv_2ch[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 78, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 3, 1,
+ 0, 0, 0, 2, 255, 255,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 252, 0, 0, 0, 128, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 2, 0, 140, 0,
+ 0, 0, 0, 0, 0, 0,
+ 156, 0, 0, 0, 2, 0,
+ 5, 0, 1, 0, 22, 0,
+ 164, 0, 0, 0, 0, 0,
+ 0, 0, 180, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 2, 0, 188, 0, 0, 0,
+ 0, 0, 0, 0, 204, 0,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 6, 0, 212, 0,
+ 0, 0, 0, 0, 0, 0,
+ 228, 0, 0, 0, 3, 0,
+ 2, 0, 1, 0, 10, 0,
+ 236, 0, 0, 0, 0, 0,
+ 0, 0, 99, 111, 108, 111,
+ 114, 109, 97, 116, 114, 105,
+ 120, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 101, 112, 116, 104, 0,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 48, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 49, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 50, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 57, 46, 50, 55, 46, 57,
+ 53, 50, 46, 51, 48, 50,
+ 50, 0, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 2, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 176, 2, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 5, 0, 0, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 255, 128,
+ 5, 0, 85, 160, 0, 0,
+ 0, 128, 5, 0, 0, 3,
+ 1, 0, 1, 128, 1, 0,
+ 0, 128, 5, 0, 0, 160,
+ 4, 0, 0, 4, 0, 0,
+ 2, 128, 1, 0, 255, 128,
+ 5, 0, 85, 160, 1, 0,
+ 0, 128, 5, 0, 0, 3,
+ 1, 0, 1, 128, 2, 0,
+ 0, 128, 5, 0, 0, 160,
+ 4, 0, 0, 4, 0, 0,
+ 4, 128, 2, 0, 255, 128,
+ 5, 0, 85, 160, 1, 0,
+ 0, 128, 1, 0, 0, 2,
+ 0, 0, 8, 128, 4, 0,
+ 0, 160, 9, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 9, 0, 0, 3, 1, 0,
+ 2, 128, 0, 0, 228, 128,
+ 1, 0, 228, 160, 9, 0,
+ 0, 3, 1, 0, 4, 128,
+ 0, 0, 228, 128, 2, 0,
+ 228, 160, 9, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 228, 128, 3, 0, 228, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 1, 0, 228, 128,
+ 255, 255, 0, 0
+};
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c
new file mode 100644
index 0000000000..2c2f56ee51
--- /dev/null
+++ b/video/out/filter_kernels.c
@@ -0,0 +1,279 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * Most code for computing the weights is taken from Anti-Grain Geometry (AGG)
+ * (licensed under GPL 2 or later), with modifications.
+ * Copyright (C) 2002-2006 Maxim Shemanarev
+ * http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h?view=markup
+ *
+ * Also see glumpy (BSD licensed), contains the same code in Python:
+ * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py
+ *
+ * Also see: Paul Heckbert's "zoom"
+ *
+ * Also see XBMC: ConvolutionKernels.cpp etc.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "filter_kernels.h"
+
+// NOTE: all filters are separable, symmetric, and are intended for use with
+// a lookup table/texture.
+
+const struct filter_kernel *mp_find_filter_kernel(const char *name)
+{
+ for (const struct filter_kernel *k = mp_filter_kernels; k->name; k++) {
+ if (strcmp(k->name, name) == 0)
+ return k;
+ }
+ return NULL;
+}
+
+// sizes = sorted list of available filter sizes, terminated with size 0
+// inv_scale = source_size / dest_size
+bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
+ double inv_scale)
+{
+ // only downscaling requires widening the filter
+ filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
+ double support = filter->radius * filter->inv_scale;
+ int size = ceil(2.0 * support);
+ // round up to smallest available size that's still large enough
+ if (size < sizes[0])
+ size = sizes[0];
+ const int *cursize = sizes;
+ while (size > *cursize && *cursize)
+ cursize++;
+ if (*cursize) {
+ filter->size = *cursize;
+ return true;
+ } else {
+ // The filter doesn't fit - instead of failing completely, use the
+ // largest filter available. This is incorrect, but better than refusing
+ // to do anything.
+ filter->size = cursize[-1];
+ filter->inv_scale = filter->size / 2.0 / filter->radius;
+ return false;
+ }
+}
+
+// Calculate the 1D filtering kernel for N sample points.
+// N = number of samples, which is filter->size
+// The weights will be stored in out_w[0] to out_w[N - 1]
+// f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1].
+void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
+{
+ assert(filter->size > 0);
+ double sum = 0;
+ for (int n = 0; n < filter->size; n++) {
+ double x = f - (n - filter->size / 2 + 1);
+ double w = filter->weight(filter, fabs(x) / filter->inv_scale);
+ out_w[n] = w;
+ sum += w;
+ }
+ //normalize
+ for (int n = 0; n < filter->size; n++)
+ out_w[n] /= sum;
+}
+
+// Fill the given array with weights for the range [0.0, 1.0]. The array is
+// interpreted as rectangular array of count * filter->size items.
+void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array)
+{
+ for (int n = 0; n < count; n++) {
+ mp_compute_weights(filter, n / (double)(count - 1),
+ out_array + filter->size * n);
+ }
+}
+
+typedef struct filter_kernel kernel;
+
+static double bilinear(kernel *k, double x)
+{
+ return 1.0 - x;
+}
+
+static double hanning(kernel *k, double x)
+{
+ return 0.5 + 0.5 * cos(M_PI * x);
+}
+
+static double hamming(kernel *k, double x)
+{
+ return 0.54 + 0.46 * cos(M_PI * x);
+}
+
+static double hermite(kernel *k, double x)
+{
+ return (2.0 * x - 3.0) * x * x + 1.0;
+}
+
+static double quadric(kernel *k, double x)
+{
+ // NOTE: glumpy uses 0.75, AGG uses 0.5
+ if (x < 0.5)
+ return 0.75 - x * x;
+ if (x < 1.5)
+ return 0.5 * (x - 1.5) * (x - 1.5);
+ return 0;
+}
+
+static double bc_pow3(double x)
+{
+ return (x <= 0) ? 0 : x * x * x;
+}
+
+static double bicubic(kernel *k, double x)
+{
+ return (1.0/6.0) * ( bc_pow3(x + 2)
+ - 4 * bc_pow3(x + 1)
+ + 6 * bc_pow3(x)
+ - 4 * bc_pow3(x - 1));
+}
+
+static double bessel_i0(double epsilon, double x)
+{
+ double sum = 1;
+ double y = x * x / 4;
+ double t = y;
+ for (int i = 2; t > epsilon; i++) {
+ sum += t;
+ t *= y / (i * i);
+ }
+ return sum;
+}
+
+static double kaiser(kernel *k, double x)
+{
+ double a = k->params[0];
+ double b = k->params[1];
+ double epsilon = 1e-12;
+ double i0a = 1 / bessel_i0(epsilon, b);
+ return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a;
+}
+
+static double catmull_rom(kernel *k, double x)
+{
+ if (x < 1.0)
+ return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
+ if (x < 2.0)
+ return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
+ return 0;
+}
+
+// Mitchell-Netravali
+static double mitchell(kernel *k, double x)
+{
+ double b = k->params[0];
+ double c = k->params[1];
+ double
+ p0 = (6.0 - 2.0 * b) / 6.0,
+ p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
+ p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0,
+ q0 = (8.0 * b + 24.0 * c) / 6.0,
+ q1 = (-12.0 * b - 48.0 * c) / 6.0,
+ q2 = (6.0 * b + 30.0 * c) / 6.0,
+ q3 = (-b - 6.0 * c) / 6.0;
+ if (x < 1.0)
+ return p0 + x * x * (p2 + x * p3);
+ if (x < 2.0)
+ return q0 + x * (q1 + x * (q2 + x * q3));
+ return 0;
+}
+
+static double spline16(kernel *k, double x)
+{
+ if (x < 1.0)
+ return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0;
+ return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1);
+}
+
+static double spline36(kernel *k, double x)
+{
+ if(x < 1.0)
+ return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0;
+ if(x < 2.0)
+ return ((-6.0/11.0 * (x - 1) + 270.0/209.0) * (x - 1) - 156.0/209.0)
+ * (x - 1);
+ return ((1.0/11.0 * (x - 2) - 45.0/209.0) * (x - 2) + 26.0/209.0)
+ * (x - 2);
+}
+
+static double gaussian(kernel *k, double x)
+{
+ return exp(-2.0 * x * x) * sqrt(2.0 / M_PI);
+}
+
+static double sinc(kernel *k, double x)
+{
+ if (x == 0.0)
+ return 1.0;
+ double pix = M_PI * x;
+ return sin(pix) / pix;
+}
+
+static double lanczos(kernel *k, double x)
+{
+ double radius = k->size / 2;
+ if (x < -radius || x > radius)
+ return 0;
+ if (x == 0)
+ return 1;
+ double pix = M_PI * x;
+ return radius * sin(pix) * sin(pix / radius) / (pix * pix);
+}
+
+static double blackman(kernel *k, double x)
+{
+ double radius = k->size / 2;
+ if (x == 0.0)
+ return 1.0;
+ if (x > radius)
+ return 0.0;
+ x *= M_PI;
+ double xr = x / radius;
+ return (sin(x) / x) * (0.42 + 0.5 * cos(xr) + 0.08 * cos(2 * xr));
+}
+
+const struct filter_kernel mp_filter_kernels[] = {
+ {"bilinear_slow", 1, bilinear},
+ {"hanning", 1, hanning},
+ {"hamming", 1, hamming},
+ {"hermite", 1, hermite},
+ {"quadric", 1.5, quadric},
+ {"bicubic", 2, bicubic},
+ {"kaiser", 1, kaiser, .params = {6.33, 6.33} },
+ {"catmull_rom", 2, catmull_rom},
+ {"mitchell", 2, mitchell, .params = {1.0/3.0, 1.0/3.0} },
+ {"spline16", 2, spline16},
+ {"spline36", 3, spline36},
+ {"gaussian", 2, gaussian},
+ {"sinc2", 2, sinc},
+ {"sinc3", 3, sinc},
+ {"sinc4", 4, sinc},
+ {"lanczos2", 2, lanczos},
+ {"lanczos3", 3, lanczos},
+ {"lanczos4", 4, lanczos},
+ {"blackman2", 2, blackman},
+ {"blackman3", 3, blackman},
+ {"blackman4", 4, blackman},
+ {0}
+};
diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h
new file mode 100644
index 0000000000..46a392c40a
--- /dev/null
+++ b/video/out/filter_kernels.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_FILTER_KERNELS_H
+#define MPLAYER_FILTER_KERNELS_H
+
+#include <stdbool.h>
+
+struct filter_kernel {
+ const char *name;
+ double radius;
+ double (*weight)(struct filter_kernel *kernel, double x);
+
+ // The filter params can be changed at runtime. Only used by some filters.
+ float params[2];
+ // The following values are set by mp_init_filter() at runtime.
+ // Number of coefficients; equals the rounded up radius multiplied with 2.
+ int size;
+ double inv_scale;
+};
+
+extern const struct filter_kernel mp_filter_kernels[];
+
+const struct filter_kernel *mp_find_filter_kernel(const char *name);
+bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
+ double scale);
+void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w);
+void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array);
+
+#endif /* MPLAYER_FILTER_KERNELS_H */
diff --git a/video/out/geometry.c b/video/out/geometry.c
new file mode 100644
index 0000000000..941528aea9
--- /dev/null
+++ b/video/out/geometry.c
@@ -0,0 +1,112 @@
+/*
+ * copyright (C) 2002 Mark Zealey <mark@zealos.org>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include "geometry.h"
+#include "mp_msg.h"
+
+/* A string of the form [WxH][+X+Y] or xpos[%]:ypos[%] */
+char *vo_geometry;
+// set when either width or height is changed
+int geometry_wh_changed;
+int geometry_xy_changed;
+
+// xpos,ypos: position of the left upper corner
+// widw,widh: width and height of the window
+// scrw,scrh: width and height of the current screen
+int geometry(int *xpos, int *ypos, int *widw, int *widh, int scrw, int scrh)
+{
+ if(vo_geometry != NULL) {
+ char xsign[2], ysign[2], dummy[2];
+ int width, height, xoff, yoff, xper, yper;
+ int i;
+ int ok = 0;
+ for (i = 0; !ok && i < 9; i++) {
+ width = height = xoff = yoff = xper = yper = INT_MIN;
+ strcpy(xsign, "+");
+ strcpy(ysign, "+");
+ switch (i) {
+ case 0:
+ ok = sscanf(vo_geometry, "%ix%i%1[+-]%i%1[+-]%i%c",
+ &width, &height, xsign, &xoff, ysign,
+ &yoff, dummy) == 6;
+ break;
+ case 1:
+ ok = sscanf(vo_geometry, "%ix%i%c", &width, &height, dummy) == 2;
+ break;
+ case 2:
+ ok = sscanf(vo_geometry, "%1[+-]%i%1[+-]%i%c",
+ xsign, &xoff, ysign, &yoff, dummy) == 4;
+ break;
+ case 3:
+ ok = sscanf(vo_geometry, "%i%%:%i%1[%]%c", &xper, &yper, dummy, dummy) == 3;
+ break;
+ case 4:
+ ok = sscanf(vo_geometry, "%i:%i%1[%]%c", &xoff, &yper, dummy, dummy) == 3;
+ break;
+ case 5:
+ ok = sscanf(vo_geometry, "%i%%:%i%c", &xper, &yoff, dummy) == 2;
+ break;
+ case 6:
+ ok = sscanf(vo_geometry, "%i:%i%c", &xoff, &yoff, dummy) == 2;
+ break;
+ case 7:
+ ok = sscanf(vo_geometry, "%i%1[%]%c", &xper, dummy, dummy) == 2;
+ break;
+ case 8:
+ ok = sscanf(vo_geometry, "%i%c", &xoff, dummy) == 1;
+ break;
+ }
+ }
+ if (!ok) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "-geometry must be in [WxH][[+-]X[+-]Y] | [X[%%]:[Y[%%]]] format, incorrect (%s)\n", vo_geometry);
+ return 0;
+ }
+
+ mp_msg(MSGT_VO, MSGL_V,"geometry window parameter: widw: %i,"
+ " widh: %i, scrw: %i, scrh: %i\n",*widw, *widh, scrw, scrh);
+
+ mp_msg(MSGT_VO, MSGL_V,"geometry set to width: %i,"
+ "height: %i, xoff: %s%i, yoff: %s%i, xper: %i, yper: %i\n",
+ width, height, xsign, xoff, ysign, yoff, xper, yper);
+
+ if (width > 0) *widw = width;
+ if (height > 0) *widh = height;
+
+ if(xoff != INT_MIN && xsign[0] == '-') xoff = scrw - *widw - xoff;
+ if(yoff != INT_MIN && ysign[0] == '-') yoff = scrh - *widh - yoff;
+ if(xper >= 0 && xper <= 100) xoff = (scrw - *widw) * ((float)xper / 100.0);
+ if(yper >= 0 && yper <= 100) yoff = (scrh - *widh) * ((float)yper / 100.0);
+
+ mp_msg(MSGT_VO, MSGL_V,"geometry set to width: %i,"
+ "height: %i, xoff: %i, yoff: %i, xper: %i, yper: %i\n",
+ width, height, xoff, yoff, xper, yper);
+
+ if (xoff != INT_MIN && xpos) *xpos = xoff;
+ if (yoff != INT_MIN && ypos) *ypos = yoff;
+
+ geometry_wh_changed = width > 0 || height > 0;
+ geometry_xy_changed = xoff != INT_MIN || yoff != INT_MIN;
+ }
+ return 1;
+}
diff --git a/video/out/geometry.h b/video/out/geometry.h
new file mode 100644
index 0000000000..77868a5aad
--- /dev/null
+++ b/video/out/geometry.h
@@ -0,0 +1,29 @@
+/*
+ * copyright (C) 2002 Mark Zealey <mark@zealos.org>
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_GEOMETRY_H
+#define MPLAYER_GEOMETRY_H
+
+extern char *vo_geometry;
+extern int geometry_wh_changed;
+extern int geometry_xy_changed;
+int geometry(int *xpos, int *ypos, int *widw, int *widh, int scrw, int scrh);
+
+#endif /* MPLAYER_GEOMETRY_H */
diff --git a/video/out/gl_common.c b/video/out/gl_common.c
new file mode 100644
index 0000000000..80db2eacc4
--- /dev/null
+++ b/video/out/gl_common.c
@@ -0,0 +1,2654 @@
+/*
+ * common OpenGL routines
+ *
+ * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
+ * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
+ * gave me lots of good ideas.
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * You can alternatively redistribute this file and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file gl_common.c
+ * \brief OpenGL helper functions used by vo_gl.c and vo_gl2.c
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+#include "talloc.h"
+#include "gl_common.h"
+#include "csputils.h"
+#include "aspect.h"
+#include "pnm_loader.h"
+#include "options.h"
+#include "sub/sub.h"
+#include "bitmap_packer.h"
+
+//! \defgroup glgeneral OpenGL general helper functions
+
+// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
+static const char *gl_error_to_string(GLenum error)
+{
+ switch (error) {
+ case GL_INVALID_ENUM: return "INVALID_ENUM";
+ case GL_INVALID_VALUE: return "INVALID_VALUE";
+ case GL_INVALID_OPERATION: return "INVALID_OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ return "INVALID_FRAMEBUFFER_OPERATION";
+ case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
+ default: return "unknown";
+ }
+}
+
+void glCheckError(GL *gl, const char *info)
+{
+ for (;;) {
+ GLenum error = gl->GetError();
+ if (error == GL_NO_ERROR)
+ break;
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] %s: OpenGL error %s.\n", info,
+ gl_error_to_string(error));
+ }
+}
+
+//! \defgroup glcontext OpenGL context management helper functions
+
+//! \defgroup gltexture OpenGL texture handling helper functions
+
+//! \defgroup glconversion OpenGL conversion helper functions
+
+/**
+ * \brief adjusts the GL_UNPACK_ALIGNMENT to fit the stride.
+ * \param stride number of bytes per line for which alignment should fit.
+ * \ingroup glgeneral
+ */
+void glAdjustAlignment(GL *gl, int stride)
+{
+ GLint gl_alignment;
+ if (stride % 8 == 0)
+ gl_alignment = 8;
+ else if (stride % 4 == 0)
+ gl_alignment = 4;
+ else if (stride % 2 == 0)
+ gl_alignment = 2;
+ else
+ gl_alignment = 1;
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, gl_alignment);
+}
+
+//! always return this format as internal texture format in glFindFormat
+#define TEXTUREFORMAT_ALWAYS GL_RGB8
+#undef TEXTUREFORMAT_ALWAYS
+
+/**
+ * \brief find the OpenGL settings coresponding to format.
+ *
+ * All parameters may be NULL.
+ * \param fmt MPlayer format to analyze.
+ * \param bpp [OUT] bits per pixel of that format.
+ * \param gl_texfmt [OUT] internal texture format that fits the
+ * image format, not necessarily the best for performance.
+ * \param gl_format [OUT] OpenGL format for this image format.
+ * \param gl_type [OUT] OpenGL type for this image format.
+ * \return 1 if format is supported by OpenGL, 0 if not.
+ * \ingroup gltexture
+ */
+int glFindFormat(uint32_t fmt, int have_texture_rg, int *bpp, GLint *gl_texfmt,
+ GLenum *gl_format, GLenum *gl_type)
+{
+ int supported = 1;
+ int dummy1;
+ GLenum dummy2;
+ GLint dummy3;
+ if (!bpp)
+ bpp = &dummy1;
+ if (!gl_texfmt)
+ gl_texfmt = &dummy3;
+ if (!gl_format)
+ gl_format = &dummy2;
+ if (!gl_type)
+ gl_type = &dummy2;
+
+ if (mp_get_chroma_shift(fmt, NULL, NULL, NULL)) {
+ // reduce the possible cases a bit
+ if (IMGFMT_IS_YUVP16_LE(fmt))
+ fmt = IMGFMT_420P16_LE;
+ else if (IMGFMT_IS_YUVP16_BE(fmt))
+ fmt = IMGFMT_420P16_BE;
+ else
+ fmt = IMGFMT_YV12;
+ }
+
+ *bpp = IMGFMT_IS_BGR(fmt) ? IMGFMT_BGR_DEPTH(fmt) : IMGFMT_RGB_DEPTH(fmt);
+ *gl_texfmt = 3;
+ switch (fmt) {
+ case IMGFMT_RGB48NE:
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_SHORT;
+ break;
+ case IMGFMT_RGB24:
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_BYTE;
+ break;
+ case IMGFMT_RGBA:
+ *gl_texfmt = 4;
+ *gl_format = GL_RGBA;
+ *gl_type = GL_UNSIGNED_BYTE;
+ break;
+ case IMGFMT_420P16:
+ supported = 0; // no native YUV support
+ *gl_texfmt = have_texture_rg ? GL_R16 : GL_LUMINANCE16;
+ *bpp = 16;
+ *gl_format = have_texture_rg ? GL_RED : GL_LUMINANCE;
+ *gl_type = GL_UNSIGNED_SHORT;
+ break;
+ case IMGFMT_YV12:
+ supported = 0; // no native YV12 support
+ case IMGFMT_Y800:
+ case IMGFMT_Y8:
+ *gl_texfmt = 1;
+ *bpp = 8;
+ *gl_format = GL_LUMINANCE;
+ *gl_type = GL_UNSIGNED_BYTE;
+ break;
+ case IMGFMT_UYVY:
+ // IMGFMT_YUY2 would be more logical for the _REV format,
+ // but gives clearly swapped colors.
+ case IMGFMT_YVYU:
+ *gl_texfmt = GL_YCBCR_MESA;
+ *bpp = 16;
+ *gl_format = GL_YCBCR_MESA;
+ *gl_type = fmt == IMGFMT_UYVY ? GL_UNSIGNED_SHORT_8_8 : GL_UNSIGNED_SHORT_8_8_REV;
+ break;
+#if 0
+ // we do not support palettized formats, although the format the
+ // swscale produces works
+ case IMGFMT_RGB8:
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_BYTE_2_3_3_REV;
+ break;
+#endif
+ case IMGFMT_RGB15:
+ *gl_format = GL_RGBA;
+ *gl_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ break;
+ case IMGFMT_RGB16:
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_SHORT_5_6_5_REV;
+ break;
+#if 0
+ case IMGFMT_BGR8:
+ // special case as red and blue have a different number of bits.
+ // GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least
+ // by nVidia drivers, and in addition would give more bits to
+ // blue than to red, which isn't wanted
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_BYTE_3_3_2;
+ break;
+#endif
+ case IMGFMT_BGR15:
+ *gl_format = GL_BGRA;
+ *gl_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ break;
+ case IMGFMT_BGR16:
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case IMGFMT_BGR24:
+ *gl_format = GL_BGR;
+ *gl_type = GL_UNSIGNED_BYTE;
+ break;
+ case IMGFMT_BGRA:
+ *gl_texfmt = 4;
+ *gl_format = GL_BGRA;
+ *gl_type = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ *gl_texfmt = 4;
+ *gl_format = GL_RGBA;
+ *gl_type = GL_UNSIGNED_BYTE;
+ supported = 0;
+ }
+#ifdef TEXTUREFORMAT_ALWAYS
+ *gl_texfmt = TEXTUREFORMAT_ALWAYS;
+#endif
+ return supported;
+}
+
+struct feature {
+ int id;
+ const char *name;
+};
+
+static const struct feature features[] = {
+ {MPGL_CAP_GL, "Basic OpenGL"},
+ {MPGL_CAP_GL_LEGACY, "Legacy OpenGL"},
+ {MPGL_CAP_GL2, "OpenGL 2.0"},
+ {MPGL_CAP_GL21, "OpenGL 2.1"},
+ {MPGL_CAP_GL3, "OpenGL 3.0"},
+ {MPGL_CAP_FB, "Framebuffers"},
+ {MPGL_CAP_VAO, "VAOs"},
+ {MPGL_CAP_SRGB_TEX, "sRGB textures"},
+ {MPGL_CAP_SRGB_FB, "sRGB framebuffers"},
+ {MPGL_CAP_FLOAT_TEX, "Float textures"},
+ {MPGL_CAP_TEX_RG, "RG textures"},
+ {MPGL_CAP_NO_SW, "NO_SW"},
+ {0},
+};
+
+static void list_features(int set, int msgl, bool invert)
+{
+ for (const struct feature *f = &features[0]; f->id; f++) {
+ if (invert == !(f->id & set))
+ mp_msg(MSGT_VO, msgl, " [%s]", f->name);
+ }
+ mp_msg(MSGT_VO, msgl, "\n");
+}
+
+// This guesses if the current GL context is a suspected software renderer.
+static bool is_software_gl(GL *gl)
+{
+ const char *renderer = gl->GetString(GL_RENDERER);
+ const char *vendor = gl->GetString(GL_VENDOR);
+ return !(renderer && vendor) ||
+ strcmp(renderer, "Software Rasterizer") == 0 ||
+ strstr(renderer, "llvmpipe") ||
+ strcmp(vendor, "Microsoft Corporation") == 0 ||
+ strcmp(renderer, "Mesa X11") == 0;
+}
+
+#ifdef HAVE_LIBDL
+#include <dlfcn.h>
+#endif
+/**
+ * \brief find address of a linked function
+ * \param s name of function to find
+ * \return address of function or NULL if not found
+ */
+static void *getdladdr(const char *s)
+{
+ void *ret = NULL;
+#ifdef HAVE_LIBDL
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (!handle)
+ return NULL;
+ ret = dlsym(handle, s);
+ dlclose(handle);
+#endif
+ return ret;
+}
+
+#define FN_OFFS(name) offsetof(GL, name)
+
+// Define the function with a "hard" reference to the function as fallback.
+// (This requires linking with a compatible OpenGL library.)
+#define DEF_FN_HARD(name) {FN_OFFS(name), {"gl" # name}, gl ## name}
+
+#define DEF_FN(name) {FN_OFFS(name), {"gl" # name}}
+#define DEF_FN_NAMES(name, ...) {FN_OFFS(name), {__VA_ARGS__}}
+
+struct gl_function {
+ ptrdiff_t offset;
+ char *funcnames[7];
+ void *fallback;
+};
+
+struct gl_functions {
+ const char *extension; // introduced with this extension in any version
+ int provides; // bitfield of MPGL_CAP_* constants
+ int ver_core; // introduced as required function
+ int ver_removed; // removed as required function (no replacement)
+ bool partial_ok; // loading only some functions is ok
+ struct gl_function *functions;
+};
+
+#define MAX_FN_COUNT 50 // max functions per gl_functions section
+
+struct gl_functions gl_functions[] = {
+ // GL functions which are always available anywhere at least since 1.1
+ {
+ .ver_core = MPGL_VER(1, 1),
+ .provides = MPGL_CAP_GL,
+ .functions = (struct gl_function[]) {
+ DEF_FN_HARD(Viewport),
+ DEF_FN_HARD(Clear),
+ DEF_FN_HARD(GenTextures),
+ DEF_FN_HARD(DeleteTextures),
+ DEF_FN_HARD(TexEnvi),
+ DEF_FN_HARD(ClearColor),
+ DEF_FN_HARD(Enable),
+ DEF_FN_HARD(Disable),
+ DEF_FN_HARD(DrawBuffer),
+ DEF_FN_HARD(DepthMask),
+ DEF_FN_HARD(BlendFunc),
+ DEF_FN_HARD(Flush),
+ DEF_FN_HARD(Finish),
+ DEF_FN_HARD(PixelStorei),
+ DEF_FN_HARD(TexImage1D),
+ DEF_FN_HARD(TexImage2D),
+ DEF_FN_HARD(TexSubImage2D),
+ DEF_FN_HARD(GetTexImage),
+ DEF_FN_HARD(TexParameteri),
+ DEF_FN_HARD(TexParameterf),
+ DEF_FN_HARD(TexParameterfv),
+ DEF_FN_HARD(GetIntegerv),
+ DEF_FN_HARD(GetBooleanv),
+ DEF_FN_HARD(ColorMask),
+ DEF_FN_HARD(ReadPixels),
+ DEF_FN_HARD(ReadBuffer),
+ DEF_FN_HARD(DrawArrays),
+ DEF_FN_HARD(GetString),
+ DEF_FN_HARD(GetError),
+ {0}
+ },
+ },
+ // GL 2.0-3.x functions
+ {
+ .ver_core = MPGL_VER(2, 0),
+ .provides = MPGL_CAP_GL2,
+ .functions = (struct gl_function[]) {
+ DEF_FN(GenBuffers),
+ DEF_FN(DeleteBuffers),
+ DEF_FN(BindBuffer),
+ DEF_FN(MapBuffer),
+ DEF_FN(UnmapBuffer),
+ DEF_FN(BufferData),
+ DEF_FN(ActiveTexture),
+ DEF_FN(BindTexture),
+ DEF_FN(GetAttribLocation),
+ DEF_FN(EnableVertexAttribArray),
+ DEF_FN(DisableVertexAttribArray),
+ DEF_FN(VertexAttribPointer),
+ DEF_FN(UseProgram),
+ DEF_FN(GetUniformLocation),
+ DEF_FN(CompileShader),
+ DEF_FN(CreateProgram),
+ DEF_FN(CreateShader),
+ DEF_FN(ShaderSource),
+ DEF_FN(LinkProgram),
+ DEF_FN(AttachShader),
+ DEF_FN(DeleteShader),
+ DEF_FN(DeleteProgram),
+ DEF_FN(GetShaderInfoLog),
+ DEF_FN(GetShaderiv),
+ DEF_FN(GetProgramInfoLog),
+ DEF_FN(GetProgramiv),
+ DEF_FN(BindAttribLocation),
+ DEF_FN(Uniform1f),
+ DEF_FN(Uniform2f),
+ DEF_FN(Uniform3f),
+ DEF_FN(Uniform1i),
+ DEF_FN(UniformMatrix3fv),
+ DEF_FN(TexImage3D),
+ {0},
+ },
+ },
+ // GL 2.1-3.x functions (also: GLSL 120 shaders)
+ {
+ .ver_core = MPGL_VER(2, 1),
+ .provides = MPGL_CAP_GL21,
+ .functions = (struct gl_function[]) {
+ DEF_FN(UniformMatrix4x3fv),
+ {0}
+ },
+ },
+ // GL 3.x core only functions.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .provides = MPGL_CAP_GL3 | MPGL_CAP_SRGB_TEX | MPGL_CAP_SRGB_FB,
+ .functions = (struct gl_function[]) {
+ DEF_FN(GetStringi),
+ {0}
+ },
+ },
+ // Framebuffers, extension in GL 2.x, core in GL 3.x core.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .extension = "GL_ARB_framebuffer_object",
+ .provides = MPGL_CAP_FB,
+ .functions = (struct gl_function[]) {
+ DEF_FN(BindFramebuffer),
+ DEF_FN(GenFramebuffers),
+ DEF_FN(DeleteFramebuffers),
+ DEF_FN(CheckFramebufferStatus),
+ DEF_FN(FramebufferTexture2D),
+ {0}
+ },
+ },
+ // Framebuffers, alternative extension name.
+ {
+ .ver_removed = MPGL_VER(3, 0), // don't touch these fn names in 3.x
+ .extension = "GL_EXT_framebuffer_object",
+ .provides = MPGL_CAP_FB,
+ .functions = (struct gl_function[]) {
+ DEF_FN_NAMES(BindFramebuffer, "glBindFramebufferEXT"),
+ DEF_FN_NAMES(GenFramebuffers, "glGenFramebuffersEXT"),
+ DEF_FN_NAMES(DeleteFramebuffers, "glDeleteFramebuffersEXT"),
+ DEF_FN_NAMES(CheckFramebufferStatus, "glCheckFramebufferStatusEXT"),
+ DEF_FN_NAMES(FramebufferTexture2D, "glFramebufferTexture2DEXT"),
+ {0}
+ },
+ },
+ // VAOs, extension in GL 2.x, core in GL 3.x core.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .extension = "GL_ARB_vertex_array_object",
+ .provides = MPGL_CAP_VAO,
+ .functions = (struct gl_function[]) {
+ DEF_FN(GenVertexArrays),
+ DEF_FN(BindVertexArray),
+ DEF_FN(DeleteVertexArrays),
+ {0}
+ }
+ },
+ // sRGB textures, extension in GL 2.x, core in GL 3.x core.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .extension = "GL_EXT_texture_sRGB",
+ .provides = MPGL_CAP_SRGB_TEX,
+ .functions = (struct gl_function[]) {{0}},
+ },
+ // sRGB framebuffers, extension in GL 2.x, core in GL 3.x core.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .extension = "GL_EXT_framebuffer_sRGB",
+ .provides = MPGL_CAP_SRGB_FB,
+ .functions = (struct gl_function[]) {{0}},
+ },
+ // Float textures, extension in GL 2.x, core in GL 3.x core.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .extension = "GL_ARB_texture_float",
+ .provides = MPGL_CAP_FLOAT_TEX,
+ .functions = (struct gl_function[]) {{0}},
+ },
+ // GL_RED / GL_RG textures, extension in GL 2.x, core in GL 3.x core.
+ {
+ .ver_core = MPGL_VER(3, 0),
+ .extension = "GL_ARB_texture_rg",
+ .provides = MPGL_CAP_TEX_RG,
+ .functions = (struct gl_function[]) {{0}},
+ },
+ // Swap control, always an OS specific extension
+ {
+ .extension = "_swap_control",
+ .functions = (struct gl_function[]) {
+ DEF_FN_NAMES(SwapInterval, "glXSwapIntervalSGI", "glXSwapInterval",
+ "wglSwapIntervalSGI", "wglSwapInterval",
+ "wglSwapIntervalEXT"),
+ {0}
+ },
+ },
+ // GL legacy functions in GL 1.x - 2.x, removed from GL 3.x
+ {
+ .ver_core = MPGL_VER(1, 1),
+ .ver_removed = MPGL_VER(3, 0),
+ .provides = MPGL_CAP_GL_LEGACY,
+ .functions = (struct gl_function[]) {
+ DEF_FN_HARD(Begin),
+ DEF_FN_HARD(End),
+ DEF_FN_HARD(MatrixMode),
+ DEF_FN_HARD(LoadIdentity),
+ DEF_FN_HARD(Translated),
+ DEF_FN_HARD(Scaled),
+ DEF_FN_HARD(Ortho),
+ DEF_FN_HARD(PushMatrix),
+ DEF_FN_HARD(PopMatrix),
+ DEF_FN_HARD(GenLists),
+ DEF_FN_HARD(DeleteLists),
+ DEF_FN_HARD(NewList),
+ DEF_FN_HARD(EndList),
+ DEF_FN_HARD(CallList),
+ DEF_FN_HARD(CallLists),
+ DEF_FN_HARD(Color4ub),
+ DEF_FN_HARD(Color4f),
+ DEF_FN_HARD(TexCoord2f),
+ DEF_FN_HARD(TexCoord2fv),
+ DEF_FN_HARD(Vertex2f),
+ DEF_FN_HARD(VertexPointer),
+ DEF_FN_HARD(ColorPointer),
+ DEF_FN_HARD(TexCoordPointer),
+ DEF_FN_HARD(EnableClientState),
+ DEF_FN_HARD(DisableClientState),
+ {0}
+ },
+ },
+ // Loading of old extensions, which are later added to GL 2.0.
+ // NOTE: actually we should be checking the extension strings: the OpenGL
+ // library could provide an entry point, but not implement it.
+ // But the previous code didn't do that, and nobody ever complained.
+ {
+ .ver_removed = MPGL_VER(2, 1),
+ .partial_ok = true,
+ .functions = (struct gl_function[]) {
+ DEF_FN_NAMES(GenBuffers, "glGenBuffers", "glGenBuffersARB"),
+ DEF_FN_NAMES(DeleteBuffers, "glDeleteBuffers", "glDeleteBuffersARB"),
+ DEF_FN_NAMES(BindBuffer, "glBindBuffer", "glBindBufferARB"),
+ DEF_FN_NAMES(MapBuffer, "glMapBuffer", "glMapBufferARB"),
+ DEF_FN_NAMES(UnmapBuffer, "glUnmapBuffer", "glUnmapBufferARB"),
+ DEF_FN_NAMES(BufferData, "glBufferData", "glBufferDataARB"),